aboutsummaryrefslogtreecommitdiff
path: root/transcoders/video_ffmpeg_wrapper.inc
blob: 2661fd9bdc7609554d3c704c9fea083883e5ed2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
<?php
//$Id$
/*
 * @file
 * Transcoder class file to handle ffmpeg_wrapper settings and conversions.
 *
 */


class video_ffmpeg_wrapper implements transcoder_interface {
  private $name = 'FFmpeg Wrapper';
  private $value = 'video_ffmpeg_wrapper';
  private $video_ext = 'flv';

  protected $thumb_command = '-i !videofile -an -y -f mjpeg -ss !seek -vframes 1 !thumbfile';

  public function __construct() {
    $this->params['thumb_command'] = variable_get('video_ffmpeg_thumbnailer_options', $this->thumb_command);
  }

  public function run_command($options) {
    watchdog('ffmpeg_wrapper', 'Sending options: '. $options, array(), WATCHDOG_DEBUG);
    $output = ffmpeg_wrapper_run_command($options);
    if(is_object($output) && ISSET($output->errors)) {
      return implode("\n", $output->errors);
    }
    return $output;
  }

  public function generate_thumbnails($video) {
    global $user;
    // Setup our thmbnail path.
    $video_thumb_path = variable_get('video_thumb_path', 'video_thumbs');
    $final_thumb_path = file_directory_path(). '/' . $video_thumb_path . '/' . $video['fid'];

    // Ensure the destination directory exists and is writable.
    $directories = explode('/', $final_thumb_path);
    // Get the file system directory.
    $file_system = file_directory_path();
    foreach ($directories as $directory) {
      $full_path = isset($full_path) ? $full_path . '/' . $directory : $directory;
      // Don't check directories outside the file system path.
      if (strpos($full_path, $file_system) === 0) {
        field_file_check_directory($full_path, FILE_CREATE_DIRECTORY);
      }
    }

    // Total thumbs to generate
    $total_thumbs = variable_get('no_of_video_thumbs', 5);
    $videofile = escapeshellarg($video['filepath']);
    //get the playtime from the current transcoder
    $duration = $this->get_playtime($video);

    $files = NULL;
    for($i = 1; $i <= $total_thumbs; $i++) {
      $seek = ($duration/$total_thumbs) * $i - 1;
      $filename = "/video-thumb-for-$fid-$i.jpg";
      $thumbfile = $final_thumb_path . $filename;
      //skip files already exists, this will save ffmpeg traffic
      if (!is_file($thumbfile)) {
        //setup the command to be passed to the transcoder.
        $options = t($this->params['thumb_command'], array('!videofile' => $videofile, '!seek' => $seek, '!thumbfile' => $thumbfile));
        // Generate the thumbnail from the video.
        $command_output = $this->run_command($options);
        if (!file_exists($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.<br />Command Executed:<br />%cmd<br />Command Output:<br />%out", $error_param);
          // Log the error message.
          watchdog('video_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   = FILE_STATUS_TEMPORARY;
      $file->filename = trim($filename);
      $file->filepath = $thumbfile;
      $file->filemime = file_get_mimetype($filename);
      $file->filesize = filesize($thumbfile);
      $file->timestamp = REQUEST_TIME;
      $files[] = $file;
    }
    return $files;
  }

  public function video_converted_extension() {
    global $conf;
    if(isset($conf['ffmpeg_output_type']) && !empty($conf['ffmpeg_output_type'])) {
      return $conf['ffmpeg_output_type'];
    }
    return $this->video_ext;
  }

  public function convert_video($video, $converted, $dimensions) {
    $ffmpeg_object = new stdClass();
    // check configuration are pass of then use global $conf
    if (empty($params)) {
      global $conf;
      $params = $conf;
    }

    // first error check, make sure that we can decode this kind of file
    if (!ffmpeg_wrapper_can_decode($video->filepath)) {
      $message = 'FFmpeg Wrapper can not decode this file: !file';
      $variables = array('!file' => l($video->filepath, file_create_url($video->filepath)));
      watchdog('video_render', $message, $variables, WATCHDOG_ERROR);
      return false;
    }

    // did the admin define a specific FFmpeg comand to run?
    //  we only run what the admin specified
    if ($params['ffmpeg_video_custom']) {
      $options[] = str_replace(array('%in_file', '%out_file'), array($video->filepath, $converted), $params['ffmpeg_video_custom_command']);
    }
    // build a standard configuration
    else {
      // build the ffmpeg command structure out
      $options = array();
      // input file
      $options[] = "-i '". $video->filepath ."'";
      // build the watermark config
      if ($params['ffmpeg_video_wm']) $options[] = "-vhook '". ffmpeg_wrapper_path_to_vhook('watermark.so') ." -f ". $params['ffmpeg_video_wm_file'] ."'";
      // build the audio config
      if ($params['ffmpeg_audio_advanced']) {
        // use a specifc codec?
        if ($params['ffmpeg_audio_acodec']) $options[] =  '-acodec '. $params['ffmpeg_audio_acodec'];
        // use a specific sample rate?
        if ($params['ffmpeg_audio_ar']) $options[] = '-ar '. $params['ffmpeg_audio_ar'];
        // use a specific bit rate?
        if ($params['ffmpeg_audio_ab']) $options[] = '-ab '. $params['ffmpeg_audio_ab'];
      }

      //custom sizing per video
      $options[] = '-s '. $dimensions;

      // build the video config
      if ($params['ffmpeg_video_advanced']) {
        // is codec set?
        if ($params['ffmpeg_video_vcodec']) $options[] = '-vcodec '. $params['ffmpeg_video_vcodec'];
        // is the bit rate set?
        if ($params['ffmpeg_video_br']) $options[] = '-b '. $params['ffmpeg_video_br'];
        // is frame rate set?
        if ($params['ffmpeg_video_fps']) $options[] = '-r '. $params['ffmpeg_video_fps'];
      }
      // implement truncating
      if ($params['ffmpeg_time_advanced']) $options[] = '-t '. $params['ffmpeg_time'];
      // add the output file
      $options[] = "'". $converted ."'";
    }
    $ffmpeg_object->command = implode(" ", $options);

    // run our command and return the output.
    return $this->run_command($ffmpeg_object->command);
  }
   
  /**
   * Return the playtime seconds of a video
   */
  public function get_playtime($video) {
    $ffmpeg_output = ffmpeg_wrapper_get_file_data($video['filepath']);
    return $ffmpeg_output['duration'];
  }

  /**
   * Return the dimensions of a video
   */
  public function get_dimensions($video) {
    $ffmpeg_output = ffmpeg_wrapper_get_file_data($video);
    $res = array('width' => 0,'height' => 0);
    if($ffmpeg_output['video']['s']) {
      $dimensions = explode("x", $ffmpeg_output['video']['s']);
      $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() {
    $help = l(t('FFMPEG Wrapper'), 'http://drupal.org/project/ffmpeg_wrapper');
    // If the module isn't loaded, show an error next to the link.
    if(!module_exists('ffmpeg_wrapper')) {
      $help .= ' <span class="error">'.t('You currently do not have this installed.').'</span>';
    }
    return $help;
  }

  /**
   * Interface Implementations
   * @see sites/all/modules/video/includes/transcoder_interface#admin_settings()
   */
  public function admin_settings() {
    global $conf;
    $form = array();
    if (!module_exists('ffmpeg_wrapper')) {
      $markup = t('You must download and install the !ffmpeg_wrapper to use the FFmpeg Wrapper as a transcoder.', array('!ffmpeg_wrapper' => l(t('FFmpeg Wrapper Module'), 'http://www.drupal.org/project/ffmpeg_wrapper')));
      $form['video_wrapper_info'] = array(
      '#type' => 'markup',
      '#value' => $markup,
      '#prefix' => '<div id="'. $this->value .'">',
      '#suffix' => '</div>',
      );
    }
    else {
      $form['video_ffmpeg_wrapper_start'] = array(
        '#type' => 'markup',
        '#value' => '<div id="video_ffmpeg_wrapper">',
      );
      if(module_exists('ffmpeg_wrapper_ui')) {
        $ffmpeg_admin_form = ffmpeg_wrapper_ui_configuration_form($conf);
      } else if (module_exists('ffmpeg_wrapper')) {
        $ffmpeg_admin_form = ffmpeg_wrapper_configuration_form($conf);
      }
      $form = array_merge($form, $ffmpeg_admin_form['ffmpeg_wrapper']);
      $form['video_ffmpeg_wrapper_end'] = array(
        '#type' => 'markup',
        '#value' => '</div>',
      );
    }
    return $form;
  }
}