balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

module.audio.dts.php (10886B)


      1 <?php
      2 
      3 /////////////////////////////////////////////////////////////////
      4 /// getID3() by James Heinrich <info@getid3.org>               //
      5 //  available at https://github.com/JamesHeinrich/getID3       //
      6 //            or https://www.getid3.org                        //
      7 //            or http://getid3.sourceforge.net                 //
      8 //  see readme.txt for more details                            //
      9 /////////////////////////////////////////////////////////////////
     10 //                                                             //
     11 // module.audio.dts.php                                        //
     12 // module for analyzing DTS Audio files                        //
     13 // dependencies: NONE                                          //
     14 //                                                             //
     15 /////////////////////////////////////////////////////////////////
     16 
     17 if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
     18 	exit;
     19 }
     20 
     21 /**
     22 * @tutorial http://wiki.multimedia.cx/index.php?title=DTS
     23 */
     24 class getid3_dts extends getid3_handler
     25 {
     26 	/**
     27 	 * Default DTS syncword used in native .cpt or .dts formats.
     28 	 */
     29 	const syncword = "\x7F\xFE\x80\x01";
     30 
     31 	/**
     32 	 * @var int
     33 	 */
     34 	private $readBinDataOffset = 0;
     35 
     36 	/**
     37 	 * Possible syncwords indicating bitstream encoding.
     38 	 */
     39 	public static $syncwords = array(
     40 		0 => "\x7F\xFE\x80\x01",  // raw big-endian
     41 		1 => "\xFE\x7F\x01\x80",  // raw little-endian
     42 		2 => "\x1F\xFF\xE8\x00",  // 14-bit big-endian
     43 		3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian
     44 
     45 	/**
     46 	 * @return bool
     47 	 */
     48 	public function Analyze() {
     49 		$info = &$this->getid3->info;
     50 		$info['fileformat'] = 'dts';
     51 
     52 		$this->fseek($info['avdataoffset']);
     53 		$DTSheader = $this->fread(20); // we only need 2 words magic + 6 words frame header, but these words may be normal 16-bit words OR 14-bit words with 2 highest bits set to zero, so 8 words can be either 8*16/8 = 16 bytes OR 8*16*(16/14)/8 = 18.3 bytes
     54 
     55 		// check syncword
     56 		$sync = substr($DTSheader, 0, 4);
     57 		if (($encoding = array_search($sync, self::$syncwords)) !== false) {
     58 
     59 			$info['dts']['raw']['magic'] = $sync;
     60 			$this->readBinDataOffset = 32;
     61 
     62 		} elseif ($this->isDependencyFor('matroska')) {
     63 
     64 			// Matroska contains DTS without syncword encoded as raw big-endian format
     65 			$encoding = 0;
     66 			$this->readBinDataOffset = 0;
     67 
     68 		} else {
     69 
     70 			unset($info['fileformat']);
     71 			return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"');
     72 
     73 		}
     74 
     75 		// decode header
     76 		$fhBS = '';
     77 		for ($word_offset = 0; $word_offset <= strlen($DTSheader); $word_offset += 2) {
     78 			switch ($encoding) {
     79 				case 0: // raw big-endian
     80 					$fhBS .=        getid3_lib::BigEndian2Bin(       substr($DTSheader, $word_offset, 2) );
     81 					break;
     82 				case 1: // raw little-endian
     83 					$fhBS .=        getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2)));
     84 					break;
     85 				case 2: // 14-bit big-endian
     86 					$fhBS .= substr(getid3_lib::BigEndian2Bin(       substr($DTSheader, $word_offset, 2) ), 2, 14);
     87 					break;
     88 				case 3: // 14-bit little-endian
     89 					$fhBS .= substr(getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))), 2, 14);
     90 					break;
     91 			}
     92 		}
     93 
     94 		$info['dts']['raw']['frame_type']             =        $this->readBinData($fhBS,  1);
     95 		$info['dts']['raw']['deficit_samples']        =        $this->readBinData($fhBS,  5);
     96 		$info['dts']['flags']['crc_present']          = (bool) $this->readBinData($fhBS,  1);
     97 		$info['dts']['raw']['pcm_sample_blocks']      =        $this->readBinData($fhBS,  7);
     98 		$info['dts']['raw']['frame_byte_size']        =        $this->readBinData($fhBS, 14);
     99 		$info['dts']['raw']['channel_arrangement']    =        $this->readBinData($fhBS,  6);
    100 		$info['dts']['raw']['sample_frequency']       =        $this->readBinData($fhBS,  4);
    101 		$info['dts']['raw']['bitrate']                =        $this->readBinData($fhBS,  5);
    102 		$info['dts']['flags']['embedded_downmix']     = (bool) $this->readBinData($fhBS,  1);
    103 		$info['dts']['flags']['dynamicrange']         = (bool) $this->readBinData($fhBS,  1);
    104 		$info['dts']['flags']['timestamp']            = (bool) $this->readBinData($fhBS,  1);
    105 		$info['dts']['flags']['auxdata']              = (bool) $this->readBinData($fhBS,  1);
    106 		$info['dts']['flags']['hdcd']                 = (bool) $this->readBinData($fhBS,  1);
    107 		$info['dts']['raw']['extension_audio']        =        $this->readBinData($fhBS,  3);
    108 		$info['dts']['flags']['extended_coding']      = (bool) $this->readBinData($fhBS,  1);
    109 		$info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS,  1);
    110 		$info['dts']['raw']['lfe_effects']            =        $this->readBinData($fhBS,  2);
    111 		$info['dts']['flags']['predictor_history']    = (bool) $this->readBinData($fhBS,  1);
    112 		if ($info['dts']['flags']['crc_present']) {
    113 			$info['dts']['raw']['crc16']              =        $this->readBinData($fhBS, 16);
    114 		}
    115 		$info['dts']['flags']['mri_perfect_reconst']  = (bool) $this->readBinData($fhBS,  1);
    116 		$info['dts']['raw']['encoder_soft_version']   =        $this->readBinData($fhBS,  4);
    117 		$info['dts']['raw']['copy_history']           =        $this->readBinData($fhBS,  2);
    118 		$info['dts']['raw']['bits_per_sample']        =        $this->readBinData($fhBS,  2);
    119 		$info['dts']['flags']['surround_es']          = (bool) $this->readBinData($fhBS,  1);
    120 		$info['dts']['flags']['front_sum_diff']       = (bool) $this->readBinData($fhBS,  1);
    121 		$info['dts']['flags']['surround_sum_diff']    = (bool) $this->readBinData($fhBS,  1);
    122 		$info['dts']['raw']['dialog_normalization']   =        $this->readBinData($fhBS,  4);
    123 
    124 
    125 		$info['dts']['bitrate']              = self::bitrateLookup($info['dts']['raw']['bitrate']);
    126 		$info['dts']['bits_per_sample']      = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']);
    127 		$info['dts']['sample_rate']          = self::sampleRateLookup($info['dts']['raw']['sample_frequency']);
    128 		$info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']);
    129 		$info['dts']['flags']['lossless']    = (($info['dts']['raw']['bitrate'] == 31) ? true  : false);
    130 		$info['dts']['bitrate_mode']         = (($info['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr');
    131 		$info['dts']['channels']             = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']);
    132 		$info['dts']['channel_arrangement']  = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']);
    133 
    134 		$info['audio']['dataformat']          = 'dts';
    135 		$info['audio']['lossless']            = $info['dts']['flags']['lossless'];
    136 		$info['audio']['bitrate_mode']        = $info['dts']['bitrate_mode'];
    137 		$info['audio']['bits_per_sample']     = $info['dts']['bits_per_sample'];
    138 		$info['audio']['sample_rate']         = $info['dts']['sample_rate'];
    139 		$info['audio']['channels']            = $info['dts']['channels'];
    140 		$info['audio']['bitrate']             = $info['dts']['bitrate'];
    141 		if (isset($info['avdataend']) && !empty($info['dts']['bitrate']) && is_numeric($info['dts']['bitrate'])) {
    142 			$info['playtime_seconds']         = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8);
    143 			if (($encoding == 2) || ($encoding == 3)) {
    144 				// 14-bit data packed into 16-bit words, so the playtime is wrong because only (14/16) of the bytes in the data portion of the file are used at the specified bitrate
    145 				$info['playtime_seconds'] *= (14 / 16);
    146 			}
    147 		}
    148 		return true;
    149 	}
    150 
    151 	/**
    152 	 * @param string $bin
    153 	 * @param int $length
    154 	 *
    155 	 * @return int
    156 	 */
    157 	private function readBinData($bin, $length) {
    158 		$data = substr($bin, $this->readBinDataOffset, $length);
    159 		$this->readBinDataOffset += $length;
    160 
    161 		return bindec($data);
    162 	}
    163 
    164 	/**
    165 	 * @param int $index
    166 	 *
    167 	 * @return int|string|false
    168 	 */
    169 	public static function bitrateLookup($index) {
    170 		static $lookup = array(
    171 			0  => 32000,
    172 			1  => 56000,
    173 			2  => 64000,
    174 			3  => 96000,
    175 			4  => 112000,
    176 			5  => 128000,
    177 			6  => 192000,
    178 			7  => 224000,
    179 			8  => 256000,
    180 			9  => 320000,
    181 			10 => 384000,
    182 			11 => 448000,
    183 			12 => 512000,
    184 			13 => 576000,
    185 			14 => 640000,
    186 			15 => 768000,
    187 			16 => 960000,
    188 			17 => 1024000,
    189 			18 => 1152000,
    190 			19 => 1280000,
    191 			20 => 1344000,
    192 			21 => 1408000,
    193 			22 => 1411200,
    194 			23 => 1472000,
    195 			24 => 1536000,
    196 			25 => 1920000,
    197 			26 => 2048000,
    198 			27 => 3072000,
    199 			28 => 3840000,
    200 			29 => 'open',
    201 			30 => 'variable',
    202 			31 => 'lossless',
    203 		);
    204 		return (isset($lookup[$index]) ? $lookup[$index] : false);
    205 	}
    206 
    207 	/**
    208 	 * @param int $index
    209 	 *
    210 	 * @return int|string|false
    211 	 */
    212 	public static function sampleRateLookup($index) {
    213 		static $lookup = array(
    214 			0  => 'invalid',
    215 			1  => 8000,
    216 			2  => 16000,
    217 			3  => 32000,
    218 			4  => 'invalid',
    219 			5  => 'invalid',
    220 			6  => 11025,
    221 			7  => 22050,
    222 			8  => 44100,
    223 			9  => 'invalid',
    224 			10 => 'invalid',
    225 			11 => 12000,
    226 			12 => 24000,
    227 			13 => 48000,
    228 			14 => 'invalid',
    229 			15 => 'invalid',
    230 		);
    231 		return (isset($lookup[$index]) ? $lookup[$index] : false);
    232 	}
    233 
    234 	/**
    235 	 * @param int $index
    236 	 *
    237 	 * @return int|false
    238 	 */
    239 	public static function bitPerSampleLookup($index) {
    240 		static $lookup = array(
    241 			0  => 16,
    242 			1  => 20,
    243 			2  => 24,
    244 			3  => 24,
    245 		);
    246 		return (isset($lookup[$index]) ? $lookup[$index] : false);
    247 	}
    248 
    249 	/**
    250 	 * @param int $index
    251 	 *
    252 	 * @return int|false
    253 	 */
    254 	public static function numChannelsLookup($index) {
    255 		switch ($index) {
    256 			case 0:
    257 				return 1;
    258 			case 1:
    259 			case 2:
    260 			case 3:
    261 			case 4:
    262 				return 2;
    263 			case 5:
    264 			case 6:
    265 				return 3;
    266 			case 7:
    267 			case 8:
    268 				return 4;
    269 			case 9:
    270 				return 5;
    271 			case 10:
    272 			case 11:
    273 			case 12:
    274 				return 6;
    275 			case 13:
    276 				return 7;
    277 			case 14:
    278 			case 15:
    279 				return 8;
    280 		}
    281 		return false;
    282 	}
    283 
    284 	/**
    285 	 * @param int $index
    286 	 *
    287 	 * @return string
    288 	 */
    289 	public static function channelArrangementLookup($index) {
    290 		static $lookup = array(
    291 			0  => 'A',
    292 			1  => 'A + B (dual mono)',
    293 			2  => 'L + R (stereo)',
    294 			3  => '(L+R) + (L-R) (sum-difference)',
    295 			4  => 'LT + RT (left and right total)',
    296 			5  => 'C + L + R',
    297 			6  => 'L + R + S',
    298 			7  => 'C + L + R + S',
    299 			8  => 'L + R + SL + SR',
    300 			9  => 'C + L + R + SL + SR',
    301 			10 => 'CL + CR + L + R + SL + SR',
    302 			11 => 'C + L + R+ LR + RR + OV',
    303 			12 => 'CF + CR + LF + RF + LR + RR',
    304 			13 => 'CL + C + CR + L + R + SL + SR',
    305 			14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2',
    306 			15 => 'CL + C+ CR + L + R + SL + S + SR',
    307 		);
    308 		return (isset($lookup[$index]) ? $lookup[$index] : 'user-defined');
    309 	}
    310 
    311 	/**
    312 	 * @param int $index
    313 	 * @param int $version
    314 	 *
    315 	 * @return int|false
    316 	 */
    317 	public static function dialogNormalization($index, $version) {
    318 		switch ($version) {
    319 			case 7:
    320 				return 0 - $index;
    321 			case 6:
    322 				return 0 - 16 - $index;
    323 		}
    324 		return false;
    325 	}
    326 
    327 }