Files
wticreatorstudio/Modules/StudioPlus/Services/WatermarkService.php
Fritz Ramirez 10d0c477c8 Initial commit
2024-02-12 22:54:20 -05:00

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