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
68class 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
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) {
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
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
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}
An exception for terminatinating execution or to throw for unit testing.
GetId3() by James Heinrich info@getid3.org //.
Definition: BaseHandler.php:26
fseek($bytes, $whence=SEEK_SET)
GetId3() by James Heinrich info@getid3.org //.
Definition: Helper.php:27
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: Helper.php:374
static LittleEndian2Int($byteword, $signed=false)
Definition: Helper.php:413
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
Definition: Helper.php:36
static CastAsInt($floatnum)
Definition: Helper.php:107
GetId3() by James Heinrich info@getid3.org //.
Definition: AMFReader.php:28
GetId3() by James Heinrich info@getid3.org //.
Definition: AMFStream.php:30
GetId3() by James Heinrich info@getid3.org //.
Definition: Flv.php:69
const GETID3_FLV_VIDEO_SCREENV2
Definition: Flv.php:83
const GETID3_FLV_VIDEO_VP6FLV_ALPHA
Definition: Flv.php:82
$info
Definition: example_052.php:80