ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
Flv.php
Go to the documentation of this file.
1 <?php
2 
4 
7 
10 // available at http://getid3.sourceforge.net //
11 // or http://www.getid3.org //
12 // //
13 // FLV module by Seth Kaufman <seth@whirl-i-gig.com> //
14 // //
15 // * version 0.1 (26 June 2005) //
16 // //
17 // //
18 // * version 0.1.1 (15 July 2005) //
19 // minor modifications by James Heinrich <info@getid3.org> //
20 // //
21 // * version 0.2 (22 February 2006) //
22 // Support for On2 VP6 codec and meta information //
23 // by Steve Webster <steve.webster@featurecreep.com> //
24 // //
25 // * version 0.3 (15 June 2006) //
26 // Modified to not read entire file into memory //
27 // by James Heinrich <info@getid3.org> //
28 // //
29 // * version 0.4 (07 December 2007) //
30 // Bugfixes for incorrectly parsed FLV dimensions //
31 // and incorrect parsing of onMetaTag //
32 // by Evgeny Moysevich <moysevich@gmail.com> //
33 // //
34 // * version 0.5 (21 May 2009) //
35 // Fixed parsing of audio tags and added additional codec //
36 // details. The duration is now read from onMetaTag (if //
37 // exists), rather than parsing whole file //
38 // by Nigel Barnes <ngbarnes@hotmail.com> //
39 // //
40 // * version 0.6 (24 May 2009) //
41 // Better parsing of files with h264 video //
42 // by Evgeny Moysevich <moysevichØgmail*com> //
43 // //
44 // * version 0.6.1 (30 May 2011) //
45 // prevent infinite loops in expGolombUe() //
46 // //
48 // //
49 // module.audio-video.flv.php //
50 // module for analyzing Shockwave Flash Video files //
51 // dependencies: AVCSequenceParameterSetReader, //
52 // AMFReader, //
53 // AMFStream //
54 // ///
56 
68 class Flv extends BaseHandler
69 {
74  public $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration
75 
78  const GETID3_FLV_TAG_META = 18;
85 
90  public function analyze()
91  {
92  $info = &$this->getid3->info;
93 
94  fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
95 
96  $FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
97  $FLVheader = fread($this->getid3->fp, 5);
98 
99  $info['fileformat'] = 'flv';
100  $info['flv']['header']['signature'] = substr($FLVheader, 0, 3);
101  $info['flv']['header']['version'] = Helper::BigEndian2Int(substr($FLVheader,
102  3,
103  1));
104  $TypeFlags = Helper::BigEndian2Int(substr($FLVheader, 4, 1));
105 
106  $magic = 'FLV';
107  if ($info['flv']['header']['signature'] != $magic) {
108  $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($info['flv']['header']['signature']) . '"';
109  unset($info['flv']);
110  unset($info['fileformat']);
111 
112  return false;
113  }
114 
115  $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
116  $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
117 
118  $FrameSizeDataLength = Helper::BigEndian2Int(fread($this->getid3->fp,
119  4));
120  $FLVheaderFrameLength = 9;
121  if ($FrameSizeDataLength > $FLVheaderFrameLength) {
122  fseek($this->getid3->fp,
123  $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
124  }
125  $Duration = 0;
126  $found_video = false;
127  $found_audio = false;
128  $found_meta = false;
129  $found_valid_meta_playtime = false;
130  $tagParseCount = 0;
131  $info['flv']['framecount'] = array('total' => 0, 'audio' => 0, 'video' => 0);
132  $flv_framecount = &$info['flv']['framecount'];
133  while (((ftell($this->getid3->fp) + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) {
134  $ThisTagHeader = fread($this->getid3->fp, 16);
135 
136  $PreviousTagLength = Helper::BigEndian2Int(substr($ThisTagHeader,
137  0, 4));
138  $TagType = Helper::BigEndian2Int(substr($ThisTagHeader,
139  4, 1));
140  $DataLength = Helper::BigEndian2Int(substr($ThisTagHeader,
141  5, 3));
142  $Timestamp = Helper::BigEndian2Int(substr($ThisTagHeader,
143  8, 3));
144  $LastHeaderByte = Helper::BigEndian2Int(substr($ThisTagHeader,
145  15, 1));
146  $NextOffset = ftell($this->getid3->fp) - 1 + $DataLength;
147  if ($Timestamp > $Duration) {
148  $Duration = $Timestamp;
149  }
150 
151  $flv_framecount['total']++;
152  switch ($TagType) {
153  case self::GETID3_FLV_TAG_AUDIO:
154  $flv_framecount['audio']++;
155  if (!$found_audio) {
156  $found_audio = true;
157  $info['flv']['audio']['audioFormat'] = ($LastHeaderByte >> 4) & 0x0F;
158  $info['flv']['audio']['audioRate'] = ($LastHeaderByte >> 2) & 0x03;
159  $info['flv']['audio']['audioSampleSize'] = ($LastHeaderByte >> 1) & 0x01;
160  $info['flv']['audio']['audioType'] = $LastHeaderByte & 0x01;
161  }
162  break;
163 
164  case self::GETID3_FLV_TAG_VIDEO:
165  $flv_framecount['video']++;
166  if (!$found_video) {
167  $found_video = true;
168  $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
169 
170  $FLVvideoHeader = fread($this->getid3->fp, 11);
171 
172  if ($info['flv']['video']['videoCodec'] == self::GETID3_FLV_VIDEO_H264) {
173  // this code block contributed by: moysevichØgmail*com
174 
175  $AVCPacketType = Helper::BigEndian2Int(substr($FLVvideoHeader,
176  0,
177  1));
179  // read AVCDecoderConfigurationRecord
180  $configurationVersion = Helper::BigEndian2Int(substr($FLVvideoHeader,
181  4,
182  1));
183  $AVCProfileIndication = Helper::BigEndian2Int(substr($FLVvideoHeader,
184  5,
185  1));
186  $profile_compatibility = Helper::BigEndian2Int(substr($FLVvideoHeader,
187  6,
188  1));
189  $lengthSizeMinusOne = Helper::BigEndian2Int(substr($FLVvideoHeader,
190  7,
191  1));
192  $numOfSequenceParameterSets = Helper::BigEndian2Int(substr($FLVvideoHeader,
193  8,
194  1));
195 
196  if (($numOfSequenceParameterSets & 0x1F) != 0) {
197  // there is at least one SequenceParameterSet
198  // read size of the first SequenceParameterSet
199  //$spsSize = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
200  $spsSize = Helper::LittleEndian2Int(substr($FLVvideoHeader,
201  9,
202  2));
203  // read the first SequenceParameterSet
204  $sps = fread($this->getid3->fp, $spsSize);
205  if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red
206  $spsReader = new AVCSequenceParameterSetReader($sps);
207  $spsReader->readData();
208  $info['video']['resolution_x'] = $spsReader->getWidth();
209  $info['video']['resolution_y'] = $spsReader->getHeight();
210  }
211  }
212  }
213  // end: moysevichØgmail*com
214  } elseif ($info['flv']['video']['videoCodec'] == self::GETID3_FLV_VIDEO_H263) {
215 
216  $PictureSizeType = (Helper::BigEndian2Int(substr($FLVvideoHeader,
217  3,
218  2))) >> 7;
219  $PictureSizeType = $PictureSizeType & 0x0007;
220  $info['flv']['header']['videoSizeType'] = $PictureSizeType;
221  switch ($PictureSizeType) {
222  case 0:
223  //$PictureSizeEnc = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
224  //$PictureSizeEnc <<= 1;
225  //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
226  //$PictureSizeEnc = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
227  //$PictureSizeEnc <<= 1;
228  //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
229 
230  $PictureSizeEnc['x'] = Helper::BigEndian2Int(substr($FLVvideoHeader,
231  4,
232  2));
233  $PictureSizeEnc['y'] = Helper::BigEndian2Int(substr($FLVvideoHeader,
234  5,
235  2));
236  $PictureSizeEnc['x'] >>= 7;
237  $PictureSizeEnc['y'] >>= 7;
238  $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
239  $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
240  break;
241 
242  case 1:
243  $PictureSizeEnc['x'] = Helper::BigEndian2Int(substr($FLVvideoHeader,
244  4,
245  3));
246  $PictureSizeEnc['y'] = Helper::BigEndian2Int(substr($FLVvideoHeader,
247  6,
248  3));
249  $PictureSizeEnc['x'] >>= 7;
250  $PictureSizeEnc['y'] >>= 7;
251  $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
252  $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
253  break;
254 
255  case 2:
256  $info['video']['resolution_x'] = 352;
257  $info['video']['resolution_y'] = 288;
258  break;
259 
260  case 3:
261  $info['video']['resolution_x'] = 176;
262  $info['video']['resolution_y'] = 144;
263  break;
264 
265  case 4:
266  $info['video']['resolution_x'] = 128;
267  $info['video']['resolution_y'] = 96;
268  break;
269 
270  case 5:
271  $info['video']['resolution_x'] = 320;
272  $info['video']['resolution_y'] = 240;
273  break;
274 
275  case 6:
276  $info['video']['resolution_x'] = 160;
277  $info['video']['resolution_y'] = 120;
278  break;
279 
280  default:
281  $info['video']['resolution_x'] = 0;
282  $info['video']['resolution_y'] = 0;
283  break;
284  }
285  }
286  $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
287  }
288  break;
289 
290  // Meta tag
291  case self::GETID3_FLV_TAG_META:
292  if (!$found_meta) {
293  $found_meta = true;
294  fseek($this->getid3->fp, -1, SEEK_CUR);
295  $datachunk = fread($this->getid3->fp, $DataLength);
296  $AMFstream = new AMFStream($datachunk);
297  $reader = new AMFReader($AMFstream);
298  $eventName = $reader->readData();
299  $info['flv']['meta'][$eventName] = $reader->readData();
300  unset($reader);
301 
302  $copykeys = array('framerate' => 'frame_rate', 'width' => 'resolution_x', 'height' => 'resolution_y', 'audiodatarate' => 'bitrate', 'videodatarate' => 'bitrate');
303  foreach ($copykeys as $sourcekey => $destkey) {
304  if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) {
305  switch ($sourcekey) {
306  case 'width':
307  case 'height':
308  $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey]));
309  break;
310  case 'audiodatarate':
311  $info['audio'][$destkey] = Helper::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000));
312  break;
313  case 'videodatarate':
314  case 'frame_rate':
315  default:
316  $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey];
317  break;
318  }
319  }
320  }
321  if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
322  $found_valid_meta_playtime = true;
323  }
324  }
325  break;
326 
327  default:
328  // noop
329  break;
330  }
331  fseek($this->getid3->fp, $NextOffset, SEEK_SET);
332  }
333 
334  $info['playtime_seconds'] = $Duration / 1000;
335  if ($info['playtime_seconds'] > 0) {
336  $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
337  }
338 
339  if ($info['flv']['header']['hasAudio']) {
340  $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['audio']['audioFormat']);
341  $info['audio']['sample_rate'] = $this->FLVaudioRate($info['flv']['audio']['audioRate']);
342  $info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info['flv']['audio']['audioSampleSize']);
343 
344  $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
345  $info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
346  $info['audio']['dataformat'] = 'flv';
347  }
348  if (!empty($info['flv']['header']['hasVideo'])) {
349  $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['video']['videoCodec']);
350  $info['video']['dataformat'] = 'flv';
351  $info['video']['lossless'] = false;
352  }
353 
354  // Set information from meta
355  if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
356  $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration'];
357  $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
358  }
359  if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) {
360  $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']);
361  }
362  if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
363  $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']);
364  }
365 
366  return true;
367  }
368 
374  public function FLVaudioFormat($id)
375  {
376  $FLVaudioFormat = array(
377  0 => 'Linear PCM, platform endian',
378  1 => 'ADPCM',
379  2 => 'mp3',
380  3 => 'Linear PCM, little endian',
381  4 => 'Nellymoser 16kHz mono',
382  5 => 'Nellymoser 8kHz mono',
383  6 => 'Nellymoser',
384  7 => 'G.711A-law logarithmic PCM',
385  8 => 'G.711 mu-law logarithmic PCM',
386  9 => 'reserved',
387  10 => 'AAC',
388  11 => false, // unknown?
389  12 => false, // unknown?
390  13 => false, // unknown?
391  14 => 'mp3 8kHz',
392  15 => 'Device-specific sound',
393  );
394 
395  return (isset($FLVaudioFormat[$id]) ? $FLVaudioFormat[$id] : false);
396  }
397 
403  public function FLVaudioRate($id)
404  {
405  $FLVaudioRate = array(
406  0 => 5500,
407  1 => 11025,
408  2 => 22050,
409  3 => 44100,
410  );
411 
412  return (isset($FLVaudioRate[$id]) ? $FLVaudioRate[$id] : false);
413  }
414 
420  public function FLVaudioBitDepth($id)
421  {
422  $FLVaudioBitDepth = array(
423  0 => 8,
424  1 => 16,
425  );
426 
427  return (isset($FLVaudioBitDepth[$id]) ? $FLVaudioBitDepth[$id] : false);
428  }
429 
435  public function FLVvideoCodec($id)
436  {
437  $FLVvideoCodec = array(
438  self::GETID3_FLV_VIDEO_H263 => 'Sorenson H.263',
439  self::GETID3_FLV_VIDEO_SCREEN => 'Screen video',
440  self::GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6',
441  self::GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel',
442  self::GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2',
443  self::GETID3_FLV_VIDEO_H264 => 'Sorenson H.264',
444  );
445 
446  return (isset($FLVvideoCodec[$id]) ? $FLVvideoCodec[$id] : false);
447  }
448 }
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
Definition: Helper.php:36
GetId3() by James Heinrich info@getid3.org //.
Definition: BaseHandler.php:25
const GETID3_FLV_VIDEO_VP6FLV_ALPHA
Definition: Flv.php:82
fseek($bytes, $whence=SEEK_SET)
$info
Definition: example_052.php:80
GetId3() by James Heinrich info@getid3.org //.
Definition: Flv.php:68
static CastAsInt($floatnum)
Definition: Helper.php:107
Create styles array
The data for the language used.
const GETID3_FLV_VIDEO_SCREENV2
Definition: Flv.php:83
static LittleEndian2Int($byteword, $signed=false)
Definition: Helper.php:413
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: Helper.php:374
GetId3() by James Heinrich info@getid3.org //.
Definition: AMFReader.php:27
GetId3() by James Heinrich info@getid3.org //.
Definition: AMFStream.php:29