new CommandPlaceholderArg($source_video_file, 'is_valid_rs_path'), ]; $audiocheck = run_command($shell_exec_cmd, false, $shell_exec_params); $has_no_audio = empty($audiocheck) ? "_noaudio" : ""; $target_completed_location = $target_temp_location . "/" . $target_order_count . $has_no_audio . "." . $target_video_extension; // SECURITY NOTE: The target_video_command and target_audio_command are not escaped as they come from config and contain multiple option/value combinations thus cannot be escaped $validcommandregex = '/^[a-zA-Z0-9\s\-:]*$/'; if (!preg_match($validcommandregex, $target_video_command)) { debug("Invalid video command: " . $target_video_command); return false; } if (!preg_match($validcommandregex, $target_audio_command)) { debug("Invalid audio command: " . $target_audio_command); return false; } $video_splice_options = ' -f ' . $target_video_command . " " . $target_audio_command . ' -vf "fps=[TARGET_FRAME_RATE],scale=[TARGET_WIDTH]:[TARGET_HEIGHT]:force_original_aspect_ratio=decrease,pad=[TARGET_WIDTH]:[TARGET_HEIGHT]:(ow-iw)/2:(oh-ih)/2" -sws_flags lanczos'; $video_splice_command = $ffmpeg_fullpath . " " . $ffmpeg_global_options . " -i [SOURCE_VIDEO_FILE] " . $video_splice_options . " [TARGET_COMPLETED_LOCATION]"; $video_splice_params = [ '[SOURCE_VIDEO_FILE]' => new CommandPlaceholderArg($source_video_file, 'is_valid_rs_path'), '[TARGET_FRAME_RATE]' => new CommandPlaceholderArg($target_frame_rate, 'is_numeric'), '[TARGET_WIDTH]' => (int) $target_width, '[TARGET_HEIGHT]' => (int) $target_height, '[TARGET_COMPLETED_LOCATION]' => new CommandPlaceholderArg($target_completed_location, 'is_valid_rs_path'), ]; run_command($video_splice_command, true, $video_splice_params); // If file has no audio channels create blank audio channel to ensure all video files contain audio thus wont loose audio on videos that have it if (empty($audiocheck)) { $no_audio_command = $ffmpeg_fullpath . " -i [NO_AUDIO_LOCATION] -f lavfi -i anullsrc -vcodec copy " . $target_audio_command . " -shortest [WITH_AUDIO_LOCATION]"; $with_audio_location = str_replace("_noaudio", "", $target_completed_location); $no_audio_params = [ '[NO_AUDIO_LOCATION]' => new CommandPlaceholderArg($target_completed_location, 'is_valid_rs_path'), '[WITH_AUDIO_LOCATION]' => new CommandPlaceholderArg($with_audio_location, 'is_valid_rs_path'), ]; run_command($no_audio_command, false, $no_audio_params); unlink($target_completed_location); $target_completed_location = $with_audio_location; } $target_completed_locations[] = $target_completed_location; $target_order_count++; } $list_file_command = ""; foreach ($target_completed_locations as $target_completed_location) { // Build list file contents (easiest way to merge videos) $list_file_command .= "file '" . $target_completed_location . "'\n"; } file_put_contents($target_temp_location . "/list.txt", $list_file_command); // Merge video files using list file $mergedfile = $target_temp_location . "/merged." . $target_video_extension; $merge_command = $ffmpeg_fullpath . " -f concat -safe 0 -i [LIST_LOCATION] -c copy [OUTPUT_FILE]"; $merge_params = [ '[LIST_LOCATION]' => new CommandPlaceholderArg($target_temp_location . "/list.txt", 'is_valid_rs_path'), '[OUTPUT_FILE]' => new CommandPlaceholderArg($mergedfile, 'is_valid_rs_path'), ]; run_command($merge_command, false, $merge_params); // Tidy up as we go along now final file created unlink($target_temp_location . "/list.txt"); foreach ($target_completed_locations as $target_completed_location) { unlink($target_completed_location); } if ($video_splice_type == "video_splice_save_new") { // Create new blank resource with the type specified in the config $ref = create_resource($videosplice_resourcetype, 0,-1,$lang["video_splice_createdfromvideosplice"]); $resource_location = get_resource_path( $ref, true, "", true, $target_video_extension, -1, 1, false, "", -1, false ); // Place new merged file in resource location rename($mergedfile, $resource_location); rmdir($target_temp_location); create_previews($ref,false,$target_video_extension); // Add file extension to db so front end recognises the file ps_query("update resource set file_extension=? where ref=?",array("s",$target_video_extension,"i",$ref)); // Add current date to date field update_field($ref, $date_field, date("Y-m-d H:i:s")); // If description provided add it if (!empty($description)) { update_field($ref, $videosplice_description_field, $description); } // If user wants video information auto populated then add it if ($auto_populate_video_info) { update_field($ref, $videosplice_video_bitrate_field, array_keys($ffmpeg_std_video_options)[array_search($target_video_command, array_column($ffmpeg_std_video_options, 'command'))]); update_field($ref, $videosplice_video_size_field, $target_width . "x" . $target_height); update_field($ref, $videosplice_frame_rate_field, array_keys($ffmpeg_std_frame_rate_options)[array_search($target_frame_rate, array_column($ffmpeg_std_frame_rate_options, 'value'))]); // Quick way to figure out standard ratios $ratio = substr($target_width > $target_height?$target_width / $target_height:$target_height / $target_width, 0, 3); switch ($ratio) { case 1.3: $aspect_ratio = "4:3"; break; case 1.6: $aspect_ratio = "16:10"; break; case 1.7: $aspect_ratio = "16:9"; break; } if (isset($aspect_ratio)) { update_field($ref, $videosplice_aspect_ratio_field, $aspect_ratio); } } return $ref; } elseif ($video_splice_type == "video_splice_download") { // Move to download directory $download_filename = $userref . "_" . md5($username . $randstring . $scramble_key) . "." . $target_video_extension; $download_file_location = get_temp_dir(false,"user_downloads") . DIRECTORY_SEPARATOR . $download_filename; rename($mergedfile, $download_file_location); rmdir($target_temp_location); if ($offline) { // For offline we want to return the location of the file so it can be added as a link in the user message return $userref . "_" . $randstring . "." . $target_video_extension; } else { // Download file $filesize = filesize_unlimited($download_file_location); ob_flush(); header(sprintf('Content-Disposition: attachment; filename="%s"', $download_filename)); header("Content-Length: " . $filesize); set_time_limit(0); $sent = 0; $handle = fopen($download_file_location, "r"); // Now we need to loop through the file and echo out chunks of file data while ($sent < $filesize) { echo fread($handle, $download_chunk_size); ob_flush(); $sent += $download_chunk_size; } // Delete File fclose($handle); unlink($download_file_location); } return true; } // if one of the above options has not worked then return false return false; }