| [ Index ] |
PHP Cross Reference of Drupal 6 (yi-drupal) |
[Summary view] [Print] [Text view]
1 <?php 2 /* 3 * @file 4 * Transcoder class file to handle ffmpeg settings and conversions. 5 */ 6 class video_ffmpeg implements transcoder_interface { 7 private $name = 'Locally Installed Transcoder (FFmpeg/Handbreke/Mcoder)'; 8 private $value = 'video_ffmpeg'; 9 10 protected $nice; 11 protected $thumbcmdoptions; 12 protected $enablefaststart; 13 protected $faststartcmd; 14 protected $cmdpath; 15 protected $logcommands; 16 17 public function __construct() { 18 $this->nice = variable_get('video_ffmpeg_nice_enable', FALSE) ? 'nice -n 19 ' : ''; 19 $this->thumbcmdoptions = variable_get('video_ffmpeg_thumbnailer_options', '-i !videofile -an -y -f mjpeg -ss !seek -vframes 1 !thumbfile'); 20 $this->enablefaststart = variable_get('video_ffmpeg_enable_faststart', FALSE); 21 $this->faststartcmd = variable_get('video_ffmpeg_faststart_cmd', '/usr/bin/qt-faststart'); 22 $this->cmdpath = variable_get('video_transcoder_path', '/usr/bin/ffmpeg'); 23 $this->logcommands = (bool)variable_get('video_ffmpeg_log_commands', TRUE); 24 } 25 26 /** 27 * Run the specified command 28 * 29 * The nice prefix is automatically added. 30 * The command is logged if the settings specify that all commands should be logged. 31 * The command and error are logged if the command results in an error 32 * 33 * @param string $command 34 * @param string $output Output of the command 35 * @param string $purpose Purpose of the command. This is logged. 36 * @param bool $ignoreoutputfilenotfound Whether to the output file not found error. Useful for input file information. 37 */ 38 private function run_command($command, &$output, $purpose = NULL, $ignoreoutputfilenotfound = FALSE) { 39 $output = ''; 40 $command = $this->nice . $command .' 2>&1'; 41 $purposetext = !empty($purpose) ? (' '. t('for') .' '. $purpose) : ''; 42 43 if ($this->logcommands) { 44 watchdog('video_ffmpeg', 'Executing ffmpeg command!purposetext: <pre>@command</pre>', array('@command' => $command, '!purposetext' => $purposetext), WATCHDOG_DEBUG); 45 } 46 47 $return_var = 0; 48 49 ob_start(); 50 passthru($command, &$return_var); 51 $output = ob_get_clean(); 52 53 // Returnvar 1 means input file not found. This is normal for information calls. 54 if ($return_var != 0 && ($return_var != 1 || !$ignoreoutputfilenotfound)) { 55 watchdog('video_ffmpeg', 56 'Error executing ffmpeg command!purposetext:<br/><pre>@command</pre>Output:<br/><pre>@output</pre>', 57 array('@command' => $command, '@output' => trim($output), '!purposetext' => $purposetext), 58 WATCHDOG_ERROR); 59 return FALSE; 60 } 61 62 return TRUE; 63 } 64 65 public function generate_thumbnails($video) { 66 global $user; 67 68 $final_thumb_path = video_thumb_path($video); 69 $total_thumbs = variable_get('video_thumbs', 5); 70 71 $files = NULL; 72 for ($i = 1; $i <= $total_thumbs; $i++) { 73 $filename = '/video-thumb-for-'. $video['fid'] .'-'. $i .'.jpg'; 74 $thumbfile = $final_thumb_path . $filename; 75 76 //skip files already exists, this will save ffmpeg traffic 77 if (!is_file($thumbfile)) { 78 if (!isset($duration)) { 79 $duration = $this->get_playtime($video['filepath']); 80 } 81 $seek = ($duration / $total_thumbs) * $i - 1; //adding minus one to prevent seek times equaling the last second of the video 82 //setup the command to be passed to the transcoder. 83 $command = $this->cmdpath .' '. strtr($this->thumbcmdoptions, array('!videofile' => escapeshellarg($video['filepath']), '!seek' => $seek, '!thumbfile' => $thumbfile)); 84 85 // Generate the thumbnail from the video. 86 $command_output = ''; 87 $this->run_command($command, $command_output, t('generating thumbnails')); 88 89 // don't consider zero-byte files a success 90 $exists = file_exists($thumbfile); 91 if (!$exists || filesize($thumbfile) == 0) { 92 $params = array('%file' => $thumbfile, '%video' => $video['filename']); 93 94 if ($exists) { 95 $error_msg = 'Error generating thumbnail for video %video: generated file %file is empty. This problem may be caused by a broken video file. The video reports that its length is @duration seconds. If this is wrong, please recreate the video and try again.'; 96 $params['@duration'] = $duration; 97 unlink($thumbfile); 98 } 99 else { 100 $error_msg = 'Error generating thumbnail for video %video: generated file %file does not exist.'; 101 } 102 103 // Log the error message and break. Other thumbnails probably will not be generated as well. 104 watchdog('video_ffmpeg', $error_msg, $params, WATCHDOG_ERROR); 105 drupal_set_message(t($error_msg, $params), 'error'); 106 break; 107 } 108 } 109 110 // Begin building the file object. 111 // @TODO : use file_munge_filename() 112 $file = new stdClass(); 113 $file->uid = $user->uid; 114 $file->status = FILE_STATUS_TEMPORARY; 115 $file->filename = $filename; 116 $file->filepath = $thumbfile; 117 $file->filemime = 'image/jpeg'; 118 $file->filesize = filesize($thumbfile); 119 $file->timestamp = time(); 120 $files[] = $file; 121 } 122 return $files; 123 } 124 125 public function convert_video($video) { 126 // This will update our current video status to active. 127 $this->change_status($video->vid, VIDEO_RENDERING_ACTIVE); 128 129 // Get the converted file object 130 //we are going to move our video to an "original" folder 131 //we are going to transcode the video to the "converted" folder 132 // @TODO This about getting the correct path from the filefield if they active it 133 $files = file_create_path(); 134 $originaldir = $files .'/videos/original'; 135 $converteddir = $files .'/videos/converted'; 136 137 if (!field_file_check_directory($originaldir, FILE_CREATE_DIRECTORY)) { 138 watchdog('video_ffmpeg', 'Video conversion failed. Could not create directory %dir for storing original videos.', array('%dir' => $originaldir), WATCHDOG_ERROR); 139 return false; 140 } 141 if (!field_file_check_directory($converteddir, FILE_CREATE_DIRECTORY)) { 142 watchdog('video_ffmpeg', 'Video conversion failed. Could not create directory %dir for storing converted videos.', array('%dir' => $converteddir), WATCHDOG_ERROR); 143 return false; 144 } 145 146 $original = $originaldir .'/'. $video->filename; 147 //lets move our video and then convert it. 148 if (!file_move($video, $original)) { 149 watchdog('video_ffmpeg', 'Could not move video %orig to the original folder.', array('%orig' => $video->filepath), WATCHDOG_ERROR); 150 $this->change_status($video->vid, VIDEO_RENDERING_FAILED); 151 return FALSE; 152 } 153 154 // Update our filepath since we moved it 155 drupal_write_record('files', $video, 'fid'); 156 157 // Increase the database timeout to prevent database errors after a long upload 158 _video_db_increase_timeout(); 159 160 // Get video information before doing a chdir 161 $dimensions = $this->dimensions($video); 162 $dimension = explode('x', $dimensions, 2); 163 $filepath = realpath($video->filepath); 164 165 // Make the directories absolute 166 $originaldir = realpath($originaldir); 167 $converteddir = realpath($converteddir); 168 169 // Create a temporary directory 170 $drupaldir = getcwd(); 171 $drupaldirrp = realpath($drupaldir); 172 $tmpdir = tempnam(file_directory_temp(), 'ffmpeg-'. $video->fid); 173 unlink($tmpdir); 174 mkdir($tmpdir, 0777); 175 chdir($tmpdir); 176 177 $result = TRUE; 178 179 // process presets 180 $presets = $video->presets; 181 $converted_files = array(); 182 foreach ($presets as $presetname => $preset) { 183 //update our filename after the move to maintain filename uniqueness. 184 $converted = file_create_filename(str_replace(' ', '_', pathinfo($video->filepath, PATHINFO_FILENAME)) .'.'. $preset['extension'], $converteddir); 185 186 //call our transcoder 187 $ffmpeg_output = $converted; 188 if ($this->enablefaststart && ($preset['extension'] == 'mp4' || $preset['extension'] == 'mov')) { 189 $ffmpeg_output = file_directory_temp() .'/'. basename($converted); 190 } 191 192 $command_output = ''; 193 194 // Setup our default command to be run. 195 foreach ($preset['command'] as $command) { 196 $command = strtr($command, array( 197 '!cmd_path' => $this->cmdpath, 198 '!videofile' => escapeshellarg($filepath), 199 '!audiobitrate' => $preset['audio_bitrate'], 200 '!width' => intval($dimension[0]), 201 '!height' => intval($dimension[1]), 202 '!videobitrate' => $preset['video_bitrate'], 203 '!convertfile' => escapeshellarg($ffmpeg_output), 204 )); 205 206 // Process our video 207 if (!$this->run_command($command, $command_output, t('rendering preset %preset', array('%preset' => $presetname)))) { 208 $result = FALSE; 209 break 2; 210 } 211 } 212 213 if ($ffmpeg_output != $converted && file_exists($ffmpeg_output)) { 214 // Because the transcoder_interface doesn't allow the run_command() to include the ability to pass 215 // the command to be execute so we need to fudge the command to run qt-faststart. 216 $command_result = $this->run_command($this->faststartcmd .' '. $ffmpeg_output .' '. $converted, $command_output, t('running qt-faststart')); 217 218 // Delete the temporary output file. 219 file_delete($ffmpeg_output); 220 } 221 222 //lets check to make sure our file exists, if not error out 223 if (!file_exists($converted) || ($filesize = filesize($converted)) === 0) { 224 watchdog('video_ffmpeg', 'Video conversion failed for preset %preset: result file was not found.', array('%preset' => $presetname), WATCHDOG_ERROR); 225 $result = FALSE; 226 break; 227 } 228 229 $converted = realpath($converted); 230 231 //Create result object 232 $converted_files[] = $file = new stdClass(); 233 $file->vid = intval($video->vid); 234 $file->filename = basename($converted); 235 $file->filepath = substr($converted, strlen($drupaldirrp) + 1); 236 $file->filemime = file_get_mimetype($converted); 237 $file->filesize = $filesize; 238 $file->preset = $presetname; 239 } 240 241 chdir($drupaldir); 242 rmdirr($tmpdir); 243 244 // Update our video_files table with the converted video information. 245 if ($result) { 246 $result = db_query('UPDATE {video_files} SET status = %d, completed = %d, data = "%s" WHERE vid = %d', VIDEO_RENDERING_COMPLETE, time(), serialize($converted_files), $video->vid); 247 248 // Prepare the watchdog statement 249 $destinationfiles = array(); 250 foreach ($converted_files as $file) { 251 $destinationfiles[] = $file->filepath; 252 } 253 watchdog('video_ffmpeg', 'Successfully converted %orig to !destination-files', array('%orig' => $video->filepath, '!destination-files' => implode(', ', $destinationfiles)), WATCHDOG_INFO); 254 } 255 else { 256 // Remove files that have been created 257 foreach ($converted_files as $file) { 258 file_delete($file->filepath); 259 } 260 261 // Try to move back the original file 262 file_move($video, $files .'/videos'); 263 drupal_write_record('files', $video, 'fid'); 264 265 $this->change_status($video->vid, VIDEO_RENDERING_FAILED); 266 } 267 268 return $result; 269 } 270 271 /** 272 * Get some information from the video file 273 */ 274 private function get_video_info($video) { 275 $command = $this->cmdpath .' -i '. escapeshellarg($video); 276 277 // Execute the command 278 $output = ''; 279 if ($this->run_command($command, $output, t('retrieving video info'), true)) { 280 return $output; 281 } 282 283 return NULL; 284 } 285 286 /** 287 * Return the playtime seconds of a video 288 */ 289 public function get_playtime($video) { 290 $ffmpeg_output = $this->get_video_info($video); 291 // Get playtime 292 $pattern = '/Duration: ([0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9])/'; 293 preg_match_all($pattern, $ffmpeg_output, $matches, PREG_PATTERN_ORDER); 294 $playtime = $matches[1][0]; 295 // ffmpeg return length as 00:00:31.1 Let's get playtime from that 296 $hmsmm = explode(':', $playtime); 297 $tmp = explode('.', $hmsmm[2]); 298 $seconds = $tmp[0]; 299 $hours = $hmsmm[0]; 300 $minutes = $hmsmm[1]; 301 return $seconds + ($hours * 3600) + ($minutes * 60); 302 } 303 304 /* 305 * Return the dimensions of a video 306 */ 307 308 public function get_dimensions($video) { 309 $ffmpeg_output = $this->get_video_info($video); 310 $res = array('width' => 0, 'height' => 0); 311 // Get dimensions 312 $regex = ereg('[0-9]?[0-9][0-9][0-9]x[0-9][0-9][0-9][0-9]?', $ffmpeg_output, $regs); 313 if (isset($regs[0])) { 314 $dimensions = explode("x", $regs[0]); 315 $res['width'] = $dimensions[0] ? $dimensions[0] : NULL; 316 $res['height'] = $dimensions[1] ? $dimensions[1] : NULL; 317 } 318 return $res; 319 } 320 321 /** 322 * Interface Implementations 323 * @see sites/all/modules/video/includes/transcoder_interface#get_name() 324 */ 325 public function get_name() { 326 return $this->name; 327 } 328 329 /** 330 * Interface Implementations 331 * @see sites/all/modules/video/includes/transcoder_interface#get_value() 332 */ 333 public function get_value() { 334 return $this->value; 335 } 336 337 /** 338 * Interface Implementations 339 * @see sites/all/modules/video/includes/transcoder_interface#get_help() 340 */ 341 public function get_help() { 342 return l(t('FFMPEG Online Manual'), 'http://www.ffmpeg.org/'); 343 } 344 345 /** 346 * Interface Implementations 347 * @see sites/all/modules/video/includes/transcoder_interface#admin_settings() 348 */ 349 public function admin_settings() { 350 $form = array(); 351 $form['video_ffmpeg_start'] = array( 352 '#type' => 'markup', 353 '#value' => '<div id="video_ffmpeg">', 354 ); 355 356 $form['video_transcoder_path'] = array( 357 '#type' => 'textfield', 358 '#title' => t('Path to Video Transcoder'), 359 '#description' => t('Absolute path to ffmpeg.'), 360 '#default_value' => $this->cmdpath, 361 ); 362 $form['video_thumbs'] = array( 363 '#type' => 'textfield', 364 '#title' => t('Number of thumbnails'), 365 '#description' => t('Number of thumbnails to display from video.'), 366 '#default_value' => variable_get('video_thumbs', 5), 367 ); 368 $form['video_ffmpeg_nice_enable'] = array( 369 '#type' => 'checkbox', 370 '#title' => t('Enable the use of nice when calling the ffmpeg command.'), 371 '#default_value' => variable_get('video_ffmpeg_nice_enable', TRUE), 372 '#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.') 373 ); 374 $form['video_ffmpeg_log_commands'] = array( 375 '#type' => 'checkbox', 376 '#title' => t('Log all executed commands to the Drupal log.'), 377 '#default_value' => $this->logcommands, 378 '#description' => t('Enable this option when debugging ffmpeg encoding to log all commands to the <a href="@dblog-page">Drupal log</a>. This may help with debugging ffmpeg problems. When this option is disabled, only errors will be logged.', array('@dblog-page' => url('admin/reports/dblog'))) 379 ); 380 // Thumbnail Videos We need to put this stuff last. 381 $form['autothumb'] = array( 382 '#type' => 'fieldset', 383 '#title' => t('Video Thumbnails'), 384 '#collapsible' => TRUE, 385 '#collapsed' => TRUE, 386 ); 387 $form['autothumb']['video_thumb_path'] = array( 388 '#type' => 'textfield', 389 '#title' => t('Path to save thumbnails'), 390 '#description' => t('Path to save video thumbnails extracted from the videos.'), 391 '#default_value' => variable_get('video_thumb_path', 'video_thumbs'), 392 ); 393 $form['autothumb']['advanced'] = array( 394 '#type' => 'fieldset', 395 '#title' => t('Advanced Settings'), 396 '#collapsible' => TRUE, 397 '#collapsed' => TRUE 398 ); 399 $form['autothumb']['advanced']['video_ffmpeg_thumbnailer_options'] = array( 400 '#type' => 'textarea', 401 '#title' => t('Video thumbnailer options'), 402 '#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>'), 403 '#default_value' => $this->thumbcmdoptions, 404 ); 405 406 // Video conversion settings. 407 $form['autoconv'] = array( 408 '#type' => 'fieldset', 409 '#title' => t('Video Conversion'), 410 '#collapsible' => TRUE, 411 '#collapsed' => TRUE 412 ); 413 $form['autoconv']['video_ffmpeg_enable_faststart'] = array( 414 '#type' => 'checkbox', 415 '#title' => t('Process mov/mp4 videos with qt-faststart'), 416 '#default_value' => $this->enablefaststart, 417 ); 418 $form['autoconv']['video_ffmpeg_faststart_cmd'] = array( 419 '#type' => 'textfield', 420 '#title' => t('Path to qt-faststart'), 421 '#default_value' => $this->faststartcmd, 422 ); 423 424 $form['autoconv']['video_ffmpeg_pad_method'] = array( 425 '#type' => 'radios', 426 '#title' => t('FFmpeg Padding method'), 427 '#default_value' => variable_get('video_ffmpeg_pad_method', 0), 428 '#options' => array( 429 0 => t('Use -padtop, -padbottom, -padleft, -padright for padding'), 430 1 => t('Use -vf "pad:w:h:x:y:c" for padding'), 431 ), 432 ); 433 434 $form['video_ffmpeg_end'] = array( 435 '#type' => 'markup', 436 '#value' => '</div>', 437 ); 438 return $form; 439 } 440 441 /** 442 * Interface Implementations 443 * @see sites/all/modules/video/includes/transcoder_interface#admin_settings_validate() 444 */ 445 public function admin_settings_validate($form, &$form_state) { 446 return; 447 } 448 449 public function create_job($video) { 450 if (empty($video['dimensions'])) { 451 watchdog('video_ffmpeg', 'Tried to create ffmpeg job for video %video with empty dimensions value.', array('%video' => $video['fid']), WATCHDOG_ERROR); 452 return FALSE; 453 } 454 455 return db_query("INSERT INTO {video_files} (fid, status, dimensions) VALUES (%d, %d, '%s')", $video['fid'], VIDEO_RENDERING_PENDING, $video['dimensions']); 456 } 457 458 public function update_job($video) { 459 if (!$this->load_job($video['fid'])) 460 return; 461 //lets update our table to include the nid 462 db_query("UPDATE {video_files} SET nid=%d WHERE fid=%d", $video['nid'], $video['fid']); 463 } 464 465 public function delete_job($video) { 466 if (!$this->load_job($video->fid)) 467 return; 468 //lets get all our videos and unlink them 469 $sql = db_query("SELECT data FROM {video_files} WHERE fid=%d", $video->fid); 470 //we loop here as future development will include multiple video types (HTML 5) 471 while ($row = db_fetch_object($sql)) { 472 $data = unserialize($row->data); 473 if (empty($data)) 474 continue; 475 foreach ($data as $file) { 476 if (file_exists($file->filepath)) 477 unlink($file->filepath); 478 } 479 } 480 //now delete our rows. 481 db_query('DELETE FROM {video_files} WHERE fid = %d', $video->fid); 482 } 483 484 public function load_job($fid) { 485 $job = null; 486 $result = db_query('SELECT f.*, vf.vid, vf.nid, vf.dimensions, vf.status as video_status FROM {video_files} vf LEFT JOIN {files} f ON vf.fid = f.fid WHERE f.fid=vf.fid AND f.fid = %d', $fid); 487 $job = db_fetch_object($result); 488 if (!empty($job)) 489 return $job; 490 else 491 return FALSE; 492 } 493 494 public function load_job_queue() { 495 $total_videos = variable_get('video_ffmpeg_instances', 5); 496 $videos = array(); 497 $result = db_query_range('SELECT f.*, vf.vid, vf.nid, vf.dimensions, vf.status as video_status FROM {video_files} vf LEFT JOIN {files} f ON vf.fid = f.fid WHERE vf.status = %d AND f.status = %d ORDER BY f.timestamp', VIDEO_RENDERING_PENDING, FILE_STATUS_PERMANENT, 0, $total_videos); 498 499 while ($row = db_fetch_object($result)) { 500 $videos[] = $row; 501 } 502 return $videos; 503 } 504 505 /** 506 * @todo : replace with the load job method 507 * @param <type> $video 508 * @return <type> 509 */ 510 public function load_completed_job(&$video) { 511 $result = db_fetch_object(db_query('SELECT * FROM {video_files} WHERE fid = %d', $video->fid)); 512 $data = unserialize($result->data); 513 if (empty($data)) 514 return $video; 515 foreach ($data as $value) { 516 $extension = pathinfo($value->filepath, PATHINFO_EXTENSION); 517 $video->files->{$extension}->filename = pathinfo($value->filepath, PATHINFO_FILENAME) . '.' . $extension; 518 $video->files->{$extension}->filepath = $value->filepath; 519 $video->files->{$extension}->url = file_create_url($value->filepath); 520 $video->files->{$extension}->extension = $extension; 521 $video->files->{$extension}->filemime = file_get_mimetype($value->filepath); 522 $video->player = strtolower($extension); 523 } 524 return $video; 525 } 526 527 /** 528 * Change the status of the file. 529 * 530 * @param (int) $vid 531 * @param (int) $status 532 */ 533 public function change_status($vid, $status) { 534 $result = db_query('UPDATE {video_files} SET status = %d WHERE vid = %d ', $status, $vid); 535 } 536 537 /* 538 * Function determines the dimensions you want and compares with the actual wxh of the video. 539 * 540 * If they are not exact or the aspect ratio does not match, we then figure out how much padding 541 * we should add. We will either add a black bar on the top/bottom or on the left/right. 542 * 543 * @TODO I need to look more at this function. I don't really like the guess work here. Need to implement 544 * a better way to check the end WxH. Maybe compare the final resolution to our defaults? I don't think 545 * that just checking to make sure the final number is even is accurate enough. 546 */ 547 548 public function dimensions($video) { 549 //lets setup our dimensions. Make sure our aspect ratio matches the dimensions to be used, if not lets add black bars. 550 $aspect_ratio = _video_aspect_ratio($video->filepath); 551 $ratio = $aspect_ratio['ratio']; 552 $width = $aspect_ratio ['width']; 553 $height = $aspect_ratio['height']; 554 555 $wxh = explode('x', $video->dimensions); 556 $output_width = $wxh[0]; 557 $output_height = $wxh[1]; 558 $output_ratio = number_format($output_width / $output_height, 4); 559 560 if ($output_ratio != $ratio && $width && $height) { 561 // TODO this probably doesn't work 562 $options = array(); 563 // Figure out our black bar padding. 564 if ($ratio < $output_width / $output_height) { 565 $end_width = $output_height * $ratio; 566 $end_height = $output_height; 567 } else { 568 $end_height = $output_width / $ratio; 569 $end_width = $output_width; 570 } 571 572 // We need to get back to an even resolution and maybe compare with our defaults? 573 // @TODO Make this more exact on actual video dimensions instead of making sure the wxh are even numbers 574 575 if ($end_width == $output_width) { 576 // We need to pad the top/bottom of the video 577 $padding = round($output_height - $end_height); 578 $pad1 = $pad2 = floor($padding / 2); 579 if ($pad1 % 2 !== 0) { 580 $pad1++; 581 $pad2--; 582 } 583 if (variable_get('video_ffmpeg_pad_method', 0)) { 584 $options[] = '-vf "pad=' . round($output_width) . ':' . round($output_height) . ':0:' . $pad1 . '"'; 585 } else { 586 $options[] = '-padtop ' . $pad1; 587 $options[] = '-padbottom ' . $pad2; 588 } 589 } else { 590 // We are padding the left/right of the video. 591 $padding = round($output_width - $end_width); 592 $pad1 = $pad2 = floor($padding / 2); //@todo does padding need to be an even number? 593 if ($pad1 % 2 !== 0) { 594 $pad1++; 595 $pad2--; 596 } 597 if (variable_get('video_ffmpeg_pad_method', 0)) { 598 $options[] = '-vf "pad=' . round($output_width) . ':' . round($output_height) . ':' . $pad1 . ':0"'; 599 } else { 600 $options[] = '-padleft ' . $pad1; 601 $options[] = '-padright ' . $pad2; 602 } 603 } 604 605 $end_width = round($end_width) % 2 !== 0 ? round($end_width) + 1 : round($end_width); 606 $end_height = round($end_height) % 2 !== 0 ? round($end_height) + 1 : round($end_height); 607 //add our size to the beginning to make sure it hits our -s 608 array_unshift($options, $end_width . 'x' . $end_height); 609 return implode(' ', $options); 610 } 611 else { 612 return $video->dimensions; 613 } 614 } 615 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Jul 9 18:01:44 2012 | Cross-referenced by PHPXref 0.7 |