You are here

class Transcoder in Video 7.2

@file Class file used to wrap the transcoder helper functions.

Hierarchy

Expanded class hierarchy of Transcoder

1 string reference to 'Transcoder'
video_scheduler.php in ./video_scheduler.php
Implement video rendering scheduling. If you are not using sites/default/settings.php as your settings file, add an optional parameter for the drupal site url: "php video_scheduler.php http://example.com/" or "php video_scheduler.php…

File

includes/Transcoder.inc, line 7
Class file used to wrap the transcoder helper functions.

View source
class Transcoder {

  /**
   * @var TranscoderFactoryInterface
   */
  private $transcoder;
  public function __construct() {
    $transcoder = variable_get('video_convertor', 'TranscoderAbstractionFactoryFfmpeg');
    $this->transcoder = self::createTranscoder($transcoder);
  }
  public function hasTranscoder() {
    return $this->transcoder != NULL;
  }

  /**
   * Returns the current transcoder implementation.
   *
   * @return
   *   TranscoderFactoryInterface
   */
  public function getTranscoder() {
    return $this->transcoder;
  }

  /**
   * Extract frames from the video file. This helper function will interact with
   * only the database and it will save all the thumbnail file reference in to
   * the database.
   *
   * @return
   *   array of file objects, or false on failure
   */
  public function extractFrames(array $video, array $field) {
    global $user;
    $thumbnails = db_query('SELECT f.* FROM {file_managed} f INNER JOIN {video_thumbnails} tn ON tn.thumbnailfid = f.fid WHERE tn.videofid = :fid ORDER BY f.fid', array(
      ':fid' => $video['fid'],
    ))
      ->fetchAllAssoc('fid');
    if (!empty($thumbnails)) {
      return $thumbnails;
    }
    if ($this->transcoder == NULL) {
      return array();
    }
    $scheme = !empty($field['settings']['uri_scheme_thumbnails']) ? $field['settings']['uri_scheme_thumbnails'] : 'public';
    $format = !empty($field['settings']['thumbnail_format']) ? $field['settings']['thumbnail_format'] : 'png';
    $this->transcoder
      ->setInput($video);
    $thumbnails = $this->transcoder
      ->extractFrames($scheme, $format);
    $this->transcoder
      ->reset();
    if ($thumbnails === FALSE) {
      return FALSE;
    }
    foreach ($thumbnails as $thumb) {

      // Determine whether there is an existing thumbnail
      $thumb->fid = (int) db_query('SELECT fid FROM {file_managed} WHERE uri = :uri', array(
        ':uri' => $thumb->uri,
      ))
        ->fetchField(0);
      $thumb->uid = (int) $user->uid;

      // For the media module
      $thumb->type = 'image';
      file_save($thumb);
      db_merge('video_thumbnails')
        ->key(array(
        'videofid' => $video['fid'],
        'thumbnailfid' => $thumb->fid,
      ))
        ->execute();
    }
    return $thumbnails;
  }

  /**
   * Processes up to 'video_ffmpeg_instances' jobs in the current thread.
   *
   * @see video_jobs::loadQueue()
   */
  public function runQueue() {
    if ($this
      ->hasTranscoder() && ($videos = video_jobs::loadQueue())) {
      foreach ($videos as $video) {
        $this
          ->executeConversion($video);
      }
    }
  }

  /**
   * This helper function will help to execute video conversion job by loading
   * job from the database and once it completed saving its data in to the
   * database.
   *
   * @param $video
   * @return
   *   TRUE on success, FALSE on failure. Also check $video->video_status.
   */
  public function executeConversion(stdClass $video) {
    global $user;

    // Check the video state
    if ($video->video_status != VIDEO_RENDERING_INQUEUE && $video->video_status != VIDEO_RENDERING_PENDING) {
      $status = array(
        VIDEO_RENDERING_ACTIVE => 'activated previously',
        VIDEO_RENDERING_COMPLETE => 'completed',
        VIDEO_RENDERING_FAILED => 'failed',
      );
      watchdog('transcoder', 'Video conversion has been @status. You should add video to the queue. Please check the re-queue to enable the video conversion.', array(
        '@status' => $status[$video->video_status],
      ), WATCHDOG_WARNING);
      return FALSE;
    }
    if ($this->transcoder == NULL) {
      return FALSE;
    }

    // update the video conversion start time and status
    $video->statusupdated = time();
    $video->started = $video->statusupdated;
    $video->video_status = VIDEO_RENDERING_ACTIVE;
    video_jobs::update($video);
    $fieldname = !empty($video->data['field_name']) ? $video->data['field_name'] : NULL;
    $presets = Preset::getEnabledPresets($fieldname);
    $converted_scheme = file_uri_scheme($video->uri);
    $thumbnail_format = 'png';
    $thumbnail_number = intval(variable_get('video_thumbnail_count', 5));

    // Apply field-specific settings
    if ($fieldname != NULL) {
      $field = field_info_field($video->data['field_name']);

      // Find the scheme and thumbnail format for the converted videos
      if (!empty($field['settings']['uri_scheme_converted'])) {
        $converted_scheme = $field['settings']['uri_scheme_converted'];
      }
      if (!empty($field['settings']['thumbnail_format'])) {
        $thumbnail_format = $field['settings']['thumbnail_format'];
      }

      // If no automatic thumbnail generation, set thumbnail number to 0
      if ($field['settings']['autothumbnail'] != 'auto') {
        $thumbnail_number = 0;
      }
    }
    $this->transcoder
      ->setInput((array) $video);
    $transcodingsuccess = TRUE;
    $output = array();

    // Set output directory for converted files
    // for following operations, we need 'file_directory' prefix from current bundle
    $entity = entity_load('node', array(
      "{$video->entity_id}",
    ));
    $entity = reset($entity);
    list(, , $bundle) = entity_extract_ids('node', $entity);
    $instance = field_info_instance('node', $video->data['field_name'], $bundle);
    $fdirectory = $instance['settings']['file_directory'];

    // if path not contains 'file_directory' string (probably because original video has been not copied to local file system)
    if (!empty($fdirectory) && strpos(drupal_dirname($video->uri), $fdirectory) === false) {

      // replace last path level from file_directory with '/imported' or add '/imported' if only one level found ...
      if (strrpos($fdirectory, '/') !== false) {
        $fdirectory = substr($fdirectory, 0, strrpos($fdirectory, '/')) . '/imported';
      }

      //... and append source path and fid
      $output_directory = $fdirectory . '/' . file_uri_target(drupal_dirname($video->uri)) . '/' . $video->fid;
      $output_directory = $converted_scheme . '://' . $output_directory;
    }
    else {

      // original behaviour for videos with original files in local file system
      $output_directory = str_replace('original', 'converted', drupal_dirname($video->uri)) . '/' . $video->fid;
      $output_directory = $converted_scheme . '://' . file_uri_target($output_directory);
    }
    if (!file_prepare_directory($output_directory, FILE_CREATE_DIRECTORY)) {
      watchdog('transcoder', 'Video conversion failed. Could not create the directory: %dir', array(
        '%dir' => $output_directory,
      ), WATCHDOG_ERROR);
      $transcodingsuccess = FALSE;
    }
    elseif (empty($presets)) {
      watchdog('transcoder', 'No preset enabled. Please !presets_message.', array(
        '!presets_message' => l(t('enable or create a preset'), 'admin/config/media/video/presets'),
      ), WATCHDOG_ERROR, 'admin/config/media/video/presets');
      $transcodingsuccess = FALSE;
    }
    else {
      foreach ($presets as $name => $preset) {

        // override the widthXheight if enabled
        $preset['settings']['wxh'] = variable_get('video_use_preset_wxh', FALSE) ? $preset['settings']['wxh'] : $video->dimensions;
        $preset['settings']['thumbnails']['format'] = $thumbnail_format;
        $preset['settings']['thumbnails']['number'] = $thumbnail_number;

        // Only create thumbnails for the first preset
        $thumbnail_number = 0;

        // set transcoder options
        if (!$this->transcoder
          ->setOptions($preset['settings'])) {

          // setOptions should write to the watchdog log.
          $transcodingsuccess = FALSE;
          break;
        }
        $output_name = file_munge_filename(str_replace(' ', '_', pathinfo($video->filename, PATHINFO_FILENAME) . ' ' . strtolower($name)) . '_' . time() . '.' . $preset['settings']['video_extension'], '');
        $this->transcoder
          ->setOutput($output_directory, $output_name);

        // Close the database connection since encodding operation may take a lot of time
        // and mysql connection will timed out
        db_close(array(
          'target' => 'default',
        ));
        if ($output_file = $this->transcoder
          ->execute()) {
          $output[] = $output_file;
        }
        else {
          $transcodingsuccess = FALSE;
          break;
        }
        $this->transcoder
          ->reset(TRUE);
      }
    }
    if (!$transcodingsuccess) {
      video_jobs::setFailed($video);
    }
    else {

      // add files to file_managed table and add reference to the file_usage table.
      $offsite = $this->transcoder
        ->isOffSite();
      $this
        ->cleanConverted($video->fid);
      foreach ($output as $file) {
        $file->filemime = video_utility::getMimeType($file->uri);
        $file->status = FILE_STATUS_PERMANENT;
        $file->uid = $video->uid;
        $file->type = 'video';

        // For the media module
        // Empty file to prevent 'Warning: filesize(): stat failed' error.
        if ($offsite && $file->filesize == 0) {
          file_put_contents($file->uri, '');
        }
        file_save($file);
        file_usage_add($file, 'file', $video->entity_type, $video->entity_id);
        $output_vid = array(
          'vid' => $video->vid,
          'original_fid' => $video->fid,
          'output_fid' => $file->fid,
          'job_id' => !empty($file->jobid) ? $file->jobid : NULL,
        );
        drupal_write_record('video_output', $output_vid);
      }

      // add duration to the video_queue table
      $video->duration = round($file->duration);

      // Change the status if the file exists for onsite transcoders.
      if (!$offsite && file_exists($file->uri)) {
        video_jobs::setCompleted($video);
      }
      else {

        // Else: just update the video.
        video_jobs::update($video);
      }
    }
    $this->transcoder
      ->reset();
    return $transcodingsuccess;
  }

  /**
   * This helper function clean the database records if exist for current job.
   */
  protected function cleanConverted($fid) {
    $output_fids = db_select('video_output', 'vo')
      ->fields('vo', array(
      'output_fid',
    ))
      ->condition('original_fid', $fid)
      ->execute()
      ->fetchCol();
    if (empty($output_fids)) {
      return;
    }
    $output_files = file_load_multiple($output_fids);

    // Delete for all output files file_usage and file if not used anymore.
    foreach ($output_files as $output_file) {
      file_usage_delete($output_file, 'file');
      file_delete($output_file);
    }

    // Delete original_fid and all output_fid's from video_output table.
    db_delete('video_output')
      ->condition('original_fid', $fid)
      ->execute();
  }

  /**
   * Retuns all transcoders implemented to work with the video module.
   */
  public function getAllTranscoders() {

    // Lets find our transcoder classes and build our radio options
    // We do this by scanning our transcoders folder
    $form = array(
      'radios' => array(
        '' => t('No transcoder'),
      ),
      'help' => array(),
      'admin_settings' => array(),
    );

    // check inside sub modules
    $modules = module_list();
    $files = array();
    foreach ($modules as $module) {
      $module_files = array();
      $module_path = drupal_get_path('module', $module) . '/transcoders';
      foreach (file_scan_directory($module_path, '/.*\\.inc/') as $filekey => $file) {
        $file->module = $module;

        // Get filename to retrieve transcoder class name
        $filename = explode(".", $file->name);
        $file->name = $filename[0];
        $module_files[] = $file;
      }
      $files = array_merge($files, $module_files);
    }
    foreach ($files as $file) {
      module_load_include('inc', $file->module, '/transcoders/' . $file->name);
      $focus = new $file->name();
      $errorMessage = '';
      if (!$focus
        ->isAvailable($errorMessage)) {
        $form['help'][] = t('@name is unavailable: !errormessage', array(
          '@name' => $focus
            ->getName(),
          '!errormessage' => $errorMessage,
        ));
      }
      else {
        $form['radios'][$file->name] = check_plain($focus
          ->getName());
        $form['admin_settings'] = $form['admin_settings'] + $focus
          ->adminSettings();
      }
    }
    return $form;
  }

  /**
   * Create a new instance of the transcoder implementation.
   *
   * @return
   *   TranscoderFactoryInterface
   */
  public static function createTranscoder($transcoder) {
    if ($transcoder == '') {
      return NULL;
    }
    if (!class_exists($transcoder, TRUE)) {
      drupal_set_message(t('The transcoder %transcoder is not configured properly.', array(
        '%transcoder' => $transcoder,
      )), 'error');
    }
    return new $transcoder();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Transcoder::$transcoder private property
Transcoder::cleanConverted protected function This helper function clean the database records if exist for current job.
Transcoder::createTranscoder public static function Create a new instance of the transcoder implementation.
Transcoder::executeConversion public function This helper function will help to execute video conversion job by loading job from the database and once it completed saving its data in to the database.
Transcoder::extractFrames public function Extract frames from the video file. This helper function will interact with only the database and it will save all the thumbnail file reference in to the database.
Transcoder::getAllTranscoders public function Retuns all transcoders implemented to work with the video module.
Transcoder::getTranscoder public function Returns the current transcoder implementation.
Transcoder::hasTranscoder public function
Transcoder::runQueue public function Processes up to 'video_ffmpeg_instances' jobs in the current thread.
Transcoder::__construct public function