module.audio-video.flv.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. <?php
  2. /////////////////////////////////////////////////////////////////
  3. /// getID3() by James Heinrich <info@getid3.org> //
  4. // available at http://getid3.sourceforge.net //
  5. // or http://www.getid3.org //
  6. // //
  7. // FLV module by Seth Kaufman <seth@whirl-i-gig.com> //
  8. // //
  9. // * version 0.1 (26 June 2005) //
  10. // //
  11. // //
  12. // * version 0.1.1 (15 July 2005) //
  13. // minor modifications by James Heinrich <info@getid3.org> //
  14. // //
  15. // * version 0.2 (22 February 2006) //
  16. // Support for On2 VP6 codec and meta information //
  17. // by Steve Webster <steve.webster@featurecreep.com> //
  18. // //
  19. // * version 0.3 (15 June 2006) //
  20. // Modified to not read entire file into memory //
  21. // by James Heinrich <info@getid3.org> //
  22. // //
  23. // * version 0.4 (07 December 2007) //
  24. // Bugfixes for incorrectly parsed FLV dimensions //
  25. // and incorrect parsing of onMetaTag //
  26. // by Evgeny Moysevich <moysevich@gmail.com> //
  27. // //
  28. // * version 0.5 (21 May 2009) //
  29. // Fixed parsing of audio tags and added additional codec //
  30. // details. The duration is now read from onMetaTag (if //
  31. // exists), rather than parsing whole file //
  32. // by Nigel Barnes <ngbarnes@hotmail.com> //
  33. // //
  34. // * version 0.6 (24 May 2009) //
  35. // Better parsing of files with h264 video //
  36. // by Evgeny Moysevich <moysevichØgmail*com> //
  37. // //
  38. // * version 0.6.1 (30 May 2011) //
  39. // prevent infinite loops in expGolombUe() //
  40. // //
  41. /////////////////////////////////////////////////////////////////
  42. // //
  43. // module.audio-video.flv.php //
  44. // module for analyzing Shockwave Flash Video files //
  45. // dependencies: NONE //
  46. // ///
  47. /////////////////////////////////////////////////////////////////
  48. define('GETID3_FLV_TAG_AUDIO', 8);
  49. define('GETID3_FLV_TAG_VIDEO', 9);
  50. define('GETID3_FLV_TAG_META', 18);
  51. define('GETID3_FLV_VIDEO_H263', 2);
  52. define('GETID3_FLV_VIDEO_SCREEN', 3);
  53. define('GETID3_FLV_VIDEO_VP6FLV', 4);
  54. define('GETID3_FLV_VIDEO_VP6FLV_ALPHA', 5);
  55. define('GETID3_FLV_VIDEO_SCREENV2', 6);
  56. define('GETID3_FLV_VIDEO_H264', 7);
  57. define('H264_AVC_SEQUENCE_HEADER', 0);
  58. define('H264_PROFILE_BASELINE', 66);
  59. define('H264_PROFILE_MAIN', 77);
  60. define('H264_PROFILE_EXTENDED', 88);
  61. define('H264_PROFILE_HIGH', 100);
  62. define('H264_PROFILE_HIGH10', 110);
  63. define('H264_PROFILE_HIGH422', 122);
  64. define('H264_PROFILE_HIGH444', 144);
  65. define('H264_PROFILE_HIGH444_PREDICTIVE', 244);
  66. class getid3_flv extends getid3_handler
  67. {
  68. var $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
  69. function Analyze() {
  70. $info = &$this->getid3->info;
  71. fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
  72. $FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
  73. $FLVheader = fread($this->getid3->fp, 5);
  74. $info['fileformat'] = 'flv';
  75. $info['flv']['header']['signature'] = substr($FLVheader, 0, 3);
  76. $info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
  77. $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
  78. $magic = 'FLV';
  79. if ($info['flv']['header']['signature'] != $magic) {
  80. $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"';
  81. unset($info['flv']);
  82. unset($info['fileformat']);
  83. return false;
  84. }
  85. $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
  86. $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
  87. $FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 4));
  88. $FLVheaderFrameLength = 9;
  89. if ($FrameSizeDataLength > $FLVheaderFrameLength) {
  90. fseek($this->getid3->fp, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
  91. }
  92. $Duration = 0;
  93. $found_video = false;
  94. $found_audio = false;
  95. $found_meta = false;
  96. $found_valid_meta_playtime = false;
  97. $tagParseCount = 0;
  98. $info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0);
  99. $flv_framecount = &$info['flv']['framecount'];
  100. while (((ftell($this->getid3->fp) + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) {
  101. $ThisTagHeader = fread($this->getid3->fp, 16);
  102. $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4));
  103. $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1));
  104. $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3));
  105. $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3));
  106. $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
  107. $NextOffset = ftell($this->getid3->fp) - 1 + $DataLength;
  108. if ($Timestamp > $Duration) {
  109. $Duration = $Timestamp;
  110. }
  111. $flv_framecount['total']++;
  112. switch ($TagType) {
  113. case GETID3_FLV_TAG_AUDIO:
  114. $flv_framecount['audio']++;
  115. if (!$found_audio) {
  116. $found_audio = true;
  117. $info['flv']['audio']['audioFormat'] = ($LastHeaderByte >> 4) & 0x0F;
  118. $info['flv']['audio']['audioRate'] = ($LastHeaderByte >> 2) & 0x03;
  119. $info['flv']['audio']['audioSampleSize'] = ($LastHeaderByte >> 1) & 0x01;
  120. $info['flv']['audio']['audioType'] = $LastHeaderByte & 0x01;
  121. }
  122. break;
  123. case GETID3_FLV_TAG_VIDEO:
  124. $flv_framecount['video']++;
  125. if (!$found_video) {
  126. $found_video = true;
  127. $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
  128. $FLVvideoHeader = fread($this->getid3->fp, 11);
  129. if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) {
  130. // this code block contributed by: moysevichØgmail*com
  131. $AVCPacketType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 0, 1));
  132. if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) {
  133. // read AVCDecoderConfigurationRecord
  134. $configurationVersion = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 1));
  135. $AVCProfileIndication = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 1));
  136. $profile_compatibility = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 1));
  137. $lengthSizeMinusOne = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 1));
  138. $numOfSequenceParameterSets = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 8, 1));
  139. if (($numOfSequenceParameterSets & 0x1F) != 0) {
  140. // there is at least one SequenceParameterSet
  141. // read size of the first SequenceParameterSet
  142. //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
  143. $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
  144. // read the first SequenceParameterSet
  145. $sps = fread($this->getid3->fp, $spsSize);
  146. if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red
  147. $spsReader = new AVCSequenceParameterSetReader($sps);
  148. $spsReader->readData();
  149. $info['video']['resolution_x'] = $spsReader->getWidth();
  150. $info['video']['resolution_y'] = $spsReader->getHeight();
  151. }
  152. }
  153. }
  154. // end: moysevichØgmail*com
  155. } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) {
  156. $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
  157. $PictureSizeType = $PictureSizeType & 0x0007;
  158. $info['flv']['header']['videoSizeType'] = $PictureSizeType;
  159. switch ($PictureSizeType) {
  160. case 0:
  161. //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
  162. //$PictureSizeEnc <<= 1;
  163. //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
  164. //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
  165. //$PictureSizeEnc <<= 1;
  166. //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
  167. $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2));
  168. $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
  169. $PictureSizeEnc['x'] >>= 7;
  170. $PictureSizeEnc['y'] >>= 7;
  171. $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
  172. $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
  173. break;
  174. case 1:
  175. $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3));
  176. $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3));
  177. $PictureSizeEnc['x'] >>= 7;
  178. $PictureSizeEnc['y'] >>= 7;
  179. $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
  180. $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
  181. break;
  182. case 2:
  183. $info['video']['resolution_x'] = 352;
  184. $info['video']['resolution_y'] = 288;
  185. break;
  186. case 3:
  187. $info['video']['resolution_x'] = 176;
  188. $info['video']['resolution_y'] = 144;
  189. break;
  190. case 4:
  191. $info['video']['resolution_x'] = 128;
  192. $info['video']['resolution_y'] = 96;
  193. break;
  194. case 5:
  195. $info['video']['resolution_x'] = 320;
  196. $info['video']['resolution_y'] = 240;
  197. break;
  198. case 6:
  199. $info['video']['resolution_x'] = 160;
  200. $info['video']['resolution_y'] = 120;
  201. break;
  202. default:
  203. $info['video']['resolution_x'] = 0;
  204. $info['video']['resolution_y'] = 0;
  205. break;
  206. }
  207. }
  208. $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
  209. }
  210. break;
  211. // Meta tag
  212. case GETID3_FLV_TAG_META:
  213. if (!$found_meta) {
  214. $found_meta = true;
  215. fseek($this->getid3->fp, -1, SEEK_CUR);
  216. $datachunk = fread($this->getid3->fp, $DataLength);
  217. $AMFstream = new AMFStream($datachunk);
  218. $reader = new AMFReader($AMFstream);
  219. $eventName = $reader->readData();
  220. $info['flv']['meta'][$eventName] = $reader->readData();
  221. unset($reader);
  222. $copykeys = array('framerate'=>'frame_rate', 'width'=>'resolution_x', 'height'=>'resolution_y', 'audiodatarate'=>'bitrate', 'videodatarate'=>'bitrate');
  223. foreach ($copykeys as $sourcekey => $destkey) {
  224. if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) {
  225. switch ($sourcekey) {
  226. case 'width':
  227. case 'height':
  228. $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey]));
  229. break;
  230. case 'audiodatarate':
  231. $info['audio'][$destkey] = getid3_lib::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000));
  232. break;
  233. case 'videodatarate':
  234. case 'frame_rate':
  235. default:
  236. $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey];
  237. break;
  238. }
  239. }
  240. }
  241. if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
  242. $found_valid_meta_playtime = true;
  243. }
  244. }
  245. break;
  246. default:
  247. // noop
  248. break;
  249. }
  250. fseek($this->getid3->fp, $NextOffset, SEEK_SET);
  251. }
  252. $info['playtime_seconds'] = $Duration / 1000;
  253. if ($info['playtime_seconds'] > 0) {
  254. $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
  255. }
  256. if ($info['flv']['header']['hasAudio']) {
  257. $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['audio']['audioFormat']);
  258. $info['audio']['sample_rate'] = $this->FLVaudioRate($info['flv']['audio']['audioRate']);
  259. $info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info['flv']['audio']['audioSampleSize']);
  260. $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
  261. $info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
  262. $info['audio']['dataformat'] = 'flv';
  263. }
  264. if (!empty($info['flv']['header']['hasVideo'])) {
  265. $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['video']['videoCodec']);
  266. $info['video']['dataformat'] = 'flv';
  267. $info['video']['lossless'] = false;
  268. }
  269. // Set information from meta
  270. if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
  271. $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration'];
  272. $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
  273. }
  274. if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) {
  275. $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']);
  276. }
  277. if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
  278. $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']);
  279. }
  280. return true;
  281. }
  282. function FLVaudioFormat($id) {
  283. $FLVaudioFormat = array(
  284. 0 => 'Linear PCM, platform endian',
  285. 1 => 'ADPCM',
  286. 2 => 'mp3',
  287. 3 => 'Linear PCM, little endian',
  288. 4 => 'Nellymoser 16kHz mono',
  289. 5 => 'Nellymoser 8kHz mono',
  290. 6 => 'Nellymoser',
  291. 7 => 'G.711A-law logarithmic PCM',
  292. 8 => 'G.711 mu-law logarithmic PCM',
  293. 9 => 'reserved',
  294. 10 => 'AAC',
  295. 11 => false, // unknown?
  296. 12 => false, // unknown?
  297. 13 => false, // unknown?
  298. 14 => 'mp3 8kHz',
  299. 15 => 'Device-specific sound',
  300. );
  301. return (isset($FLVaudioFormat[$id]) ? $FLVaudioFormat[$id] : false);
  302. }
  303. function FLVaudioRate($id) {
  304. $FLVaudioRate = array(
  305. 0 => 5500,
  306. 1 => 11025,
  307. 2 => 22050,
  308. 3 => 44100,
  309. );
  310. return (isset($FLVaudioRate[$id]) ? $FLVaudioRate[$id] : false);
  311. }
  312. function FLVaudioBitDepth($id) {
  313. $FLVaudioBitDepth = array(
  314. 0 => 8,
  315. 1 => 16,
  316. );
  317. return (isset($FLVaudioBitDepth[$id]) ? $FLVaudioBitDepth[$id] : false);
  318. }
  319. function FLVvideoCodec($id) {
  320. $FLVvideoCodec = array(
  321. GETID3_FLV_VIDEO_H263 => 'Sorenson H.263',
  322. GETID3_FLV_VIDEO_SCREEN => 'Screen video',
  323. GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6',
  324. GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel',
  325. GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2',
  326. GETID3_FLV_VIDEO_H264 => 'Sorenson H.264',
  327. );
  328. return (isset($FLVvideoCodec[$id]) ? $FLVvideoCodec[$id] : false);
  329. }
  330. }
  331. class AMFStream {
  332. var $bytes;
  333. var $pos;
  334. function AMFStream(&$bytes) {
  335. $this->bytes =& $bytes;
  336. $this->pos = 0;
  337. }
  338. function readByte() {
  339. return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
  340. }
  341. function readInt() {
  342. return ($this->readByte() << 8) + $this->readByte();
  343. }
  344. function readLong() {
  345. return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
  346. }
  347. function readDouble() {
  348. return getid3_lib::BigEndian2Float($this->read(8));
  349. }
  350. function readUTF() {
  351. $length = $this->readInt();
  352. return $this->read($length);
  353. }
  354. function readLongUTF() {
  355. $length = $this->readLong();
  356. return $this->read($length);
  357. }
  358. function read($length) {
  359. $val = substr($this->bytes, $this->pos, $length);
  360. $this->pos += $length;
  361. return $val;
  362. }
  363. function peekByte() {
  364. $pos = $this->pos;
  365. $val = $this->readByte();
  366. $this->pos = $pos;
  367. return $val;
  368. }
  369. function peekInt() {
  370. $pos = $this->pos;
  371. $val = $this->readInt();
  372. $this->pos = $pos;
  373. return $val;
  374. }
  375. function peekLong() {
  376. $pos = $this->pos;
  377. $val = $this->readLong();
  378. $this->pos = $pos;
  379. return $val;
  380. }
  381. function peekDouble() {
  382. $pos = $this->pos;
  383. $val = $this->readDouble();
  384. $this->pos = $pos;
  385. return $val;
  386. }
  387. function peekUTF() {
  388. $pos = $this->pos;
  389. $val = $this->readUTF();
  390. $this->pos = $pos;
  391. return $val;
  392. }
  393. function peekLongUTF() {
  394. $pos = $this->pos;
  395. $val = $this->readLongUTF();
  396. $this->pos = $pos;
  397. return $val;
  398. }
  399. }
  400. class AMFReader {
  401. var $stream;
  402. function AMFReader(&$stream) {
  403. $this->stream =& $stream;
  404. }
  405. function readData() {
  406. $value = null;
  407. $type = $this->stream->readByte();
  408. switch ($type) {
  409. // Double
  410. case 0:
  411. $value = $this->readDouble();
  412. break;
  413. // Boolean
  414. case 1:
  415. $value = $this->readBoolean();
  416. break;
  417. // String
  418. case 2:
  419. $value = $this->readString();
  420. break;
  421. // Object
  422. case 3:
  423. $value = $this->readObject();
  424. break;
  425. // null
  426. case 6:
  427. return null;
  428. break;
  429. // Mixed array
  430. case 8:
  431. $value = $this->readMixedArray();
  432. break;
  433. // Array
  434. case 10:
  435. $value = $this->readArray();
  436. break;
  437. // Date
  438. case 11:
  439. $value = $this->readDate();
  440. break;
  441. // Long string
  442. case 13:
  443. $value = $this->readLongString();
  444. break;
  445. // XML (handled as string)
  446. case 15:
  447. $value = $this->readXML();
  448. break;
  449. // Typed object (handled as object)
  450. case 16:
  451. $value = $this->readTypedObject();
  452. break;
  453. // Long string
  454. default:
  455. $value = '(unknown or unsupported data type)';
  456. break;
  457. }
  458. return $value;
  459. }
  460. function readDouble() {
  461. return $this->stream->readDouble();
  462. }
  463. function readBoolean() {
  464. return $this->stream->readByte() == 1;
  465. }
  466. function readString() {
  467. return $this->stream->readUTF();
  468. }
  469. function readObject() {
  470. // Get highest numerical index - ignored
  471. // $highestIndex = $this->stream->readLong();
  472. $data = array();
  473. while ($key = $this->stream->readUTF()) {
  474. $data[$key] = $this->readData();
  475. }
  476. // Mixed array record ends with empty string (0x00 0x00) and 0x09
  477. if (($key == '') && ($this->stream->peekByte() == 0x09)) {
  478. // Consume byte
  479. $this->stream->readByte();
  480. }
  481. return $data;
  482. }
  483. function readMixedArray() {
  484. // Get highest numerical index - ignored
  485. $highestIndex = $this->stream->readLong();
  486. $data = array();
  487. while ($key = $this->stream->readUTF()) {
  488. if (is_numeric($key)) {
  489. $key = (float) $key;
  490. }
  491. $data[$key] = $this->readData();
  492. }
  493. // Mixed array record ends with empty string (0x00 0x00) and 0x09
  494. if (($key == '') && ($this->stream->peekByte() == 0x09)) {
  495. // Consume byte
  496. $this->stream->readByte();
  497. }
  498. return $data;
  499. }
  500. function readArray() {
  501. $length = $this->stream->readLong();
  502. $data = array();
  503. for ($i = 0; $i < $length; $i++) {
  504. $data[] = $this->readData();
  505. }
  506. return $data;
  507. }
  508. function readDate() {
  509. $timestamp = $this->stream->readDouble();
  510. $timezone = $this->stream->readInt();
  511. return $timestamp;
  512. }
  513. function readLongString() {
  514. return $this->stream->readLongUTF();
  515. }
  516. function readXML() {
  517. return $this->stream->readLongUTF();
  518. }
  519. function readTypedObject() {
  520. $className = $this->stream->readUTF();
  521. return $this->readObject();
  522. }
  523. }
  524. class AVCSequenceParameterSetReader {
  525. var $sps;
  526. var $start = 0;
  527. var $currentBytes = 0;
  528. var $currentBits = 0;
  529. var $width;
  530. var $height;
  531. function AVCSequenceParameterSetReader($sps) {
  532. $this->sps = $sps;
  533. }
  534. function readData() {
  535. $this->skipBits(8);
  536. $this->skipBits(8);
  537. $profile = $this->getBits(8); // read profile
  538. $this->skipBits(16);
  539. $this->expGolombUe(); // read sps id
  540. if (in_array($profile, array(H264_PROFILE_HIGH, H264_PROFILE_HIGH10, H264_PROFILE_HIGH422, H264_PROFILE_HIGH444, H264_PROFILE_HIGH444_PREDICTIVE))) {
  541. if ($this->expGolombUe() == 3) {
  542. $this->skipBits(1);
  543. }
  544. $this->expGolombUe();
  545. $this->expGolombUe();
  546. $this->skipBits(1);
  547. if ($this->getBit()) {
  548. for ($i = 0; $i < 8; $i++) {
  549. if ($this->getBit()) {
  550. $size = $i < 6 ? 16 : 64;
  551. $lastScale = 8;
  552. $nextScale = 8;
  553. for ($j = 0; $j < $size; $j++) {
  554. if ($nextScale != 0) {
  555. $deltaScale = $this->expGolombUe();
  556. $nextScale = ($lastScale + $deltaScale + 256) % 256;
  557. }
  558. if ($nextScale != 0) {
  559. $lastScale = $nextScale;
  560. }
  561. }
  562. }
  563. }
  564. }
  565. }
  566. $this->expGolombUe();
  567. $pocType = $this->expGolombUe();
  568. if ($pocType == 0) {
  569. $this->expGolombUe();
  570. } elseif ($pocType == 1) {
  571. $this->skipBits(1);
  572. $this->expGolombSe();
  573. $this->expGolombSe();
  574. $pocCycleLength = $this->expGolombUe();
  575. for ($i = 0; $i < $pocCycleLength; $i++) {
  576. $this->expGolombSe();
  577. }
  578. }
  579. $this->expGolombUe();
  580. $this->skipBits(1);
  581. $this->width = ($this->expGolombUe() + 1) * 16;
  582. $heightMap = $this->expGolombUe() + 1;
  583. $this->height = (2 - $this->getBit()) * $heightMap * 16;
  584. }
  585. function skipBits($bits) {
  586. $newBits = $this->currentBits + $bits;
  587. $this->currentBytes += (int)floor($newBits / 8);
  588. $this->currentBits = $newBits % 8;
  589. }
  590. function getBit() {
  591. $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01;
  592. $this->skipBits(1);
  593. return $result;
  594. }
  595. function getBits($bits) {
  596. $result = 0;
  597. for ($i = 0; $i < $bits; $i++) {
  598. $result = ($result << 1) + $this->getBit();
  599. }
  600. return $result;
  601. }
  602. function expGolombUe() {
  603. $significantBits = 0;
  604. $bit = $this->getBit();
  605. while ($bit == 0) {
  606. $significantBits++;
  607. $bit = $this->getBit();
  608. if ($significantBits > 31) {
  609. // something is broken, this is an emergency escape to prevent infinite loops
  610. return 0;
  611. }
  612. }
  613. return (1 << $significantBits) + $this->getBits($significantBits) - 1;
  614. }
  615. function expGolombSe() {
  616. $result = $this->expGolombUe();
  617. if (($result & 0x01) == 0) {
  618. return -($result >> 1);
  619. } else {
  620. return ($result + 1) >> 1;
  621. }
  622. }
  623. function getWidth() {
  624. return $this->width;
  625. }
  626. function getHeight() {
  627. return $this->height;
  628. }
  629. }
  630. ?>