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); } }