diff options
author | Mohamed Tarek <mt@rockbox.org> | 2009-07-06 22:40:45 +0000 |
---|---|---|
committer | Mohamed Tarek <mt@rockbox.org> | 2009-07-06 22:40:45 +0000 |
commit | e184ef1027ba8f41aca65dbae2af05662b23c722 (patch) | |
tree | b7b108acf795d52e0c4f9f841906b02d1df3f773 /apps/metadata | |
parent | 03fe562a95a2b4fe4b3e316d3877140c3b4c822f (diff) | |
download | rockbox-e184ef1027ba8f41aca65dbae2af05662b23c722.tar.gz rockbox-e184ef1027ba8f41aca65dbae2af05662b23c722.zip |
Adding support for rm playback. Only cook codec is supported for now and no seeking.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21695 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/metadata')
-rw-r--r-- | apps/metadata/metadata_parsers.h | 1 | ||||
-rw-r--r-- | apps/metadata/rm.c | 420 |
2 files changed, 421 insertions, 0 deletions
diff --git a/apps/metadata/metadata_parsers.h b/apps/metadata/metadata_parsers.h index 1521f1301d..760d9a0da3 100644 --- a/apps/metadata/metadata_parsers.h +++ b/apps/metadata/metadata_parsers.h | |||
@@ -39,3 +39,4 @@ bool get_wavpack_metadata(int fd, struct mp3entry* id3); | |||
39 | bool get_a52_metadata(int fd, struct mp3entry* id3); | 39 | bool get_a52_metadata(int fd, struct mp3entry* id3); |
40 | bool get_asf_metadata(int fd, struct mp3entry* id3); | 40 | bool get_asf_metadata(int fd, struct mp3entry* id3); |
41 | bool get_asap_metadata(int fd, struct mp3entry* id3); | 41 | bool get_asap_metadata(int fd, struct mp3entry* id3); |
42 | bool get_rm_metadata(int fd, struct mp3entry* id3); | ||
diff --git a/apps/metadata/rm.c b/apps/metadata/rm.c new file mode 100644 index 0000000000..4fefdeb00d --- /dev/null +++ b/apps/metadata/rm.c | |||
@@ -0,0 +1,420 @@ | |||
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 | //#define DEBUG_RM | ||
35 | #ifndef DEBUG_RM | ||
36 | #undef DEBUGF | ||
37 | #define DEBUGF(...) | ||
38 | #endif | ||
39 | |||
40 | static inline int read_cook_extradata(int fd, RMContext *rmctx) { | ||
41 | read_uint32be(fd, &rmctx->cook_version); | ||
42 | read_uint16be(fd, &rmctx->samples_pf_pc); | ||
43 | read_uint16be(fd, &rmctx->nb_subbands); | ||
44 | if(rmctx->extradata_size == 16) { | ||
45 | lseek(fd, sizeof(uint32_t), SEEK_CUR); /* reserved */ | ||
46 | read_uint16be(fd, &rmctx->js_subband_start); | ||
47 | read_uint16be(fd, &rmctx->js_vlc_bits); | ||
48 | } | ||
49 | return rmctx->extradata_size; /* for 'skipped' */ | ||
50 | } | ||
51 | |||
52 | static inline void print_cook_extradata(RMContext *rmctx) { | ||
53 | |||
54 | DEBUGF(" cook_version = 0x%08lx\n", rmctx->cook_version); | ||
55 | DEBUGF(" samples_per_frame_per_channel = %d\n", rmctx->samples_pf_pc); | ||
56 | DEBUGF(" number_of_subbands_in_freq_domain = %d\n", rmctx->nb_subbands); | ||
57 | if(rmctx->extradata_size == 16) { | ||
58 | DEBUGF(" joint_stereo_subband_start = %d\n",rmctx->js_subband_start); | ||
59 | DEBUGF(" joint_stereo_vlc_bits = %d\n", rmctx->js_vlc_bits); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | |||
64 | struct real_object_t | ||
65 | { | ||
66 | uint32_t fourcc; | ||
67 | uint32_t size; | ||
68 | uint16_t version; | ||
69 | }; | ||
70 | |||
71 | #define FOURCC(a,b,c,d) (((a)<<24) | ((b) << 16) | ((c) << 8) | (d)) | ||
72 | |||
73 | static int real_read_object_header(int fd, struct real_object_t* obj) | ||
74 | { | ||
75 | int n; | ||
76 | |||
77 | if ((n = read_uint32be(fd, &obj->fourcc)) <= 0) return n; | ||
78 | if ((n = read_uint32be(fd, &obj->size)) <= 0) return n; | ||
79 | if ((n = read_uint16be(fd, &obj->version)) <= 0) return n; | ||
80 | |||
81 | return 1; | ||
82 | } | ||
83 | |||
84 | #if (defined(SIMULATOR) && defined(DEBUG_RM)) | ||
85 | static char* fourcc2str(uint32_t f) | ||
86 | { | ||
87 | static char res[5]; | ||
88 | |||
89 | res[0] = (f & 0xff000000) >> 24; | ||
90 | res[1] = (f & 0xff0000) >> 16; | ||
91 | res[2] = (f & 0xff00) >> 8; | ||
92 | res[3] = (f & 0xff); | ||
93 | res[4] = 0; | ||
94 | |||
95 | return res; | ||
96 | } | ||
97 | #endif | ||
98 | |||
99 | static inline int real_read_audio_stream_info(int fd, RMContext *rmctx) | ||
100 | { | ||
101 | int skipped = 0; | ||
102 | uint32_t version; | ||
103 | struct real_object_t obj; | ||
104 | #ifdef SIMULATOR | ||
105 | uint32_t header_size; | ||
106 | uint16_t flavor; | ||
107 | uint32_t coded_framesize; | ||
108 | uint8_t interleaver_id_length; | ||
109 | uint8_t fourcc_length; | ||
110 | #endif | ||
111 | uint32_t interleaver_id; | ||
112 | uint32_t fourcc = 0; | ||
113 | |||
114 | memset(&obj,0,sizeof(obj)); | ||
115 | read_uint32be(fd, &version); | ||
116 | skipped += 4; | ||
117 | |||
118 | DEBUGF(" version=0x%04lx\n",((version >> 16) & 0xff)); | ||
119 | if (((version >> 16) & 0xff) == 3) { | ||
120 | /* Very old version */ | ||
121 | } else { | ||
122 | #ifdef SIMULATOR | ||
123 | real_read_object_header(fd, &obj); | ||
124 | read_uint32be(fd, &header_size); | ||
125 | /* obj.size will be filled with an unknown value, replaced with header_size */ | ||
126 | DEBUGF(" Object: %s, size: %ld bytes, version: 0x%04x\n",fourcc2str(obj.fourcc),header_size,obj.version); | ||
127 | |||
128 | read_uint16be(fd, &flavor); | ||
129 | read_uint32be(fd, &coded_framesize); | ||
130 | #else | ||
131 | lseek(fd, 20, SEEK_CUR); | ||
132 | #endif | ||
133 | lseek(fd, 12, SEEK_CUR); /* unknown */ | ||
134 | read_uint16be(fd, &rmctx->sub_packet_h); | ||
135 | read_uint16be(fd, &rmctx->block_align); | ||
136 | read_uint16be(fd, &rmctx->sub_packet_size); | ||
137 | lseek(fd, 2, SEEK_CUR); /* unknown */ | ||
138 | skipped += 40; | ||
139 | if (((version >> 16) & 0xff) == 5) | ||
140 | { | ||
141 | lseek(fd, 6, SEEK_CUR); /* unknown */ | ||
142 | skipped += 6; | ||
143 | } | ||
144 | read_uint16be(fd, &rmctx->sample_rate); | ||
145 | lseek(fd, 4, SEEK_CUR); /* unknown */ | ||
146 | read_uint16be(fd, &rmctx->nb_channels); | ||
147 | skipped += 8; | ||
148 | if (((version >> 16) & 0xff) == 4) | ||
149 | { | ||
150 | #ifdef SIMULATOR | ||
151 | read_uint8(fd, &interleaver_id_length); | ||
152 | read_uint32be(fd, &interleaver_id); | ||
153 | read_uint8(fd, &fourcc_length); | ||
154 | #else | ||
155 | lseek(fd, 6, SEEK_CUR); | ||
156 | #endif | ||
157 | read_uint32be(fd, &fourcc); | ||
158 | skipped += 10; | ||
159 | } | ||
160 | if (((version >> 16) & 0xff) == 5) | ||
161 | { | ||
162 | read_uint32be(fd, &interleaver_id); | ||
163 | read_uint32be(fd, &fourcc); | ||
164 | skipped += 8; | ||
165 | } | ||
166 | lseek(fd, 3, SEEK_CUR); /* unknown */ | ||
167 | skipped += 3; | ||
168 | if (((version >> 16) & 0xff) == 5) | ||
169 | { | ||
170 | lseek(fd, 1, SEEK_CUR); /* unknown */ | ||
171 | skipped += 1; | ||
172 | } | ||
173 | |||
174 | read_uint32be(fd, &rmctx->extradata_size); | ||
175 | skipped += 4; | ||
176 | /*if(!strncmp(fourcc2str(fourcc),"cook",4)){ | ||
177 | skipped += read_cook_extradata(fd, rmctx); | ||
178 | rmctx->codec_type = cook; | ||
179 | }*/ | ||
180 | switch(fourcc) { | ||
181 | case FOURCC('c','o','o','k'): | ||
182 | skipped += read_cook_extradata(fd, rmctx); | ||
183 | rmctx->codec_type = cook; | ||
184 | break; | ||
185 | |||
186 | default: /* Not a supported codec */ | ||
187 | return -1; | ||
188 | } | ||
189 | |||
190 | DEBUGF(" flavor = %d\n",flavor); | ||
191 | DEBUGF(" coded_frame_size = %ld\n",coded_framesize); | ||
192 | DEBUGF(" sub_packet_h = %d\n",rmctx->sub_packet_h); | ||
193 | DEBUGF(" frame_size = %d\n",rmctx->block_align); | ||
194 | DEBUGF(" sub_packet_size = %d\n",rmctx->sub_packet_size); | ||
195 | DEBUGF(" sample_rate= %d\n",rmctx->sample_rate); | ||
196 | DEBUGF(" channels= %d\n",rmctx->nb_channels); | ||
197 | DEBUGF(" fourcc = %s\n",fourcc2str(fourcc)); | ||
198 | DEBUGF(" codec_extra_data_length = %ld\n",rmctx->extradata_size); | ||
199 | DEBUGF(" codec_extradata :\n"); | ||
200 | print_cook_extradata(rmctx); | ||
201 | |||
202 | } | ||
203 | |||
204 | return skipped; | ||
205 | } | ||
206 | |||
207 | static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3) | ||
208 | { | ||
209 | struct real_object_t obj; | ||
210 | int res; | ||
211 | int skipped; | ||
212 | off_t curpos; | ||
213 | uint8_t len; /* Holds a string_length, which is then passed to read_string() */ | ||
214 | |||
215 | #ifdef SIMULATOR | ||
216 | uint32_t avg_bitrate = 0; | ||
217 | uint32_t max_packet_size; | ||
218 | uint32_t avg_packet_size; | ||
219 | uint32_t packet_count; | ||
220 | uint32_t duration; | ||
221 | uint32_t preroll; | ||
222 | uint32_t index_offset; | ||
223 | uint16_t stream_id; | ||
224 | uint32_t start_time; | ||
225 | uint32_t codec_data_size; | ||
226 | #endif | ||
227 | uint32_t v; | ||
228 | uint32_t max_bitrate; | ||
229 | uint16_t num_streams; | ||
230 | uint32_t next_data_off; | ||
231 | uint8_t header_end; | ||
232 | |||
233 | memset(&obj,0,sizeof(obj)); | ||
234 | curpos = lseek(fd, 0, SEEK_SET); | ||
235 | res = real_read_object_header(fd, &obj); | ||
236 | |||
237 | if (obj.fourcc == FOURCC('.','r','a',0xfd)) | ||
238 | { | ||
239 | /* Very old .ra format - not yet supported */ | ||
240 | return -1; | ||
241 | } | ||
242 | else if (obj.fourcc != FOURCC('.','R','M','F')) | ||
243 | { | ||
244 | return -1; | ||
245 | } | ||
246 | |||
247 | lseek(fd, 8, SEEK_CUR); /* unknown */ | ||
248 | |||
249 | DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); | ||
250 | |||
251 | res = real_read_object_header(fd, &obj); | ||
252 | header_end = 0; | ||
253 | while(res) | ||
254 | { | ||
255 | DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); | ||
256 | skipped = 10; | ||
257 | if(obj.fourcc == FOURCC('I','N','D','X')) | ||
258 | break; | ||
259 | switch (obj.fourcc) | ||
260 | { | ||
261 | case FOURCC('P','R','O','P'): /* File properties */ | ||
262 | read_uint32be(fd, &max_bitrate); | ||
263 | read_uint32be(fd, &rmctx->bit_rate); /*avg bitrate*/ | ||
264 | #ifdef SIMULATOR | ||
265 | read_uint32be(fd, &max_packet_size); | ||
266 | read_uint32be(fd, &avg_packet_size); | ||
267 | read_uint32be(fd, &packet_count); | ||
268 | #else | ||
269 | lseek(fd, 3*sizeof(uint32_t), SEEK_CUR); | ||
270 | #endif | ||
271 | read_uint32be(fd, &rmctx->duration); | ||
272 | #ifdef SIMULATOR | ||
273 | read_uint32be(fd, &preroll); | ||
274 | read_uint32be(fd, &index_offset); | ||
275 | #else | ||
276 | lseek(fd, 2*sizeof(uint32_t), SEEK_CUR); | ||
277 | #endif | ||
278 | read_uint32be(fd, &rmctx->data_offset); | ||
279 | read_uint16be(fd, &num_streams); | ||
280 | read_uint16be(fd, &rmctx->flags); | ||
281 | skipped += 40; | ||
282 | |||
283 | DEBUGF(" max_bitrate = %ld\n",max_bitrate); | ||
284 | DEBUGF(" avg_bitrate = %ld\n",rmctx->bit_Rate); | ||
285 | DEBUGF(" max_packet_size = %ld\n",max_packet_size); | ||
286 | DEBUGF(" avg_packet_size = %ld\n",avg_packet_size); | ||
287 | DEBUGF(" packet_count = %ld\n",packet_count); | ||
288 | DEBUGF(" duration = %ld\n",rmctx->duration); | ||
289 | DEBUGF(" preroll = %ld\n",preroll); | ||
290 | DEBUGF(" index_offset = %ld\n",index_offset); | ||
291 | DEBUGF(" data_offset = %ld\n",rmctx->data_offset); | ||
292 | DEBUGF(" num_streams = %d\n",num_streams); | ||
293 | DEBUGF(" flags=0x%04x\n",rmctx->flags); | ||
294 | break; | ||
295 | |||
296 | case FOURCC('C','O','N','T'): | ||
297 | /* Four strings - Title, Author, Copyright, Comment */ | ||
298 | read_uint8(fd,&len); | ||
299 | skipped += (int)read_string(fd, id3->id3v1buf[0], sizeof(id3->id3v1buf[0]), '\0', len); | ||
300 | read_uint8(fd,&len); | ||
301 | skipped += (int)read_string(fd, id3->id3v1buf[1], sizeof(id3->id3v1buf[1]), '\0', len); | ||
302 | read_uint8(fd,&len); | ||
303 | skipped += (int)read_string(fd, id3->id3v1buf[2], sizeof(id3->id3v1buf[2]), '\0', len); | ||
304 | read_uint8(fd,&len); | ||
305 | skipped += (int)read_string(fd, id3->id3v1buf[3], sizeof(id3->id3v1buf[3]), '\0', len); | ||
306 | skipped += 4; | ||
307 | |||
308 | DEBUGF(" title=\"%s\"\n",id3->id3v1buf[0]); | ||
309 | DEBUGF(" author=\"%s\"\n",id3->id3v1buf[1]); | ||
310 | DEBUGF(" copyright=\"%s\"\n",id3->id3v1buf[2]); | ||
311 | DEBUGF(" comment=\"%s\"\n",id3->id3v1buf[3]); | ||
312 | break; | ||
313 | |||
314 | case FOURCC('M','D','P','R'): /* Media properties */ | ||
315 | #ifdef SIMULATOR | ||
316 | read_uint16be(fd,&stream_id); | ||
317 | read_uint32be(fd,&max_bitrate); | ||
318 | read_uint32be(fd,&avg_bitrate); | ||
319 | read_uint32be(fd,&max_packet_size); | ||
320 | read_uint32be(fd,&avg_packet_size); | ||
321 | read_uint32be(fd,&start_time); | ||
322 | read_uint32be(fd,&preroll); | ||
323 | read_uint32be(fd,&duration); | ||
324 | #else | ||
325 | lseek(fd, 30, SEEK_CUR); | ||
326 | #endif | ||
327 | skipped += 30; | ||
328 | read_uint8(fd,&len); | ||
329 | skipped += 1; | ||
330 | lseek(fd, len, SEEK_CUR); /* desc */ | ||
331 | skipped += len; | ||
332 | read_uint8(fd,&len); | ||
333 | skipped += 1; | ||
334 | #ifdef SIMULATOR | ||
335 | lseek(fd, len, SEEK_CUR); /* mimetype */ | ||
336 | read_uint32be(fd,&codec_data_size); | ||
337 | #else | ||
338 | lseek(fd, len + 4, SEEK_CUR); | ||
339 | #endif | ||
340 | skipped += len + 4; | ||
341 | //From ffmpeg: codec_pos = url_ftell(pb); | ||
342 | read_uint32be(fd,&v); | ||
343 | skipped += 4; | ||
344 | |||
345 | DEBUGF(" stream_id = 0x%04x\n",stream_id); | ||
346 | DEBUGF(" max_bitrate = %ld\n",max_bitrate); | ||
347 | DEBUGF(" avg_bitrate = %ld\n",avg_bitrate); | ||
348 | DEBUGF(" max_packet_size = %ld\n",max_packet_size); | ||
349 | DEBUGF(" avg_packet_size = %ld\n",avg_packet_size); | ||
350 | DEBUGF(" start_time = %ld\n",start_time); | ||
351 | DEBUGF(" preroll = %ld\n",preroll); | ||
352 | DEBUGF(" duration = %ld\n",duration); | ||
353 | DEBUGF(" codec_data_size = %ld\n",codec_data_size); | ||
354 | DEBUGF(" v=\"%s\"\n", fourcc2str(v)); | ||
355 | |||
356 | if (v == FOURCC('.','r','a',0xfd)) | ||
357 | { | ||
358 | skipped += real_read_audio_stream_info(fd, rmctx); | ||
359 | if(skipped < 0) | ||
360 | return -1; | ||
361 | } | ||
362 | |||
363 | break; | ||
364 | |||
365 | case FOURCC('D','A','T','A'): | ||
366 | read_uint32be(fd,&rmctx->nb_packets); | ||
367 | skipped += 4; | ||
368 | read_uint32be(fd,&next_data_off); | ||
369 | skipped += 4; | ||
370 | |||
371 | /*** | ||
372 | * nb_packets correction : | ||
373 | * in some samples, number of packets may not exactly form | ||
374 | * an integer number of scrambling units. This is corrected | ||
375 | * by constructing a partially filled unit out of the few | ||
376 | * remaining samples at the end of decoding. | ||
377 | ***/ | ||
378 | if(rmctx->nb_packets % rmctx->sub_packet_h) | ||
379 | rmctx->nb_packets += rmctx->sub_packet_h - (rmctx->nb_packets % rmctx->sub_packet_h); | ||
380 | |||
381 | DEBUGF(" data_nb_packets = %ld\n",rmctx->nb_packets); | ||
382 | DEBUGF(" next DATA offset = %ld\n",next_data_off); | ||
383 | header_end = 1; | ||
384 | break; | ||
385 | } | ||
386 | if(header_end) break; | ||
387 | curpos = lseek(fd, obj.size - skipped, SEEK_CUR); | ||
388 | res = real_read_object_header(fd, &obj); | ||
389 | } | ||
390 | |||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | |||
396 | bool get_rm_metadata(int fd, struct mp3entry* id3) | ||
397 | { | ||
398 | RMContext *rmctx = (RMContext*)id3->id3v2buf; | ||
399 | memset(rmctx,0,sizeof(RMContext)); | ||
400 | if(rm_parse_header(fd, rmctx, id3) < 0) | ||
401 | return false; | ||
402 | |||
403 | /* Copy tags */ | ||
404 | id3->title = id3->id3v1buf[0]; | ||
405 | id3->artist = id3->id3v1buf[1]; | ||
406 | id3->comment = id3->id3v1buf[3]; | ||
407 | |||
408 | /*switch(rmctx->codec_type) | ||
409 | { | ||
410 | case cook: | ||
411 | id3->codectype = AFMT_COOK; | ||
412 | break; | ||
413 | }*/ | ||
414 | |||
415 | id3->bitrate = rmctx->bit_rate / 1000; | ||
416 | id3->frequency = rmctx->sample_rate; | ||
417 | id3->length = rmctx->duration; | ||
418 | id3->filesize = filesize(fd); | ||
419 | return true; | ||
420 | } | ||