PageRenderTime 15ms CodeModel.GetById 2ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

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