[3544] | 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 | // See readme.txt for more details // |
---|
| 8 | ///////////////////////////////////////////////////////////////// |
---|
| 9 | // // |
---|
| 10 | // module.audio.aac.php // |
---|
| 11 | // module for analyzing AAC Audio files // |
---|
| 12 | // dependencies: NONE // |
---|
| 13 | // /// |
---|
| 14 | ///////////////////////////////////////////////////////////////// |
---|
| 15 | |
---|
| 16 | |
---|
| 17 | class getid3_aac |
---|
| 18 | { |
---|
| 19 | |
---|
| 20 | // new combined constructor |
---|
| 21 | function getid3_aac(&$fd, &$ThisFileInfo, $option) { |
---|
| 22 | |
---|
| 23 | if ($option === 'adif') { |
---|
| 24 | $this->getAACADIFheaderFilepointer($fd, $ThisFileInfo); |
---|
| 25 | } |
---|
| 26 | elseif ($option === 'adts') { |
---|
| 27 | $this->getAACADTSheaderFilepointer($fd, $ThisFileInfo); |
---|
| 28 | } |
---|
| 29 | } |
---|
| 30 | |
---|
| 31 | |
---|
| 32 | |
---|
| 33 | function getAACADIFheaderFilepointer(&$fd, &$ThisFileInfo) { |
---|
| 34 | $ThisFileInfo['fileformat'] = 'aac'; |
---|
| 35 | $ThisFileInfo['audio']['dataformat'] = 'aac'; |
---|
| 36 | $ThisFileInfo['audio']['lossless'] = false; |
---|
| 37 | |
---|
| 38 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); |
---|
| 39 | $AACheader = fread($fd, 1024); |
---|
| 40 | $offset = 0; |
---|
| 41 | |
---|
| 42 | if (substr($AACheader, 0, 4) == 'ADIF') { |
---|
| 43 | |
---|
| 44 | // http://faac.sourceforge.net/wiki/index.php?page=ADIF |
---|
| 45 | |
---|
| 46 | // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf |
---|
| 47 | // adif_header() { |
---|
| 48 | // adif_id 32 |
---|
| 49 | // copyright_id_present 1 |
---|
| 50 | // if( copyright_id_present ) |
---|
| 51 | // copyright_id 72 |
---|
| 52 | // original_copy 1 |
---|
| 53 | // home 1 |
---|
| 54 | // bitstream_type 1 |
---|
| 55 | // bitrate 23 |
---|
| 56 | // num_program_config_elements 4 |
---|
| 57 | // for (i = 0; i < num_program_config_elements + 1; i++ ) { |
---|
| 58 | // if( bitstream_type == '0' ) |
---|
| 59 | // adif_buffer_fullness 20 |
---|
| 60 | // program_config_element() |
---|
| 61 | // } |
---|
| 62 | // } |
---|
| 63 | |
---|
| 64 | $AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader); |
---|
| 65 | $bitoffset = 0; |
---|
| 66 | |
---|
| 67 | $ThisFileInfo['aac']['header_type'] = 'ADIF'; |
---|
| 68 | $bitoffset += 32; |
---|
| 69 | $ThisFileInfo['aac']['header']['mpeg_version'] = 4; |
---|
| 70 | |
---|
| 71 | $ThisFileInfo['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); |
---|
| 72 | $bitoffset += 1; |
---|
| 73 | if ($ThisFileInfo['aac']['header']['copyright']) { |
---|
| 74 | $ThisFileInfo['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72)); |
---|
| 75 | $bitoffset += 72; |
---|
| 76 | } |
---|
| 77 | $ThisFileInfo['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); |
---|
| 78 | $bitoffset += 1; |
---|
| 79 | $ThisFileInfo['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); |
---|
| 80 | $bitoffset += 1; |
---|
| 81 | $ThisFileInfo['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); |
---|
| 82 | $bitoffset += 1; |
---|
| 83 | if ($ThisFileInfo['aac']['header']['is_vbr']) { |
---|
| 84 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; |
---|
| 85 | $ThisFileInfo['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); |
---|
| 86 | $bitoffset += 23; |
---|
| 87 | } else { |
---|
| 88 | $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; |
---|
| 89 | $ThisFileInfo['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); |
---|
| 90 | $bitoffset += 23; |
---|
| 91 | $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['aac']['header']['bitrate']; |
---|
| 92 | } |
---|
| 93 | if ($ThisFileInfo['audio']['bitrate'] == 0) { |
---|
| 94 | $ThisFileInfo['error'][] = 'Corrupt AAC file: bitrate_audio == zero'; |
---|
| 95 | return false; |
---|
| 96 | } |
---|
| 97 | $ThisFileInfo['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 98 | $bitoffset += 4; |
---|
| 99 | |
---|
| 100 | for ($i = 0; $i < $ThisFileInfo['aac']['header']['num_program_configs']; $i++) { |
---|
| 101 | // http://www.audiocoding.com/wiki/index.php?page=program_config_element |
---|
| 102 | |
---|
| 103 | // buffer_fullness 20 |
---|
| 104 | |
---|
| 105 | // element_instance_tag 4 |
---|
| 106 | // object_type 2 |
---|
| 107 | // sampling_frequency_index 4 |
---|
| 108 | // num_front_channel_elements 4 |
---|
| 109 | // num_side_channel_elements 4 |
---|
| 110 | // num_back_channel_elements 4 |
---|
| 111 | // num_lfe_channel_elements 2 |
---|
| 112 | // num_assoc_data_elements 3 |
---|
| 113 | // num_valid_cc_elements 4 |
---|
| 114 | // mono_mixdown_present 1 |
---|
| 115 | // mono_mixdown_element_number 4 if mono_mixdown_present == 1 |
---|
| 116 | // stereo_mixdown_present 1 |
---|
| 117 | // stereo_mixdown_element_number 4 if stereo_mixdown_present == 1 |
---|
| 118 | // matrix_mixdown_idx_present 1 |
---|
| 119 | // matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1 |
---|
| 120 | // pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1 |
---|
| 121 | // for (i = 0; i < num_front_channel_elements; i++) { |
---|
| 122 | // front_element_is_cpe[i] 1 |
---|
| 123 | // front_element_tag_select[i] 4 |
---|
| 124 | // } |
---|
| 125 | // for (i = 0; i < num_side_channel_elements; i++) { |
---|
| 126 | // side_element_is_cpe[i] 1 |
---|
| 127 | // side_element_tag_select[i] 4 |
---|
| 128 | // } |
---|
| 129 | // for (i = 0; i < num_back_channel_elements; i++) { |
---|
| 130 | // back_element_is_cpe[i] 1 |
---|
| 131 | // back_element_tag_select[i] 4 |
---|
| 132 | // } |
---|
| 133 | // for (i = 0; i < num_lfe_channel_elements; i++) { |
---|
| 134 | // lfe_element_tag_select[i] 4 |
---|
| 135 | // } |
---|
| 136 | // for (i = 0; i < num_assoc_data_elements; i++) { |
---|
| 137 | // assoc_data_element_tag_select[i] 4 |
---|
| 138 | // } |
---|
| 139 | // for (i = 0; i < num_valid_cc_elements; i++) { |
---|
| 140 | // cc_element_is_ind_sw[i] 1 |
---|
| 141 | // valid_cc_element_tag_select[i] 4 |
---|
| 142 | // } |
---|
| 143 | // byte_alignment() VAR |
---|
| 144 | // comment_field_bytes 8 |
---|
| 145 | // for (i = 0; i < comment_field_bytes; i++) { |
---|
| 146 | // comment_field_data[i] 8 |
---|
| 147 | // } |
---|
| 148 | |
---|
| 149 | if (!$ThisFileInfo['aac']['header']['is_vbr']) { |
---|
| 150 | $ThisFileInfo['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20)); |
---|
| 151 | $bitoffset += 20; |
---|
| 152 | } |
---|
| 153 | $ThisFileInfo['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 154 | $bitoffset += 4; |
---|
| 155 | $ThisFileInfo['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); |
---|
| 156 | $bitoffset += 2; |
---|
| 157 | $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 158 | $bitoffset += 4; |
---|
| 159 | $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 160 | $bitoffset += 4; |
---|
| 161 | $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 162 | $bitoffset += 4; |
---|
| 163 | $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 164 | $bitoffset += 4; |
---|
| 165 | $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); |
---|
| 166 | $bitoffset += 2; |
---|
| 167 | $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); |
---|
| 168 | $bitoffset += 3; |
---|
| 169 | $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 170 | $bitoffset += 4; |
---|
| 171 | $ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 172 | $bitoffset += 1; |
---|
| 173 | if ($ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present']) { |
---|
| 174 | $ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 175 | $bitoffset += 4; |
---|
| 176 | } |
---|
| 177 | $ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 178 | $bitoffset += 1; |
---|
| 179 | if ($ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present']) { |
---|
| 180 | $ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 181 | $bitoffset += 4; |
---|
| 182 | } |
---|
| 183 | $ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 184 | $bitoffset += 1; |
---|
| 185 | if ($ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) { |
---|
| 186 | $ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); |
---|
| 187 | $bitoffset += 2; |
---|
| 188 | $ThisFileInfo['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 189 | $bitoffset += 1; |
---|
| 190 | } |
---|
| 191 | for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) { |
---|
| 192 | $ThisFileInfo['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 193 | $bitoffset += 1; |
---|
| 194 | $ThisFileInfo['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 195 | $bitoffset += 4; |
---|
| 196 | } |
---|
| 197 | for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) { |
---|
| 198 | $ThisFileInfo['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 199 | $bitoffset += 1; |
---|
| 200 | $ThisFileInfo['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 201 | $bitoffset += 4; |
---|
| 202 | } |
---|
| 203 | for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) { |
---|
| 204 | $ThisFileInfo['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 205 | $bitoffset += 1; |
---|
| 206 | $ThisFileInfo['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 207 | $bitoffset += 4; |
---|
| 208 | } |
---|
| 209 | for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) { |
---|
| 210 | $ThisFileInfo['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 211 | $bitoffset += 4; |
---|
| 212 | } |
---|
| 213 | for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) { |
---|
| 214 | $ThisFileInfo['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 215 | $bitoffset += 4; |
---|
| 216 | } |
---|
| 217 | for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) { |
---|
| 218 | $ThisFileInfo['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 219 | $bitoffset += 1; |
---|
| 220 | $ThisFileInfo['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 221 | $bitoffset += 4; |
---|
| 222 | } |
---|
| 223 | |
---|
| 224 | $bitoffset = ceil($bitoffset / 8) * 8; |
---|
| 225 | |
---|
| 226 | $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8)); |
---|
| 227 | $bitoffset += 8; |
---|
| 228 | $ThisFileInfo['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'])); |
---|
| 229 | $bitoffset += 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes']; |
---|
| 230 | |
---|
| 231 | |
---|
| 232 | $ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['program_configs'][$i]['object_type'], $ThisFileInfo['aac']['header']['mpeg_version']); |
---|
| 233 | $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index']); |
---|
| 234 | $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency']; |
---|
| 235 | $ThisFileInfo['audio']['channels'] = $this->AACchannelCountCalculate($ThisFileInfo['aac']['program_configs'][$i]); |
---|
| 236 | if ($ThisFileInfo['aac']['program_configs'][$i]['comment_field']) { |
---|
| 237 | $ThisFileInfo['aac']['comments'][] = $ThisFileInfo['aac']['program_configs'][$i]['comment_field']; |
---|
| 238 | } |
---|
| 239 | } |
---|
| 240 | $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']; |
---|
| 241 | |
---|
| 242 | $ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text']; |
---|
| 243 | |
---|
| 244 | |
---|
| 245 | |
---|
| 246 | return true; |
---|
| 247 | |
---|
| 248 | } else { |
---|
| 249 | |
---|
| 250 | unset($ThisFileInfo['fileformat']); |
---|
| 251 | unset($ThisFileInfo['aac']); |
---|
| 252 | $ThisFileInfo['error'][] = 'AAC-ADIF synch not found at offset '.$ThisFileInfo['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)'; |
---|
| 253 | return false; |
---|
| 254 | |
---|
| 255 | } |
---|
| 256 | |
---|
| 257 | } |
---|
| 258 | |
---|
| 259 | |
---|
| 260 | function getAACADTSheaderFilepointer(&$fd, &$ThisFileInfo, $MaxFramesToScan=1000000, $ReturnExtendedInfo=false) { |
---|
| 261 | // based loosely on code from AACfile by Jurgen Faul <jfaulØgmx.de> |
---|
| 262 | // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html |
---|
| 263 | |
---|
| 264 | |
---|
| 265 | // http://faac.sourceforge.net/wiki/index.php?page=ADTS |
---|
| 266 | |
---|
| 267 | // * ADTS Fixed Header: these don't change from frame to frame |
---|
| 268 | // syncword 12 always: '111111111111' |
---|
| 269 | // ID 1 0: MPEG-4, 1: MPEG-2 |
---|
| 270 | // layer 2 always: '00' |
---|
| 271 | // protection_absent 1 |
---|
| 272 | // profile 2 |
---|
| 273 | // sampling_frequency_index 4 |
---|
| 274 | // private_bit 1 |
---|
| 275 | // channel_configuration 3 |
---|
| 276 | // original/copy 1 |
---|
| 277 | // home 1 |
---|
| 278 | // emphasis 2 only if ID == 0 (ie MPEG-4) |
---|
| 279 | |
---|
| 280 | // * ADTS Variable Header: these can change from frame to frame |
---|
| 281 | // copyright_identification_bit 1 |
---|
| 282 | // copyright_identification_start 1 |
---|
| 283 | // aac_frame_length 13 length of the frame including header (in bytes) |
---|
| 284 | // adts_buffer_fullness 11 0x7FF indicates VBR |
---|
| 285 | // no_raw_data_blocks_in_frame 2 |
---|
| 286 | |
---|
| 287 | // * ADTS Error check |
---|
| 288 | // crc_check 16 only if protection_absent == 0 |
---|
| 289 | |
---|
| 290 | $byteoffset = 0; |
---|
| 291 | $framenumber = 0; |
---|
| 292 | |
---|
| 293 | // Init bit pattern array |
---|
| 294 | static $decbin = array(); |
---|
| 295 | |
---|
| 296 | // Populate $bindec |
---|
| 297 | for ($i = 0; $i < 256; $i++) { |
---|
| 298 | $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT); |
---|
| 299 | } |
---|
| 300 | |
---|
| 301 | // used to calculate bitrate below |
---|
| 302 | $BitrateCache = array(); |
---|
| 303 | |
---|
| 304 | |
---|
| 305 | while (true) { |
---|
| 306 | // breaks out when end-of-file encountered, or invalid data found, |
---|
| 307 | // or MaxFramesToScan frames have been scanned |
---|
| 308 | |
---|
| 309 | if ($byteoffset >= pow(2, 31)) { |
---|
| 310 | $ThisFileInfo['warning'][] = 'Unable to parse AAC file beyond '.ftell($fd).' (PHP does not support file operations beyond 2GB)'; |
---|
| 311 | return false; |
---|
| 312 | } |
---|
| 313 | fseek($fd, $byteoffset, SEEK_SET); |
---|
| 314 | |
---|
| 315 | // First get substring |
---|
| 316 | $substring = fread($fd, 10); |
---|
| 317 | $substringlength = strlen($substring); |
---|
| 318 | if ($substringlength != 10) { |
---|
| 319 | $ThisFileInfo['error'][] = 'Failed to read 10 bytes at offset '.(ftell($fd) - $substringlength).' (only read '.$substringlength.' bytes)'; |
---|
| 320 | return false; |
---|
| 321 | } |
---|
| 322 | |
---|
| 323 | // Initialise $AACheaderBitstream |
---|
| 324 | $AACheaderBitstream = ''; |
---|
| 325 | |
---|
| 326 | // Loop thru substring chars |
---|
| 327 | for ($i = 0; $i < 10; $i++) { |
---|
| 328 | $AACheaderBitstream .= $decbin[$substring{$i}]; |
---|
| 329 | } |
---|
| 330 | |
---|
| 331 | $bitoffset = 0; |
---|
| 332 | |
---|
| 333 | $synctest = bindec(substr($AACheaderBitstream, $bitoffset, 12)); |
---|
| 334 | |
---|
| 335 | $bitoffset += 12; |
---|
| 336 | if ($synctest != 0x0FFF) { |
---|
| 337 | $ThisFileInfo['error'][] = 'Synch pattern (0x0FFF) not found at offset '.(ftell($fd) - 10).' (found 0x0'.strtoupper(dechex($synctest)).' instead)'; |
---|
| 338 | if ($ThisFileInfo['fileformat'] == 'aac') { |
---|
| 339 | return true; |
---|
| 340 | } |
---|
| 341 | return false; |
---|
| 342 | } |
---|
| 343 | |
---|
| 344 | // Gather info for first frame only - this takes time to do 1000 times! |
---|
| 345 | if ($framenumber > 0) { |
---|
| 346 | |
---|
| 347 | if (!$AACheaderBitstream[$bitoffset]) { |
---|
| 348 | |
---|
| 349 | // MPEG-4 |
---|
| 350 | $bitoffset += 20; |
---|
| 351 | |
---|
| 352 | } else { |
---|
| 353 | |
---|
| 354 | // MPEG-2 |
---|
| 355 | $bitoffset += 18; |
---|
| 356 | |
---|
| 357 | } |
---|
| 358 | |
---|
| 359 | } else { |
---|
| 360 | |
---|
| 361 | $ThisFileInfo['aac']['header_type'] = 'ADTS'; |
---|
| 362 | $ThisFileInfo['aac']['header']['synch'] = $synctest; |
---|
| 363 | $ThisFileInfo['fileformat'] = 'aac'; |
---|
| 364 | $ThisFileInfo['audio']['dataformat'] = 'aac'; |
---|
| 365 | |
---|
| 366 | $ThisFileInfo['aac']['header']['mpeg_version'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? 4 : 2); |
---|
| 367 | $bitoffset += 1; |
---|
| 368 | $ThisFileInfo['aac']['header']['layer'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); |
---|
| 369 | $bitoffset += 2; |
---|
| 370 | if ($ThisFileInfo['aac']['header']['layer'] != 0) { |
---|
| 371 | $ThisFileInfo['error'][] = 'Layer error - expected 0x00, found 0x'.dechex($ThisFileInfo['aac']['header']['layer']).' instead'; |
---|
| 372 | return false; |
---|
| 373 | } |
---|
| 374 | $ThisFileInfo['aac']['header']['crc_present'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? true : false); |
---|
| 375 | $bitoffset += 1; |
---|
| 376 | $ThisFileInfo['aac']['header']['profile_id'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); |
---|
| 377 | $bitoffset += 2; |
---|
| 378 | $ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['header']['profile_id'], $ThisFileInfo['aac']['header']['mpeg_version']); |
---|
| 379 | |
---|
| 380 | $ThisFileInfo['aac']['header']['sample_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); |
---|
| 381 | $bitoffset += 4; |
---|
| 382 | $ThisFileInfo['aac']['header']['sample_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['header']['sample_frequency_index']); |
---|
| 383 | if ($ThisFileInfo['aac']['header']['sample_frequency'] == 0) { |
---|
| 384 | $ThisFileInfo['error'][] = 'Corrupt AAC file: sample_frequency == zero'; |
---|
| 385 | return false; |
---|
| 386 | } |
---|
| 387 | $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['header']['sample_frequency']; |
---|
| 388 | |
---|
| 389 | $ThisFileInfo['aac']['header']['private'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 390 | $bitoffset += 1; |
---|
| 391 | $ThisFileInfo['aac']['header']['channel_configuration'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); |
---|
| 392 | $bitoffset += 3; |
---|
| 393 | $ThisFileInfo['audio']['channels'] = $ThisFileInfo['aac']['header']['channel_configuration']; |
---|
| 394 | $ThisFileInfo['aac']['header']['original'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 395 | $bitoffset += 1; |
---|
| 396 | $ThisFileInfo['aac']['header']['home'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 397 | $bitoffset += 1; |
---|
| 398 | |
---|
| 399 | if ($ThisFileInfo['aac']['header']['mpeg_version'] == 4) { |
---|
| 400 | $ThisFileInfo['aac']['header']['emphasis'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); |
---|
| 401 | $bitoffset += 2; |
---|
| 402 | } |
---|
| 403 | |
---|
| 404 | if ($ReturnExtendedInfo) { |
---|
| 405 | |
---|
| 406 | $ThisFileInfo['aac'][$framenumber]['copyright_id_bit'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 407 | $bitoffset += 1; |
---|
| 408 | $ThisFileInfo['aac'][$framenumber]['copyright_id_start'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); |
---|
| 409 | $bitoffset += 1; |
---|
| 410 | |
---|
| 411 | } else { |
---|
| 412 | |
---|
| 413 | $bitoffset += 2; |
---|
| 414 | |
---|
| 415 | } |
---|
| 416 | |
---|
| 417 | } |
---|
| 418 | |
---|
| 419 | $FrameLength = bindec(substr($AACheaderBitstream, $bitoffset, 13)); |
---|
| 420 | |
---|
| 421 | if (!isset($BitrateCache[$FrameLength])) { |
---|
| 422 | $BitrateCache[$FrameLength] = ($ThisFileInfo['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8; |
---|
| 423 | } |
---|
| 424 | @$ThisFileInfo['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]]++; |
---|
| 425 | |
---|
| 426 | $ThisFileInfo['aac'][$framenumber]['aac_frame_length'] = $FrameLength; |
---|
| 427 | $bitoffset += 13; |
---|
| 428 | $ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] = bindec(substr($AACheaderBitstream, $bitoffset, 11)); |
---|
| 429 | $bitoffset += 11; |
---|
| 430 | if ($ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) { |
---|
| 431 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; |
---|
| 432 | } else { |
---|
| 433 | $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; |
---|
| 434 | } |
---|
| 435 | $ThisFileInfo['aac'][$framenumber]['num_raw_data_blocks'] = bindec(substr($AACheaderBitstream, $bitoffset, 2)); |
---|
| 436 | $bitoffset += 2; |
---|
| 437 | |
---|
| 438 | if ($ThisFileInfo['aac']['header']['crc_present']) { |
---|
| 439 | //$ThisFileInfo['aac'][$framenumber]['crc'] = bindec(substr($AACheaderBitstream, $bitoffset, 16)); |
---|
| 440 | $bitoffset += 16; |
---|
| 441 | } |
---|
| 442 | |
---|
| 443 | if (!$ReturnExtendedInfo) { |
---|
| 444 | unset($ThisFileInfo['aac'][$framenumber]); |
---|
| 445 | } |
---|
| 446 | |
---|
| 447 | $byteoffset += $FrameLength; |
---|
| 448 | if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $ThisFileInfo['avdataend'])) { |
---|
| 449 | |
---|
| 450 | // keep scanning |
---|
| 451 | |
---|
| 452 | } else { |
---|
| 453 | |
---|
| 454 | $ThisFileInfo['aac']['frames'] = $framenumber; |
---|
| 455 | $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] / $byteoffset) * (($framenumber * 1024) / $ThisFileInfo['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds |
---|
| 456 | if ($ThisFileInfo['playtime_seconds'] == 0) { |
---|
| 457 | $ThisFileInfo['error'][] = 'Corrupt AAC file: playtime_seconds == zero'; |
---|
| 458 | return false; |
---|
| 459 | } |
---|
| 460 | $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; |
---|
| 461 | ksort($ThisFileInfo['aac']['bitrate_distribution']); |
---|
| 462 | |
---|
| 463 | $ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text']; |
---|
| 464 | |
---|
| 465 | return true; |
---|
| 466 | |
---|
| 467 | } |
---|
| 468 | } |
---|
| 469 | // should never get here. |
---|
| 470 | } |
---|
| 471 | |
---|
| 472 | function AACsampleRateLookup($samplerateid) { |
---|
| 473 | static $AACsampleRateLookup = array(); |
---|
| 474 | if (empty($AACsampleRateLookup)) { |
---|
| 475 | $AACsampleRateLookup[0] = 96000; |
---|
| 476 | $AACsampleRateLookup[1] = 88200; |
---|
| 477 | $AACsampleRateLookup[2] = 64000; |
---|
| 478 | $AACsampleRateLookup[3] = 48000; |
---|
| 479 | $AACsampleRateLookup[4] = 44100; |
---|
| 480 | $AACsampleRateLookup[5] = 32000; |
---|
| 481 | $AACsampleRateLookup[6] = 24000; |
---|
| 482 | $AACsampleRateLookup[7] = 22050; |
---|
| 483 | $AACsampleRateLookup[8] = 16000; |
---|
| 484 | $AACsampleRateLookup[9] = 12000; |
---|
| 485 | $AACsampleRateLookup[10] = 11025; |
---|
| 486 | $AACsampleRateLookup[11] = 8000; |
---|
| 487 | $AACsampleRateLookup[12] = 0; |
---|
| 488 | $AACsampleRateLookup[13] = 0; |
---|
| 489 | $AACsampleRateLookup[14] = 0; |
---|
| 490 | $AACsampleRateLookup[15] = 0; |
---|
| 491 | } |
---|
| 492 | return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid'); |
---|
| 493 | } |
---|
| 494 | |
---|
| 495 | function AACprofileLookup($profileid, $mpegversion) { |
---|
| 496 | static $AACprofileLookup = array(); |
---|
| 497 | if (empty($AACprofileLookup)) { |
---|
| 498 | $AACprofileLookup[2][0] = 'Main profile'; |
---|
| 499 | $AACprofileLookup[2][1] = 'Low Complexity profile (LC)'; |
---|
| 500 | $AACprofileLookup[2][2] = 'Scalable Sample Rate profile (SSR)'; |
---|
| 501 | $AACprofileLookup[2][3] = '(reserved)'; |
---|
| 502 | $AACprofileLookup[4][0] = 'AAC_MAIN'; |
---|
| 503 | $AACprofileLookup[4][1] = 'AAC_LC'; |
---|
| 504 | $AACprofileLookup[4][2] = 'AAC_SSR'; |
---|
| 505 | $AACprofileLookup[4][3] = 'AAC_LTP'; |
---|
| 506 | } |
---|
| 507 | return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid'); |
---|
| 508 | } |
---|
| 509 | |
---|
| 510 | function AACchannelCountCalculate($program_configs) { |
---|
| 511 | $channels = 0; |
---|
| 512 | for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) { |
---|
| 513 | $channels++; |
---|
| 514 | if ($program_configs['front_element_is_cpe'][$i]) { |
---|
| 515 | // each front element is channel pair (CPE = Channel Pair Element) |
---|
| 516 | $channels++; |
---|
| 517 | } |
---|
| 518 | } |
---|
| 519 | for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) { |
---|
| 520 | $channels++; |
---|
| 521 | if ($program_configs['side_element_is_cpe'][$i]) { |
---|
| 522 | // each side element is channel pair (CPE = Channel Pair Element) |
---|
| 523 | $channels++; |
---|
| 524 | } |
---|
| 525 | } |
---|
| 526 | for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) { |
---|
| 527 | $channels++; |
---|
| 528 | if ($program_configs['back_element_is_cpe'][$i]) { |
---|
| 529 | // each back element is channel pair (CPE = Channel Pair Element) |
---|
| 530 | $channels++; |
---|
| 531 | } |
---|
| 532 | } |
---|
| 533 | for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) { |
---|
| 534 | $channels++; |
---|
| 535 | } |
---|
| 536 | return $channels; |
---|
| 537 | } |
---|
| 538 | |
---|
| 539 | } |
---|
| 540 | |
---|
| 541 | |
---|
| 542 | ?> |
---|