transcoder = new PHPVideoToolkit(variable_get('video_ffmpeg_path', '/usr/bin/ffmpeg'), realpath(file_directory_temp()) . '/'); PHPVideoToolkit::$ffmpeg_info = $this->getCachedFFmpegInfo(); parent::__construct(); } public function setInput(array $file) { parent::setInput($file); $srcuri = $this->settings['input']['uri']; $srcpath = drupal_realpath($srcuri); if (empty($srcpath)) { // If stored on a remote file system, such as S3, download the video to a temporary file. $srcpath = video_utility::createTemporaryLocalCopy($srcuri); if (empty($srcpath)) { watchdog('transcoder', 'Could not download @uri to a temporary file for transcoding.', array('@uri' => $srcuri), WATCHDOG_ERROR); return FALSE; } } $result = $this->transcoder->setInputFile($srcpath); if ($result !== PHPVideoToolkit::RESULT_OK) { watchdog('transcoder', 'Error set options @message', array('@message' => $this->transcoder->getLastError()), WATCHDOG_ERROR); $this->errors['input'] = $this->transcoder->getLastError(); $this->transcoder->reset(); return FALSE; } return TRUE; } public function setOptions(array $options) { // Reset the transcoder class keeping the input file info $this->transcoder->reset(TRUE); $this->multipass = TRUE; $this->outputextension = NULL; $clipstart = NULL; $cliplength = NULL; $hasvideo = empty($options['skip_video']); $hasaudio = empty($options['skip_audio']); $this->setAspectRatioOptions($options); foreach ($options as $key => $value) { if (empty($value) || $value === 'none') { continue; } if (strncmp($key, 'audio_', 6) === 0 && !$hasaudio) { continue; } if (strncmp($key, 'video_', 6) === 0 && !$hasvideo) { continue; } $result = TRUE; switch ($key) { case 'max_frame_rate': $result = $this->transcoder->setVideoFrameRate($value); break; case 'video_codec': if ($hasvideo) { $result = $this->transcoder->setVideoCodec($value); } break; case 'video_preset': $result = $this->transcoder->setVideoPreset($value); break; case 'audio_sample_rate': if ($value < 1000) { $value *= 1000; } $value = min($value, 44100); $result = $this->transcoder->setAudioSampleFrequency($value); break; case 'audio_codec': $result = $this->transcoder->setAudioCodec($value); break; case 'audio_channels': $result = $this->transcoder->setAudioChannels($value); break; case 'audio_bitrate': if ($value < 1000) { $value .= 'k'; } $result = $this->transcoder->setAudioBitRate($value); break; case 'video_bitrate': $result = $this->transcoder->setVideoBitRate($value); break; case 'pixel_format': $result = $this->transcoder->setPixelFormat($value); break; case 'one_pass': if ($value == 1) { $this->multipass = FALSE; } break; case 'video_extension': $this->outputextension = $value; break; case 'video_quality': $result = $this->transcoder->setConstantQuality($value * 20); // phpVideoToolkit expects 1 to 100 range. break; case 'clip_start': if (preg_match('#^(\d+)$#', $value)) { $clipstart = intval($value); } else { $clipstart = $this->transcoder->formatTimecode($value, '%hh:%mm:%ss.%ms', '%st'); } break; case 'clip_length': if (preg_match('#^(\d+)$#', $value)) { $cliplength = intval($value); } else { $cliplength = $this->transcoder->formatTimecode($value, '%hh:%mm:%ss.%ms', '%st'); } break; case 'skip_video': $result = $this->transcoder->addCommand('-vn'); $this->multipass = FALSE; break; case 'skip_audio': $result = $this->transcoder->addCommand('-an'); break; } if ($cliplength !== NULL && $clipstart !== NULL) { $result = $this->transcoder->extractSegment($clipstart, $clipstart + $cliplength, '%st', FALSE, FALSE); $cliplength = NULL; $clipstart = NULL; } if ($result !== PHPVideoToolkit::RESULT_OK) { watchdog('transcoder', 'Error set options @message', array('@message' => $this->transcoder->getLastError()), WATCHDOG_ERROR); $this->errors['options'] = $this->transcoder->getLastError(); $this->transcoder->reset(true); return FALSE; } } return TRUE; } public function setOutput($output_directory, $output_name, $overwrite_mode = FILE_EXISTS_REPLACE) { $this->realoutputdir = $output_directory; $this->realoutputname = $output_name; $tmpoutput = video_utility::createTempFile(video_utility::getExtension($output_name)); $tmpoutputdir = dirname($tmpoutput); $tmpoutputname = basename($tmpoutput); parent::setOutput($tmpoutputdir, $tmpoutputname, $overwrite_mode); // Overwrite is necessary to have two-pass encoding for WebM and Ogg Theora $result = $this->transcoder->setOutput($tmpoutputdir . '/', $tmpoutputname, PHPVideoToolkit::OVERWRITE_EXISTING); if ($result !== PHPVideoToolkit::RESULT_OK) { watchdog('transcoder', 'Error set options @message', array('@message' => $this->transcoder->getLastError()), WATCHDOG_ERROR); $this->errors['output'] = $this->transcoder->getLastError(); $this->transcoder->reset(true); return FALSE; } return TRUE; } public function extractFrames($destinationScheme, $format) { // When $job is null, we are viewing the thumbnails before the form has been submitted. $fid = intval($this->settings['input']['fid']); $job = video_jobs::load($fid); // Get the file system directory. $dsturibase = $destinationScheme . '://' . variable_get('video_thumbnail_path', 'videos/thumbnails') . '/' . $fid . '/'; file_prepare_directory($dsturibase, FILE_CREATE_DIRECTORY); $dstwrapper = file_stream_wrapper_get_instance_by_scheme($destinationScheme); // get the video file informations $file_info = $this->getFileInfo(); $duration = floor($file_info['duration']['seconds']); $no_of_thumbnails = variable_get('video_thumbnail_count', 5); // Precaution for very short videos if ((2 * $no_of_thumbnails) > $duration) { $no_of_thumbnails = floor($duration / 2); } $thumbs = array(); for ($i = 1; $i <= $no_of_thumbnails; $i++) { $seek = ceil($duration / ($no_of_thumbnails + 1) * $i); $filename = file_munge_filename('thumbnail-' . $fid . '_' . sprintf('%04d', $i) . '.' . $format, '', FALSE); $dsturi = $dsturibase . $filename; if (!file_exists($dsturi)) { // Create a temporary file that will be moved to the final URI later $dstpath = video_utility::createTempFile($format); $error = NULL; if (class_exists('ffmpeg_movie')) { $movie = new ffmpeg_movie($this->transcoder->getInputFile()); $frames = $movie->getFrameCount(); $fps = $movie->getFrameRate(); $frame = $movie->getFrame(min($frames, (int) $seek * $fps)); $thumb = $frame->toGDImage(); $result = video_utility::imageSave($thumb, $dstpath, $format); if (!$result) { $error = t('Unknown FFmpeg-php error'); } } else { $this->transcoder->extractFrame($seek, FALSE, '%st'); $result = $this->transcoder->setOutput(dirname($dstpath) . '/', basename($dstpath), PHPVideoToolkit::OVERWRITE_EXISTING); if ($result === PHPVideoToolkit::RESULT_OK) { if (isset($file_info['video']['rotate'])) { switch ($file_info['video']['rotate']) { case 90 : $this->transcoder->addCommand('-vf', 'transpose=1'); break; case 180 : $this->transcoder->addCommand('-vf', 'vflip'); break; case 270 : $this->transcoder->addCommand('-vf', 'transpose=2'); break; } } $result = $this->transcoder->execute() === PHPVideoToolkit::RESULT_OK; } if (!$result) { $error = $this->transcoder->getLastError(); $this->transcoder->reset(true); } } // Check if the extraction was successfull if ($error === NULL) { if (!file_exists($dstpath)) { $error = t('generated file %file does not exist', array('%file' => $dstpath)); } elseif (filesize($dstpath) == 0) { $error = t('generated file %file is empty', array('%file' => $dstpath)); drupal_unlink($dstpath); } } if ($error !== NULL) { form_set_error(NULL, t('Error generating thumbnail for video %videofilename: !error.', array('%videofilename' => $this->settings['input']['filename'], '!error' => $error))); watchdog('transcoder', 'Error generating thumbnail for video %videofilename: !error.', array('%videofilename' => $this->settings['input']['filename'], '!error' => $error), WATCHDOG_ERROR); continue; } // Move the file to the final URI copy($dstpath, $dsturi); } // Begin building the file object. $thumb = new stdClass(); $thumb->status = 0; $thumb->filename = basename($dsturi); $thumb->uri = $dsturi; $thumb->filemime = $dstwrapper->getMimeType($dsturi); $thumbs[] = $thumb; } return !empty($thumbs) ? $thumbs : FALSE; } public function execute() { // Execute the command in a temporary directory $drupaldir = getcwd(); $tmpdir = video_utility::createTempDir(); chmod($tmpdir, 0777); chdir($tmpdir); // Make sure that exec() is enabled. if (!function_exists('exec')) { watchdog('trancoder', 'Php can\'t use FFmpeg because php.ini has disabled the exec command. Please remove exec from the disable_functions directive (http://us1.php.net/manual/en/ini.core.php#ini.disable-functions)', WATCHDOG_ERROR); } // Execute the command $result = $this->transcoder->execute($this->multipass); // Restore the directory chdir($drupaldir); // Log an error when trancoding fails $tmpoutputpath = $this->settings['base_url'] . '/' . $this->settings['filename']; if ($result !== PHPVideoToolkit::RESULT_OK || !file_exists($tmpoutputpath) || ($filesize = filesize($tmpoutputpath)) == 0) { $errorlist = $this->transcoder->getErrors(); $_commandoutput = $this->transcoder->getCommandOutput(); $commandoutput = array(); foreach ($_commandoutput as $cmd) { $commandoutput[] = '
' . check_plain($cmd['command']) . '
' . check_plain($cmd['output']) . '

'; } watchdog('transcoder', 'FFmpeg failed to transcode %video. !errorlist !commandlist', array( '%video' => $this->settings['input']['filename'], '!errorlist' => theme('item_list', array('type' => 'ol', 'items' => $errorlist, 'title' => t('Reported errors'))), '!commandlist' => theme('item_list', array('type' => 'ol', 'items' => $commandoutput, 'title' => t('Executed commands and output'))) ), WATCHDOG_ERROR); $this->errors['execute'] = $errorlist; $this->transcoder->reset(true); return FALSE; } // Post-process the file with qt-faststart $cmd = variable_get('video_ffmpeg_qtfaststart_path'); if ($cmd != NULL && is_file($cmd) && $this->outputextension == 'mp4') { $qttmpfile = $tmpoutputpath . '-qt'; $output = array(); $retval = 0; exec($cmd . ' ' . escapeshellarg($tmpoutputpath) . ' ' . escapeshellarg($qttmpfile) . ' 2>&1', $output, $retval); // qt-faststart does not return an error code when it doesn't generate an output file, // so also check if the output file has been generated. if ($retval == 0 && file_exists($qttmpfile)) { drupal_unlink($tmpoutputpath); rename($qttmpfile, $tmpoutputpath); } else { watchdog('transcoder', 'Error while executing @cmdname on video @filename: @output', array('@cmdname' => 'qt-faststart', '@filename' => $this->realoutputname, '@output' => implode("\n", $output)), WATCHDOG_ERROR); if (file_exists($qttmpfile)) { drupal_unlink($qttmpfile); } } } // Post-process the file with flvtool2 $cmd = variable_get('video_ffmpeg_flvtool2_path'); if ($cmd != NULL && is_file($cmd) && $this->outputextension == 'flv') { $output = array(); $retval = 0; exec($cmd . ' -U ' . escapeshellarg($tmpoutputpath) . ' 2>&1', $output, $retval); if ($retval != 0) { watchdog('transcoder', 'Error while executing @cmdname on video @filename: @output', array('@cmdname' => 'flvtool2', '@filename' => $this->realoutputname, '@output' => implode("\n", $output)), WATCHDOG_ERROR); } } $file_info = $this->getFileInfo(); $realoutputuri = $this->realoutputdir . '/' . $this->realoutputname; copy($tmpoutputpath, $realoutputuri); drupal_unlink($tmpoutputpath); $output = new stdClass(); $output->filename = $this->realoutputname; $output->uri = $realoutputuri; $output->filesize = $filesize; $output->timestamp = REQUEST_TIME; $output->jobid = NULL; $output->duration = floor($file_info['duration']['seconds']); return $output; } public function getFileInfo() { $file = $this->settings['input']['uri']; $cid = 'video:file:' . md5($file); $cache = cache_get($cid); if (!empty($cache->data)) { return $cache->data; } $data = $this->transcoder->getFileInfo(); cache_set($cid, $data, self::INFO_CACHE, time() + 7 * 24 * 3600); return $data; } public function getCodecs() { $info = $this->getCachedFFmpegInfo(); $codecs = array( 'decode' => array('audio' => array(), 'video' => array()), 'encode' => array('audio' => array(), 'video' => array()), ); if (!empty($info['codecs'])) { foreach ($info['codecs'] as $key => $value) { $codecs['encode'][$key] = array(); $codecs['decode'][$key] = array(); foreach ($value as $codec_key => $codec) { if ($codec['encode']) { $codecs['encode'][$key][$codec_key] = $codec['fullname']; } if ($codec['decode']) { $codecs['decode'][$key][$codec_key] = $codec['fullname']; } } uasort($codecs['encode'][$key], 'strnatcasecmp'); uasort($codecs['encode'][$key], 'strnatcasecmp'); } } return $codecs; } public function getAvailableFormats($type = FALSE) { $info = $this->getCachedFFmpegInfo(); if (empty($info['formats'])) { return array(); } $formats = array(); switch ($type) { case FALSE: return array_keys($info['formats']); case 'both' : foreach ($info['formats'] as $id => $data) { if ($data['mux'] === TRUE && $data['demux'] === TRUE) { $formats[$id] = $data['fullname']; } } break; case 'muxing' : foreach ($info['formats'] as $id => $data) { if ($data['mux'] === TRUE) { $formats[$id] = $data['fullname']; } } break; case 'demuxing' : foreach ($info['formats'] as $id => $data) { if ($data['demux'] === TRUE) { $formats[$id] = $data['fullname']; } } break; } if (isset($formats['ogg']) && !isset($formats['ogv'])) { $formats['ogv'] = $formats['ogg']; unset($formats['ogg']); asort($formats); } return $formats; } public function getPixelFormats() { $info = $this->getCachedFFmpegInfo(); if (empty($info['pixelformats'])) { return array(); } return array_keys($info['pixelformats']); } public function getVersion() { $info = $this->getCachedFFmpegInfo(); if ($info['ffmpeg-found']) { $ffmpegoravconv = NULL; return self::getVersionFromOutput($info['raw'], $ffmpegoravconv); } return FALSE; } public function getName() { return 'FFmpeg / avconv'; } public function getValue() { return 'TranscoderAbstractionFactoryFfmpeg'; } public function adminSettings() { $form = array(); $form['ffmpeg'] = array( '#type' => 'fieldset', '#title' => $this->getName(), '#collapsible' => FALSE, '#collapsed' => FALSE, '#states' => array( 'visible' => array( ':input[name=video_convertor]' => array('value' => 'TranscoderAbstractionFactoryFfmpeg'), ), ), ); $form['ffmpeg']['video_ffmpeg_path'] = array( '#type' => 'textfield', '#title' => t('Path to FFmpeg or avconv executable'), '#description' => t('Absolute path to the FFmpeg or avconv executable.') . ' ' . t('When you install a new FFmpeg version, please clear the caches to let Drupal detect the updated codec support.', array('@performance-page' => url('admin/config/development/performance'))), '#default_value' => variable_get('video_ffmpeg_path', '/usr/bin/ffmpeg'), ); // Video thumbnails $form['ffmpeg']['video_thumbnail_path'] = array( '#type' => 'textfield', '#title' => t('Path to save thumbnails'), '#description' => t('Path to save video thumbnails extracted from videos.'), '#default_value' => variable_get('video_thumbnail_path', 'videos/thumbnails'), ); $form['ffmpeg']['video_thumbnail_count'] = array( '#type' => 'textfield', '#title' => t('Number of thumbnails'), '#description' => t('Number of thumbnails to extract from video.'), '#default_value' => variable_get('video_thumbnail_count', 5), '#size' => 5, ); // Video conversion settings. $form['ffmpeg']['helpers'] = array( '#type' => 'fieldset', '#title' => t('Helper programs'), '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['ffmpeg']['helpers']['video_ffmpeg_qtfaststart_path'] = array( '#type' => 'textfield', '#title' => t('Path to qt-faststart'), '#default_value' => variable_get('video_ffmpeg_qtfaststart_path'), '#description' => t('When you enter the path to @toolname here, it will be run after any @filetype files are transcoded. It improves the loading time of @filetype videos. On Linux, the default for this field is %default_value.', array('@toolname' => 'qt-faststart', '@filetype' => 'MP4', '%default_value' => '/usr/bin/qt-faststart')), ); $form['ffmpeg']['helpers']['video_ffmpeg_flvtool2_path'] = array( '#type' => 'textfield', '#title' => t('Path to flvtool2'), '#default_value' => variable_get('video_ffmpeg_flvtool2_path'), '#description' => t('When you enter the path to @toolname here, it will be run after any @filetype files are transcoded. It improves the loading time of @filetype videos. On Linux, the default for this field is %default_value.', array('@toolname' => 'flvtool2', '@filetype' => 'FLV', '%default_value' => '/usr/bin/flvtool2')), ); return $form; } public function adminSettingsValidate($form, &$form_state) { $v =& $form_state['values']; if (!empty($v['video_ffmpeg_path'])) { $errorhelp = ''; if (module_exists('video_ui')) { $errorhelp = '
' . t('Visit the FFmpeg debug page for information thay may help you find the cause of this problem.', array('@ffmpeg-debug-page' => url('admin/config/media/video/ffmpeg-info', array('query' => array('ffmpegpath' => $v['video_ffmpeg_path']))))); } if (!file_exists($v['video_ffmpeg_path']) || !is_executable($v['video_ffmpeg_path'])) { form_error($form['ffmpeg']['video_ffmpeg_path'], t('The path @path must point to an executable file.', array( '@path' => $v['video_ffmpeg_path'], ))); return; } $toolkit = new PHPVideoToolkit($v['video_ffmpeg_path']); $ffmpeg = $toolkit->getFFmpegInfo(FALSE); $ffmpegoravconv = NULL; if (!$ffmpeg['ffmpeg-found'] || ($version = self::getVersionFromOutput($ffmpeg['raw'], $ffmpegoravconv)) == NULL) { form_error($form['ffmpeg']['video_ffmpeg_path'], t('FFmpeg not found at %path. To convert videos and create thumbnails you have to install FFmpeg on your server. For more information please see the !documentation.' . $errorhelp, array('%path' => $v['video_ffmpeg_path'], '!documentation' => l(t('documentation'), 'http://video.heidisoft.com/documentation/ffmpeg-installtion-scripts')))); } elseif (empty($ffmpeg['codecs']['video']) || empty($ffmpeg['codecs']['audio'])) { form_error($form['ffmpeg']['video_ffmpeg_path'], t('FFmpeg version %version was found, but no supported codecs could be found. Please check whether FFmpeg and all support libraries have been installed correctly.' . $errorhelp, array('%version' => $version))); } else { drupal_set_message(t('%ffmpegoravconv version %version found on your system.', array('%version' => $version, '%ffmpegoravconv' => $ffmpegoravconv)), 'status'); } // Clear FFmpeg information when the path has changed. cache_clear_all(self::INFO_CID, self::INFO_CACHE); } if (!empty($v['video_ffmpeg_qtfaststart_path'])) { if (!is_file($v['video_ffmpeg_qtfaststart_path'])) { form_error($form['ffmpeg']['helpers']['video_ffmpeg_qtfaststart_path'], t('The file %file does not exist.', array('%file' => $v['video_ffmpeg_qtfaststart_path']))); } } if (!empty($v['video_ffmpeg_flvtool2_path'])) { if (!is_file($v['video_ffmpeg_flvtool2_path'])) { form_error($form['ffmpeg']['helpers']['video_ffmpeg_flvtool2_path'], t('The file %file does not exist.', array('%file' => $v['video_ffmpeg_flvtool2_path']))); } } } /** * Returns a cached copy of PHPVideoToolkit::getFFmpegInfo() * * @return * array of FFmpeg installation information. */ private function getCachedFFmpegInfo() { $cache = cache_get(self::INFO_CID, self::INFO_CACHE); if (!empty($cache->data)) { return $cache->data; } $info = $this->transcoder->getFFmpegInfo(FALSE); cache_set(self::INFO_CID, $info, self::INFO_CACHE); return $info; } /** * Returns the FFmpeg version string from an output string. * * FFmpeg returns its version in the output of almost any call. * * Used instead of PHPVideoToolkit::getVersion(), because PHPVideoToolkit * has not been updated and does not support git versions. * * @param * string containing output of ffmpeg -version * @return * string containing version of FFmpeg */ public static function getVersionFromOutput($output, &$ffmpegoravconv) { $version = NULL; $ffmpegoravconv = NULL; $matches = array(); // Git check outs if (preg_match('/(ffmpeg|avconv) version N-\d+-g([a-f0-9]+)/', $output, $matches)) { $ffmpegoravconv = $matches[1]; $version = 'git: ' . $matches[2]; } // Git check outs elseif (preg_match('/(ffmpeg|avconv) version git-(\d{4}-\d{2}-\d{2}-[a-f0-9]+)/', $output, $matches)) { $ffmpegoravconv = $matches[1]; $version = 'git: ' . $matches[2]; } // Release elseif (preg_match('/(ffmpeg|avconv) version ([0-9.]+)/i', $output, $matches)) { $ffmpegoravconv = $matches[1]; $version = $matches[2]; } // Fallback to unrecognized string elseif (preg_match('/(ffmpeg|avconv) version ([^\n]+)/i', $output, $matches)) { $ffmpegoravconv = $matches[1]; $version = $matches[2]; if (($pos = strpos($version, ' Copyright')) !== FALSE) { $version = drupal_substr($version, 0, $pos); } $version = t('unrecognized: !version', array('!version' => $version)); } if ($ffmpegoravconv == 'ffmpeg') { $ffmpegoravconv = 'FFmpeg'; } return $version; } /** * Reset internal variables to their initial state. */ public function reset($keepinput = FALSE) { parent::reset($keepinput); $this->transcoder->reset($keepinput); $this->multipass = NULL; } /** * Whether the transcoder works by sending jobs to an external system. * * True for transcoders like Zencoder, false for transcoders like FFmpeg. */ public function isOffSite() { return FALSE; } /** * Set aspect ratio and size related transcoder options. */ private function setAspectRatioOptions(array $options) { $targetdimensions = explode('x', $options['wxh'], 2); $file_info = $this->getFileInfo(); $sourcedimensions = array( intval($file_info['video']['dimensions']['width']), intval($file_info['video']['dimensions']['height']) ); // If no change is necessary, just return. if ($targetdimensions == $sourcedimensions) { return; } $targetaspect = round($targetdimensions[0] / $targetdimensions[1], 4); $sourceaspect = round($sourcedimensions[0] / $sourcedimensions[1], 4); $aspectmode = !empty($options['video_aspectmode']) ? $options['video_aspectmode'] : 'preserve'; switch ($aspectmode) { default: case 'preserve': if ($sourceaspect >= $targetaspect) { // Source video is wider than destination. // Decrease the height. $factor = $targetdimensions[0] < $sourcedimensions[0] ? $targetdimensions[0] / $sourcedimensions[0] : $sourcedimensions[0] / $targetdimensions[0]; $width = $targetdimensions[0]; $height = $sourcedimensions[1] * $factor; } else { $factor = $targetdimensions[1] < $sourcedimensions[1] ? $targetdimensions[1] / $sourcedimensions[1] : $sourcedimensions[1] / $targetdimensions[1]; $width = $sourcedimensions[0] * $factor; $height = $targetdimensions[1]; } $width = video_utility::roundToEvenNumber($width); $height = video_utility::roundToEvenNumber($height); if ($width != $sourcedimensions[0] || $height != $sourcedimensions[1]) { $this->transcoder->addCommand('-vf', 'scale='.$width.':'.$height); } break; case 'crop': if ($sourceaspect >= $targetaspect) { // Source video is wider than destination. // Cut off left and right. $factor = $targetdimensions[1] < $sourcedimensions[1] ? $targetdimensions[1] / $sourcedimensions[1] : $sourcedimensions[1] / $targetdimensions[1]; $width = round($factor * $sourcedimensions[0]); $height = $sourcedimensions[1]; } else { $factor = $targetdimensions[0] < $sourcedimensions[0] ? $targetdimensions[0] / $sourcedimensions[0] : $sourcedimensions[0] / $targetdimensions[0]; $width = $sourcedimensions[0]; $height = round($factor * $sourcedimensions[1]); } $this->transcoder->addCommand('-vf', 'crop='.$width.':'.$height.',scale='.$targetdimensions[0].':'.$targetdimensions[1]); break; case 'pad': if ($sourceaspect >= $targetaspect) { // Source video is wider than destination. // Add padding to top and bottom. $factor = $targetdimensions[1] > $sourcedimensions[1] ? $targetdimensions[1] / $sourcedimensions[1] : $sourcedimensions[1] / $targetdimensions[1]; $width = $sourcedimensions[0]; $height = round($factor * $sourcedimensions[1]); $x = 0; $y = ($height - $sourcedimensions[1]) / 2; } else { $factor = $targetdimensions[0] > $sourcedimensions[0] ? $targetdimensions[0] / $sourcedimensions[0] : $sourcedimensions[0] / $targetdimensions[0]; $width = round($factor * $sourcedimensions[0]); $height = $sourcedimensions[1]; $x = ($width - $sourcedimensions[0]) / 2; $y = 0; } $this->transcoder->addCommand('-vf', 'pad='.$width.':'.$height.':'.$x.':'.$y.',scale='.$targetdimensions[0].':'.$targetdimensions[1]); break; case 'stretch': $result = $this->transcoder->setVideoDimensions($targetdimensions[0], $targetdimensions[1]); break; } } }