PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/sites/all/modules/video/transcoders/video_ffmpeg.inc

https://bitbucket.org/becomplete/enrollment123
PHP | 611 lines | 446 code | 43 blank | 122 comment | 41 complexity | a67d6a6a1d17ecc4b983fc3f6c1a18c9 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /*
  3. * @file
  4. * Transcoder class file to handle ffmpeg settings and conversions.
  5. *
  6. */
  7. class video_ffmpeg implements transcoder_interface {
  8. // Naming for our radio options. Makes it easy to extend our transcoders.
  9. private $name = 'Locally Installed Transcoders (FFMPEG/Handbreke/Mcoder)';
  10. private $value = 'video_ffmpeg';
  11. protected $params = array();
  12. protected $thumb_command = '-i !videofile -an -y -f mjpeg -ss !seek -vframes 1 !thumbfile';
  13. protected $nice;
  14. public function __construct() {
  15. // setting up trasncoders path
  16. $this->params['ffmpeg'] = variable_get('video_ffmpeg_path', '/usr/bin/ffmpeg');
  17. $this->params['ffmpeg2theora'] = variable_get('video_ffmpeg2theora_path', '/usr/bin/ffmpeg2theora');
  18. $this->params['mcoder'] = variable_get('video_mcoder_path', '/usr/bin/mcoder');
  19. $this->params['handbreke'] = variable_get('video_handbreke_path', '/usr/bin/HandBrakeCLI');
  20. $this->params['other'] = variable_get('video_other_path', '');
  21. $this->params['thumb_command'] = variable_get('video_ffmpeg_thumbnailer_options', $this->thumb_command);
  22. $this->nice = variable_get('video_ffmpeg_nice_enable', false) ? 'nice -n 19 ' : '';
  23. $this->params['enable_faststart'] = variable_get('video_ffmpeg_enable_faststart', 0);
  24. $this->params['faststart_cmd'] = variable_get('video_ffmpeg_faststart_cmd', '/usr/bin/qt-faststart');
  25. }
  26. public function run_command($command) {
  27. // transcoder switching
  28. $command = strtr($command, array(
  29. '!ffmpeg' => $this->params['ffmpeg'],
  30. '!ffmpeg2theora' => $this->params['ffmpeg2theora'],
  31. '!mcoder' => $this->params['mcoder'],
  32. '!handbreke' => $this->params['handbreke'],
  33. '!other' => $this->params['other'],
  34. ));
  35. $command = $this->nice . $command . ' 2>&1';
  36. watchdog('transcoder', 'Executing command: ' . $command, array(), WATCHDOG_DEBUG);
  37. // ob_start();
  38. $command_return = shell_exec($command);
  39. // $output = ob_get_contents();
  40. // ob_end_clean();
  41. return $command_return;
  42. }
  43. public function generate_thumbnails($video) {
  44. global $user;
  45. // Setup our thmbnail path.
  46. $video_thumb_path = variable_get('video_thumb_path', 'videos/thumbnails');
  47. // Get the file system directory.
  48. // @todo : get the field file system settings to this
  49. $schema_thumb_path = file_default_scheme() . '://' . $video_thumb_path . '/' . $video['fid'];
  50. file_prepare_directory($schema_thumb_path, FILE_CREATE_DIRECTORY);
  51. // Total thumbs to generate
  52. $total_thumbs = variable_get('video_thumbs', 5);
  53. $videofile = file_load($video['fid']);
  54. //get the actual video file path from the stream wrappers
  55. $videopath = drupal_realpath($videofile->uri);
  56. //get the playtime from the current transcoder
  57. $duration = $this->get_playtime($videopath);
  58. $files = NULL;
  59. for ($i = 1; $i <= $total_thumbs; $i++) {
  60. $seek = ($duration / $total_thumbs) * $i - 1; //adding minus one to prevent seek times equaling the last second of the video
  61. $filename = file_munge_filename("video-thumb-" . $video['fid'] . "-$i.jpg", '', TRUE);
  62. $thumbfile = $schema_thumb_path . '/' . $filename;
  63. //skip files already exists, this will save ffmpeg traffic
  64. if (!is_file(drupal_realpath($thumbfile))) {
  65. //setup the command to be passed to the transcoder.
  66. $command = strtr($this->params['thumb_command'], array(
  67. '!videofile' => '"' . $videopath . '"',
  68. '!seek' => $seek,
  69. '!thumbfile' => '"' . drupal_realpath($thumbfile) . '"'
  70. ));
  71. // Generate the thumbnail from the video.
  72. $command_output = $this->run_command($command);
  73. if (!file_exists(drupal_realpath($thumbfile))) {
  74. $error_param = array('%file' => $thumbfile, '%cmd' => $command, '%out' => $command_output);
  75. $error_msg = t("Error generating thumbnail for video: generated file %file does not exist.<br />Command Executed:<br />%cmd<br />Command Output:<br />%out", $error_param);
  76. // Log the error message.
  77. watchdog('transcoder', $error_msg, array(), WATCHDOG_ERROR);
  78. continue;
  79. }
  80. }
  81. // Begin building the file object.
  82. // @TODO : use file_munge_filename()
  83. $file = new stdClass();
  84. $file->uid = $user->uid;
  85. $file->status = 0;
  86. $file->filename = trim($filename);
  87. $file->uri = $thumbfile;
  88. $file->filemime = file_get_mimetype($filename);
  89. $file->filesize = filesize(drupal_realpath($thumbfile));
  90. $file->timestamp = time();
  91. $files[] = $file;
  92. }
  93. return $files;
  94. }
  95. // Returns available codecs
  96. public function get_codecs() {
  97. $codecs = array(
  98. 'encode' => array(
  99. 'video' => array(
  100. 'h264' => 'H.264 (default)',
  101. 'vp8' => 'VP8',
  102. 'theora' => 'Theora',
  103. 'vp6' => 'VP6',
  104. 'mpeg4' => 'MPEG-4',
  105. 'wmv' => 'WMV'
  106. ),
  107. 'audio' => array(
  108. 'aac' => 'AAC (default for most cases)',
  109. 'mp3' => 'MP3',
  110. 'vorbis' => 'Vorbis (default for VP8 and Theora)',
  111. 'wma' => 'WMA'
  112. )
  113. ),
  114. 'decoding' => array()
  115. );
  116. return $codecs;
  117. }
  118. public function convert_video($video) {
  119. // This will update our current video status to active.
  120. // $this->change_status($video->vid, VIDEO_RENDERING_ACTIVE);
  121. // get the paths so tokens will compatible with this
  122. // @todo : add best method to get existing file path and add converted there
  123. $target = str_replace('original', '', drupal_dirname($video->uri));
  124. $converted_base_dir = $target . 'converted/' . $video->fid;
  125. if (!file_prepare_directory($converted_base_dir, FILE_CREATE_DIRECTORY)) {
  126. watchdog('transcoder', 'Video conversion failed. Could not create the directory: ' . $converted_base_dir, array(), WATCHDOG_ERROR);
  127. return FALSE;
  128. }
  129. //get the actual video file path from the stream wrappers
  130. $original_video_path = drupal_realpath($video->uri);
  131. // process presets
  132. $presets = $video->presets;
  133. $converted_files = array();
  134. foreach ($presets as $name => $preset) {
  135. $settings = $preset['settings'];
  136. // override with preset settings
  137. if (isset($settings['width']) && !empty($settings['width']) && isset($settings['height']) && !empty($settings['height'])
  138. && variable_get('video_use_preset_wxh', FALSE)) {
  139. $video->dimensions = $settings['width'] . 'x' . $settings['height'];
  140. }
  141. $converted = $converted_base_dir . '/' . file_munge_filename(str_replace(' ', '_', pathinfo($original_video_path, PATHINFO_FILENAME)) . '.' . $settings['video_extension'], $settings['video_extension']);
  142. //get the actual video file path from the stream wrappers
  143. $converted_video_path = drupal_realpath($converted);
  144. $dimensions = $this->dimensions($video);
  145. $dimention = explode('x', $dimensions);
  146. if ($this->params['enable_faststart'] && in_array($settings['video_extension'], array('mov', 'mp4'))) {
  147. $ffmpeg_output = file_directory_temp() . '/' . basename($converted_video_path);
  148. } else {
  149. $ffmpeg_output = $converted_video_path;
  150. }
  151. // Setup our default command to be run.
  152. $command = strtr($settings['cli_code'], array(
  153. '!videofile' => '"' . $original_video_path . '"',
  154. '!audiobitrate' => $settings['audio_bitrate'],
  155. '!width' => $dimention[0],
  156. '!height' => $dimention[1],
  157. '!videobitrate' => $settings['video_bitrate'],
  158. '!convertfile' => '"' . $ffmpeg_output . '"',
  159. ));
  160. // process our video
  161. $command_output = $this->run_command($command);
  162. if ($ffmpeg_output != $converted_video_path && file_exists($ffmpeg_output)) {
  163. // Because the transcoder_interface doesn't allow the run_command() to include the ability to pass
  164. // the command to be execute so we need to fudge the command to run qt-faststart.
  165. $cmd_path = $this->params['cmd_path'];
  166. $this->params['cmd_path'] = $this->params['faststart_cmd'];
  167. $command_output .= $this->run_command($ffmpeg_output . ' ' . $converted_video_path, $verbose);
  168. $this->params['cmd_path'] = $cmd_path;
  169. // Delete the temporary output file.
  170. drupal_unlink($ffmpeg_output);
  171. }
  172. //lets check to make sure our file exists, if not error out
  173. if (!file_exists($converted_video_path) || !filesize($converted_video_path)) {
  174. watchdog('transcoder', 'Video conversion failed for preset %preset. FFMPEG reported the following output: ' . $command_output, array('%orig' => $video->uri, '%preset' => $name), WATCHDOG_ERROR);
  175. $this->change_status($video->vid, VIDEO_RENDERING_FAILED);
  176. return FALSE;
  177. }
  178. // Setup our converted video object
  179. $video_info = pathinfo($converted_video_path);
  180. //update our converted video
  181. $video->converted = new stdClass();
  182. $video->converted->vid = $video->vid;
  183. $video->converted->filename = $video_info['basename'];
  184. $video->converted->uri = $converted;
  185. $video->converted->filemime = file_get_mimetype($converted);
  186. $video->converted->filesize = filesize($converted);
  187. $video->converted->status = VIDEO_RENDERING_COMPLETE;
  188. $video->converted->preset = $name;
  189. $video->converted->completed = time();
  190. $converted_files[] = $video->converted;
  191. }
  192. // Update our video_files table with the converted video information.
  193. db_update('video_files')
  194. ->fields(array(
  195. 'status' => VIDEO_RENDERING_COMPLETE,
  196. 'completed' => time(),
  197. 'data' => serialize($converted_files)))
  198. ->condition('vid', $video->converted->vid, '=')
  199. ->execute();
  200. watchdog('transcoder', 'Successfully converted %orig to %dest', array('%orig' => $video->uri, '%dest' => $video->converted->uri), WATCHDOG_INFO);
  201. return TRUE;
  202. }
  203. /**
  204. * Get some information from the video file
  205. */
  206. public function get_video_info($video) {
  207. static $command_ouput;
  208. if (!empty($command_output))
  209. return $command_output;
  210. $file = escapeshellarg($video);
  211. // Execute the command
  212. $options = '!ffmpeg -i ' . $file;
  213. $command_output = $this->run_command($options);
  214. return $command_output;
  215. }
  216. /**
  217. * Return the playtime seconds of a video
  218. */
  219. public function get_playtime($video) {
  220. $ffmpeg_output = $this->get_video_info($video);
  221. // Get playtime
  222. $pattern = '/Duration: ([0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9])/';
  223. preg_match_all($pattern, $ffmpeg_output, $matches, PREG_PATTERN_ORDER);
  224. $playtime = $matches[1][0];
  225. // ffmpeg return length as 00:00:31.1 Let's get playtime from that
  226. $hmsmm = explode(":", $playtime);
  227. $tmp = explode(".", $hmsmm[2]);
  228. $seconds = $tmp[0];
  229. $hours = $hmsmm[0];
  230. $minutes = $hmsmm[1];
  231. return $seconds + ($hours * 3600) + ($minutes * 60);
  232. }
  233. /*
  234. * Return the dimensions of a video
  235. */
  236. public function get_dimensions($video) {
  237. $ffmpeg_output = $this->get_video_info($video);
  238. $res = array('width' => 0, 'height' => 0);
  239. // Get dimensions
  240. $regex = preg_match('/([0-9]{1,5})x([0-9]{1,5})/', $ffmpeg_output, $regs);
  241. if (isset($regs[0])) {
  242. $dimensions = explode("x", $regs[0]);
  243. $res['width'] = $dimensions[0] ? $dimensions[0] : NULL;
  244. $res['height'] = $dimensions[1] ? $dimensions[1] : NULL;
  245. }
  246. return $res;
  247. }
  248. /**
  249. * Interface Implementations
  250. * @see sites/all/modules/video/includes/transcoder_interface#get_name()
  251. */
  252. public function get_name() {
  253. return $this->name;
  254. }
  255. /**
  256. * Interface Implementations
  257. * @see sites/all/modules/video/includes/transcoder_interface#get_value()
  258. */
  259. public function get_value() {
  260. return $this->value;
  261. }
  262. /**
  263. * Interface Implementations
  264. * @see sites/all/modules/video/includes/transcoder_interface#get_help()
  265. */
  266. public function get_help() {
  267. return l(t('FFMPEG Online Manual'), 'http://www.ffmpeg.org/');
  268. }
  269. /**
  270. * Interface Implementations
  271. * @see sites/all/modules/video/includes/transcoder_interface#admin_settings()
  272. */
  273. public function admin_settings() {
  274. $form = array();
  275. $form['video_ffmpeg_start'] = array(
  276. '#type' => 'markup',
  277. '#markup' => '<div id="video_ffmpeg">',
  278. );
  279. $form['video_ffmpeg_nice_enable'] = array(
  280. '#type' => 'checkbox',
  281. '#title' => t('Enable the use of <b>nice</b> when calling the command.'),
  282. '#default_value' => variable_get('video_ffmpeg_nice_enable', TRUE),
  283. '#description' => t('The nice command Invokes a command with an altered scheduling priority. This option may not be available on windows machines, so disable it.')
  284. );
  285. // FFMPEG
  286. $form['transcoders'] = array(
  287. '#type' => 'fieldset',
  288. '#title' => t('Path to Transcoder Executables'),
  289. '#collapsible' => TRUE,
  290. '#collapsed' => FALSE
  291. );
  292. $form['transcoders']['video_ffmpeg_path'] = array(
  293. '#type' => 'textfield',
  294. '#title' => t('FFMPEG'),
  295. '#description' => t('Absolute path to ffmpeg executable. This will provide a token of !ffmpeg to preset commands.'),
  296. '#default_value' => variable_get('video_ffmpeg_path', '/usr/bin/ffmpeg'),
  297. );
  298. $form['transcoders']['video_ffmpeg2theora_path'] = array(
  299. '#type' => 'textfield',
  300. '#title' => t('Ffmpeg2Theora'),
  301. '#description' => t('Absolute path to ffmpeg2theora executable. This will provide a token of !ffmpeg2theora to preset commands.'),
  302. '#default_value' => variable_get('video_ffmpeg2theora_path', '/usr/bin/ffmpeg2theora'),
  303. );
  304. $form['transcoders']['video_macoder_path'] = array(
  305. '#type' => 'textfield',
  306. '#title' => t('Mcoder'),
  307. '#description' => t('Absolute path to Mcoder executable. This will provide a token of !macoder to preset commands.'),
  308. '#default_value' => variable_get('video_macoder_path', '/usr/bin/mcoder'),
  309. );
  310. $form['transcoders']['video_handbreke_path'] = array(
  311. '#type' => 'textfield',
  312. '#title' => t('HandBrakeCLI'),
  313. '#description' => t('Absolute path to Handbreke executable. This will provide a token of !handbreke to preset commands.'),
  314. '#default_value' => variable_get('video_handbreke_path', '/usr/bin/HandBrakeCLI'),
  315. );
  316. $form['transcoders']['video_other_path'] = array(
  317. '#type' => 'textfield',
  318. '#title' => t('Other'),
  319. '#description' => t('Absolute path to other transcoder executable. This will provide a token of !other to preset commands.'),
  320. '#default_value' => variable_get('video_other_path', ''),
  321. );
  322. // Thumbnail Videos We need to put this stuff last.
  323. $form['autothumb'] = array(
  324. '#type' => 'fieldset',
  325. '#title' => t('Video Thumbnails'),
  326. '#collapsible' => TRUE,
  327. '#collapsed' => FALSE,
  328. );
  329. $form['autothumb']['video_thumb_path'] = array(
  330. '#type' => 'textfield',
  331. '#title' => t('Path to save thumbnails'),
  332. '#description' => t('Path to save video thumbnails extracted from the videos.'),
  333. '#default_value' => variable_get('video_thumb_path', 'videos/thumbnails'),
  334. );
  335. $form['autothumb']['video_thumbs'] = array(
  336. '#type' => 'textfield',
  337. '#title' => t('Number of thumbnails'),
  338. '#description' => t('Number of thumbnails to extract from video.'),
  339. '#default_value' => variable_get('video_thumbs', 5),
  340. );
  341. $form['autothumb']['video_thumb_save_all'] = array(
  342. '#type' => 'checkbox',
  343. '#title' => t('Save all thumbnails in {file_manged} table'),
  344. '#description' => t('Save all auto created thumbnails to the {file_managed} table. Change file status as PERMANENT'),
  345. '#default_value' => variable_get('video_thumb_save_all', FALSE),
  346. );
  347. $form['autothumb']['advanced'] = array(
  348. '#type' => 'fieldset',
  349. '#title' => t('Advanced Settings'),
  350. '#collapsible' => TRUE,
  351. '#collapsed' => TRUE
  352. );
  353. $form['autothumb']['advanced']['video_ffmpeg_thumbnailer_options'] = array(
  354. '#type' => 'textarea',
  355. '#title' => t('Video thumbnailer options'),
  356. '#description' => t('Provide the options for the thumbnailer. Available argument values are: ') . '<ol><li>' . t('!videofile (the video file to thumbnail)') . '<li>' . t('!thumbfile (a newly created temporary file to overwrite with the thumbnail)</ol>'),
  357. '#default_value' => variable_get('video_ffmpeg_thumbnailer_options', '!ffmpeg -i !videofile -an -y -f mjpeg -ss !seek -vframes 1 !thumbfile'),
  358. );
  359. // Video conversion settings.
  360. $form['autoconv'] = array(
  361. '#type' => 'fieldset',
  362. '#title' => t('Advanced Video Conversion'),
  363. '#collapsible' => TRUE,
  364. '#collapsed' => TRUE
  365. );
  366. $form['autoconv']['video_ffmpeg_enable_faststart'] = array(
  367. '#type' => 'checkbox',
  368. '#title' => t('Process mov/mp4 videos with qt-faststart'),
  369. '#default_value' => variable_get('video_ffmpeg_enable_faststart', 0),
  370. );
  371. $form['autoconv']['video_ffmpeg_faststart_cmd'] = array(
  372. '#type' => 'textfield',
  373. '#title' => t('Path to qt-faststart'),
  374. '#default_value' => variable_get('video_ffmpeg_faststart_cmd', '/usr/bin/qt-faststart'),
  375. );
  376. $form['autoconv']['video_ffmpeg_pad_method'] = array(
  377. '#type' => 'radios',
  378. '#title' => t('FFMPeg Padding method'),
  379. '#default_value' => variable_get('video_ffmpeg_pad_method', 0),
  380. '#options' => array(
  381. 0 => t('Use -padtop, -padbottom, -padleft, -padright for padding'),
  382. 1 => t('Use -vf "pad:w:h:x:y:c" for padding'),
  383. ),
  384. );
  385. $form['video_ffmpeg_end'] = array(
  386. '#type' => 'markup',
  387. '#markup' => '</div>',
  388. );
  389. return $form;
  390. }
  391. /**
  392. * Interface Implementations
  393. * @see sites/all/modules/video/includes/transcoder_interface#admin_settings_validate()
  394. */
  395. public function admin_settings_validate($form, &$form_state) {
  396. return;
  397. }
  398. /**
  399. * Interface Implementations
  400. * @see sites/all/modules/video/includes/transcoder_interface#create_job()
  401. */
  402. public function create_job($video, $nid) {
  403. return db_insert('video_files')
  404. ->fields(array(
  405. 'fid' => $video['fid'],
  406. 'nid' => $nid,
  407. 'status' => VIDEO_RENDERING_PENDING,
  408. 'dimensions' => $video['dimensions'],
  409. ))
  410. ->execute();
  411. }
  412. /**
  413. * Interface Implementations
  414. * @see sites/all/modules/video/includes/transcoder_interface#delete_job()
  415. */
  416. public function delete_job($video) {
  417. $video = (object) $video;
  418. if (!$video = $this->load_job($video->fid))
  419. return;
  420. // converted output values
  421. $converted = unserialize($video->data);
  422. if (!empty($converted)) {
  423. foreach ($converted as $file) {
  424. if (file_exists(drupal_realpath($file->uri)))
  425. @drupal_unlink($file->uri);
  426. }
  427. }
  428. //now delete our rows.
  429. db_delete('video_files')
  430. ->condition('fid', $video->fid)
  431. ->execute();
  432. }
  433. /**
  434. * Interface Implementations
  435. * @see sites/all/modules/video/includes/transcoder_interface#load_job()
  436. */
  437. public function load_job($fid) {
  438. $job = null;
  439. $job = db_query("SELECT f.*, vf.vid, vf.nid, vf.dimensions, vf.data, vf.status as video_status
  440. FROM {video_files} vf LEFT JOIN {file_managed} f ON vf.fid = f.fid WHERE f.fid=vf.fid AND f.fid = :fid",
  441. array(':fid' => $fid))
  442. ->fetch();
  443. if (!empty($job))
  444. return $job;
  445. else
  446. return FALSE;
  447. }
  448. /**
  449. * Interface Implementations
  450. * @see sites/all/modules/video/includes/transcoder_interface#load_job_queue()
  451. */
  452. public function load_job_queue() {
  453. $total_videos = variable_get('video_ffmpeg_instances', 5);
  454. $videos = array();
  455. $result = db_query_range('SELECT f.*, vf.vid, vf.nid, vf.dimensions, vf.status as video_status
  456. FROM {video_files} vf LEFT JOIN {file_managed} f ON vf.fid = f.fid
  457. WHERE vf.status = :vstatus AND f.status = :fstatus ORDER BY f.timestamp',
  458. 0, $total_videos, array(':vstatus' => VIDEO_RENDERING_PENDING, ':fstatus' => FILE_STATUS_PERMANENT));
  459. foreach ($result as $row) {
  460. $videos[] = $row;
  461. }
  462. return $videos;
  463. }
  464. /**
  465. * Interface Implementations
  466. * @see sites/all/modules/video/includes/transcoder_interface#load_completed_job()
  467. */
  468. public function load_completed_job(&$video) {
  469. $file = $this->load_job($video->fid);
  470. $data = unserialize($file->data);
  471. if (!empty($data))
  472. foreach ($data as $value) {
  473. $extension = pathinfo(drupal_realpath($value->uri), PATHINFO_EXTENSION);
  474. $video->files->{$extension}->filename = $value->filename;
  475. $video->files->{$extension}->filepath = $value->uri;
  476. $video->files->{$extension}->filemime = file_get_mimetype($value->uri);
  477. $video->files->{$extension}->url = file_create_url($value->uri);
  478. $video->files->{$extension}->uri = $value->uri;
  479. $video->files->{$extension}->extension = $extension;
  480. $video->player = strtolower($extension);
  481. }
  482. else
  483. return FALSE;
  484. }
  485. /**
  486. * Change the status of the file.
  487. *
  488. * @param (int) $vid
  489. * @param (int) $status
  490. */
  491. public function change_status($vid, $status) {
  492. db_update('video_files')->fields(array(
  493. 'status' => $status,))
  494. ->condition('vid', $vid, '=')
  495. ->execute();
  496. }
  497. /*
  498. * Function determines the dimensions you want and compares with the actual wxh of the video.
  499. *
  500. * If they are not exact or the aspect ratio does not match, we then figure out how much padding
  501. * we should add. We will either add a black bar on the top/bottom or on the left/right.
  502. *
  503. * @TODO I need to look more at this function. I don't really like the guess work here. Need to implement
  504. * a better way to check the end WxH. Maybe compare the final resolution to our defaults? I don't think
  505. * that just checking to make sure the final number is even is accurate enough.
  506. */
  507. public function dimensions($video) {
  508. //lets setup our dimensions. Make sure our aspect ratio matches the dimensions to be used, if not lets add black bars.
  509. $aspect_ratio = _video_aspect_ratio(drupal_realpath($video->uri));
  510. $ratio = $aspect_ratio['ratio'];
  511. $width = $aspect_ratio ['width'];
  512. $height = $aspect_ratio['height'];
  513. $wxh = explode('x', $video->dimensions);
  514. $output_width = $wxh[0];
  515. $output_height = $wxh[1];
  516. $output_ratio = number_format($output_width / $output_height, 4);
  517. if ($output_ratio != $ratio && $width && $height) {
  518. $options = array();
  519. // Figure out our black bar padding.
  520. if ($ratio < $output_width / $output_height) {
  521. $end_width = $output_height * $ratio;
  522. $end_height = $output_height;
  523. } else {
  524. $end_height = $output_width / $ratio;
  525. $end_width = $output_width;
  526. }
  527. // We need to get back to an even resolution and maybe compare with our defaults?
  528. // @TODO Make this more exact on actual video dimensions instead of making sure the wxh are even numbers
  529. if ($end_width == $output_width) {
  530. // We need to pad the top/bottom of the video
  531. $padding = round($output_height - $end_height);
  532. $pad1 = $pad2 = floor($padding / 2);
  533. if ($pad1 % 2 !== 0) {
  534. $pad1++;
  535. $pad2--;
  536. }
  537. if (variable_get('video_ffmpeg_pad_method', 0)) {
  538. $options[] = '-vf "pad=' . round($output_width) . ':' . round($output_height) . ':0:' . $pad1 . '"';
  539. } else {
  540. $options[] = '-padtop ' . $pad1;
  541. $options[] = '-padbottom ' . $pad2;
  542. }
  543. } else {
  544. // We are padding the left/right of the video.
  545. $padding = round($output_width - $end_width);
  546. $pad1 = $pad2 = floor($padding / 2); //@todo does padding need to be an even number?
  547. if ($pad1 % 2 !== 0) {
  548. $pad1++;
  549. $pad2--;
  550. }
  551. if (variable_get('video_ffmpeg_pad_method', 0)) {
  552. $options[] = '-vf "pad=' . round($output_width) . ':' . round($output_height) . ':' . $pad1 . ':0"';
  553. } else {
  554. $options[] = '-padleft ' . $pad1;
  555. $options[] = '-padright ' . $pad2;
  556. }
  557. }
  558. $end_width = round($end_width) % 2 !== 0 ? round($end_width) + 1 : round($end_width);
  559. $end_height = round($end_height) % 2 !== 0 ? round($end_height) + 1 : round($end_height);
  560. //add our size to the beginning to make sure it hits our -s
  561. array_unshift($options, $end_width . 'x' . $end_height);
  562. return implode(' ', $options);
  563. } else {
  564. return $video->dimensions;
  565. }
  566. }
  567. }
  568. ?>