/src/Zend/Media/Mpeg/Abs/Frame.php

http://php-reader.googlecode.com/ · PHP · 574 lines · 235 code · 52 blank · 287 comment · 14 complexity · 24215485382dc635a4ff0e0bfbbb0ae9 MD5 · raw file

  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Media
  17. * @subpackage MPEG
  18. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Frame.php 261 2012-03-05 20:43:15Z svollbehr $
  21. */
  22. /**#@+ @ignore */
  23. require_once 'Zend/Bit/Twiddling.php';
  24. require_once 'Zend/Media/Mpeg/Abs/Object.php';
  25. /**#@-*/
  26. /**
  27. * This class represents an MPEG Audio Bit Stream frame as described in
  28. * ISO/IEC 11172-3 and ISO/IEC 13818-3 standards.
  29. *
  30. * To accommodate fast header processing the error checking data and the audio
  31. * data are lazy fetch by default. You can change this behaviour by giving a
  32. * proper option to the {@link Zend_Media_Mpeg_Abs} class.
  33. *
  34. * @category Zend
  35. * @package Zend_Media
  36. * @subpackage MPEG
  37. * @author Ryan Butterfield <buttza@gmail.com>
  38. * @author Sven Vollbehr <sven@vollbehr.eu>
  39. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  40. * @license http://framework.zend.com/license/new-bsd New BSD License
  41. * @version $Id: Frame.php 261 2012-03-05 20:43:15Z svollbehr $
  42. */
  43. final class Zend_Media_Mpeg_Abs_Frame extends Zend_Media_Mpeg_Abs_Object
  44. {
  45. /**
  46. * The bitrate lookup table. The table has the following format.
  47. *
  48. * <code>
  49. * array (
  50. * SAMPLING_FREQUENCY_HIGH | SAMPLING_FREQUENCY_LOW => array (
  51. * LAYER_ONE | LAYER_TWO | LAYER_TREE => array ( <bitrates> )
  52. * )
  53. * )
  54. * </code>
  55. *
  56. * @var Array
  57. */
  58. private static $bitrates = array (
  59. self::SAMPLING_FREQUENCY_HIGH => array (
  60. self::LAYER_ONE => array (
  61. 1 => 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384,
  62. 416, 448
  63. ),
  64. self::LAYER_TWO => array (
  65. 1 => 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,
  66. 384
  67. ),
  68. self::LAYER_THREE => array (
  69. 1 => 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256,
  70. 320
  71. )
  72. ),
  73. self::SAMPLING_FREQUENCY_LOW => array (
  74. self::LAYER_ONE => array (
  75. 1 => 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224,
  76. 256
  77. ),
  78. self::LAYER_TWO => array (
  79. 1 => 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160
  80. ),
  81. self::LAYER_THREE => array (
  82. 1 => 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160
  83. )
  84. )
  85. );
  86. /**
  87. * Sample rate lookup table. The table has the following format.
  88. *
  89. * <code>
  90. * array (
  91. * LAYER_ONE | LAYER_TWO | LAYER_TREE => array ( <sample rates> )
  92. * )
  93. * </code>
  94. *
  95. * @var Array
  96. */
  97. private static $samplingFrequencies = array (
  98. self::VERSION_ONE => array (44100, 48000, 32000),
  99. self::VERSION_TWO => array (22050, 24000, 16000),
  100. self::VERSION_TWO_FIVE => array (11025, 12000, 8000)
  101. );
  102. /**
  103. * Samples per frame lookup table. The table has the following format.
  104. *
  105. * <code>
  106. * array (
  107. * SAMPLING_FREQUENCY_HIGH | SAMPLING_FREQUENCY_LOW => array (
  108. * LAYER_ONE | LAYER_TWO | LAYER_TREE => <sample count>
  109. * )
  110. * )
  111. * </code>
  112. *
  113. * @var Array
  114. */
  115. private static $samples = array (
  116. self::SAMPLING_FREQUENCY_HIGH => array (
  117. self::LAYER_ONE => 384,
  118. self::LAYER_TWO => 1152,
  119. self::LAYER_THREE => 1152),
  120. self::SAMPLING_FREQUENCY_LOW => array (
  121. self::LAYER_ONE => 384,
  122. self::LAYER_TWO => 1152,
  123. self::LAYER_THREE => 576));
  124. /**
  125. * Coefficient lookup table. The table has the following format.
  126. *
  127. * <code>
  128. * array (
  129. * SAMPLING_FREQUENCY_HIGH | SAMPLING_FREQUENCY_LOW => array (
  130. * LAYER_ONE | LAYER_TWO | LAYER_TREE => array ( <coefficient> )
  131. * )
  132. * )
  133. * </code>
  134. *
  135. * @var Array
  136. */
  137. private static $coefficients = array (
  138. self::SAMPLING_FREQUENCY_HIGH => array (
  139. self::LAYER_ONE => 12, self::LAYER_TWO => 144,
  140. self::LAYER_THREE => 144
  141. ),
  142. self::SAMPLING_FREQUENCY_LOW => array (
  143. self::LAYER_ONE => 12, self::LAYER_TWO => 144,
  144. self::LAYER_THREE => 72
  145. )
  146. );
  147. /**
  148. * Slot size per layer lookup table. The table has the following format.
  149. *
  150. * <code>
  151. * array (
  152. * LAYER_ONE | LAYER_TWO | LAYER_TREE => <size>
  153. * )
  154. * </code>
  155. *
  156. * @var Array
  157. */
  158. private static $slotsizes = array (
  159. self::LAYER_ONE => 4, self::LAYER_TWO => 1, self::LAYER_THREE => 1
  160. );
  161. /** @var integer */
  162. private $_offset;
  163. /** @var integer */
  164. private $_version;
  165. /** @var integer */
  166. private $_frequencyType;
  167. /** @var integer */
  168. private $_layer;
  169. /** @var integer */
  170. private $_redundancy;
  171. /** @var integer */
  172. private $_bitrate;
  173. /** @var integer */
  174. private $_samplingFrequency;
  175. /** @var integer */
  176. private $_padding;
  177. /** @var integer */
  178. private $_mode;
  179. /** @var integer */
  180. private $_modeExtension;
  181. /** @var integer */
  182. private $_copyright;
  183. /** @var integer */
  184. private $_original;
  185. /** @var integer */
  186. private $_emphasis;
  187. /** @var integer */
  188. private $_length;
  189. /** @var integer */
  190. private $_samples;
  191. /** @var integer */
  192. private $_crc = false;
  193. /** @var string */
  194. private $_data = false;
  195. /**
  196. * Constructs the class with given parameters and reads object related data
  197. * from the frame.
  198. *
  199. * @param Zend_Io_Reader $reader The reader object.
  200. * @param Array $options Array of options.
  201. */
  202. public function __construct($reader, &$options = array())
  203. {
  204. parent::__construct($reader, $options);
  205. $this->_offset = $this->_reader->getOffset();
  206. $header = null;
  207. for ($i = 0; $i < 5775 /* max attempts: max frame size x2 */; $i++) {
  208. $header = $this->_reader->readUInt32BE();
  209. if (Zend_Bit_Twiddling::testAllBits(Zend_Bit_Twiddling::getValue($header, 21, 32), 0xffe)) {
  210. break;
  211. }
  212. $this->_reader->setOffset(++$this->_offset + 1);
  213. if ($this->_offset == $this->_reader->getSize() || $i == (5775 - 1)) {
  214. require_once 'Zend/Media/Mpeg/Exception.php';
  215. throw new Zend_Media_Mpeg_Exception
  216. ('File does not contain a valid MPEG Audio Bit Stream (Invalid frame sync and resynchronization failed)');
  217. }
  218. }
  219. $this->_version = Zend_Bit_Twiddling::getValue($header, 19, 20);
  220. $this->_frequencyType = Zend_Bit_Twiddling::testBit($header, 19);
  221. $this->_layer = Zend_Bit_Twiddling::getValue($header, 17, 18);
  222. $this->_redundancy = !Zend_Bit_Twiddling::testBit($header, 16);
  223. $this->_bitrate = isset
  224. (self::$bitrates[$this->_frequencyType][$this->_layer]
  225. [$index = Zend_Bit_Twiddling::getValue($header, 12, 15)]) ?
  226. self::$bitrates[$this->_frequencyType][$this->_layer][$index] :
  227. false;
  228. $this->_samplingFrequency = isset
  229. (self::$samplingFrequencies[$this->_version]
  230. [$index = Zend_Bit_Twiddling::getValue($header, 10, 11)]) ?
  231. self::$samplingFrequencies[$this->_version][$index] : false;
  232. $this->_padding = Zend_Bit_Twiddling::testBit($header, 9);
  233. $this->_mode = Zend_Bit_Twiddling::getValue($header, 6, 7);
  234. $this->_modeExtension = Zend_Bit_Twiddling::getValue($header, 4, 5);
  235. $this->_copyright = Zend_Bit_Twiddling::testBit($header, 3);
  236. $this->_original = Zend_Bit_Twiddling::testBit($header, 2);
  237. $this->_emphasis = Zend_Bit_Twiddling::getValue($header, 0, 1);
  238. $this->_length = (int)
  239. ((self::$coefficients[$this->_frequencyType][$this->_layer] *
  240. ($this->_bitrate * 1000) / $this->_samplingFrequency) +
  241. ($this->_padding ? 1 : 0)) * self::$slotsizes[$this->_layer];
  242. $this->_samples = self::$samples[$this->_frequencyType][$this->_layer];
  243. if ($this->getOption('readmode', 'lazy') == 'full') {
  244. $this->_readCrc();
  245. $this->_readData();
  246. }
  247. $this->_reader->skip($this->_length - 4);
  248. }
  249. /**
  250. * Returns the offset where the frame actually begins (stream error may
  251. * cause resynchronization).
  252. *
  253. * @return integer
  254. */
  255. public function getOffset()
  256. {
  257. return $this->_offset;
  258. }
  259. /**
  260. * Returns the version identifier of the algorithm.
  261. *
  262. * @see VERSION_ONE, VERSION_TWO, VERSION_TWO_FIVE
  263. * @return integer
  264. */
  265. public function getVersion()
  266. {
  267. return $this->_version;
  268. }
  269. /**
  270. * Returns the sampling frequency type. This can be one of the following
  271. * values.
  272. *
  273. * o <b>{@link SAMPLING_FREQUENCY_HIGH}</b> -- Higher Sampling Frequency
  274. * (Version 1)
  275. * o <b>{@link SAMPLING_FREQUENCY_LOW}</b> -- Lower Sampling Frequency
  276. * (Version 2 and 2.5)
  277. *
  278. * @see SAMPLING_FREQUENCY_LOW, SAMPLING_FREQUENCY_HIGH
  279. * @return integer
  280. */
  281. public function getFrequencyType()
  282. {
  283. return $this->_frequencyType;
  284. }
  285. /**
  286. * Returns the type of layer used.
  287. *
  288. * @see LAYER_ONE, LAYER_TWO, LAYER_THREE
  289. * @return integer
  290. */
  291. public function getLayer()
  292. {
  293. return $this->_layer;
  294. }
  295. /**
  296. * An alias to getRedundancy().
  297. *
  298. * @see getRedundancy
  299. * @return boolean
  300. */
  301. public function hasRedundancy()
  302. {
  303. return $this->getRedundancy();
  304. }
  305. /**
  306. * Returns boolean corresponding to whether redundancy has been added in the
  307. * audio bitstream to facilitate error detection and concealment. Equals
  308. * <var>false</var> if no redundancy has been added, <var>true</var> if
  309. * redundancy has been added.
  310. *
  311. * @return boolean
  312. */
  313. public function getRedundancy()
  314. {
  315. return $this->_redundancy;
  316. }
  317. /**
  318. * Returns the bitrate in kbps. The returned value indicates the total bitrate
  319. * irrespective of the mode (stereo, joint_stereo, dual_channel,
  320. * single_channel).
  321. *
  322. * @return integer
  323. */
  324. public function getBitrate()
  325. {
  326. return $this->_bitrate;
  327. }
  328. /**
  329. * Returns the sampling frequency in Hz.
  330. *
  331. * @return integer
  332. */
  333. public function getSamplingFrequency()
  334. {
  335. return $this->_samplingFrequency;
  336. }
  337. /**
  338. * An alias to getPadding().
  339. *
  340. * @see getPadding
  341. * @return boolean
  342. */
  343. public function hasPadding()
  344. {
  345. return $this->getPadding();
  346. }
  347. /**
  348. * Returns boolean corresponding the frame contains an additional slot to
  349. * adjust the mean bitrate to the sampling frequency. Equals to
  350. * <var>true</var> if padding has been added, <var>false</var> otherwise.
  351. *
  352. * Padding is only necessary with a sampling frequency of 44.1kHz.
  353. *
  354. * @return boolean
  355. */
  356. public function getPadding()
  357. {
  358. return $this->_padding;
  359. }
  360. /**
  361. * Returns the mode. In Layer I and II the CHANNEL_JOINT_STEREO mode is
  362. * intensity_stereo, in Layer III it is intensity_stereo and/or ms_stereo.
  363. *
  364. * @see CHANNEL_STEREO, CHANNEL_JOINT_STEREO, CHANNEL_DUAL_CHANNEL,
  365. * CHANNEL_SINGLE_CHANNEL
  366. * @return integer
  367. */
  368. public function getMode()
  369. {
  370. return $this->_mode;
  371. }
  372. /**
  373. * Returns the mode extension used in CHANNEL_JOINT_STEREO mode.
  374. *
  375. * In Layer I and II the return type indicates which subbands are in
  376. * intensity_stereo. All other subbands are coded in stereo.
  377. *
  378. * o <b>{@link MODE_SUBBAND_4_TO_31}</b> -- subbands 4-31 in
  379. * intensity_stereo, bound==4
  380. * o <b>{@link MODE_SUBBAND_8_TO_31}</b> -- subbands 8-31 in
  381. * intensity_stereo, bound==8
  382. * o <b>{@link MODE_SUBBAND_12_TO_31}</b> -- subbands 12-31 in
  383. * intensity_stereo, bound==12
  384. * o <b>{@link MODE_SUBBAND_16_TO_31}</b> -- subbands 16-31 in
  385. * intensity_stereo, bound==16
  386. *
  387. * In Layer III they indicate which type of joint stereo coding method is
  388. * applied. The frequency ranges over which the intensity_stereo and
  389. * ms_stereo modes are applied are implicit in the algorithm. Please see
  390. * {@link MODE_ISOFF_MSSOFF}, {@link MODE_ISON_MSSOFF},
  391. * {@link MODE_ISOFF_MSSON}, and {@link MODE_ISON_MSSON}.
  392. *
  393. * @return integer
  394. */
  395. public function getModeExtension()
  396. {
  397. return $this->_modeExtension;
  398. }
  399. /**
  400. * An alias to getCopyright().
  401. *
  402. * @see getCopyright
  403. * @return boolean
  404. */
  405. public function hasCopyright()
  406. {
  407. return $this->getCopyright();
  408. }
  409. /**
  410. * Returns <var>true</var> if the coded bitstream is copyright protected,
  411. * <var>false</var> otherwise.
  412. *
  413. * @return boolean
  414. */
  415. public function getCopyright()
  416. {
  417. return $this->_copyright;
  418. }
  419. /**
  420. * An alias to getOriginal().
  421. *
  422. * @see getOriginal
  423. * @return boolean
  424. */
  425. public function isOriginal()
  426. {
  427. return $this->getOriginal();
  428. }
  429. /**
  430. * Returns whether the bitstream is original or home made.
  431. *
  432. * @return boolean
  433. */
  434. public function getOriginal()
  435. {
  436. return $this->_original;
  437. }
  438. /**
  439. * Returns the type of de-emphasis that shall be used. The value is one of
  440. * the following.
  441. *
  442. * o <b>{@link EMPHASIS_NONE}</b> -- No emphasis
  443. * o <b>{@link EMPHASIS_50_15}</b> -- 50/15 microsec. emphasis
  444. * o <b>{@link EMPHASIS_CCIT_J17}</b> -- CCITT J.17
  445. *
  446. * @see EMPHASIS_NONE, EMPHASIS_50_15, EMPHASIS_CCIT_J17
  447. * @return integer
  448. */
  449. public function getEmphasis()
  450. {
  451. return $this->_emphasis;
  452. }
  453. /**
  454. * Returns the length of the frame based on the current layer, bit rate,
  455. * sampling frequency and padding, in bytes.
  456. *
  457. * @return integer
  458. */
  459. public function getLength()
  460. {
  461. return $this->_length;
  462. }
  463. /**
  464. * Returns the number of samples contained in the frame.
  465. *
  466. * @return integer
  467. */
  468. public function getSamples()
  469. {
  470. return $this->_samples;
  471. }
  472. /**
  473. * Returns the 16-bit CRC of the frame or <var>false</var> if not present.
  474. *
  475. * @return integer
  476. */
  477. public function getCrc()
  478. {
  479. if ($this->getOption('readmode', 'lazy') == 'lazy' &&
  480. $this->hasRedundancy() && $this->_crc === false) {
  481. $this->_readCrc();
  482. }
  483. return $this->_crc;
  484. }
  485. /**
  486. * Reads the CRC data.
  487. */
  488. private function _readCrc()
  489. {
  490. if ($this->hasRedundancy()) {
  491. $offset = $this->_reader->getOffset();
  492. $this->_reader->setOffset($this->_offset + 4);
  493. $this->_crc = $this->_reader->readUInt16BE();
  494. $this->_reader->setOffset($offset);
  495. }
  496. }
  497. /**
  498. * Returns the audio data.
  499. *
  500. * @return string
  501. */
  502. public function getData()
  503. {
  504. if ($this->getOption('readmode', 'lazy') == 'lazy' &&
  505. $this->_data === false) {
  506. $this->_readData();
  507. }
  508. return $this->_data;
  509. }
  510. /**
  511. * Reads the frame data.
  512. */
  513. private function _readData()
  514. {
  515. $offset = $this->_reader->getOffset();
  516. $this->_reader->setOffset
  517. ($this->_offset + 4 + ($this->hasRedundancy() ? 2 : 0));
  518. $this->_data = $this->_reader->read
  519. ($this->getLength() - 4 - ($this->hasRedundancy() ? 2 : 0));
  520. $this->_reader->setOffset($offset);
  521. }
  522. }