summaryrefslogtreecommitdiff
path: root/apps/metadata
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2007-07-03 09:25:36 +0000
committerDave Chapman <dave@dchapman.com>2007-07-03 09:25:36 +0000
commitc72824786a0e8c68921ebb9b72f02a2e80aaee17 (patch)
treeadf8dac26d074ee3620df4ab482ff108561ead01 /apps/metadata
parent2ca895bae7a25ea8ef7f295b4e8ab01ff75a4914 (diff)
downloadrockbox-c72824786a0e8c68921ebb9b72f02a2e80aaee17.tar.gz
rockbox-c72824786a0e8c68921ebb9b72f02a2e80aaee17.zip
Initial, work-in-progress, version of a WMA codec using Michael Giacomelli's fixed-point and malloc-less WMA decoder (based on the ffmpeg WMA decoder from early 2006, and also building on the work started by Paul Jones). The codec itself and the ASF parsing code were written by me, inspired by the ASF parser in libasf. Current performance is around 400% realtime on gigabeat, 100% realtime on PP and 20% realtime on Coldfire.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13769 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/metadata')
-rw-r--r--apps/metadata/asf.c418
-rw-r--r--apps/metadata/metadata_common.c35
-rw-r--r--apps/metadata/metadata_common.h8
-rw-r--r--apps/metadata/metadata_parsers.h1
4 files changed, 461 insertions, 1 deletions
diff --git a/apps/metadata/asf.c b/apps/metadata/asf.c
new file mode 100644
index 0000000000..85d30f50fd
--- /dev/null
+++ b/apps/metadata/asf.c
@@ -0,0 +1,418 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * $Id$
10 *
11 * Copyright (C) 2007 Dave Chapman
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20#include <stdio.h>
21#include <string.h>
22#include <stdlib.h>
23#include <ctype.h>
24#include <inttypes.h>
25
26#include "id3.h"
27#include "debug.h"
28#include "rbunicode.h"
29#include "metadata_common.h"
30#include <codecs/libwma/asf.h>
31
32static asf_waveformatex_t wfx;
33
34/* TODO: Just read the GUIDs into a 16-byte array, and use memcmp to compare */
35struct guid_s {
36 uint32_t v1;
37 uint16_t v2;
38 uint16_t v3;
39 uint8_t v4[8];
40};
41typedef struct guid_s guid_t;
42
43struct asf_object_s {
44 guid_t guid;
45 uint64_t size;
46 uint64_t datalen;
47};
48typedef struct asf_object_s asf_object_t;
49
50enum asf_error_e {
51 ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */
52 ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */
53 ASF_ERROR_EOF = -3, /* unexpected end of file */
54 ASF_ERROR_IO = -4, /* error reading or writing to file */
55 ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */
56 ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */
57 ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */
58 ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */
59 ASF_ERROR_SEEKABLE = -9, /* file not seekable */
60 ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */
61};
62
63static const guid_t asf_guid_null =
64{0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
65
66/* top level object guids */
67
68static const guid_t asf_guid_header =
69{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
70
71static const guid_t asf_guid_data =
72{0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
73
74static const guid_t asf_guid_index =
75{0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
76
77/* header level object guids */
78
79static const guid_t asf_guid_file_properties =
80{0x8cabdca1, 0xa947, 0x11cf, {0x8E, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
81
82static const guid_t asf_guid_stream_properties =
83{0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
84
85static const guid_t asf_guid_content_description =
86{0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
87
88static const guid_t asf_guid_extended_content_description =
89{0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}};
90
91/* stream type guids */
92
93static const guid_t asf_guid_stream_type_audio =
94{0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
95
96static int asf_guid_match(const guid_t *guid1, const guid_t *guid2)
97{
98 if((guid1->v1 != guid2->v1) ||
99 (guid1->v2 != guid2->v2) ||
100 (guid1->v3 != guid2->v3) ||
101 (memcmp(guid1->v4, guid2->v4, 8))) {
102 return 0;
103 }
104
105 return 1;
106}
107
108/* Read the 16 byte GUID from a file */
109static void asf_readGUID(int fd, guid_t* guid)
110{
111 read_uint32le(fd, &guid->v1);
112 read_uint16le(fd, &guid->v2);
113 read_uint16le(fd, &guid->v3);
114 read(fd, guid->v4, 8);
115}
116
117static void asf_read_object_header(asf_object_t *obj, int fd)
118{
119 asf_readGUID(fd, &obj->guid);
120 read_uint64le(fd, &obj->size);
121 obj->datalen = 0;
122}
123
124static int asf_parse_header(int fd, struct mp3entry* id3)
125{
126 asf_object_t current;
127 asf_object_t header;
128 uint64_t datalen;
129 int i;
130 int fileprop = 0;
131 uint64_t play_duration;
132 uint64_t tmp64;
133 uint32_t tmp32;
134 uint16_t tmp16;
135 uint8_t tmp8;
136 uint16_t flags;
137 uint32_t subobjects;
138 uint8_t utf16buf[512];
139 uint8_t utf8buf[512];
140
141 asf_read_object_header((asf_object_t *) &header, fd);
142
143 DEBUGF("header.size=%d\n",(int)header.size);
144 if (header.size < 30) {
145 /* invalid size for header object */
146 return ASF_ERROR_OBJECT_SIZE;
147 }
148
149 read_uint32le(fd, &subobjects);
150
151 /* Two reserved bytes - do we need to read them? */
152 lseek(fd, 2, SEEK_CUR);
153
154 DEBUGF("Read header - size=%d, subobjects=%lu\n",(int)header.size, subobjects);
155
156 if (subobjects > 0) {
157 header.datalen = header.size - 30;
158
159 /* TODO: Check that we have datalen bytes left in the file */
160 datalen = header.datalen;
161
162 for (i=0; i<(int)subobjects; i++) {
163 DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen);
164 if (datalen < 24) {
165 DEBUGF("not enough data for reading object\n");
166 break;
167 }
168
169 asf_read_object_header(&current, fd);
170
171 if (current.size > datalen || current.size < 24) {
172 DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen);
173 break;
174 }
175
176 if (asf_guid_match(&current.guid, &asf_guid_file_properties)) {
177 if (current.size < 104)
178 return ASF_ERROR_OBJECT_SIZE;
179
180 if (fileprop) {
181 /* multiple file properties objects not allowed */
182 return ASF_ERROR_INVALID_OBJECT;
183 }
184
185 fileprop = 1;
186 /* All we want is the play duration - uint64_t at offset 40 */
187 lseek(fd, 40, SEEK_CUR);
188
189 read_uint64le(fd, &play_duration);
190 id3->length = play_duration / 10000;
191
192 DEBUGF("****** length = %lums\n", id3->length);
193
194 /* Read the packet size - uint32_t at offset 68 */
195 lseek(fd, 20, SEEK_CUR);
196 read_uint32le(fd, &wfx.packet_size);
197
198 /* Skip bytes remaining in object */
199 lseek(fd, current.size - 24 - 72, SEEK_CUR);
200 } else if (asf_guid_match(&current.guid, &asf_guid_stream_properties)) {
201 guid_t guid;
202 uint32_t propdatalen;
203
204 if (current.size < 78)
205 return ASF_ERROR_OBJECT_SIZE;
206
207#if 0
208 asf_byteio_getGUID(&guid, current->data);
209 datalen = asf_byteio_getDWLE(current->data + 40);
210 flags = asf_byteio_getWLE(current->data + 48);
211#endif
212
213 asf_readGUID(fd, &guid);
214
215 lseek(fd, 24, SEEK_CUR);
216 read_uint32le(fd, &propdatalen);
217 lseek(fd, 4, SEEK_CUR);
218 read_uint16le(fd, &flags);
219
220 if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) {
221 DEBUGF("Found stream properties for non audio stream, skipping\n");
222 lseek(fd,current.size - 24 - 50,SEEK_CUR);
223 } else {
224 lseek(fd, 4, SEEK_CUR);
225 DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f);
226
227 /* TODO: Check codec_id and find the lowest numbered audio stream in the file */
228 wfx.audiostream = flags&0x7f;
229
230 if (propdatalen < 18) {
231 return ASF_ERROR_INVALID_LENGTH;
232 }
233
234#if 0
235 if (asf_byteio_getWLE(data + 16) > datalen - 16) {
236 return ASF_ERROR_INVALID_LENGTH;
237 }
238#endif
239 read_uint16le(fd, &wfx.codec_id);
240 read_uint16le(fd, &wfx.channels);
241 read_uint32le(fd, &wfx.rate);
242 read_uint32le(fd, &wfx.bitrate);
243 wfx.bitrate *= 8;
244 read_uint16le(fd, &wfx.blockalign);
245 read_uint16le(fd, &wfx.bitspersample);
246 read_uint16le(fd, &wfx.datalen);
247
248 /* Round bitrate to the nearest kbit */
249 id3->bitrate = (wfx.bitrate + 500) / 1000;
250 id3->frequency = wfx.rate;
251
252 if (wfx.codec_id == ASF_CODEC_ID_WMAV1) {
253 read(fd, wfx.data, 4);
254 lseek(fd,current.size - 24 - 72 - 4,SEEK_CUR);
255 /* A hack - copy the wfx struct to the MP3 TOC field in the id3 struct */
256 memcpy(id3->toc, &wfx, sizeof(wfx));
257 } else if (wfx.codec_id == ASF_CODEC_ID_WMAV2) {
258 read(fd, wfx.data, 6);
259 lseek(fd,current.size - 24 - 72 - 6,SEEK_CUR);
260 /* A hack - copy the wfx struct to the MP3 TOC field in the id3 struct */
261 memcpy(id3->toc, &wfx, sizeof(wfx));
262 } else {
263 lseek(fd,current.size - 24 - 72,SEEK_CUR);
264 }
265
266 }
267 } else if (asf_guid_match(&current.guid, &asf_guid_content_description)) {
268 /* Object contains five 16-bit string lengths, followed by the five strings:
269 title, artist, copyright, description, rating
270 */
271 uint16_t strlength[5];
272 int i;
273
274 DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24));
275
276 /* Read the 5 string lengths - number of bytes included trailing zero */
277 for (i=0; i<5; i++) {
278 read_uint16le(fd, &strlength[i]);
279 DEBUGF("strlength = %u\n",strlength[i]);
280 }
281
282 for (i=0; i<5 ; i++) {
283 if (strlength[i] > 0) {
284 read(fd, utf16buf, strlength[i]);
285 utf16LEdecode(utf16buf, utf8buf, strlength[i]);
286 DEBUGF("TAG %d = %s\n",i,utf8buf);
287 }
288 }
289 } else if (asf_guid_match(&current.guid, &asf_guid_extended_content_description)) {
290 uint16_t count;
291 int i;
292 int bytesleft = current.size - 24;
293 DEBUGF("Found GUID_EXTENDED_CONTENT_DESCRIPTION\n");
294
295 read_uint16le(fd, &count);
296 bytesleft -= 2;
297 DEBUGF("extended metadata count = %u\n",count);
298
299 for (i=0; i < count; i++) {
300 uint16_t length, type;
301
302 read_uint16le(fd, &length);
303 read(fd, utf16buf, length);
304 utf16LEdecode(utf16buf, utf8buf, length);
305 DEBUGF("Key=\"%s\" ",utf8buf);
306 bytesleft -= 2 + length;
307
308 read_uint16le(fd, &type);
309 read_uint16le(fd, &length);
310 switch(type)
311 {
312 case 0: /* String */
313 read(fd, utf16buf, length);
314 utf16LEdecode(utf16buf, utf8buf, length);
315 DEBUGF("Value=\"%s\"\n",utf8buf);
316 break;
317
318 case 1: /* Hex string */
319 DEBUGF("Value=NOT YET IMPLEMENTED (HEX STRING)\n");
320 lseek(fd,length,SEEK_CUR);
321 break;
322
323 case 2: /* Bool */
324 read(fd, &tmp8, 1);
325 DEBUGF("Value=%s\n",(tmp8 ? "TRUE" : "FALSE"));
326 lseek(fd,length - 1,SEEK_CUR);
327 break;
328
329 case 3: /* 32-bit int */
330 read_uint32le(fd, &tmp32);
331 DEBUGF("Value=%lu\n",tmp32);
332 lseek(fd,length - 4,SEEK_CUR);
333 break;
334
335 case 4: /* 64-bit int */
336 read_uint64le(fd, &tmp64);
337 DEBUGF("Value=%llu\n",tmp64);
338 lseek(fd,length - 8,SEEK_CUR);
339 break;
340
341 case 5: /* 16-bit int */
342 read_uint16le(fd, &tmp16);
343 DEBUGF("Value=%u\n",tmp16);
344 lseek(fd,length - 2,SEEK_CUR);
345 break;
346
347 default:
348 lseek(fd,length,SEEK_CUR);
349 break;
350 }
351 bytesleft -= 4 + length;
352 }
353
354 lseek(fd, bytesleft, SEEK_CUR);
355 } else {
356 DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24));
357 lseek(fd,current.size - 24,SEEK_CUR);
358 }
359
360 DEBUGF("Parsed object - size = %d\n",(int)current.size);
361 datalen -= current.size;
362 }
363
364 if (i != (int)subobjects || datalen != 0) {
365 DEBUGF("header data doesn't match given subobject count\n");
366 return ASF_ERROR_INVALID_VALUE;
367 }
368
369 DEBUGF("%d subobjects read successfully\n", i);
370 }
371
372#if 0
373 tmp = asf_parse_header_validate(file, &header);
374 if (tmp < 0) {
375 /* header read ok but doesn't validate correctly */
376 return tmp;
377 }
378#endif
379
380 DEBUGF("header validated correctly\n");
381
382 return 0;
383}
384
385bool get_asf_metadata(int fd, struct mp3entry* id3)
386{
387 int res;
388 asf_object_t obj;
389
390 wfx.audiostream = -1;
391
392 res = asf_parse_header(fd, id3);
393
394 if (res < 0) {
395 DEBUGF("ASF: parsing error - %d\n",res);
396 return false;
397 }
398
399 if (wfx.audiostream == -1) {
400 DEBUGF("ASF: No WMA streams found\n");
401 return false;
402 }
403
404 asf_read_object_header(&obj, fd);
405
406 if (!asf_guid_match(&obj.guid, &asf_guid_data)) {
407 DEBUGF("ASF: No data object found\n");
408 return false;
409 }
410
411 /* Store the current file position - no need to parse the header
412 again in the codec. The +26 skips the rest of the data object
413 header.
414 */
415 id3->first_frame_offset = lseek(fd, 0, SEEK_CUR) + 26;
416
417 return true;
418}
diff --git a/apps/metadata/metadata_common.c b/apps/metadata/metadata_common.c
index 685b32a25e..d81d9f71d3 100644
--- a/apps/metadata/metadata_common.c
+++ b/apps/metadata/metadata_common.c
@@ -94,8 +94,8 @@ long read_string(int fd, char* buf, long buf_size, int eos, long size)
94 return read_bytes; 94 return read_bytes;
95} 95}
96 96
97/* Read an unsigned 32-bit integer from a big-endian file. */
98#ifdef ROCKBOX_LITTLE_ENDIAN 97#ifdef ROCKBOX_LITTLE_ENDIAN
98/* Read an unsigned 32-bit integer from a big-endian file. */
99int read_uint32be(int fd, unsigned int* buf) 99int read_uint32be(int fd, unsigned int* buf)
100{ 100{
101 size_t n; 101 size_t n;
@@ -104,6 +104,39 @@ int read_uint32be(int fd, unsigned int* buf)
104 *buf = betoh32(*buf); 104 *buf = betoh32(*buf);
105 return n; 105 return n;
106} 106}
107#else
108/* Read unsigned integers from a little-endian file. */
109int read_uint16le(int fd, uint16_t* buf)
110{
111 size_t n;
112
113 n = read(fd, (char*) buf, 2);
114 *buf = letoh16(*buf);
115 return n;
116}
117int read_uint32le(int fd, uint32_t* buf)
118{
119 size_t n;
120
121 n = read(fd, (char*) buf, 4);
122 *buf = letoh32(*buf);
123 return n;
124}
125int read_uint64le(int fd, uint64_t* buf)
126{
127 size_t n;
128 uint8_t data[8];
129 int i;
130
131 n = read(fd, data, 8);
132
133 for (i=7, *buf=0; i>=0; i--) {
134 *buf <<= 8;
135 *buf |= data[i];
136 }
137
138 return n;
139}
107#endif 140#endif
108 141
109/* Read an unaligned 32-bit little endian long from buffer. */ 142/* Read an unaligned 32-bit little endian long from buffer. */
diff --git a/apps/metadata/metadata_common.h b/apps/metadata/metadata_common.h
index 8041dad5e7..272a3913ab 100644
--- a/apps/metadata/metadata_common.h
+++ b/apps/metadata/metadata_common.h
@@ -35,11 +35,19 @@ bool read_vorbis_tags(int fd, struct mp3entry *id3,
35 35
36bool skip_id3v2(int fd, struct mp3entry *id3); 36bool skip_id3v2(int fd, struct mp3entry *id3);
37long read_string(int fd, char* buf, long buf_size, int eos, long size); 37long read_string(int fd, char* buf, long buf_size, int eos, long size);
38
38#ifdef ROCKBOX_BIG_ENDIAN 39#ifdef ROCKBOX_BIG_ENDIAN
39#define read_uint32be(fd,buf) read((fd), (buf), 4) 40#define read_uint32be(fd,buf) read((fd), (buf), 4)
41int read_uint16le(int fd, uint16_t* buf);
42int read_uint32le(int fd, uint32_t* buf);
43int read_uint64le(int fd, uint64_t* buf);
40#else 44#else
41int read_uint32be(int fd, unsigned int* buf); 45int read_uint32be(int fd, unsigned int* buf);
46#define read_uint16le(fd,buf) read((fd), (buf), 2)
47#define read_uint32le(fd,buf) read((fd), (buf), 4)
48#define read_uint64le(fd,buf) read((fd), (buf), 8)
42#endif 49#endif
50
43unsigned long get_long_le(void* buf); 51unsigned long get_long_le(void* buf);
44unsigned short get_short_le(void* buf); 52unsigned short get_short_le(void* buf);
45unsigned long get_long_be(void* buf); 53unsigned long get_long_be(void* buf);
diff --git a/apps/metadata/metadata_parsers.h b/apps/metadata/metadata_parsers.h
index f52ce69dd2..5fc674d31b 100644
--- a/apps/metadata/metadata_parsers.h
+++ b/apps/metadata/metadata_parsers.h
@@ -29,3 +29,4 @@ bool get_spc_metadata(int fd, struct mp3entry* id3);
29bool get_speex_metadata(int fd, struct mp3entry* id3); 29bool get_speex_metadata(int fd, struct mp3entry* id3);
30bool get_vorbis_metadata(int fd, struct mp3entry* id3); 30bool get_vorbis_metadata(int fd, struct mp3entry* id3);
31bool get_wave_metadata(int fd, struct mp3entry* id3); 31bool get_wave_metadata(int fd, struct mp3entry* id3);
32bool get_asf_metadata(int fd, struct mp3entry* id3);