rlm@3
|
1 <?php
|
rlm@3
|
2 // +----------------------------------------------------------------------+
|
rlm@3
|
3 // | PHP version 5 |
|
rlm@3
|
4 // +----------------------------------------------------------------------+
|
rlm@3
|
5 // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen |
|
rlm@3
|
6 // +----------------------------------------------------------------------+
|
rlm@3
|
7 // | This source file is subject to version 2 of the GPL license, |
|
rlm@3
|
8 // | that is bundled with this package in the file license.txt and is |
|
rlm@3
|
9 // | available through the world-wide-web at the following url: |
|
rlm@3
|
10 // | http://www.gnu.org/copyleft/gpl.html |
|
rlm@3
|
11 // +----------------------------------------------------------------------+
|
rlm@3
|
12 // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org |
|
rlm@3
|
13 // +----------------------------------------------------------------------+
|
rlm@3
|
14 // | Authors: James Heinrich <infoØgetid3*org> |
|
rlm@3
|
15 // | Allan Hansen <ahØartemis*dk> |
|
rlm@3
|
16 // +----------------------------------------------------------------------+
|
rlm@3
|
17 // | module.audio.aac_adif.php |
|
rlm@3
|
18 // | Module for analyzing AAC files with ADIF header. |
|
rlm@3
|
19 // | dependencies: NONE |
|
rlm@3
|
20 // +----------------------------------------------------------------------+
|
rlm@3
|
21 //
|
rlm@3
|
22 // $Id: module.audio.aac_adif.php,v 1.3 2006/11/02 10:48:00 ah Exp $
|
rlm@3
|
23
|
rlm@3
|
24
|
rlm@3
|
25
|
rlm@3
|
26 class getid3_aac_adif extends getid3_handler
|
rlm@3
|
27 {
|
rlm@3
|
28
|
rlm@3
|
29 public function Analyze() {
|
rlm@3
|
30
|
rlm@3
|
31 $getid3 = $this->getid3;
|
rlm@3
|
32
|
rlm@3
|
33 // http://faac.sourceforge.net/wiki/index.php?page=ADIF
|
rlm@3
|
34 // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
|
rlm@3
|
35 // adif_header() {
|
rlm@3
|
36 // adif_id 32
|
rlm@3
|
37 // copyright_id_present 1
|
rlm@3
|
38 // if( copyright_id_present )
|
rlm@3
|
39 // copyright_id 72
|
rlm@3
|
40 // original_copy 1
|
rlm@3
|
41 // home 1
|
rlm@3
|
42 // bitstream_type 1
|
rlm@3
|
43 // bitrate 23
|
rlm@3
|
44 // num_program_config_elements 4
|
rlm@3
|
45 // for (i = 0; i < num_program_config_elements + 1; i++ ) {
|
rlm@3
|
46 // if( bitstream_type == '0' )
|
rlm@3
|
47 // adif_buffer_fullness 20
|
rlm@3
|
48 // program_config_element()
|
rlm@3
|
49 // }
|
rlm@3
|
50 // }
|
rlm@3
|
51
|
rlm@3
|
52
|
rlm@3
|
53 $getid3->info['fileformat'] = 'aac';
|
rlm@3
|
54 $getid3->info['audio']['dataformat'] = 'aac';
|
rlm@3
|
55 $getid3->info['audio']['lossless'] = false;
|
rlm@3
|
56
|
rlm@3
|
57 $getid3->info['aac']['header'] = array () ;
|
rlm@3
|
58 $info_aac = &$getid3->info['aac'];
|
rlm@3
|
59 $info_aac_header = & $info_aac['header'];
|
rlm@3
|
60
|
rlm@3
|
61 fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
|
rlm@3
|
62 $aac_header_bitstream = getid3_lib::BigEndian2Bin(fread($getid3->fp, 1024));
|
rlm@3
|
63
|
rlm@3
|
64 $info_aac['header_type'] = 'ADIF';
|
rlm@3
|
65 $info_aac_header['mpeg_version'] = 4;
|
rlm@3
|
66 $bit_offset = 32;
|
rlm@3
|
67
|
rlm@3
|
68 $info_aac_header['copyright'] = $aac_header_bitstream{$bit_offset++} == '1';
|
rlm@3
|
69 if ($info_aac_header['copyright']) {
|
rlm@3
|
70 $info_aac_header['copyright_id'] = getid3_aac_adif::Bin2String(substr($aac_header_bitstream, $bit_offset, 72));
|
rlm@3
|
71 $bit_offset += 72;
|
rlm@3
|
72 }
|
rlm@3
|
73
|
rlm@3
|
74 $info_aac_header['original_copy'] = $aac_header_bitstream{$bit_offset++} == '1';
|
rlm@3
|
75 $info_aac_header['home'] = $aac_header_bitstream{$bit_offset++} == '1';
|
rlm@3
|
76 $info_aac_header['is_vbr'] = $aac_header_bitstream{$bit_offset++} == '1';
|
rlm@3
|
77
|
rlm@3
|
78 if ($info_aac_header['is_vbr']) {
|
rlm@3
|
79 $getid3->info['audio']['bitrate_mode'] = 'vbr';
|
rlm@3
|
80 $info_aac_header['bitrate_max'] = bindec(substr($aac_header_bitstream, $bit_offset, 23));
|
rlm@3
|
81 $bit_offset += 23;
|
rlm@3
|
82 }
|
rlm@3
|
83 else {
|
rlm@3
|
84 $getid3->info['audio']['bitrate_mode'] = 'cbr';
|
rlm@3
|
85 $info_aac_header['bitrate'] = bindec(substr($aac_header_bitstream, $bit_offset, 23));
|
rlm@3
|
86 $bit_offset += 23;
|
rlm@3
|
87 $getid3->info['audio']['bitrate'] = $info_aac_header['bitrate'];
|
rlm@3
|
88 }
|
rlm@3
|
89
|
rlm@3
|
90 $info_aac_header['num_program_configs'] = 1 + bindec(substr($aac_header_bitstream, $bit_offset, 4));
|
rlm@3
|
91 $bit_offset += 4;
|
rlm@3
|
92
|
rlm@3
|
93 for ($i = 0; $i < $info_aac_header['num_program_configs']; $i++) {
|
rlm@3
|
94
|
rlm@3
|
95 // http://www.audiocoding.com/wiki/index.php?page=program_config_element
|
rlm@3
|
96
|
rlm@3
|
97 // buffer_fullness 20
|
rlm@3
|
98
|
rlm@3
|
99 // element_instance_tag 4
|
rlm@3
|
100 // object_type 2
|
rlm@3
|
101 // sampling_frequency_index 4
|
rlm@3
|
102 // num_front_channel_elements 4
|
rlm@3
|
103 // num_side_channel_elements 4
|
rlm@3
|
104 // num_back_channel_elements 4
|
rlm@3
|
105 // num_lfe_channel_elements 2
|
rlm@3
|
106 // num_assoc_data_elements 3
|
rlm@3
|
107 // num_valid_cc_elements 4
|
rlm@3
|
108 // mono_mixdown_present 1
|
rlm@3
|
109 // mono_mixdown_element_number 4 if mono_mixdown_present == 1
|
rlm@3
|
110 // stereo_mixdown_present 1
|
rlm@3
|
111 // stereo_mixdown_element_number 4 if stereo_mixdown_present == 1
|
rlm@3
|
112 // matrix_mixdown_idx_present 1
|
rlm@3
|
113 // matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1
|
rlm@3
|
114 // pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1
|
rlm@3
|
115 // for (i = 0; i < num_front_channel_elements; i++) {
|
rlm@3
|
116 // front_element_is_cpe[i] 1
|
rlm@3
|
117 // front_element_tag_select[i] 4
|
rlm@3
|
118 // }
|
rlm@3
|
119 // for (i = 0; i < num_side_channel_elements; i++) {
|
rlm@3
|
120 // side_element_is_cpe[i] 1
|
rlm@3
|
121 // side_element_tag_select[i] 4
|
rlm@3
|
122 // }
|
rlm@3
|
123 // for (i = 0; i < num_back_channel_elements; i++) {
|
rlm@3
|
124 // back_element_is_cpe[i] 1
|
rlm@3
|
125 // back_element_tag_select[i] 4
|
rlm@3
|
126 // }
|
rlm@3
|
127 // for (i = 0; i < num_lfe_channel_elements; i++) {
|
rlm@3
|
128 // lfe_element_tag_select[i] 4
|
rlm@3
|
129 // }
|
rlm@3
|
130 // for (i = 0; i < num_assoc_data_elements; i++) {
|
rlm@3
|
131 // assoc_data_element_tag_select[i] 4
|
rlm@3
|
132 // }
|
rlm@3
|
133 // for (i = 0; i < num_valid_cc_elements; i++) {
|
rlm@3
|
134 // cc_element_is_ind_sw[i] 1
|
rlm@3
|
135 // valid_cc_element_tag_select[i] 4
|
rlm@3
|
136 // }
|
rlm@3
|
137 // byte_alignment() VAR
|
rlm@3
|
138 // comment_field_bytes 8
|
rlm@3
|
139 // for (i = 0; i < comment_field_bytes; i++) {
|
rlm@3
|
140 // comment_field_data[i] 8
|
rlm@3
|
141 // }
|
rlm@3
|
142
|
rlm@3
|
143 $info_aac['program_configs'][$i] = array ();
|
rlm@3
|
144 $info_aac_program_configs_i = &$info_aac['program_configs'][$i];
|
rlm@3
|
145
|
rlm@3
|
146 if (!$info_aac_header['is_vbr']) {
|
rlm@3
|
147 $info_aac_program_configs_i['buffer_fullness'] = bindec(substr($aac_header_bitstream, $bit_offset, 20));
|
rlm@3
|
148 $bit_offset += 20;
|
rlm@3
|
149 }
|
rlm@3
|
150
|
rlm@3
|
151 $info_aac_program_configs_i['element_instance_tag'] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
|
rlm@3
|
152 $info_aac_program_configs_i['object_type'] = bindec(substr($aac_header_bitstream, $bit_offset + 4, 2));
|
rlm@3
|
153 $info_aac_program_configs_i['sampling_frequency_index'] = bindec(substr($aac_header_bitstream, $bit_offset + 6, 4));
|
rlm@3
|
154 $info_aac_program_configs_i['num_front_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 10, 4));
|
rlm@3
|
155 $info_aac_program_configs_i['num_side_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 14, 4));
|
rlm@3
|
156 $info_aac_program_configs_i['num_back_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 18, 4));
|
rlm@3
|
157 $info_aac_program_configs_i['num_lfe_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 22, 2));
|
rlm@3
|
158 $info_aac_program_configs_i['num_assoc_data_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 24, 3));
|
rlm@3
|
159 $info_aac_program_configs_i['num_valid_cc_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 27, 4));
|
rlm@3
|
160 $bit_offset += 31;
|
rlm@3
|
161
|
rlm@3
|
162 $info_aac_program_configs_i['mono_mixdown_present'] = $aac_header_bitstream{$bit_offset++} == 1;
|
rlm@3
|
163 if ($info_aac_program_configs_i['mono_mixdown_present']) {
|
rlm@3
|
164 $info_aac_program_configs_i['mono_mixdown_element_number'] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
|
rlm@3
|
165 $bit_offset += 4;
|
rlm@3
|
166 }
|
rlm@3
|
167
|
rlm@3
|
168 $info_aac_program_configs_i['stereo_mixdown_present'] = $aac_header_bitstream{$bit_offset++} == 1;
|
rlm@3
|
169 if ($info_aac_program_configs_i['stereo_mixdown_present']) {
|
rlm@3
|
170 $info_aac_program_configs_i['stereo_mixdown_element_number'] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
|
rlm@3
|
171 $bit_offset += 4;
|
rlm@3
|
172 }
|
rlm@3
|
173
|
rlm@3
|
174 $info_aac_program_configs_i['matrix_mixdown_idx_present'] = $aac_header_bitstream{$bit_offset++} == 1;
|
rlm@3
|
175 if ($info_aac_program_configs_i['matrix_mixdown_idx_present']) {
|
rlm@3
|
176 $info_aac_program_configs_i['matrix_mixdown_idx'] = bindec(substr($aac_header_bitstream, $bit_offset, 2));
|
rlm@3
|
177 $bit_offset += 2;
|
rlm@3
|
178 $info_aac_program_configs_i['pseudo_surround_enable'] = $aac_header_bitstream{$bit_offset++} == 1;
|
rlm@3
|
179 }
|
rlm@3
|
180
|
rlm@3
|
181 for ($j = 0; $j < $info_aac_program_configs_i['num_front_channel_elements']; $j++) {
|
rlm@3
|
182 $info_aac_program_configs_i['front_element_is_cpe'][$j] = $aac_header_bitstream{$bit_offset++} == 1;
|
rlm@3
|
183 $info_aac_program_configs_i['front_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
|
rlm@3
|
184 $bit_offset += 4;
|
rlm@3
|
185 }
|
rlm@3
|
186 for ($j = 0; $j < $info_aac_program_configs_i['num_side_channel_elements']; $j++) {
|
rlm@3
|
187 $info_aac_program_configs_i['side_element_is_cpe'][$j] = $aac_header_bitstream{$bit_offset++} == 1;
|
rlm@3
|
188 $info_aac_program_configs_i['side_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
|
rlm@3
|
189 $bit_offset += 4;
|
rlm@3
|
190 }
|
rlm@3
|
191 for ($j = 0; $j < $info_aac_program_configs_i['num_back_channel_elements']; $j++) {
|
rlm@3
|
192 $info_aac_program_configs_i['back_element_is_cpe'][$j] = $aac_header_bitstream{$bit_offset++} == 1;
|
rlm@3
|
193 $info_aac_program_configs_i['back_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
|
rlm@3
|
194 $bit_offset += 4;
|
rlm@3
|
195 }
|
rlm@3
|
196 for ($j = 0; $j < $info_aac_program_configs_i['num_lfe_channel_elements']; $j++) {
|
rlm@3
|
197 $info_aac_program_configs_i['lfe_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
|
rlm@3
|
198 $bit_offset += 4;
|
rlm@3
|
199 }
|
rlm@3
|
200 for ($j = 0; $j < $info_aac_program_configs_i['num_assoc_data_elements']; $j++) {
|
rlm@3
|
201 $info_aac_program_configs_i['assoc_data_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
|
rlm@3
|
202 $bit_offset += 4;
|
rlm@3
|
203 }
|
rlm@3
|
204 for ($j = 0; $j < $info_aac_program_configs_i['num_valid_cc_elements']; $j++) {
|
rlm@3
|
205 $info_aac_program_configs_i['cc_element_is_ind_sw'][$j] = $aac_header_bitstream{$bit_offset++} == 1;
|
rlm@3
|
206 $info_aac_program_configs_i['valid_cc_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
|
rlm@3
|
207 $bit_offset += 4;
|
rlm@3
|
208 }
|
rlm@3
|
209
|
rlm@3
|
210 $bit_offset = ceil($bit_offset / 8) * 8;
|
rlm@3
|
211
|
rlm@3
|
212 $info_aac_program_configs_i['comment_field_bytes'] = bindec(substr($aac_header_bitstream, $bit_offset, 8));
|
rlm@3
|
213 $bit_offset += 8;
|
rlm@3
|
214
|
rlm@3
|
215 $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
|
216 $bit_offset += 8 * $info_aac_program_configs_i['comment_field_bytes'];
|
rlm@3
|
217
|
rlm@3
|
218 $info_aac_header['profile_text'] = getid3_aac_adif::AACprofileLookup($info_aac_program_configs_i['object_type'], $info_aac_header['mpeg_version']);
|
rlm@3
|
219 $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
|
220 $getid3->info['audio']['channels'] = getid3_aac_adif::AACchannelCountCalculate($info_aac_program_configs_i);
|
rlm@3
|
221
|
rlm@3
|
222 if ($info_aac_program_configs_i['comment_field']) {
|
rlm@3
|
223 $info_aac['comments'][] = $info_aac_program_configs_i['comment_field'];
|
rlm@3
|
224 }
|
rlm@3
|
225 }
|
rlm@3
|
226
|
rlm@3
|
227 $getid3->info['playtime_seconds'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['audio']['bitrate'];
|
rlm@3
|
228 $getid3->info['audio']['encoder_options'] = $info_aac['header_type'].' '.$info_aac_header['profile_text'];
|
rlm@3
|
229
|
rlm@3
|
230 return true;
|
rlm@3
|
231 }
|
rlm@3
|
232
|
rlm@3
|
233
|
rlm@3
|
234
|
rlm@3
|
235 public static function Bin2String($bin_string) {
|
rlm@3
|
236 // return 'hi' for input of '0110100001101001'
|
rlm@3
|
237 $string = '';
|
rlm@3
|
238 $bin_string_reversed = strrev($bin_string);
|
rlm@3
|
239 for ($i = 0; $i < strlen($bin_string_reversed); $i += 8) {
|
rlm@3
|
240 $string = chr(bindec(strrev(substr($bin_string_reversed, $i, 8)))).$string;
|
rlm@3
|
241 }
|
rlm@3
|
242 return $string;
|
rlm@3
|
243 }
|
rlm@3
|
244
|
rlm@3
|
245
|
rlm@3
|
246
|
rlm@3
|
247 public static function AACsampleRateLookup($samplerate_id) {
|
rlm@3
|
248
|
rlm@3
|
249 static $lookup = array (
|
rlm@3
|
250 0 => 96000,
|
rlm@3
|
251 1 => 88200,
|
rlm@3
|
252 2 => 64000,
|
rlm@3
|
253 3 => 48000,
|
rlm@3
|
254 4 => 44100,
|
rlm@3
|
255 5 => 32000,
|
rlm@3
|
256 6 => 24000,
|
rlm@3
|
257 7 => 22050,
|
rlm@3
|
258 8 => 16000,
|
rlm@3
|
259 9 => 12000,
|
rlm@3
|
260 10 => 11025,
|
rlm@3
|
261 11 => 8000,
|
rlm@3
|
262 12 => 0,
|
rlm@3
|
263 13 => 0,
|
rlm@3
|
264 14 => 0,
|
rlm@3
|
265 15 => 0
|
rlm@3
|
266 );
|
rlm@3
|
267 return (isset($lookup[$samplerate_id]) ? $lookup[$samplerate_id] : 'invalid');
|
rlm@3
|
268 }
|
rlm@3
|
269
|
rlm@3
|
270
|
rlm@3
|
271
|
rlm@3
|
272 public static function AACprofileLookup($profile_id, $mpeg_version) {
|
rlm@3
|
273
|
rlm@3
|
274 static $lookup = array (
|
rlm@3
|
275 2 => array (
|
rlm@3
|
276 0 => 'Main profile',
|
rlm@3
|
277 1 => 'Low Complexity profile (LC)',
|
rlm@3
|
278 2 => 'Scalable Sample Rate profile (SSR)',
|
rlm@3
|
279 3 => '(reserved)'
|
rlm@3
|
280 ),
|
rlm@3
|
281 4 => array (
|
rlm@3
|
282 0 => 'AAC_MAIN',
|
rlm@3
|
283 1 => 'AAC_LC',
|
rlm@3
|
284 2 => 'AAC_SSR',
|
rlm@3
|
285 3 => 'AAC_LTP'
|
rlm@3
|
286 )
|
rlm@3
|
287 );
|
rlm@3
|
288 return (isset($lookup[$mpeg_version][$profile_id]) ? $lookup[$mpeg_version][$profile_id] : 'invalid');
|
rlm@3
|
289 }
|
rlm@3
|
290
|
rlm@3
|
291
|
rlm@3
|
292
|
rlm@3
|
293 public static function AACchannelCountCalculate($program_configs) {
|
rlm@3
|
294
|
rlm@3
|
295 $channels = 0;
|
rlm@3
|
296
|
rlm@3
|
297 foreach (array ('front', 'side', 'back') as $placement) {
|
rlm@3
|
298 for ($i = 0; $i < $program_configs['num_'.$placement.'_channel_elements']; $i++) {
|
rlm@3
|
299
|
rlm@3
|
300 // Each element is channel pair (CPE = Channel Pair Element)
|
rlm@3
|
301 $channels += 1 + ($program_configs[$placement.'_element_is_cpe'][$i] ? 1 : 0);
|
rlm@3
|
302 }
|
rlm@3
|
303 }
|
rlm@3
|
304
|
rlm@3
|
305 return $channels + $program_configs['num_lfe_channel_elements'];
|
rlm@3
|
306 }
|
rlm@3
|
307
|
rlm@3
|
308 }
|
rlm@3
|
309
|
rlm@3
|
310
|
rlm@3
|
311 ?> |