File indexing completed on 2024-12-22 05:33:12
0001 <?php 0002 ///////////////////////////////////////////////////////////////// 0003 /// getID3() by James Heinrich <info@getid3.org> // 0004 // available at http://getid3.sourceforge.net // 0005 // or http://www.getid3.org // 0006 // also https://github.com/JamesHeinrich/getID3 // 0007 ///////////////////////////////////////////////////////////////// 0008 // See readme.txt for more details // 0009 ///////////////////////////////////////////////////////////////// 0010 // // 0011 // module.audio.dts.php // 0012 // module for analyzing DTS Audio files // 0013 // dependencies: NONE // 0014 // // 0015 ///////////////////////////////////////////////////////////////// 0016 0017 0018 /** 0019 * @tutorial http://wiki.multimedia.cx/index.php?title=DTS 0020 */ 0021 class getid3_dts extends getid3_handler 0022 { 0023 /** 0024 * Default DTS syncword used in native .cpt or .dts formats 0025 */ 0026 const syncword = "\x7F\xFE\x80\x01"; 0027 0028 private $readBinDataOffset = 0; 0029 0030 /** 0031 * Possible syncwords indicating bitstream encoding 0032 */ 0033 public static $syncwords = array( 0034 0 => "\x7F\xFE\x80\x01", // raw big-endian 0035 1 => "\xFE\x7F\x01\x80", // raw little-endian 0036 2 => "\x1F\xFF\xE8\x00", // 14-bit big-endian 0037 3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian 0038 0039 public function Analyze() { 0040 $info = &$this->getid3->info; 0041 $info['fileformat'] = 'dts'; 0042 0043 $this->fseek($info['avdataoffset']); 0044 $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 0045 0046 // check syncword 0047 $sync = substr($DTSheader, 0, 4); 0048 if (($encoding = array_search($sync, self::$syncwords)) !== false) { 0049 0050 $info['dts']['raw']['magic'] = $sync; 0051 $this->readBinDataOffset = 32; 0052 0053 } elseif ($this->isDependencyFor('matroska')) { 0054 0055 // Matroska contains DTS without syncword encoded as raw big-endian format 0056 $encoding = 0; 0057 $this->readBinDataOffset = 0; 0058 0059 } else { 0060 0061 unset($info['fileformat']); 0062 return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"'); 0063 0064 } 0065 0066 // decode header 0067 $fhBS = ''; 0068 for ($word_offset = 0; $word_offset <= strlen($DTSheader); $word_offset += 2) { 0069 switch ($encoding) { 0070 case 0: // raw big-endian 0071 $fhBS .= getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) ); 0072 break; 0073 case 1: // raw little-endian 0074 $fhBS .= getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))); 0075 break; 0076 case 2: // 14-bit big-endian 0077 $fhBS .= substr(getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) ), 2, 14); 0078 break; 0079 case 3: // 14-bit little-endian 0080 $fhBS .= substr(getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))), 2, 14); 0081 break; 0082 } 0083 } 0084 0085 $info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, 1); 0086 $info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, 5); 0087 $info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, 1); 0088 $info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, 7); 0089 $info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, 14); 0090 $info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, 6); 0091 $info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, 4); 0092 $info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, 5); 0093 $info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, 1); 0094 $info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, 1); 0095 $info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, 1); 0096 $info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, 1); 0097 $info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, 1); 0098 $info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, 3); 0099 $info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, 1); 0100 $info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, 1); 0101 $info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, 2); 0102 $info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, 1); 0103 if ($info['dts']['flags']['crc_present']) { 0104 $info['dts']['raw']['crc16'] = $this->readBinData($fhBS, 16); 0105 } 0106 $info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, 1); 0107 $info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, 4); 0108 $info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, 2); 0109 $info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, 2); 0110 $info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, 1); 0111 $info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, 1); 0112 $info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, 1); 0113 $info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, 4); 0114 0115 0116 $info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']); 0117 $info['dts']['bits_per_sample'] = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']); 0118 $info['dts']['sample_rate'] = self::sampleRateLookup($info['dts']['raw']['sample_frequency']); 0119 $info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']); 0120 $info['dts']['flags']['lossless'] = (($info['dts']['raw']['bitrate'] == 31) ? true : false); 0121 $info['dts']['bitrate_mode'] = (($info['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr'); 0122 $info['dts']['channels'] = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']); 0123 $info['dts']['channel_arrangement'] = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']); 0124 0125 $info['audio']['dataformat'] = 'dts'; 0126 $info['audio']['lossless'] = $info['dts']['flags']['lossless']; 0127 $info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode']; 0128 $info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample']; 0129 $info['audio']['sample_rate'] = $info['dts']['sample_rate']; 0130 $info['audio']['channels'] = $info['dts']['channels']; 0131 $info['audio']['bitrate'] = $info['dts']['bitrate']; 0132 if (isset($info['avdataend']) && !empty($info['dts']['bitrate']) && is_numeric($info['dts']['bitrate'])) { 0133 $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8); 0134 if (($encoding == 2) || ($encoding == 3)) { 0135 // 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 0136 $info['playtime_seconds'] *= (14 / 16); 0137 } 0138 } 0139 return true; 0140 } 0141 0142 private function readBinData($bin, $length) { 0143 $data = substr($bin, $this->readBinDataOffset, $length); 0144 $this->readBinDataOffset += $length; 0145 0146 return bindec($data); 0147 } 0148 0149 public static function bitrateLookup($index) { 0150 static $lookup = array( 0151 0 => 32000, 0152 1 => 56000, 0153 2 => 64000, 0154 3 => 96000, 0155 4 => 112000, 0156 5 => 128000, 0157 6 => 192000, 0158 7 => 224000, 0159 8 => 256000, 0160 9 => 320000, 0161 10 => 384000, 0162 11 => 448000, 0163 12 => 512000, 0164 13 => 576000, 0165 14 => 640000, 0166 15 => 768000, 0167 16 => 960000, 0168 17 => 1024000, 0169 18 => 1152000, 0170 19 => 1280000, 0171 20 => 1344000, 0172 21 => 1408000, 0173 22 => 1411200, 0174 23 => 1472000, 0175 24 => 1536000, 0176 25 => 1920000, 0177 26 => 2048000, 0178 27 => 3072000, 0179 28 => 3840000, 0180 29 => 'open', 0181 30 => 'variable', 0182 31 => 'lossless', 0183 ); 0184 return (isset($lookup[$index]) ? $lookup[$index] : false); 0185 } 0186 0187 public static function sampleRateLookup($index) { 0188 static $lookup = array( 0189 0 => 'invalid', 0190 1 => 8000, 0191 2 => 16000, 0192 3 => 32000, 0193 4 => 'invalid', 0194 5 => 'invalid', 0195 6 => 11025, 0196 7 => 22050, 0197 8 => 44100, 0198 9 => 'invalid', 0199 10 => 'invalid', 0200 11 => 12000, 0201 12 => 24000, 0202 13 => 48000, 0203 14 => 'invalid', 0204 15 => 'invalid', 0205 ); 0206 return (isset($lookup[$index]) ? $lookup[$index] : false); 0207 } 0208 0209 public static function bitPerSampleLookup($index) { 0210 static $lookup = array( 0211 0 => 16, 0212 1 => 20, 0213 2 => 24, 0214 3 => 24, 0215 ); 0216 return (isset($lookup[$index]) ? $lookup[$index] : false); 0217 } 0218 0219 public static function numChannelsLookup($index) { 0220 switch ($index) { 0221 case 0: 0222 return 1; 0223 break; 0224 case 1: 0225 case 2: 0226 case 3: 0227 case 4: 0228 return 2; 0229 break; 0230 case 5: 0231 case 6: 0232 return 3; 0233 break; 0234 case 7: 0235 case 8: 0236 return 4; 0237 break; 0238 case 9: 0239 return 5; 0240 break; 0241 case 10: 0242 case 11: 0243 case 12: 0244 return 6; 0245 break; 0246 case 13: 0247 return 7; 0248 break; 0249 case 14: 0250 case 15: 0251 return 8; 0252 break; 0253 } 0254 return false; 0255 } 0256 0257 public static function channelArrangementLookup($index) { 0258 static $lookup = array( 0259 0 => 'A', 0260 1 => 'A + B (dual mono)', 0261 2 => 'L + R (stereo)', 0262 3 => '(L+R) + (L-R) (sum-difference)', 0263 4 => 'LT + RT (left and right total)', 0264 5 => 'C + L + R', 0265 6 => 'L + R + S', 0266 7 => 'C + L + R + S', 0267 8 => 'L + R + SL + SR', 0268 9 => 'C + L + R + SL + SR', 0269 10 => 'CL + CR + L + R + SL + SR', 0270 11 => 'C + L + R+ LR + RR + OV', 0271 12 => 'CF + CR + LF + RF + LR + RR', 0272 13 => 'CL + C + CR + L + R + SL + SR', 0273 14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2', 0274 15 => 'CL + C+ CR + L + R + SL + S + SR', 0275 ); 0276 return (isset($lookup[$index]) ? $lookup[$index] : 'user-defined'); 0277 } 0278 0279 public static function dialogNormalization($index, $version) { 0280 switch ($version) { 0281 case 7: 0282 return 0 - $index; 0283 break; 0284 case 6: 0285 return 0 - 16 - $index; 0286 break; 0287 } 0288 return false; 0289 } 0290 0291 }