params['ffmpeg'] = variable_get('video_ffmpeg_path', '/usr/bin/ffmpeg');
$this->params['ffmpeg2theora'] = variable_get('video_ffmpeg2theora_path', '/usr/bin/ffmpeg2theora');
$this->params['mcoder'] = variable_get('video_mcoder_path', '/usr/bin/mcoder');
$this->params['handbreke'] = variable_get('video_handbreke_path', '/usr/bin/HandBrakeCLI');
$this->params['other'] = variable_get('video_other_path', '');
$this->params['thumb_command'] = variable_get('video_ffmpeg_thumbnailer_options', $this->thumb_command);
$this->nice = variable_get('video_ffmpeg_nice_enable', false) ? 'nice -n 19 ' : '';
$this->params['enable_faststart'] = variable_get('video_ffmpeg_enable_faststart', 0);
$this->params['faststart_cmd'] = variable_get('video_ffmpeg_faststart_cmd', '/usr/bin/qt-faststart');
}
public function run_command($command) {
// transcoder switching
$command = strtr($command, array(
'!ffmpeg' => $this->params['ffmpeg'],
'!ffmpeg2theora' => $this->params['ffmpeg2theora'],
'!mcoder' => $this->params['mcoder'],
'!handbreke' => $this->params['handbreke'],
'!other' => $this->params['other'],
));
$command = $this->nice . $command . ' 2>&1';
watchdog('transcoder', 'Executing command: ' . $command, array(), WATCHDOG_DEBUG);
// ob_start();
$command_return = shell_exec($command);
// $output = ob_get_contents();
// ob_end_clean();
return $command_return;
}
public function generate_thumbnails($video) {
global $user;
// Setup our thmbnail path.
$video_thumb_path = variable_get('video_thumb_path', 'videos/thumbnails');
// Get the file system directory.
// @todo : get the field file system settings to this
$schema_thumb_path = file_default_scheme() . '://' . $video_thumb_path . '/' . $video['fid'];
file_prepare_directory($schema_thumb_path, FILE_CREATE_DIRECTORY);
// Total thumbs to generate
$total_thumbs = variable_get('video_thumbs', 5);
$videofile = file_load($video['fid']);
//get the actual video file path from the stream wrappers
$videopath = drupal_realpath($videofile->uri);
//get the playtime from the current transcoder
$duration = $this->get_playtime($videopath);
$files = NULL;
for ($i = 1; $i <= $total_thumbs; $i++) {
$seek = ($duration / $total_thumbs) * $i - 1; //adding minus one to prevent seek times equaling the last second of the video
$filename = file_munge_filename("video-thumb-" . $video['fid'] . "-$i.jpg", '', TRUE);
$thumbfile = $schema_thumb_path . '/' . $filename;
//skip files already exists, this will save ffmpeg traffic
if (!is_file(drupal_realpath($thumbfile))) {
//setup the command to be passed to the transcoder.
$command = strtr($this->params['thumb_command'], array(
'!videofile' => '"' . $videopath . '"',
'!seek' => $seek,
'!thumbfile' => '"' . drupal_realpath($thumbfile) . '"'
));
// Generate the thumbnail from the video.
$command_output = $this->run_command($command);
if (!file_exists(drupal_realpath($thumbfile))) {
$error_param = array('%file' => $thumbfile, '%cmd' => $command, '%out' => $command_output);
$error_msg = t("Error generating thumbnail for video: generated file %file does not exist.
Command Executed:
%cmd
Command Output:
%out", $error_param);
// Log the error message.
watchdog('transcoder', $error_msg, array(), WATCHDOG_ERROR);
continue;
}
}
// Begin building the file object.
// @TODO : use file_munge_filename()
$file = new stdClass();
$file->uid = $user->uid;
$file->status = 0;
$file->filename = trim($filename);
$file->uri = $thumbfile;
$file->filemime = file_get_mimetype($filename);
$file->filesize = filesize(drupal_realpath($thumbfile));
$file->timestamp = time();
$files[] = $file;
}
return $files;
}
// Returns available codecs
public function get_codecs() {
$codecs = array(
'encode' => array(
'video' => array(
'h264' => 'H.264 (default)',
'vp8' => 'VP8',
'theora' => 'Theora',
'vp6' => 'VP6',
'mpeg4' => 'MPEG-4',
'wmv' => 'WMV'
),
'audio' => array(
'aac' => 'AAC (default for most cases)',
'mp3' => 'MP3',
'vorbis' => 'Vorbis (default for VP8 and Theora)',
'wma' => 'WMA'
)
),
'decoding' => array()
);
return $codecs;
}
public function convert_video($video) {
// This will update our current video status to active.
// $this->change_status($video->vid, VIDEO_RENDERING_ACTIVE);
// get the paths so tokens will compatible with this
// @todo : add best method to get existing file path and add converted there
$target = str_replace('original', '', drupal_dirname($video->uri));
$converted_base_dir = $target . 'converted/' . $video->fid;
if (!file_prepare_directory($converted_base_dir, FILE_CREATE_DIRECTORY)) {
watchdog('transcoder', 'Video conversion failed. Could not create the directory: ' . $converted_base_dir, array(), WATCHDOG_ERROR);
return FALSE;
}
//get the actual video file path from the stream wrappers
$original_video_path = drupal_realpath($video->uri);
// process presets
$presets = $video->presets;
$converted_files = array();
foreach ($presets as $name => $preset) {
$settings = $preset['settings'];
// override with preset settings
if (isset($settings['width']) && !empty($settings['width']) && isset($settings['height']) && !empty($settings['height'])
&& variable_get('video_use_preset_wxh', FALSE)) {
$video->dimensions = $settings['width'] . 'x' . $settings['height'];
}
$converted = $converted_base_dir . '/' . file_munge_filename(str_replace(' ', '_', pathinfo($original_video_path, PATHINFO_FILENAME)) . '.' . $settings['video_extension'], $settings['video_extension']);
//get the actual video file path from the stream wrappers
$converted_video_path = drupal_realpath($converted);
$dimensions = $this->dimensions($video);
$dimention = explode('x', $dimensions);
if ($this->params['enable_faststart'] && in_array($settings['video_extension'], array('mov', 'mp4'))) {
$ffmpeg_output = file_directory_temp() . '/' . basename($converted_video_path);
} else {
$ffmpeg_output = $converted_video_path;
}
// Setup our default command to be run.
$command = strtr($settings['cli_code'], array(
'!videofile' => '"' . $original_video_path . '"',
'!audiobitrate' => $settings['audio_bitrate'],
'!width' => $dimention[0],
'!height' => $dimention[1],
'!videobitrate' => $settings['video_bitrate'],
'!convertfile' => '"' . $ffmpeg_output . '"',
));
// process our video
$command_output = $this->run_command($command);
if ($ffmpeg_output != $converted_video_path && file_exists($ffmpeg_output)) {
// Because the transcoder_interface doesn't allow the run_command() to include the ability to pass
// the command to be execute so we need to fudge the command to run qt-faststart.
$cmd_path = $this->params['cmd_path'];
$this->params['cmd_path'] = $this->params['faststart_cmd'];
$command_output .= $this->run_command($ffmpeg_output . ' ' . $converted_video_path, $verbose);
$this->params['cmd_path'] = $cmd_path;
// Delete the temporary output file.
drupal_unlink($ffmpeg_output);
}
//lets check to make sure our file exists, if not error out
if (!file_exists($converted_video_path) || !filesize($converted_video_path)) {
watchdog('transcoder', 'Video conversion failed for preset %preset. FFMPEG reported the following output: ' . $command_output, array('%orig' => $video->uri, '%preset' => $name), WATCHDOG_ERROR);
$this->change_status($video->vid, VIDEO_RENDERING_FAILED);
return FALSE;
}
// Setup our converted video object
$video_info = pathinfo($converted_video_path);
//update our converted video
$video->converted = new stdClass();
$video->converted->vid = $video->vid;
$video->converted->filename = $video_info['basename'];
$video->converted->uri = $converted;
$video->converted->filemime = file_get_mimetype($converted);
$video->converted->filesize = filesize($converted);
$video->converted->status = VIDEO_RENDERING_COMPLETE;
$video->converted->preset = $name;
$video->converted->completed = time();
$converted_files[] = $video->converted;
}
// Update our video_files table with the converted video information.
db_update('video_files')
->fields(array(
'status' => VIDEO_RENDERING_COMPLETE,
'completed' => time(),
'data' => serialize($converted_files)))
->condition('vid', $video->converted->vid, '=')
->execute();
watchdog('transcoder', 'Successfully converted %orig to %dest', array('%orig' => $video->uri, '%dest' => $video->converted->uri), WATCHDOG_INFO);
return TRUE;
}
/**
* Get some information from the video file
*/
public function get_video_info($video) {
static $command_ouput;
if (!empty($command_output))
return $command_output;
$file = escapeshellarg($video);
// Execute the command
$options = '!ffmpeg -i ' . $file;
$command_output = $this->run_command($options);
return $command_output;
}
/**
* Return the playtime seconds of a video
*/
public function get_playtime($video) {
$ffmpeg_output = $this->get_video_info($video);
// Get playtime
$pattern = '/Duration: ([0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9])/';
preg_match_all($pattern, $ffmpeg_output, $matches, PREG_PATTERN_ORDER);
$playtime = $matches[1][0];
// ffmpeg return length as 00:00:31.1 Let's get playtime from that
$hmsmm = explode(":", $playtime);
$tmp = explode(".", $hmsmm[2]);
$seconds = $tmp[0];
$hours = $hmsmm[0];
$minutes = $hmsmm[1];
return $seconds + ($hours * 3600) + ($minutes * 60);
}
/*
* Return the dimensions of a video
*/
public function get_dimensions($video) {
$ffmpeg_output = $this->get_video_info($video);
$res = array('width' => 0, 'height' => 0);
// Get dimensions
$regex = preg_match('/([0-9]{1,5})x([0-9]{1,5})/', $ffmpeg_output, $regs);
if (isset($regs[0])) {
$dimensions = explode("x", $regs[0]);
$res['width'] = $dimensions[0] ? $dimensions[0] : NULL;
$res['height'] = $dimensions[1] ? $dimensions[1] : NULL;
}
return $res;
}
/**
* Interface Implementations
* @see sites/all/modules/video/includes/transcoder_interface#get_name()
*/
public function get_name() {
return $this->name;
}
/**
* Interface Implementations
* @see sites/all/modules/video/includes/transcoder_interface#get_value()
*/
public function get_value() {
return $this->value;
}
/**
* Interface Implementations
* @see sites/all/modules/video/includes/transcoder_interface#get_help()
*/
public function get_help() {
return l(t('FFMPEG Online Manual'), 'http://www.ffmpeg.org/');
}
/**
* Interface Implementations
* @see sites/all/modules/video/includes/transcoder_interface#admin_settings()
*/
public function admin_settings() {
$form = array();
$form['video_ffmpeg_start'] = array(
'#type' => 'markup',
'#markup' => '