mirror of
https://github.com/Bigherollc/wticreatorstudio.git
synced 2026-01-16 19:05:08 -05:00
373 lines
14 KiB
PHP
373 lines
14 KiB
PHP
<?php
|
|
|
|
namespace Modules\StudioPlus\Services;
|
|
|
|
use FFMpeg\FFProbe;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Symfony\Component\Process\Process;
|
|
use Modules\StudioPlus\Entities\StudioPlusVideo;
|
|
use Modules\StudioPlus\Repositories\ContentWatermarksRepository;
|
|
|
|
class WatermarkService
|
|
{
|
|
protected $repository;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->repository = new ContentWatermarksRepository();
|
|
}
|
|
|
|
public function addWatermark($video, $output, $reprocess = false)
|
|
{
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Adding watermarks to video #' . $video->id);
|
|
$mem_usage = memory_get_usage();
|
|
$start_time = microtime(true);
|
|
|
|
if ($reprocess) {
|
|
$temp_path = $this->getTempReprocessStoragePath($video->id);
|
|
}
|
|
else {
|
|
$temp_path = $this->getTempUploadStoragePath($video->id);
|
|
}
|
|
|
|
$video_path = storage_path("app/$temp_path/video.mp4");
|
|
$file_size = Storage::size("$temp_path/video.mp4");
|
|
$resolution = $this->getVideoResolution($video_path);
|
|
$duration = $this->getVideoDuration($video_path);
|
|
|
|
$watermarks = $this->repository->getActive();
|
|
|
|
if ($watermarks->count() > 0) {
|
|
$types = $video->types;
|
|
|
|
$i = 0;
|
|
$applied = false;
|
|
foreach ($watermarks as $watermark) {
|
|
$apply = false;
|
|
|
|
if ($watermark->contentVariant) {
|
|
$variant = $watermark->contentVariant->name;
|
|
|
|
if (strtoupper($variant) == 'BASED ON A JOB') {
|
|
$variant = "Video $variant";
|
|
}
|
|
|
|
if (in_array(strtoupper($variant), $types)) {
|
|
$apply = true;
|
|
}
|
|
}
|
|
else {
|
|
if (count($types) == 0) {
|
|
$apply = true;
|
|
}
|
|
}
|
|
|
|
if ($apply) {
|
|
$video_input = $video_path;
|
|
$video_output = "$temp_path/result.mp4";
|
|
|
|
if ($i > 0) {
|
|
$video_input = str_replace('result', "result-$i", $video_output);
|
|
Storage::delete($video_input);
|
|
Storage::copy($video_output, $video_input);
|
|
Storage::delete($video_output);
|
|
$video_input = storage_path("app/$video_input");
|
|
}
|
|
|
|
//Log::channel('watermarking')->info('===============');
|
|
$isolated_mem_usage = memory_get_usage();
|
|
if ($watermark->type == 'text') {
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Applying text watermark #' . $watermark->id);
|
|
$this->applyTextWatermark($video_input, $watermark, $duration, $output);
|
|
}
|
|
else {
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Applying image watermark #' . $watermark->id);
|
|
$this->applyImageWatermark($video_input, $watermark, $duration, $output);
|
|
}
|
|
$isolated_mem_peak_usage = memory_get_peak_usage();
|
|
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Memory initial usage: ' . round($isolated_mem_usage / 1024) . 'KB');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Memory peak usage: ' . round($isolated_mem_peak_usage / 1024) . 'KB');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Memory utilization: ' . round(($isolated_mem_peak_usage - $isolated_mem_usage) / 1024) . 'KB');
|
|
|
|
$i++;
|
|
$applied = true;
|
|
}
|
|
}
|
|
//Log::channel('watermarking')->info('===============');
|
|
|
|
if ($applied) {
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Adding watermarks completed.');
|
|
}
|
|
else {
|
|
$this->convert($video_path, $output);
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] No watermarks applied.');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Converted original video to mp4 video.');
|
|
}
|
|
}
|
|
else {
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] No active watermarks added.');
|
|
$this->convert($video_path, $output);
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Converted original video to mp4 video.');
|
|
}
|
|
|
|
$end_time = microtime(true);
|
|
$mem_peak_usage = memory_get_peak_usage();
|
|
$output_file_size = Storage::size("$temp_path/result.mp4");
|
|
|
|
//Log::channel('watermarking')->info('===============');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] OVERALL METRICS');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Original Video file size: ' . round($file_size / (1024 * 1024), 2) . 'MB');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Output video file size: ' . round($output_file_size / (1024 * 1024), 2) . 'MB');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Video duration: ' . round($duration, 2) . 's');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Video resolution: ' . $resolution['width'] . ' x ' . $resolution['height']);
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Memory initial usage: ' . round($mem_usage / 1024) . 'KB');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Memory peak usage: ' . round($mem_peak_usage / 1024) . 'KB');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Memory utilization: ' . round(($mem_peak_usage - $mem_usage) / 1024) . 'KB');
|
|
Log::channel('watermarking')->info('[#' . $video->id . '] Execution time: ' . round($end_time - $start_time, 2) . 's');
|
|
}
|
|
|
|
protected function getTempUploadStoragePath($id)
|
|
{
|
|
$video = StudioPlusVideo::find($id);
|
|
|
|
if ($video) {
|
|
return "CreatorStudio/temp/upload/$id";
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected function getTempReprocessStoragePath($id)
|
|
{
|
|
$video = StudioPlusVideo::find($id);
|
|
|
|
if ($video) {
|
|
return "CreatorStudio/temp/reprocess/videos/$id";
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected function getVideoResolution($video)
|
|
{
|
|
$process = new Process(['ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=width,height', '-of', 'json', $video]);
|
|
$process->run();
|
|
|
|
if ($process->isSuccessful()) {
|
|
$output = json_decode($process->getOutput(), true);
|
|
|
|
return $output['streams'][0];
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
protected function getVideoDuration($video)
|
|
{
|
|
$ffprobe = FFProbe::create(get_ffprobe_config());
|
|
$codeAc = $ffprobe->format($video)->get('duration');
|
|
return round($codeAc, PHP_ROUND_HALF_UP);
|
|
}
|
|
|
|
protected function applyTextWatermark($video, $watermark, $duration, $output)
|
|
{
|
|
$font = storage_path("app/CreatorStudio/sample.ttf");
|
|
$font = str_replace('\\', '/', $font);
|
|
$font = 'D\:/Works/wticreatorstudio/storage/app/CreatorStudio/sample.ttf';
|
|
$position = $this->getTextPosition($watermark);
|
|
$timing = $this->getTiming($watermark, $duration);
|
|
$x = $position['x'];
|
|
$y = $position['y'];
|
|
$single = $timing['single'];
|
|
|
|
if ($single) {
|
|
$period = $timing['periods'][0];
|
|
$start = $period['start'];
|
|
$end = $period['end'];
|
|
|
|
`ffmpeg -i "$video" -vf "drawtext=fontfile='$font':text='{$watermark->watermark}':fontcolor=white:fontsize=48:box=1:boxcolor=black@0.5:boxborderw=20:x=$x:y=$y:enable='between(t\\,$start\\,$end)'" -c:v libx264 -c:a aac "$output"`;
|
|
}
|
|
else {
|
|
$periods = $timing['periods'];
|
|
$intro = $periods[0];
|
|
$outro = $periods[1];
|
|
$start_1 = $intro['start'];
|
|
$end_1 = $intro['end'];
|
|
$start_2 = $outro['start'];
|
|
$end_2 = $outro['end'];
|
|
|
|
`ffmpeg -i "$video" -vf "drawtext=fontfile='$font':text='{$watermark->watermark}':fontcolor=white:fontsize=48:box=1:boxcolor=black@0.5:boxborderw=20:x=$x:y=$y:enable='between(t\\,$start_1\\,$end_1)',drawtext=fontfile='$font':text='{$watermark->watermark}':fontcolor=white:fontsize=48:box=1:boxcolor=black@0.5:boxborderw=20:x=$x:y=$y:enable='between(t\\,$start_2\\,$end_2)'" -c:v libx264 -c:a aac "$output"`;
|
|
}
|
|
}
|
|
|
|
protected function applyImageWatermark($video, $watermark, $duration, $output)
|
|
{
|
|
$watermark_path = storage_path($watermark->watermark);
|
|
$position = $this->getImagePosition($watermark);
|
|
$timing = $this->getTiming($watermark, $duration);
|
|
$x = $position['x'];
|
|
$y = $position['y'];
|
|
$single = $timing['single'];
|
|
|
|
if ($single) {
|
|
$period = $timing['periods'][0];
|
|
$start = $period['start'];
|
|
$end = $period['end'];
|
|
|
|
`ffmpeg -i "$video" -i "$watermark_path" -filter_complex "overlay=10:10:x=$x:y=$y:enable='between(t\\,$start\\,$end)'" -c:v libx264 -c:a aac "$output"`;
|
|
}
|
|
else {
|
|
$periods = $timing['periods'];
|
|
$intro = $periods[0];
|
|
$outro = $periods[1];
|
|
$start_1 = $intro['start'];
|
|
$end_1 = $intro['end'];
|
|
$start_2 = $outro['start'];
|
|
$end_2 = $outro['end'];
|
|
|
|
`ffmpeg -i "$video" -i "$watermark_path" -i "$watermark_path" -filter_complex "[0:v][1:v]overlay=x=$x:y=$y:enable='between(t\\,$start_1\\,$end_1)'[v1];[v1][2:v]overlay=x=$x:y=$y:enable='between(t\\,$start_2\\,$end_2)'" -c:v libx264 -c:a aac "$output"`;
|
|
}
|
|
}
|
|
|
|
protected function getTextPosition($watermark)
|
|
{
|
|
$x = 0;
|
|
$y = 0;
|
|
|
|
if ($watermark->left !== null) {
|
|
$x = $watermark->left;
|
|
}
|
|
|
|
if ($watermark->top !== null) {
|
|
$y = $watermark->top;
|
|
}
|
|
|
|
if ($watermark->right !== null) {
|
|
if ($watermark->left !== null) {
|
|
$x = "(((w-text_w)/2)-{$watermark->right})+{$watermark->left}";
|
|
}
|
|
else {
|
|
$x = "(w-text_w)-{$watermark->right}";
|
|
}
|
|
}
|
|
|
|
if ($watermark->bottom !== null) {
|
|
if ($watermark->top !== null) {
|
|
$y = "(((h-text_h)/2)-{$watermark->bottom})+{$watermark->top}";
|
|
}
|
|
else {
|
|
$y = "(h-text_h)-{$watermark->bottom}";
|
|
}
|
|
}
|
|
|
|
return [
|
|
'x' => $x,
|
|
'y' => $y
|
|
];
|
|
}
|
|
|
|
protected function getImagePosition($watermark)
|
|
{
|
|
$x = 0;
|
|
$y = 0;
|
|
|
|
if ($watermark->left !== null) {
|
|
$x = $watermark->left;
|
|
}
|
|
|
|
if ($watermark->top !== null) {
|
|
$y = $watermark->top;
|
|
}
|
|
|
|
if ($watermark->right !== null) {
|
|
if ($watermark->left !== null) {
|
|
$x = "(((main_w-overlay_w)/2)-{$watermark->right})+{$watermark->left}";
|
|
}
|
|
else {
|
|
$x = "(main_w-overlay_w)-{$watermark->right}";
|
|
}
|
|
}
|
|
|
|
if ($watermark->bottom !== null) {
|
|
if ($watermark->top !== null) {
|
|
$y = "(((main_h-overlay_h)/2)-{$watermark->bottom})+{$watermark->top}";
|
|
}
|
|
else {
|
|
$y = "(main_h-overlay_h)-{$watermark->bottom}";
|
|
}
|
|
}
|
|
|
|
return [
|
|
'x' => $x,
|
|
'y' => $y
|
|
];
|
|
}
|
|
|
|
protected function getTiming($watermark, $duration)
|
|
{
|
|
$periods = [];
|
|
$single = true;
|
|
$start = 0;
|
|
$end = ceil($duration);
|
|
|
|
if ($watermark->in_duration !== null && $watermark->out_duration !== null) {
|
|
$single = false;
|
|
}
|
|
|
|
if ($single) {
|
|
if ($watermark->in_time !== null) {
|
|
$start = $watermark->in_time;
|
|
}
|
|
|
|
if ($watermark->out_time !== null) {
|
|
$end = ceil($duration) - $watermark->out_time;
|
|
}
|
|
|
|
if ($watermark->in_duration !== null) {
|
|
$end = $start + $watermark->in_duration;
|
|
}
|
|
|
|
if ($watermark->out_duration !== null) {
|
|
$start = $end - $watermark->out_duration;
|
|
}
|
|
|
|
$periods[] = [
|
|
'start' => $start,
|
|
'end' => $end
|
|
];
|
|
}
|
|
else {
|
|
$start_1 = $watermark->in_time ? $watermark->in_time : 0;
|
|
$end_1 = $start_1 + $watermark->in_duration;
|
|
$end_2 = ceil($duration) - ($watermark->out_time ? $watermark->out_time : 0);
|
|
$start_2 = $end_2 - $watermark->out_duration;
|
|
|
|
$periods[] = [
|
|
'start' => $start_1,
|
|
'end' => $end_1
|
|
];
|
|
$periods[] = [
|
|
'start' => $start_2,
|
|
'end' => $end_2
|
|
];
|
|
}
|
|
|
|
return [
|
|
'single' => $single,
|
|
'periods' => $periods
|
|
];
|
|
}
|
|
|
|
protected function convert($video, $output)
|
|
{
|
|
`ffmpeg -i "$video" "$output"`;
|
|
}
|
|
|
|
public function dispatch($job)
|
|
{
|
|
return app(\Illuminate\Contracts\Bus\Dispatcher::class)->dispatch($job);
|
|
}
|
|
}
|