rlm@3: | rlm@3: // | Allan Hansen | rlm@3: // +----------------------------------------------------------------------+ rlm@3: // | module.audio.aac_adif.php | rlm@3: // | Module for analyzing AAC files with ADIF header. | rlm@3: // | dependencies: NONE | rlm@3: // +----------------------------------------------------------------------+ rlm@3: // rlm@3: // $Id: module.audio.aac_adif.php,v 1.3 2006/11/02 10:48:00 ah Exp $ rlm@3: rlm@3: rlm@3: rlm@3: class getid3_aac_adif extends getid3_handler rlm@3: { rlm@3: rlm@3: public function Analyze() { rlm@3: rlm@3: $getid3 = $this->getid3; rlm@3: rlm@3: // http://faac.sourceforge.net/wiki/index.php?page=ADIF rlm@3: // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf rlm@3: // adif_header() { rlm@3: // adif_id 32 rlm@3: // copyright_id_present 1 rlm@3: // if( copyright_id_present ) rlm@3: // copyright_id 72 rlm@3: // original_copy 1 rlm@3: // home 1 rlm@3: // bitstream_type 1 rlm@3: // bitrate 23 rlm@3: // num_program_config_elements 4 rlm@3: // for (i = 0; i < num_program_config_elements + 1; i++ ) { rlm@3: // if( bitstream_type == '0' ) rlm@3: // adif_buffer_fullness 20 rlm@3: // program_config_element() rlm@3: // } rlm@3: // } rlm@3: rlm@3: rlm@3: $getid3->info['fileformat'] = 'aac'; rlm@3: $getid3->info['audio']['dataformat'] = 'aac'; rlm@3: $getid3->info['audio']['lossless'] = false; rlm@3: rlm@3: $getid3->info['aac']['header'] = array () ; rlm@3: $info_aac = &$getid3->info['aac']; rlm@3: $info_aac_header = & $info_aac['header']; rlm@3: rlm@3: fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); rlm@3: $aac_header_bitstream = getid3_lib::BigEndian2Bin(fread($getid3->fp, 1024)); rlm@3: rlm@3: $info_aac['header_type'] = 'ADIF'; rlm@3: $info_aac_header['mpeg_version'] = 4; rlm@3: $bit_offset = 32; rlm@3: rlm@3: $info_aac_header['copyright'] = $aac_header_bitstream{$bit_offset++} == '1'; rlm@3: if ($info_aac_header['copyright']) { rlm@3: $info_aac_header['copyright_id'] = getid3_aac_adif::Bin2String(substr($aac_header_bitstream, $bit_offset, 72)); rlm@3: $bit_offset += 72; rlm@3: } rlm@3: rlm@3: $info_aac_header['original_copy'] = $aac_header_bitstream{$bit_offset++} == '1'; rlm@3: $info_aac_header['home'] = $aac_header_bitstream{$bit_offset++} == '1'; rlm@3: $info_aac_header['is_vbr'] = $aac_header_bitstream{$bit_offset++} == '1'; rlm@3: rlm@3: if ($info_aac_header['is_vbr']) { rlm@3: $getid3->info['audio']['bitrate_mode'] = 'vbr'; rlm@3: $info_aac_header['bitrate_max'] = bindec(substr($aac_header_bitstream, $bit_offset, 23)); rlm@3: $bit_offset += 23; rlm@3: } rlm@3: else { rlm@3: $getid3->info['audio']['bitrate_mode'] = 'cbr'; rlm@3: $info_aac_header['bitrate'] = bindec(substr($aac_header_bitstream, $bit_offset, 23)); rlm@3: $bit_offset += 23; rlm@3: $getid3->info['audio']['bitrate'] = $info_aac_header['bitrate']; rlm@3: } rlm@3: rlm@3: $info_aac_header['num_program_configs'] = 1 + bindec(substr($aac_header_bitstream, $bit_offset, 4)); rlm@3: $bit_offset += 4; rlm@3: rlm@3: for ($i = 0; $i < $info_aac_header['num_program_configs']; $i++) { rlm@3: rlm@3: // http://www.audiocoding.com/wiki/index.php?page=program_config_element rlm@3: rlm@3: // buffer_fullness 20 rlm@3: rlm@3: // element_instance_tag 4 rlm@3: // object_type 2 rlm@3: // sampling_frequency_index 4 rlm@3: // num_front_channel_elements 4 rlm@3: // num_side_channel_elements 4 rlm@3: // num_back_channel_elements 4 rlm@3: // num_lfe_channel_elements 2 rlm@3: // num_assoc_data_elements 3 rlm@3: // num_valid_cc_elements 4 rlm@3: // mono_mixdown_present 1 rlm@3: // mono_mixdown_element_number 4 if mono_mixdown_present == 1 rlm@3: // stereo_mixdown_present 1 rlm@3: // stereo_mixdown_element_number 4 if stereo_mixdown_present == 1 rlm@3: // matrix_mixdown_idx_present 1 rlm@3: // matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1 rlm@3: // pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1 rlm@3: // for (i = 0; i < num_front_channel_elements; i++) { rlm@3: // front_element_is_cpe[i] 1 rlm@3: // front_element_tag_select[i] 4 rlm@3: // } rlm@3: // for (i = 0; i < num_side_channel_elements; i++) { rlm@3: // side_element_is_cpe[i] 1 rlm@3: // side_element_tag_select[i] 4 rlm@3: // } rlm@3: // for (i = 0; i < num_back_channel_elements; i++) { rlm@3: // back_element_is_cpe[i] 1 rlm@3: // back_element_tag_select[i] 4 rlm@3: // } rlm@3: // for (i = 0; i < num_lfe_channel_elements; i++) { rlm@3: // lfe_element_tag_select[i] 4 rlm@3: // } rlm@3: // for (i = 0; i < num_assoc_data_elements; i++) { rlm@3: // assoc_data_element_tag_select[i] 4 rlm@3: // } rlm@3: // for (i = 0; i < num_valid_cc_elements; i++) { rlm@3: // cc_element_is_ind_sw[i] 1 rlm@3: // valid_cc_element_tag_select[i] 4 rlm@3: // } rlm@3: // byte_alignment() VAR rlm@3: // comment_field_bytes 8 rlm@3: // for (i = 0; i < comment_field_bytes; i++) { rlm@3: // comment_field_data[i] 8 rlm@3: // } rlm@3: rlm@3: $info_aac['program_configs'][$i] = array (); rlm@3: $info_aac_program_configs_i = &$info_aac['program_configs'][$i]; rlm@3: rlm@3: if (!$info_aac_header['is_vbr']) { rlm@3: $info_aac_program_configs_i['buffer_fullness'] = bindec(substr($aac_header_bitstream, $bit_offset, 20)); rlm@3: $bit_offset += 20; rlm@3: } rlm@3: rlm@3: $info_aac_program_configs_i['element_instance_tag'] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); rlm@3: $info_aac_program_configs_i['object_type'] = bindec(substr($aac_header_bitstream, $bit_offset + 4, 2)); rlm@3: $info_aac_program_configs_i['sampling_frequency_index'] = bindec(substr($aac_header_bitstream, $bit_offset + 6, 4)); rlm@3: $info_aac_program_configs_i['num_front_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 10, 4)); rlm@3: $info_aac_program_configs_i['num_side_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 14, 4)); rlm@3: $info_aac_program_configs_i['num_back_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 18, 4)); rlm@3: $info_aac_program_configs_i['num_lfe_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 22, 2)); rlm@3: $info_aac_program_configs_i['num_assoc_data_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 24, 3)); rlm@3: $info_aac_program_configs_i['num_valid_cc_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 27, 4)); rlm@3: $bit_offset += 31; rlm@3: rlm@3: $info_aac_program_configs_i['mono_mixdown_present'] = $aac_header_bitstream{$bit_offset++} == 1; rlm@3: if ($info_aac_program_configs_i['mono_mixdown_present']) { rlm@3: $info_aac_program_configs_i['mono_mixdown_element_number'] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); rlm@3: $bit_offset += 4; rlm@3: } rlm@3: rlm@3: $info_aac_program_configs_i['stereo_mixdown_present'] = $aac_header_bitstream{$bit_offset++} == 1; rlm@3: if ($info_aac_program_configs_i['stereo_mixdown_present']) { rlm@3: $info_aac_program_configs_i['stereo_mixdown_element_number'] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); rlm@3: $bit_offset += 4; rlm@3: } rlm@3: rlm@3: $info_aac_program_configs_i['matrix_mixdown_idx_present'] = $aac_header_bitstream{$bit_offset++} == 1; rlm@3: if ($info_aac_program_configs_i['matrix_mixdown_idx_present']) { rlm@3: $info_aac_program_configs_i['matrix_mixdown_idx'] = bindec(substr($aac_header_bitstream, $bit_offset, 2)); rlm@3: $bit_offset += 2; rlm@3: $info_aac_program_configs_i['pseudo_surround_enable'] = $aac_header_bitstream{$bit_offset++} == 1; rlm@3: } rlm@3: rlm@3: for ($j = 0; $j < $info_aac_program_configs_i['num_front_channel_elements']; $j++) { rlm@3: $info_aac_program_configs_i['front_element_is_cpe'][$j] = $aac_header_bitstream{$bit_offset++} == 1; rlm@3: $info_aac_program_configs_i['front_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); rlm@3: $bit_offset += 4; rlm@3: } rlm@3: for ($j = 0; $j < $info_aac_program_configs_i['num_side_channel_elements']; $j++) { rlm@3: $info_aac_program_configs_i['side_element_is_cpe'][$j] = $aac_header_bitstream{$bit_offset++} == 1; rlm@3: $info_aac_program_configs_i['side_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); rlm@3: $bit_offset += 4; rlm@3: } rlm@3: for ($j = 0; $j < $info_aac_program_configs_i['num_back_channel_elements']; $j++) { rlm@3: $info_aac_program_configs_i['back_element_is_cpe'][$j] = $aac_header_bitstream{$bit_offset++} == 1; rlm@3: $info_aac_program_configs_i['back_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); rlm@3: $bit_offset += 4; rlm@3: } rlm@3: for ($j = 0; $j < $info_aac_program_configs_i['num_lfe_channel_elements']; $j++) { rlm@3: $info_aac_program_configs_i['lfe_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); rlm@3: $bit_offset += 4; rlm@3: } rlm@3: for ($j = 0; $j < $info_aac_program_configs_i['num_assoc_data_elements']; $j++) { rlm@3: $info_aac_program_configs_i['assoc_data_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); rlm@3: $bit_offset += 4; rlm@3: } rlm@3: for ($j = 0; $j < $info_aac_program_configs_i['num_valid_cc_elements']; $j++) { rlm@3: $info_aac_program_configs_i['cc_element_is_ind_sw'][$j] = $aac_header_bitstream{$bit_offset++} == 1; rlm@3: $info_aac_program_configs_i['valid_cc_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4)); rlm@3: $bit_offset += 4; rlm@3: } rlm@3: rlm@3: $bit_offset = ceil($bit_offset / 8) * 8; rlm@3: rlm@3: $info_aac_program_configs_i['comment_field_bytes'] = bindec(substr($aac_header_bitstream, $bit_offset, 8)); rlm@3: $bit_offset += 8; rlm@3: rlm@3: $info_aac_program_configs_i['comment_field'] = getid3_aac_adif::Bin2String(substr($aac_header_bitstream, $bit_offset, 8 * $info_aac_program_configs_i['comment_field_bytes'])); rlm@3: $bit_offset += 8 * $info_aac_program_configs_i['comment_field_bytes']; rlm@3: rlm@3: $info_aac_header['profile_text'] = getid3_aac_adif::AACprofileLookup($info_aac_program_configs_i['object_type'], $info_aac_header['mpeg_version']); rlm@3: $info_aac_program_configs_i['sampling_frequency'] = $getid3->info['audio']['sample_rate'] = getid3_aac_adif::AACsampleRateLookup($info_aac_program_configs_i['sampling_frequency_index']); rlm@3: $getid3->info['audio']['channels'] = getid3_aac_adif::AACchannelCountCalculate($info_aac_program_configs_i); rlm@3: rlm@3: if ($info_aac_program_configs_i['comment_field']) { rlm@3: $info_aac['comments'][] = $info_aac_program_configs_i['comment_field']; rlm@3: } rlm@3: } rlm@3: rlm@3: $getid3->info['playtime_seconds'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['audio']['bitrate']; rlm@3: $getid3->info['audio']['encoder_options'] = $info_aac['header_type'].' '.$info_aac_header['profile_text']; rlm@3: rlm@3: return true; rlm@3: } rlm@3: rlm@3: rlm@3: rlm@3: public static function Bin2String($bin_string) { rlm@3: // return 'hi' for input of '0110100001101001' rlm@3: $string = ''; rlm@3: $bin_string_reversed = strrev($bin_string); rlm@3: for ($i = 0; $i < strlen($bin_string_reversed); $i += 8) { rlm@3: $string = chr(bindec(strrev(substr($bin_string_reversed, $i, 8)))).$string; rlm@3: } rlm@3: return $string; rlm@3: } rlm@3: rlm@3: rlm@3: rlm@3: public static function AACsampleRateLookup($samplerate_id) { rlm@3: rlm@3: static $lookup = array ( rlm@3: 0 => 96000, rlm@3: 1 => 88200, rlm@3: 2 => 64000, rlm@3: 3 => 48000, rlm@3: 4 => 44100, rlm@3: 5 => 32000, rlm@3: 6 => 24000, rlm@3: 7 => 22050, rlm@3: 8 => 16000, rlm@3: 9 => 12000, rlm@3: 10 => 11025, rlm@3: 11 => 8000, rlm@3: 12 => 0, rlm@3: 13 => 0, rlm@3: 14 => 0, rlm@3: 15 => 0 rlm@3: ); rlm@3: return (isset($lookup[$samplerate_id]) ? $lookup[$samplerate_id] : 'invalid'); rlm@3: } rlm@3: rlm@3: rlm@3: rlm@3: public static function AACprofileLookup($profile_id, $mpeg_version) { rlm@3: rlm@3: static $lookup = array ( rlm@3: 2 => array ( rlm@3: 0 => 'Main profile', rlm@3: 1 => 'Low Complexity profile (LC)', rlm@3: 2 => 'Scalable Sample Rate profile (SSR)', rlm@3: 3 => '(reserved)' rlm@3: ), rlm@3: 4 => array ( rlm@3: 0 => 'AAC_MAIN', rlm@3: 1 => 'AAC_LC', rlm@3: 2 => 'AAC_SSR', rlm@3: 3 => 'AAC_LTP' rlm@3: ) rlm@3: ); rlm@3: return (isset($lookup[$mpeg_version][$profile_id]) ? $lookup[$mpeg_version][$profile_id] : 'invalid'); rlm@3: } rlm@3: rlm@3: rlm@3: rlm@3: public static function AACchannelCountCalculate($program_configs) { rlm@3: rlm@3: $channels = 0; rlm@3: rlm@3: foreach (array ('front', 'side', 'back') as $placement) { rlm@3: for ($i = 0; $i < $program_configs['num_'.$placement.'_channel_elements']; $i++) { rlm@3: rlm@3: // Each element is channel pair (CPE = Channel Pair Element) rlm@3: $channels += 1 + ($program_configs[$placement.'_element_is_cpe'][$i] ? 1 : 0); rlm@3: } rlm@3: } rlm@3: rlm@3: return $channels + $program_configs['num_lfe_channel_elements']; rlm@3: } rlm@3: rlm@3: } rlm@3: rlm@3: rlm@3: ?>