Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
video_file.inc
1 <?php
17 require_once SQ_CORE_PACKAGE_PATH.'/files/file/file.inc';
18 require_once SQ_DATA_PATH.'/private/conf/tools.inc';
19 
37 class Video_File extends File
38 {
39 
40 
41  public $allowed_extensions = Array('mov', 'avi', 'wmv', 'asf', 'flv', 'mp4', 'm4v', 'mpg', 'mpeg', 'ogv', 'ogg', 'webm');
42 
43 
50  function __construct($assetid=0)
51  {
52  parent::__construct($assetid);
53 
54  }//end constructor
55 
56 
71  public function getAvailableKeywords()
72  {
73  $keywords = parent::getAvailableKeywords();
74 
75  $keywords['asset_attribute_length'] = 'Length of File (in Minutes:Seconds)';
76  $keywords['asset_attribute_length_seconds'] = 'Length of File (in Seconds)';
77 
78  // Add Alt Asset Keyword
79  $keywords['asset_attribute_alt_asset_X'] = 'Alternate Asset Reference for Accessibility (replace X with keyword)';
80 
81  $keywords['asset_attribute_file_format_dynamic'] = 'Dynamic Keyword for File Format';
82  $keywords['asset_attribute_audio_codec_dynamic'] = 'Dynamic Keyword for Audio Codec';
83  $keywords['asset_attribute_channels_dynamic'] = 'Dynamic Keyword for Channels';
84  $keywords['asset_attribute_channel_mode_dynamic'] = 'Dynamic Keyword for Channel Mode';
85  $keywords['asset_attribute_bitrate_dynamic'] = 'Dynamic Keyword for Bit Rate';
86  $keywords['asset_attribute_sample_rate_dynamic'] = 'Dynamic Keyword for Sample Rate';
87  $keywords['asset_attribute_video_codec_dynamic'] = 'Dynamic Keyword for Video Codec';
88  $keywords['asset_attribute_width_dynamic'] = 'Dynamic Keyword for Width';
89  $keywords['asset_attribute_height_dynamic'] = 'Dynamic Keyword for Height';
90  $keywords['asset_attribute_fps_dynamic'] = 'Dynamic Keyword for Frames Per Second';
91  $keywords['asset_attribute_length_dynamic'] = 'Dynamic Keyword for Length (in Minutes:Seconds)';
92  $keywords['asset_attribute_length_seconds_dynamic'] = 'Dynamic Keyword for Length (in Seconds)';
93  $keywords['asset_attribute_video_title_dynamic'] = 'Dynamic Keyword for Video Title';
94  $keywords['asset_attribute_artist_dynamic'] = 'Dynamic Keyword for Artist';
95  $keywords['asset_attribute_album_dynamic'] = 'Dynamic Keyword for Album';
96  $keywords['asset_attribute_genre_dynamic'] = 'Dynamic Keyword for Genre';
97  $keywords['asset_attribute_comments_dynamic'] = 'Dynamic Keyword for Comments';
98  $keywords['asset_attribute_copyright_dynamic'] = 'Dynamic Keyword for Copyright';
99  $keywords['asset_attribute_year_dynamic'] = 'Dynamic Keyword for Year';
100 
101  // Remove set Alt Asset Keyword, replace with alternate keyword
102  foreach ($keywords as $key => $val) {
103  if ($key == 'asset_attribute_alt_asset') {
104  unset($keywords[$key]);
105  break;
106  }//end if
107  }//end foreach
108 
109  return $keywords;
110 
111  }//end getAvailableKeywords()
112 
113 
127  function getKeywordReplacement($keyword)
128  {
129  $replacement = NULL;
130 
131  // Remove any modifiers from keyword...but save the full keyword to
132  // pass to parent asset in case our results are fruitless
133  $full_keyword = $keyword;
134  $keyword = parse_keyword($keyword, $modifiers);
135  $contextid = extract_context_modifier($modifiers);
136 
137  if ($contextid !== NULL) {
138  // If we were able to extract a context ID to change to, and it's
139  // different to our current one, then change and then reload a copy
140  // of the asset in that context (as we would need to do anyway)
141 
142  if ((int)$contextid !== $GLOBALS['SQ_SYSTEM']->getContextId()) {
143  $GLOBALS['SQ_SYSTEM']->changeContext($contextid);
144  $contexted_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->id);
145 
146  // Get the keyword without any modifiers
147  $replacement = $contexted_asset->getKeywordReplacement($keyword);
148 
149  // Then apply the ones we had remaining, then return it
150  apply_keyword_modifiers($replacement, $modifiers, Array('assetid' => $contexted_asset->id));
151 
152  unset($contexted_asset);
153  $GLOBALS['SQ_SYSTEM']->restoreContext();
154  return $replacement;
155 
156  }//end if contextid is not the currently active context
157 
158  }//end if contextid is not NULL
159 
160  // Static/overriding keywords
161  switch ($keyword) {
162  case 'asset_attribute_length' :
163  // Length should be the time in minutes:seconds
164  $length = $this->attr('length');
165  $minutes = floor($length / 60);
166  $seconds = $length % 60;
167  $replacement = $minutes.':'.str_pad($seconds, 2, '0', STR_PAD_LEFT);
168  break;
169 
170  case 'asset_attribute_length_seconds' :
171  // Should also be able to provide time in seconds if we need to
172  $replacement = $this->attr('length');
173  break;
174 
175  }//end switch
176 
177  // Keywords for alternate video file asset
178  if (strpos($keyword, 'asset_attribute_alt_asset_') !== FALSE) {
179  $alt_asset_key = substr($keyword, 26);
180  // Set our new asset object
181  $alt_assetid = $this->attr('alt_asset');
182  // Make sure we have an asset id
183  if (!empty($alt_assetid) && is_numeric($alt_assetid)) {
184  $alt_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($alt_assetid);
185  $replacement = !empty($alt_asset) ? $alt_asset->getKeywordReplacement($alt_asset_key) : '';
186  } else {
187  $replacement = '';
188  }//end else
189  }//end if
190 
191  // Dynamic keywords lie below here. These strip the video information direct from
192  // the files, ignoring what is currently stored in the asset.
193  if (substr($keyword, -8) === '_dynamic') {
194  // Extract tag information - if failed then pretend it returned nothing
195  $tags = $this->extractID3Metadata();
196  if ($tags === FALSE) $tags = Array();
197 
198  // Get the actual attribute being dynamically requested
199  $sub_keyword = substr($keyword, 16, -8);
200 
201  switch ($sub_keyword) {
202  case 'file_format':
203  $format = array_get_index($tags, 'file_format', '');
204  $format = ucfirst($format);
205  $replacement = $format;
206  break;
207 
208  case 'audio_codec':
209  case 'channels':
210  case 'sample_rate':
211  case 'video_codec':
212  case 'width':
213  case 'height':
214  case 'fps':
215  case 'video_title':
216  case 'artist':
217  case 'album':
218  case 'genre':
219  case 'comments':
220  case 'copyright':
221  case 'year':
222  $replacement = array_get_index($tags, $sub_keyword, '');
223  break;
224 
225  case 'channel_mode':
226  $channel_mode = array_get_index($tags, 'channel_mode', '');
227  $channel_mode = ucfirst($channel_mode);
228  $replacement = $channel_mode;
229  break;
230 
231  case 'bitrate':
232  $bitrate = array_get_index($tags, $sub_keyword, '');
233  if (is_numeric($bitrate)) {
234  // Bitrate is returned in bit/s, but we are displaying in
235  // kbit/s (rounded down)
236  $bitrate = floor($bitrate / 1000);
237  }
238  $replacement = $bitrate;
239  break;
240 
241  case 'length_seconds':
242  $length = array_get_index($tags, 'length', '');
243  if (is_numeric($length)) {
244  // Round off the number of seconds (up or down)
245  $length = round($length);
246  }
247  $replacement = $length;
248  break;
249 
250  case 'length':
251  $length = array_get_index($tags, 'length', '');
252  if (is_numeric($length)) {
253  // Round off the number of seconds (up or down)
254  $length = round($length);
255  // Now format it
256  $minutes = floor($length / 60);
257  $seconds = $length % 60;
258  $length = $minutes.':'.str_pad($seconds, 2, '0', STR_PAD_LEFT);
259  }
260  $replacement = $length;
261  break;
262 
263  }//end switch
264 
265  }//end if dynamic keyword
266 
267  if (isset($replacement)) {
268  if (count($modifiers) > 0) {
269  apply_keyword_modifiers($replacement, $modifiers, Array('assetid' => $this->id));
270  }
271 
272  return $replacement;
273  } else {
274  // Use the full keyword so modifiers still get used
275  return parent::getKeywordReplacement($full_keyword);
276  }
277 
278  }//end getKeywordReplacement()
279 
280 
293  public function saveAttributes($dont_run_updated=FALSE)
294  {
295  if ($this->attr('extract_id3')) {
296  $tags = $this->extractID3Metadata();
297  if ($tags !== FALSE) {
298 
299  // Go through the info we do have, setting what we can, and leaving
300  // alone what is not there.
301  foreach ($tags as $tag_name => $tag_value) {
302  switch ($tag_name) {
303  case 'file_format' :
304  $this->setAttrValue('file_format', ucfirst($tag_value));
305  break;
306 
307  case 'channel_mode' :
308  $this->setAttrValue('channel_mode', ucfirst($tag_value));
309  break;
310 
311  case 'bitrate' :
312  // Bitrate is provided in bit/s. We are saving this as a rounded-down
313  // bitrate in kbit/s instead.
314  $this->setAttrValue($tag_name, floor($tag_value / 1000));
315  break;
316 
317  // These are all the same, run them through the same function
318  case 'channels' :
319  case 'audio_codec' :
320  case 'sample_rate' :
321  // Attribute had no underscore.
322  case 'video_codec' :
323  case 'width' :
324  case 'height' :
325  case 'fps' :
326  case 'video_title' :
327  case 'artist' :
328  case 'album' :
329  case 'genre' :
330  case 'comments' :
331  case 'copyright' :
332  case 'year' :
333  $this->setAttrValue($tag_name, $tag_value);
334  break;
335 
336  case 'length' :
337  // Length is stored in fractional seconds. This will be rounded (up
338  // or down) to the nearest whole second.
339  $this->setAttrValue($tag_name, round($tag_value));
340  break;
341  }//end switch
342 
343  }//end foreach
344 
345  }//end if tags returned
346 
347  // Set our extraction ID3 option back to NO so that changed values don't get overwritten
348  $this->setAttrValue('extract_id3', FALSE);
349 
350  }//end if extract ID3 is true
351 
352  return parent::saveAttributes($dont_run_updated);
353 
354  }//end saveAttributes()
355 
356 
372  public function extractID3Metadata($file_name=NULL)
373  {
374  if (is_null($file_name)) {
375  if ($this->usePublicPath()) {
376  $file_name = $this->data_path_public.'/'.$this->attr('name');
377  } else {
378  $file_name = $this->data_path.'/'.$this->attr('name');
379  }
380  }
381 
382  // getID3 tool disabled in External Tools? Silently fail.
383  if (!SQ_TOOL_GETID3_ENABLED) return FALSE;
384 
385  // getID3 external tool directory does not exist?
386  if (!is_dir(SQ_TOOL_GETID3_PATH)) {
387  trigger_error('Cannot extract Video metadata; path to getID3() external tool does not exist or is not a directory', E_USER_WARNING);
388  return FALSE;
389  }
390 
391  // ...or is not a getID3 module at all?
392  if (!is_file(SQ_TOOL_GETID3_PATH.'/getid3.php')) {
393  trigger_error('Cannot extract Video metadata; path to getID3() external tool does not point to a valid getID3() install', E_USER_WARNING);
394  return FALSE;
395  }
396 
397  if (!is_file($file_name)) {
398  trigger_error('Cannot extract Video metadata; Video file path provided does not exist', E_USER_WARNING);
399  }
400 
401  require_once SQ_TOOL_GETID3_PATH.'/getid3.php';
402 
403  // Now get a new getID3() object and analyse the stored file
404  $id3 = new getID3;
405  $id3->Analyze($file_name);
406  $use_ogg = FALSE;
407 
408  $file_duration = 0;
409  $file_bitrate = 0;
410  $file_samplerate = 0;
411 
412  // Check and see if we are using OGG, which getID3() does not support
413  if ($id3->info['fileformat'] == 'ogg') {
414  require_once SQ_DATA_PATH.'/private/conf/tools.inc';
415 
416  // Go away please
417  if (!SQ_TOOL_OGG_METADATA_EXTRACTION_ENABLED) return FALSE;
418 
419  if (!is_file(SQ_TOOL_OGG_METADATA_EXTRACTION_PATH)) {
420  trigger_error('Cannot extract OGG metadata; path to OGG Metadata Extraction external tool does not exist or is not a file', E_USER_WARNING);
421  return FALSE;
422  }
423 
424  $use_ogg = TRUE;
425 
426  // Include our support file which is our own class file
427  // this class file extends the original ogg.class.php
428  require_once SQ_CORE_PACKAGE_PATH.'/files/video_file/lib/Matrix_Ogg.inc';
429 
430  // We don't want to use the cache option
431  $ogg = new Matrix_Ogg($file_name, NOCACHING);
432  // Video
433  $theora = isset($ogg->Streams['theora']) ? $ogg->Streams['theora'] : NULL;
434  // Audio
435  $vorbis = isset($ogg->Streams['vorbis']) ? $ogg->Streams['vorbis'] : NULL;
436 
437  // Data relating to the file itself.
438  $file_duration = $ogg->Streams['duration'];
439  $file_samplerate = isset($vorbis) ? $vorbis['samplerate'] : 0;
440 
441  if ($theora) {
442  $file_bitrate = $theora['nombr'];
443  } else if ($vorbis) {
444  $file_bitrate = $vorbis['bitrate'];
445  }
446 
447  } else {
448  $file_duration = $id3->info['playtime_seconds'];
449  $file_bitrate = $id3->info['bitrate'];
450  $file_samplerate = isset($id3->info['audio']['sample_rate']) ? $id3->info['audio']['sample_rate'] : 0;
451  }
452 
453  $audio_data = Array(
454  'length' => $file_duration,
455  'bitrate' => $file_bitrate,
456  'sample_rate' => $file_samplerate,
457  'file_format' => $id3->info['fileformat'],
458  );
459 
460  $id3_tag_data = Array();
461 
462  // Set our vars to grab info from the audio and video arrays
463  $tag_audio =& $id3->info['audio'];
464  $tag_video =& $id3->info['video'];
465 
466  $tag_quicktime = NULL;
467  if (isset($id3->info['tags']['quicktime'])) {
468  $tag_quicktime =& $id3->info['tags']['quicktime'];
469  }
470 
471  // Grab the data and put it into our array
472  if ($use_ogg) {
473  if (!is_null($vorbis)) {
474  $id3_tag_data['audio_codec'] = $vorbis['vendor'];
475  $id3_tag_data['channels'] = $vorbis['channels'];
476  $id3_tag_data['channel_mode'] = $vorbis['channels'] == 2 ? 'Stereo' : 'Mono';
477  }
478  if (!is_null($theora)) {
479  $id3_tag_data['video_codec'] = $theora['vendor'];
480  $id3_tag_data['width'] = $theora['width'];
481  $id3_tag_data['height'] = $theora['height'];
482  $id3_tag_data['fps'] = $theora['frate'];
483 
484  // Extra data fields
485  if (isset($theora['comments'])) {
486  foreach($theora['comments'] as $comment) {
487  $comment_field = explode('=', $comment);
488  $cf = $comment_field[1];
489  switch ($comment_field[0]) {
490  case 'title' :
491  $id3_tag_data['video_title'] = $cf;
492  break;
493  case 'artist' :
494  $id3_tag_data['artist'] = $cf;
495  break;
496  case 'album' :
497  $id3_tag_data['album'] = $cf;
498  break;
499  case 'genre' :
500  $id3_tag_data['genre'] = $cf;
501  break;
502  case 'description' :
503  $id3_tag_data['comments'] = $cf;
504  break;
505  case 'copyright' :
506  $id3_tag_data['copyright'] = $cf;
507  break;
508  case 'year' :
509  $id3_tag_data['year'] = $cf;
510  break;
511  }//end switch
512 
513  }//end foreach
514 
515  }//end if (comments)
516 
517  }//end if (theora)
518 
519  } else {
520  // Not ogg format
521 
522  if (isset($tag_audio['codec'])) {
523  $id3_tag_data['audio_codec'] = $tag_audio['codec'];
524  }
525  if (isset($tag_audio['channels'])) {
526  $id3_tag_data['channels'] = $tag_audio['channels'];
527  }
528  if (isset($tag_audio['channelmode'])) {
529  $id3_tag_data['channel_mode'] = $tag_audio['channelmode'];
530  }
531  if (isset($tag_video['codec'])) {
532  $id3_tag_data['video_codec'] = $tag_video['codec'];
533  }
534  if (isset($tag_video['resolution_x'])) {
535  $id3_tag_data['width'] = $tag_video['resolution_x'];
536  }
537  if (isset($tag_video['resolution_y'])) {
538  $id3_tag_data['height'] = $tag_video['resolution_y'];
539  }
540  // Support for FPS is sketchy across differnt video formats, but we will try
541  // getID3() v1.7.9 must be used to return correct frame rates for Quicktime
542  if (isset($tag_video['frame_rate'])) {
543  $id3_tag_data['fps'] = $tag_video['frame_rate'];
544  }
545  // Extra data fields
546  if (!is_null($tag_quicktime)) {
547  if (isset($tag_quicktime['title'][0])) {
548  $id3_tag_data['video_title'] = $tag_quicktime['title'][0];
549  }
550  if (isset($tag_quicktime['artist'][0])) {
551  $id3_tag_data['artist'] = $tag_quicktime['artist'][0];
552  }
553  if (isset($tag_quicktime['album'][0])) {
554  $id3_tag_data['album'] = $tag_quicktime['album'][0];
555  }
556  if (isset($tag_quicktime['genre'][0])) {
557  $id3_tag_data['genre'] = $tag_quicktime['genre'][0];
558  }
559  if (isset($tag_quicktime['comment'][0])) {
560  $id3_tag_data['comments'] = $tag_quicktime['comment'][0];
561  }
562  if (isset($tag_quicktime['copyright'][0])) {
563  $id3_tag_data['copyright'] = $tag_quicktime['copyright'][0];
564  }
565  if (isset($tag_quicktime['creation_date'][0])) {
566  $id3_tag_data['year'] = $tag_quicktime['creation_date'][0];
567  }
568  }
569 
570  }//end else (ogg)
571 
572  $data = array_merge($audio_data, $id3_tag_data);
573  return $data;
574 
575  }//end extractID3Metadata()
576 
577 
578 }//end class
579 ?>