diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-24 01:25:21 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-03-18 12:00:39 +0100 |
commit | b5716df4cb2837bbbc42195cf1aefcf03e21d6a6 (patch) | |
tree | 130cd712e2e00893b6df9959a375a8d9523a1aca /lib/rbcodec/metadata/rm.c | |
parent | 24bd9d5393dbe39a5c6194877bc00ede669b1d5d (diff) | |
download | rockbox-b5716df4cb2837bbbc42195cf1aefcf03e21d6a6.tar.gz rockbox-b5716df4cb2837bbbc42195cf1aefcf03e21d6a6.zip |
Build librbcodec with DSP and metadata.
All associated files are moved to /lib/rbcodec.
Change-Id: I572ddd2b8a996aae1e98c081d06b1ed356dce222
Diffstat (limited to 'lib/rbcodec/metadata/rm.c')
-rw-r--r-- | lib/rbcodec/metadata/rm.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/lib/rbcodec/metadata/rm.c b/lib/rbcodec/metadata/rm.c new file mode 100644 index 0000000000..27f541cb25 --- /dev/null +++ b/lib/rbcodec/metadata/rm.c | |||
@@ -0,0 +1,464 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2009 Mohamed Tarek | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include <codecs/librm/rm.h> | ||
28 | #include "system.h" | ||
29 | #include "metadata.h" | ||
30 | #include "metadata_common.h" | ||
31 | #include "metadata_parsers.h" | ||
32 | #include "logf.h" | ||
33 | |||
34 | /* Uncomment the following line for debugging */ | ||
35 | //#define DEBUG_RM | ||
36 | #ifndef DEBUG_RM | ||
37 | #undef DEBUGF | ||
38 | #define DEBUGF(...) | ||
39 | #endif | ||
40 | |||
41 | #define ID3V1_OFFSET -128 | ||
42 | #define METADATA_FOOTER_OFFSET -140 | ||
43 | |||
44 | static inline void print_cook_extradata(RMContext *rmctx) { | ||
45 | |||
46 | DEBUGF(" cook_version = 0x%08lx\n", rm_get_uint32be(rmctx->codec_extradata)); | ||
47 | DEBUGF(" samples_per_frame_per_channel = %d\n", rm_get_uint16be(&rmctx->codec_extradata[4])); | ||
48 | DEBUGF(" number_of_subbands_in_freq_domain = %d\n", rm_get_uint16be(&rmctx->codec_extradata[6])); | ||
49 | if(rmctx->extradata_size == 16) { | ||
50 | DEBUGF(" joint_stereo_subband_start = %d\n",rm_get_uint16be(&rmctx->codec_extradata[12])); | ||
51 | DEBUGF(" joint_stereo_vlc_bits = %d\n", rm_get_uint16be(&rmctx->codec_extradata[14])); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | |||
56 | struct real_object_t | ||
57 | { | ||
58 | uint32_t fourcc; | ||
59 | uint32_t size; | ||
60 | uint16_t version; | ||
61 | }; | ||
62 | |||
63 | static int real_read_object_header(int fd, struct real_object_t* obj) | ||
64 | { | ||
65 | int n; | ||
66 | |||
67 | if ((n = read_uint32be(fd, &obj->fourcc)) <= 0) | ||
68 | return n; | ||
69 | if ((n = read_uint32be(fd, &obj->size)) <= 0) | ||
70 | return n; | ||
71 | if ((n = read_uint16be(fd, &obj->version)) <= 0) | ||
72 | return n; | ||
73 | |||
74 | return 1; | ||
75 | } | ||
76 | |||
77 | #if (defined(SIMULATOR) && defined(DEBUG_RM)) | ||
78 | static char* fourcc2str(uint32_t f) | ||
79 | { | ||
80 | static char res[5]; | ||
81 | |||
82 | res[0] = (f & 0xff000000) >> 24; | ||
83 | res[1] = (f & 0xff0000) >> 16; | ||
84 | res[2] = (f & 0xff00) >> 8; | ||
85 | res[3] = (f & 0xff); | ||
86 | res[4] = 0; | ||
87 | |||
88 | return res; | ||
89 | } | ||
90 | #endif | ||
91 | |||
92 | static inline int real_read_audio_stream_info(int fd, RMContext *rmctx) | ||
93 | { | ||
94 | int skipped = 0; | ||
95 | uint32_t version; | ||
96 | struct real_object_t obj; | ||
97 | #ifdef SIMULATOR | ||
98 | uint32_t header_size; | ||
99 | uint16_t flavor; | ||
100 | uint32_t coded_framesize; | ||
101 | uint8_t interleaver_id_length; | ||
102 | uint8_t fourcc_length; | ||
103 | #endif | ||
104 | uint32_t interleaver_id; | ||
105 | uint32_t fourcc = 0; | ||
106 | |||
107 | memset(&obj,0,sizeof(obj)); | ||
108 | read_uint32be(fd, &version); | ||
109 | skipped += 4; | ||
110 | |||
111 | DEBUGF(" version=0x%04lx\n",((version >> 16) & 0xff)); | ||
112 | if (((version >> 16) & 0xff) == 3) { | ||
113 | /* Very old version */ | ||
114 | } else { | ||
115 | #ifdef SIMULATOR | ||
116 | real_read_object_header(fd, &obj); | ||
117 | read_uint32be(fd, &header_size); | ||
118 | /* obj.size will be filled with an unknown value, replaced with header_size */ | ||
119 | DEBUGF(" Object: %s, size: %ld bytes, version: 0x%04x\n",fourcc2str(obj.fourcc),header_size,obj.version); | ||
120 | |||
121 | read_uint16be(fd, &flavor); | ||
122 | read_uint32be(fd, &coded_framesize); | ||
123 | #else | ||
124 | lseek(fd, 20, SEEK_CUR); | ||
125 | #endif | ||
126 | lseek(fd, 12, SEEK_CUR); /* unknown */ | ||
127 | read_uint16be(fd, &rmctx->sub_packet_h); | ||
128 | read_uint16be(fd, &rmctx->block_align); | ||
129 | read_uint16be(fd, &rmctx->sub_packet_size); | ||
130 | lseek(fd, 2, SEEK_CUR); /* unknown */ | ||
131 | skipped += 40; | ||
132 | if (((version >> 16) & 0xff) == 5) | ||
133 | { | ||
134 | lseek(fd, 6, SEEK_CUR); /* unknown */ | ||
135 | skipped += 6; | ||
136 | } | ||
137 | read_uint16be(fd, &rmctx->sample_rate); | ||
138 | lseek(fd, 4, SEEK_CUR); /* unknown */ | ||
139 | read_uint16be(fd, &rmctx->nb_channels); | ||
140 | skipped += 8; | ||
141 | if (((version >> 16) & 0xff) == 4) | ||
142 | { | ||
143 | #ifdef SIMULATOR | ||
144 | read_uint8(fd, &interleaver_id_length); | ||
145 | read_uint32be(fd, &interleaver_id); | ||
146 | read_uint8(fd, &fourcc_length); | ||
147 | #else | ||
148 | lseek(fd, 6, SEEK_CUR); | ||
149 | #endif | ||
150 | read_uint32be(fd, &fourcc); | ||
151 | skipped += 10; | ||
152 | } | ||
153 | if (((version >> 16) & 0xff) == 5) | ||
154 | { | ||
155 | read_uint32be(fd, &interleaver_id); | ||
156 | read_uint32be(fd, &fourcc); | ||
157 | skipped += 8; | ||
158 | } | ||
159 | lseek(fd, 3, SEEK_CUR); /* unknown */ | ||
160 | skipped += 3; | ||
161 | if (((version >> 16) & 0xff) == 5) | ||
162 | { | ||
163 | lseek(fd, 1, SEEK_CUR); /* unknown */ | ||
164 | skipped += 1; | ||
165 | } | ||
166 | |||
167 | switch(fourcc) { | ||
168 | case FOURCC('c','o','o','k'): | ||
169 | rmctx->codec_type = CODEC_COOK; | ||
170 | read_uint32be(fd, &rmctx->extradata_size); | ||
171 | skipped += 4; | ||
172 | read(fd, rmctx->codec_extradata, rmctx->extradata_size); | ||
173 | skipped += rmctx->extradata_size; | ||
174 | break; | ||
175 | |||
176 | case FOURCC('r','a','a','c'): | ||
177 | case FOURCC('r','a','c','p'): | ||
178 | rmctx->codec_type = CODEC_AAC; | ||
179 | read_uint32be(fd, &rmctx->extradata_size); | ||
180 | skipped += 4; | ||
181 | read(fd, rmctx->codec_extradata, rmctx->extradata_size); | ||
182 | skipped += rmctx->extradata_size; | ||
183 | break; | ||
184 | |||
185 | case FOURCC('d','n','e','t'): | ||
186 | rmctx->codec_type = CODEC_AC3; | ||
187 | break; | ||
188 | |||
189 | case FOURCC('a','t','r','c'): | ||
190 | rmctx->codec_type = CODEC_ATRAC; | ||
191 | read_uint32be(fd, &rmctx->extradata_size); | ||
192 | skipped += 4; | ||
193 | read(fd, rmctx->codec_extradata, rmctx->extradata_size); | ||
194 | skipped += rmctx->extradata_size; | ||
195 | break; | ||
196 | |||
197 | default: /* Not a supported codec */ | ||
198 | return -1; | ||
199 | } | ||
200 | |||
201 | DEBUGF(" flavor = %d\n",flavor); | ||
202 | DEBUGF(" coded_frame_size = %ld\n",coded_framesize); | ||
203 | DEBUGF(" sub_packet_h = %d\n",rmctx->sub_packet_h); | ||
204 | DEBUGF(" frame_size = %d\n",rmctx->block_align); | ||
205 | DEBUGF(" sub_packet_size = %d\n",rmctx->sub_packet_size); | ||
206 | DEBUGF(" sample_rate= %d\n",rmctx->sample_rate); | ||
207 | DEBUGF(" channels= %d\n",rmctx->nb_channels); | ||
208 | DEBUGF(" fourcc = %s\n",fourcc2str(fourcc)); | ||
209 | DEBUGF(" codec_extra_data_length = %ld\n",rmctx->extradata_size); | ||
210 | DEBUGF(" codec_extradata :\n"); | ||
211 | if(rmctx->codec_type == CODEC_COOK) { | ||
212 | DEBUGF(" cook_extradata :\n"); | ||
213 | print_cook_extradata(rmctx); | ||
214 | } | ||
215 | |||
216 | } | ||
217 | |||
218 | return skipped; | ||
219 | } | ||
220 | |||
221 | static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3) | ||
222 | { | ||
223 | struct real_object_t obj; | ||
224 | int res; | ||
225 | int skipped; | ||
226 | off_t curpos __attribute__((unused)); | ||
227 | uint8_t len; /* Holds a string_length, which is then passed to read_string() */ | ||
228 | |||
229 | #ifdef SIMULATOR | ||
230 | uint32_t avg_bitrate = 0; | ||
231 | uint32_t max_packet_size; | ||
232 | uint32_t avg_packet_size; | ||
233 | uint32_t packet_count; | ||
234 | uint32_t duration; | ||
235 | uint32_t preroll; | ||
236 | uint32_t index_offset; | ||
237 | uint16_t stream_id; | ||
238 | uint32_t start_time; | ||
239 | uint32_t codec_data_size; | ||
240 | #endif | ||
241 | uint32_t v; | ||
242 | uint32_t max_bitrate; | ||
243 | uint16_t num_streams; | ||
244 | uint32_t next_data_off; | ||
245 | uint8_t header_end; | ||
246 | |||
247 | memset(&obj,0,sizeof(obj)); | ||
248 | curpos = lseek(fd, 0, SEEK_SET); | ||
249 | res = real_read_object_header(fd, &obj); | ||
250 | |||
251 | if (obj.fourcc == FOURCC('.','r','a',0xfd)) | ||
252 | { | ||
253 | /* Very old .ra format - not yet supported */ | ||
254 | return -1; | ||
255 | } | ||
256 | else if (obj.fourcc != FOURCC('.','R','M','F')) | ||
257 | { | ||
258 | return -1; | ||
259 | } | ||
260 | |||
261 | lseek(fd, 8, SEEK_CUR); /* unknown */ | ||
262 | |||
263 | DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); | ||
264 | |||
265 | res = real_read_object_header(fd, &obj); | ||
266 | header_end = 0; | ||
267 | while(res) | ||
268 | { | ||
269 | DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); | ||
270 | skipped = 10; | ||
271 | if(obj.fourcc == FOURCC('I','N','D','X')) | ||
272 | break; | ||
273 | switch (obj.fourcc) | ||
274 | { | ||
275 | case FOURCC('P','R','O','P'): /* File properties */ | ||
276 | read_uint32be(fd, &max_bitrate); | ||
277 | read_uint32be(fd, &rmctx->bit_rate); /*avg bitrate*/ | ||
278 | #ifdef SIMULATOR | ||
279 | read_uint32be(fd, &max_packet_size); | ||
280 | read_uint32be(fd, &avg_packet_size); | ||
281 | read_uint32be(fd, &packet_count); | ||
282 | #else | ||
283 | lseek(fd, 3*sizeof(uint32_t), SEEK_CUR); | ||
284 | #endif | ||
285 | read_uint32be(fd, &rmctx->duration); | ||
286 | #ifdef SIMULATOR | ||
287 | read_uint32be(fd, &preroll); | ||
288 | read_uint32be(fd, &index_offset); | ||
289 | #else | ||
290 | lseek(fd, 2*sizeof(uint32_t), SEEK_CUR); | ||
291 | #endif | ||
292 | read_uint32be(fd, &rmctx->data_offset); | ||
293 | read_uint16be(fd, &num_streams); | ||
294 | read_uint16be(fd, &rmctx->flags); | ||
295 | skipped += 40; | ||
296 | |||
297 | DEBUGF(" max_bitrate = %ld\n",max_bitrate); | ||
298 | DEBUGF(" avg_bitrate = %ld\n",rmctx->bit_rate); | ||
299 | DEBUGF(" max_packet_size = %ld\n",max_packet_size); | ||
300 | DEBUGF(" avg_packet_size = %ld\n",avg_packet_size); | ||
301 | DEBUGF(" packet_count = %ld\n",packet_count); | ||
302 | DEBUGF(" duration = %ld\n",rmctx->duration); | ||
303 | DEBUGF(" preroll = %ld\n",preroll); | ||
304 | DEBUGF(" index_offset = %ld\n",index_offset); | ||
305 | DEBUGF(" data_offset = %ld\n",rmctx->data_offset); | ||
306 | DEBUGF(" num_streams = %d\n",num_streams); | ||
307 | DEBUGF(" flags=0x%04x\n",rmctx->flags); | ||
308 | break; | ||
309 | |||
310 | case FOURCC('C','O','N','T'): | ||
311 | /* Four strings - Title, Author, Copyright, Comment */ | ||
312 | read_uint8(fd,&len); | ||
313 | skipped += (int)read_string(fd, id3->id3v1buf[0], sizeof(id3->id3v1buf[0]), '\0', len); | ||
314 | read_uint8(fd,&len); | ||
315 | skipped += (int)read_string(fd, id3->id3v1buf[1], sizeof(id3->id3v1buf[1]), '\0', len); | ||
316 | read_uint8(fd,&len); | ||
317 | skipped += (int)read_string(fd, id3->id3v1buf[2], sizeof(id3->id3v1buf[2]), '\0', len); | ||
318 | read_uint8(fd,&len); | ||
319 | skipped += (int)read_string(fd, id3->id3v1buf[3], sizeof(id3->id3v1buf[3]), '\0', len); | ||
320 | skipped += 4; | ||
321 | |||
322 | DEBUGF(" title=\"%s\"\n",id3->id3v1buf[0]); | ||
323 | DEBUGF(" author=\"%s\"\n",id3->id3v1buf[1]); | ||
324 | DEBUGF(" copyright=\"%s\"\n",id3->id3v1buf[2]); | ||
325 | DEBUGF(" comment=\"%s\"\n",id3->id3v1buf[3]); | ||
326 | break; | ||
327 | |||
328 | case FOURCC('M','D','P','R'): /* Media properties */ | ||
329 | #ifdef SIMULATOR | ||
330 | read_uint16be(fd,&stream_id); | ||
331 | read_uint32be(fd,&max_bitrate); | ||
332 | read_uint32be(fd,&avg_bitrate); | ||
333 | read_uint32be(fd,&max_packet_size); | ||
334 | read_uint32be(fd,&avg_packet_size); | ||
335 | read_uint32be(fd,&start_time); | ||
336 | read_uint32be(fd,&preroll); | ||
337 | read_uint32be(fd,&duration); | ||
338 | #else | ||
339 | lseek(fd, 30, SEEK_CUR); | ||
340 | #endif | ||
341 | skipped += 30; | ||
342 | read_uint8(fd,&len); | ||
343 | skipped += 1; | ||
344 | lseek(fd, len, SEEK_CUR); /* desc */ | ||
345 | skipped += len; | ||
346 | read_uint8(fd,&len); | ||
347 | skipped += 1; | ||
348 | #ifdef SIMULATOR | ||
349 | lseek(fd, len, SEEK_CUR); /* mimetype */ | ||
350 | read_uint32be(fd,&codec_data_size); | ||
351 | #else | ||
352 | lseek(fd, len + 4, SEEK_CUR); | ||
353 | #endif | ||
354 | skipped += len + 4; | ||
355 | read_uint32be(fd,&v); | ||
356 | skipped += 4; | ||
357 | |||
358 | DEBUGF(" stream_id = 0x%04x\n",stream_id); | ||
359 | DEBUGF(" max_bitrate = %ld\n",max_bitrate); | ||
360 | DEBUGF(" avg_bitrate = %ld\n",avg_bitrate); | ||
361 | DEBUGF(" max_packet_size = %ld\n",max_packet_size); | ||
362 | DEBUGF(" avg_packet_size = %ld\n",avg_packet_size); | ||
363 | DEBUGF(" start_time = %ld\n",start_time); | ||
364 | DEBUGF(" preroll = %ld\n",preroll); | ||
365 | DEBUGF(" duration = %ld\n",duration); | ||
366 | DEBUGF(" codec_data_size = %ld\n",codec_data_size); | ||
367 | DEBUGF(" v=\"%s\"\n", fourcc2str(v)); | ||
368 | |||
369 | if (v == FOURCC('.','r','a',0xfd)) | ||
370 | { | ||
371 | int temp; | ||
372 | temp= real_read_audio_stream_info(fd, rmctx); | ||
373 | if(temp < 0) | ||
374 | return -1; | ||
375 | else | ||
376 | skipped += temp; | ||
377 | } | ||
378 | else if (v == FOURCC('L','S','D',':')) | ||
379 | { | ||
380 | DEBUGF("Real audio lossless is not supported."); | ||
381 | return -1; | ||
382 | } | ||
383 | else | ||
384 | { | ||
385 | /* We shall not abort with -1 here. *.rm file often seem | ||
386 | * to have a second media properties header that contains | ||
387 | * other metadata. */ | ||
388 | DEBUGF("Unknown header signature :\"%s\"\n", fourcc2str(v)); | ||
389 | } | ||
390 | |||
391 | |||
392 | break; | ||
393 | |||
394 | case FOURCC('D','A','T','A'): | ||
395 | read_uint32be(fd,&rmctx->nb_packets); | ||
396 | skipped += 4; | ||
397 | read_uint32be(fd,&next_data_off); | ||
398 | skipped += 4; | ||
399 | |||
400 | /*** | ||
401 | * nb_packets correction : | ||
402 | * in some samples, number of packets may not exactly form | ||
403 | * an integer number of scrambling units. This is corrected | ||
404 | * by constructing a partially filled unit out of the few | ||
405 | * remaining samples at the end of decoding. | ||
406 | ***/ | ||
407 | if(rmctx->nb_packets % rmctx->sub_packet_h) | ||
408 | rmctx->nb_packets += rmctx->sub_packet_h - (rmctx->nb_packets % rmctx->sub_packet_h); | ||
409 | |||
410 | DEBUGF(" data_nb_packets = %ld\n",rmctx->nb_packets); | ||
411 | DEBUGF(" next DATA offset = %ld\n",next_data_off); | ||
412 | header_end = 1; | ||
413 | break; | ||
414 | } | ||
415 | if(header_end) break; | ||
416 | curpos = lseek(fd, obj.size - skipped, SEEK_CUR); | ||
417 | res = real_read_object_header(fd, &obj); | ||
418 | } | ||
419 | |||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | |||
425 | bool get_rm_metadata(int fd, struct mp3entry* id3) | ||
426 | { | ||
427 | RMContext *rmctx = (RMContext*) (( (intptr_t)id3->id3v2buf + 3 ) &~ 3); | ||
428 | memset(rmctx,0,sizeof(RMContext)); | ||
429 | if(rm_parse_header(fd, rmctx, id3) < 0) | ||
430 | return false; | ||
431 | |||
432 | if (!setid3v1title(fd, id3)) { | ||
433 | /* file has no id3v1 tags, use the tags from CONT chunk */ | ||
434 | id3->title = id3->id3v1buf[0]; | ||
435 | id3->artist = id3->id3v1buf[1]; | ||
436 | id3->comment= id3->id3v1buf[3]; | ||
437 | } | ||
438 | |||
439 | switch(rmctx->codec_type) | ||
440 | { | ||
441 | case CODEC_COOK: | ||
442 | /* Already set, do nothing */ | ||
443 | break; | ||
444 | case CODEC_AAC: | ||
445 | id3->codectype = AFMT_RM_AAC; | ||
446 | break; | ||
447 | |||
448 | case CODEC_AC3: | ||
449 | id3->codectype = AFMT_RM_AC3; | ||
450 | break; | ||
451 | |||
452 | case CODEC_ATRAC: | ||
453 | id3->codectype = AFMT_RM_ATRAC3; | ||
454 | break; | ||
455 | } | ||
456 | |||
457 | id3->channels = rmctx->nb_channels; | ||
458 | id3->extradata_size = rmctx->extradata_size; | ||
459 | id3->bitrate = rmctx->bit_rate / 1000; | ||
460 | id3->frequency = rmctx->sample_rate; | ||
461 | id3->length = rmctx->duration; | ||
462 | id3->filesize = filesize(fd); | ||
463 | return true; | ||
464 | } | ||