65 define('H264_PROFILE_HIGH10', 110); |
71 define('H264_PROFILE_HIGH10', 110); |
66 define('H264_PROFILE_HIGH422', 122); |
72 define('H264_PROFILE_HIGH422', 122); |
67 define('H264_PROFILE_HIGH444', 144); |
73 define('H264_PROFILE_HIGH444', 144); |
68 define('H264_PROFILE_HIGH444_PREDICTIVE', 244); |
74 define('H264_PROFILE_HIGH444_PREDICTIVE', 244); |
69 |
75 |
70 class getid3_flv extends getid3_handler |
76 class getid3_flv extends getid3_handler { |
71 { |
77 |
|
78 const magic = 'FLV'; |
|
79 |
72 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 |
80 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 |
73 |
81 |
74 public function Analyze() { |
82 public function Analyze() { |
75 $info = &$this->getid3->info; |
83 $info = &$this->getid3->info; |
76 |
84 |
77 fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); |
85 $this->fseek($info['avdataoffset']); |
78 |
86 |
79 $FLVdataLength = $info['avdataend'] - $info['avdataoffset']; |
87 $FLVdataLength = $info['avdataend'] - $info['avdataoffset']; |
80 $FLVheader = fread($this->getid3->fp, 5); |
88 $FLVheader = $this->fread(5); |
81 |
89 |
82 $info['fileformat'] = 'flv'; |
90 $info['fileformat'] = 'flv'; |
83 $info['flv']['header']['signature'] = substr($FLVheader, 0, 3); |
91 $info['flv']['header']['signature'] = substr($FLVheader, 0, 3); |
84 $info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1)); |
92 $info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1)); |
85 $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1)); |
93 $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1)); |
86 |
94 |
87 $magic = 'FLV'; |
95 if ($info['flv']['header']['signature'] != self::magic) { |
88 if ($info['flv']['header']['signature'] != $magic) { |
96 $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(self::magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"'; |
89 $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"'; |
97 unset($info['flv'], $info['fileformat']); |
90 unset($info['flv']); |
|
91 unset($info['fileformat']); |
|
92 return false; |
98 return false; |
93 } |
99 } |
94 |
100 |
95 $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04); |
101 $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04); |
96 $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01); |
102 $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01); |
97 |
103 |
98 $FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 4)); |
104 $FrameSizeDataLength = getid3_lib::BigEndian2Int($this->fread(4)); |
99 $FLVheaderFrameLength = 9; |
105 $FLVheaderFrameLength = 9; |
100 if ($FrameSizeDataLength > $FLVheaderFrameLength) { |
106 if ($FrameSizeDataLength > $FLVheaderFrameLength) { |
101 fseek($this->getid3->fp, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); |
107 $this->fseek($FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); |
102 } |
108 } |
103 $Duration = 0; |
109 $Duration = 0; |
104 $found_video = false; |
110 $found_video = false; |
105 $found_audio = false; |
111 $found_audio = false; |
106 $found_meta = false; |
112 $found_meta = false; |
107 $found_valid_meta_playtime = false; |
113 $found_valid_meta_playtime = false; |
108 $tagParseCount = 0; |
114 $tagParseCount = 0; |
109 $info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0); |
115 $info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0); |
110 $flv_framecount = &$info['flv']['framecount']; |
116 $flv_framecount = &$info['flv']['framecount']; |
111 while (((ftell($this->getid3->fp) + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) { |
117 while ((($this->ftell() + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) { |
112 $ThisTagHeader = fread($this->getid3->fp, 16); |
118 $ThisTagHeader = $this->fread(16); |
113 |
119 |
114 $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4)); |
120 $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4)); |
115 $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1)); |
121 $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1)); |
116 $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3)); |
122 $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3)); |
117 $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3)); |
123 $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3)); |
118 $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1)); |
124 $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1)); |
119 $NextOffset = ftell($this->getid3->fp) - 1 + $DataLength; |
125 $NextOffset = $this->ftell() - 1 + $DataLength; |
120 if ($Timestamp > $Duration) { |
126 if ($Timestamp > $Duration) { |
121 $Duration = $Timestamp; |
127 $Duration = $Timestamp; |
122 } |
128 } |
123 |
129 |
124 $flv_framecount['total']++; |
130 $flv_framecount['total']++; |
158 // there is at least one SequenceParameterSet |
164 // there is at least one SequenceParameterSet |
159 // read size of the first SequenceParameterSet |
165 // read size of the first SequenceParameterSet |
160 //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2)); |
166 //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2)); |
161 $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2)); |
167 $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2)); |
162 // read the first SequenceParameterSet |
168 // read the first SequenceParameterSet |
163 $sps = fread($this->getid3->fp, $spsSize); |
169 $sps = $this->fread($spsSize); |
164 if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red |
170 if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red |
165 $spsReader = new AVCSequenceParameterSetReader($sps); |
171 $spsReader = new AVCSequenceParameterSetReader($sps); |
166 $spsReader->readData(); |
172 $spsReader->readData(); |
167 $info['video']['resolution_x'] = $spsReader->getWidth(); |
173 $info['video']['resolution_x'] = $spsReader->getWidth(); |
168 $info['video']['resolution_y'] = $spsReader->getHeight(); |
174 $info['video']['resolution_y'] = $spsReader->getHeight(); |
183 //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8; |
189 //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8; |
184 //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); |
190 //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); |
185 //$PictureSizeEnc <<= 1; |
191 //$PictureSizeEnc <<= 1; |
186 //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; |
192 //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; |
187 |
193 |
188 $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)); |
194 $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)) >> 7; |
189 $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); |
195 $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)) >> 7; |
190 $PictureSizeEnc['x'] >>= 7; |
|
191 $PictureSizeEnc['y'] >>= 7; |
|
192 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF; |
196 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF; |
193 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF; |
197 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF; |
194 break; |
198 break; |
195 |
199 |
196 case 1: |
200 case 1: |
197 $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3)); |
201 $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3)) >> 7; |
198 $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)); |
202 $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)) >> 7; |
199 $PictureSizeEnc['x'] >>= 7; |
|
200 $PictureSizeEnc['y'] >>= 7; |
|
201 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF; |
203 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF; |
202 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF; |
204 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF; |
203 break; |
205 break; |
204 |
206 |
205 case 2: |
207 case 2: |
231 $info['video']['resolution_x'] = 0; |
233 $info['video']['resolution_x'] = 0; |
232 $info['video']['resolution_y'] = 0; |
234 $info['video']['resolution_y'] = 0; |
233 break; |
235 break; |
234 |
236 |
235 } |
237 } |
|
238 |
|
239 } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_VP6FLV_ALPHA) { |
|
240 |
|
241 /* contributed by schouwerwouØgmail*com */ |
|
242 if (!isset($info['video']['resolution_x'])) { // only when meta data isn't set |
|
243 $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); |
|
244 $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 2)); |
|
245 $info['video']['resolution_x'] = ($PictureSizeEnc['x'] & 0xFF) << 3; |
|
246 $info['video']['resolution_y'] = ($PictureSizeEnc['y'] & 0xFF) << 3; |
|
247 } |
|
248 /* end schouwerwouØgmail*com */ |
|
249 |
236 } |
250 } |
237 $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y']; |
251 if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) { |
|
252 $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y']; |
|
253 } |
238 } |
254 } |
239 break; |
255 break; |
240 |
256 |
241 // Meta tag |
257 // Meta tag |
242 case GETID3_FLV_TAG_META: |
258 case GETID3_FLV_TAG_META: |
243 if (!$found_meta) { |
259 if (!$found_meta) { |
244 $found_meta = true; |
260 $found_meta = true; |
245 fseek($this->getid3->fp, -1, SEEK_CUR); |
261 $this->fseek(-1, SEEK_CUR); |
246 $datachunk = fread($this->getid3->fp, $DataLength); |
262 $datachunk = $this->fread($DataLength); |
247 $AMFstream = new AMFStream($datachunk); |
263 $AMFstream = new AMFStream($datachunk); |
248 $reader = new AMFReader($AMFstream); |
264 $reader = new AMFReader($AMFstream); |
249 $eventName = $reader->readData(); |
265 $eventName = $reader->readData(); |
250 $info['flv']['meta'][$eventName] = $reader->readData(); |
266 $info['flv']['meta'][$eventName] = $reader->readData(); |
251 unset($reader); |
267 unset($reader); |
277 |
293 |
278 default: |
294 default: |
279 // noop |
295 // noop |
280 break; |
296 break; |
281 } |
297 } |
282 fseek($this->getid3->fp, $NextOffset, SEEK_SET); |
298 $this->fseek($NextOffset); |
283 } |
299 } |
284 |
300 |
285 $info['playtime_seconds'] = $Duration / 1000; |
301 $info['playtime_seconds'] = $Duration / 1000; |
286 if ($info['playtime_seconds'] > 0) { |
302 if ($info['playtime_seconds'] > 0) { |
287 $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; |
303 $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; |
288 } |
304 } |
289 |
305 |
290 if ($info['flv']['header']['hasAudio']) { |
306 if ($info['flv']['header']['hasAudio']) { |
291 $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['audio']['audioFormat']); |
307 $info['audio']['codec'] = self::audioFormatLookup($info['flv']['audio']['audioFormat']); |
292 $info['audio']['sample_rate'] = $this->FLVaudioRate($info['flv']['audio']['audioRate']); |
308 $info['audio']['sample_rate'] = self::audioRateLookup($info['flv']['audio']['audioRate']); |
293 $info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info['flv']['audio']['audioSampleSize']); |
309 $info['audio']['bits_per_sample'] = self::audioBitDepthLookup($info['flv']['audio']['audioSampleSize']); |
294 |
310 |
295 $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo |
311 $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo |
296 $info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed |
312 $info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed |
297 $info['audio']['dataformat'] = 'flv'; |
313 $info['audio']['dataformat'] = 'flv'; |
298 } |
314 } |
299 if (!empty($info['flv']['header']['hasVideo'])) { |
315 if (!empty($info['flv']['header']['hasVideo'])) { |
300 $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['video']['videoCodec']); |
316 $info['video']['codec'] = self::videoCodecLookup($info['flv']['video']['videoCodec']); |
301 $info['video']['dataformat'] = 'flv'; |
317 $info['video']['dataformat'] = 'flv'; |
302 $info['video']['lossless'] = false; |
318 $info['video']['lossless'] = false; |
303 } |
319 } |
304 |
320 |
305 // Set information from meta |
321 // Set information from meta |
306 if (!empty($info['flv']['meta']['onMetaData']['duration'])) { |
322 if (!empty($info['flv']['meta']['onMetaData']['duration'])) { |
307 $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration']; |
323 $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration']; |
308 $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; |
324 $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; |
309 } |
325 } |
310 if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) { |
326 if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) { |
311 $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']); |
327 $info['audio']['codec'] = self::audioFormatLookup($info['flv']['meta']['onMetaData']['audiocodecid']); |
312 } |
328 } |
313 if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) { |
329 if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) { |
314 $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']); |
330 $info['video']['codec'] = self::videoCodecLookup($info['flv']['meta']['onMetaData']['videocodecid']); |
315 } |
331 } |
316 return true; |
332 return true; |
317 } |
333 } |
318 |
334 |
319 |
335 |
320 public function FLVaudioFormat($id) { |
336 public static function audioFormatLookup($id) { |
321 $FLVaudioFormat = array( |
337 static $lookup = array( |
322 0 => 'Linear PCM, platform endian', |
338 0 => 'Linear PCM, platform endian', |
323 1 => 'ADPCM', |
339 1 => 'ADPCM', |
324 2 => 'mp3', |
340 2 => 'mp3', |
325 3 => 'Linear PCM, little endian', |
341 3 => 'Linear PCM, little endian', |
326 4 => 'Nellymoser 16kHz mono', |
342 4 => 'Nellymoser 16kHz mono', |
328 6 => 'Nellymoser', |
344 6 => 'Nellymoser', |
329 7 => 'G.711A-law logarithmic PCM', |
345 7 => 'G.711A-law logarithmic PCM', |
330 8 => 'G.711 mu-law logarithmic PCM', |
346 8 => 'G.711 mu-law logarithmic PCM', |
331 9 => 'reserved', |
347 9 => 'reserved', |
332 10 => 'AAC', |
348 10 => 'AAC', |
333 11 => false, // unknown? |
349 11 => 'Speex', |
334 12 => false, // unknown? |
350 12 => false, // unknown? |
335 13 => false, // unknown? |
351 13 => false, // unknown? |
336 14 => 'mp3 8kHz', |
352 14 => 'mp3 8kHz', |
337 15 => 'Device-specific sound', |
353 15 => 'Device-specific sound', |
338 ); |
354 ); |
339 return (isset($FLVaudioFormat[$id]) ? $FLVaudioFormat[$id] : false); |
355 return (isset($lookup[$id]) ? $lookup[$id] : false); |
340 } |
356 } |
341 |
357 |
342 public function FLVaudioRate($id) { |
358 public static function audioRateLookup($id) { |
343 $FLVaudioRate = array( |
359 static $lookup = array( |
344 0 => 5500, |
360 0 => 5500, |
345 1 => 11025, |
361 1 => 11025, |
346 2 => 22050, |
362 2 => 22050, |
347 3 => 44100, |
363 3 => 44100, |
348 ); |
364 ); |
349 return (isset($FLVaudioRate[$id]) ? $FLVaudioRate[$id] : false); |
365 return (isset($lookup[$id]) ? $lookup[$id] : false); |
350 } |
366 } |
351 |
367 |
352 public function FLVaudioBitDepth($id) { |
368 public static function audioBitDepthLookup($id) { |
353 $FLVaudioBitDepth = array( |
369 static $lookup = array( |
354 0 => 8, |
370 0 => 8, |
355 1 => 16, |
371 1 => 16, |
356 ); |
372 ); |
357 return (isset($FLVaudioBitDepth[$id]) ? $FLVaudioBitDepth[$id] : false); |
373 return (isset($lookup[$id]) ? $lookup[$id] : false); |
358 } |
374 } |
359 |
375 |
360 public function FLVvideoCodec($id) { |
376 public static function videoCodecLookup($id) { |
361 $FLVvideoCodec = array( |
377 static $lookup = array( |
362 GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', |
378 GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', |
363 GETID3_FLV_VIDEO_SCREEN => 'Screen video', |
379 GETID3_FLV_VIDEO_SCREEN => 'Screen video', |
364 GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6', |
380 GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6', |
365 GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel', |
381 GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel', |
366 GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2', |
382 GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2', |
367 GETID3_FLV_VIDEO_H264 => 'Sorenson H.264', |
383 GETID3_FLV_VIDEO_H264 => 'Sorenson H.264', |
368 ); |
384 ); |
369 return (isset($FLVvideoCodec[$id]) ? $FLVvideoCodec[$id] : false); |
385 return (isset($lookup[$id]) ? $lookup[$id] : false); |
370 } |
386 } |
371 } |
387 } |
372 |
388 |
373 class AMFStream { |
389 class AMFStream { |
374 public $bytes; |
390 public $bytes; |
375 public $pos; |
391 public $pos; |
376 |
392 |
377 public function AMFStream(&$bytes) { |
393 public function __construct(&$bytes) { |
378 $this->bytes =& $bytes; |
394 $this->bytes =& $bytes; |
379 $this->pos = 0; |
395 $this->pos = 0; |
380 } |
396 } |
381 |
397 |
382 public function readByte() { |
398 public function readByte() { |
617 public $currentBytes = 0; |
633 public $currentBytes = 0; |
618 public $currentBits = 0; |
634 public $currentBits = 0; |
619 public $width; |
635 public $width; |
620 public $height; |
636 public $height; |
621 |
637 |
622 public function AVCSequenceParameterSetReader($sps) { |
638 public function __construct($sps) { |
623 $this->sps = $sps; |
639 $this->sps = $sps; |
624 } |
640 } |
625 |
641 |
626 public function readData() { |
642 public function readData() { |
627 $this->skipBits(8); |
643 $this->skipBits(8); |
628 $this->skipBits(8); |
644 $this->skipBits(8); |
629 $profile = $this->getBits(8); // read profile |
645 $profile = $this->getBits(8); // read profile |
630 $this->skipBits(16); |
646 if ($profile > 0) { |
631 $this->expGolombUe(); // read sps id |
647 $this->skipBits(8); |
632 if (in_array($profile, array(H264_PROFILE_HIGH, H264_PROFILE_HIGH10, H264_PROFILE_HIGH422, H264_PROFILE_HIGH444, H264_PROFILE_HIGH444_PREDICTIVE))) { |
648 $level_idc = $this->getBits(8); // level_idc |
633 if ($this->expGolombUe() == 3) { |
649 $this->expGolombUe(); // seq_parameter_set_id // sps |
634 $this->skipBits(1); |
650 $this->expGolombUe(); // log2_max_frame_num_minus4 |
635 } |
651 $picOrderType = $this->expGolombUe(); // pic_order_cnt_type |
636 $this->expGolombUe(); |
652 if ($picOrderType == 0) { |
637 $this->expGolombUe(); |
653 $this->expGolombUe(); // log2_max_pic_order_cnt_lsb_minus4 |
638 $this->skipBits(1); |
654 } elseif ($picOrderType == 1) { |
639 if ($this->getBit()) { |
655 $this->skipBits(1); // delta_pic_order_always_zero_flag |
640 for ($i = 0; $i < 8; $i++) { |
656 $this->expGolombSe(); // offset_for_non_ref_pic |
641 if ($this->getBit()) { |
657 $this->expGolombSe(); // offset_for_top_to_bottom_field |
642 $size = $i < 6 ? 16 : 64; |
658 $num_ref_frames_in_pic_order_cnt_cycle = $this->expGolombUe(); // num_ref_frames_in_pic_order_cnt_cycle |
643 $lastScale = 8; |
659 for ($i = 0; $i < $num_ref_frames_in_pic_order_cnt_cycle; $i++) { |
644 $nextScale = 8; |
660 $this->expGolombSe(); // offset_for_ref_frame[ i ] |
645 for ($j = 0; $j < $size; $j++) { |
|
646 if ($nextScale != 0) { |
|
647 $deltaScale = $this->expGolombUe(); |
|
648 $nextScale = ($lastScale + $deltaScale + 256) % 256; |
|
649 } |
|
650 if ($nextScale != 0) { |
|
651 $lastScale = $nextScale; |
|
652 } |
|
653 } |
|
654 } |
|
655 } |
661 } |
656 } |
662 } |
657 } |
663 $this->expGolombUe(); // num_ref_frames |
658 $this->expGolombUe(); |
664 $this->skipBits(1); // gaps_in_frame_num_value_allowed_flag |
659 $pocType = $this->expGolombUe(); |
665 $pic_width_in_mbs_minus1 = $this->expGolombUe(); // pic_width_in_mbs_minus1 |
660 if ($pocType == 0) { |
666 $pic_height_in_map_units_minus1 = $this->expGolombUe(); // pic_height_in_map_units_minus1 |
661 $this->expGolombUe(); |
667 |
662 } elseif ($pocType == 1) { |
668 $frame_mbs_only_flag = $this->getBits(1); // frame_mbs_only_flag |
663 $this->skipBits(1); |
669 if ($frame_mbs_only_flag == 0) { |
664 $this->expGolombSe(); |
670 $this->skipBits(1); // mb_adaptive_frame_field_flag |
665 $this->expGolombSe(); |
|
666 $pocCycleLength = $this->expGolombUe(); |
|
667 for ($i = 0; $i < $pocCycleLength; $i++) { |
|
668 $this->expGolombSe(); |
|
669 } |
671 } |
670 } |
672 $this->skipBits(1); // direct_8x8_inference_flag |
671 $this->expGolombUe(); |
673 $frame_cropping_flag = $this->getBits(1); // frame_cropping_flag |
672 $this->skipBits(1); |
674 |
673 $this->width = ($this->expGolombUe() + 1) * 16; |
675 $frame_crop_left_offset = 0; |
674 $heightMap = $this->expGolombUe() + 1; |
676 $frame_crop_right_offset = 0; |
675 $this->height = (2 - $this->getBit()) * $heightMap * 16; |
677 $frame_crop_top_offset = 0; |
|
678 $frame_crop_bottom_offset = 0; |
|
679 |
|
680 if ($frame_cropping_flag) { |
|
681 $frame_crop_left_offset = $this->expGolombUe(); // frame_crop_left_offset |
|
682 $frame_crop_right_offset = $this->expGolombUe(); // frame_crop_right_offset |
|
683 $frame_crop_top_offset = $this->expGolombUe(); // frame_crop_top_offset |
|
684 $frame_crop_bottom_offset = $this->expGolombUe(); // frame_crop_bottom_offset |
|
685 } |
|
686 $this->skipBits(1); // vui_parameters_present_flag |
|
687 // etc |
|
688 |
|
689 $this->width = (($pic_width_in_mbs_minus1 + 1) * 16) - ($frame_crop_left_offset * 2) - ($frame_crop_right_offset * 2); |
|
690 $this->height = ((2 - $frame_mbs_only_flag) * ($pic_height_in_map_units_minus1 + 1) * 16) - ($frame_crop_top_offset * 2) - ($frame_crop_bottom_offset * 2); |
|
691 } |
676 } |
692 } |
677 |
693 |
678 public function skipBits($bits) { |
694 public function skipBits($bits) { |
679 $newBits = $this->currentBits + $bits; |
695 $newBits = $this->currentBits + $bits; |
680 $this->currentBytes += (int)floor($newBits / 8); |
696 $this->currentBytes += (int)floor($newBits / 8); |