Add incomplete support for files using noise coding but not LSP. In practice, this...
[Rockbox.git] / apps / metadata / asf.c
blobe2553463c03303881e555825d23e6a3edf9d1c1c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
9 * $Id$
11 * Copyright (C) 2007 Dave Chapman
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.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <ctype.h>
24 #include <atoi.h>
25 #include <inttypes.h>
27 #include "id3.h"
28 #include "debug.h"
29 #include "rbunicode.h"
30 #include "metadata_common.h"
31 #include "system.h"
32 #include <codecs/libwma/asf.h>
34 /* TODO: Just read the GUIDs into a 16-byte array, and use memcmp to compare */
35 struct guid_s {
36 uint32_t v1;
37 uint16_t v2;
38 uint16_t v3;
39 uint8_t v4[8];
41 typedef struct guid_s guid_t;
43 struct asf_object_s {
44 guid_t guid;
45 uint64_t size;
46 uint64_t datalen;
48 typedef struct asf_object_s asf_object_t;
50 enum 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 */
63 static const guid_t asf_guid_null =
64 {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
66 /* top level object guids */
68 static const guid_t asf_guid_header =
69 {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
71 static const guid_t asf_guid_data =
72 {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
74 static const guid_t asf_guid_index =
75 {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
77 /* header level object guids */
79 static const guid_t asf_guid_file_properties =
80 {0x8cabdca1, 0xa947, 0x11cf, {0x8E, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
82 static const guid_t asf_guid_stream_properties =
83 {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
85 static const guid_t asf_guid_content_description =
86 {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
88 static const guid_t asf_guid_extended_content_description =
89 {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}};
91 /* stream type guids */
93 static const guid_t asf_guid_stream_type_audio =
94 {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
96 static int asf_guid_match(const guid_t *guid1, const guid_t *guid2)
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;
105 return 1;
108 /* Read the 16 byte GUID from a file */
109 static void asf_readGUID(int fd, guid_t* guid)
111 read_uint32le(fd, &guid->v1);
112 read_uint16le(fd, &guid->v2);
113 read_uint16le(fd, &guid->v3);
114 read(fd, guid->v4, 8);
117 static void asf_read_object_header(asf_object_t *obj, int fd)
119 asf_readGUID(fd, &obj->guid);
120 read_uint64le(fd, &obj->size);
121 obj->datalen = 0;
124 /* Parse an integer from the extended content object - we always
125 convert to an int, regardless of native format.
127 static int asf_intdecode(int fd, int type, int length)
129 uint16_t tmp16;
130 uint32_t tmp32;
131 uint64_t tmp64;
133 if (type==3) {
134 read_uint32le(fd, &tmp32);
135 lseek(fd,length - 4,SEEK_CUR);
136 return (int)tmp32;
137 } else if (type==4) {
138 read_uint64le(fd, &tmp64);
139 lseek(fd,length - 8,SEEK_CUR);
140 return (int)tmp64;
141 } else if (type == 5) {
142 read_uint16le(fd, &tmp16);
143 lseek(fd,length - 2,SEEK_CUR);
144 return (int)tmp16;
147 return 0;
150 /* Decode a LE utf16 string from a disk buffer into a fixed-sized
151 utf8 buffer.
154 static void asf_utf16LEdecode(int fd,
155 uint16_t utf16bytes,
156 unsigned char **utf8,
157 int* utf8bytes
160 unsigned long ucs;
161 int n;
162 unsigned char utf16buf[256];
163 unsigned char* utf16 = utf16buf;
164 unsigned char* newutf8;
166 n = read(fd, utf16buf, MIN(sizeof(utf16buf), utf16bytes));
167 utf16bytes -= n;
169 while (n > 0) {
170 /* Check for a surrogate pair */
171 if (utf16[1] >= 0xD8 && utf16[1] < 0xE0) {
172 if (n < 4) {
173 /* Run out of utf16 bytes, read some more */
174 utf16buf[0] = utf16[0];
175 utf16buf[1] = utf16[1];
177 n = read(fd, utf16buf + 2, MIN(sizeof(utf16buf)-2, utf16bytes));
178 utf16 = utf16buf;
179 utf16bytes -= n;
180 n += 2;
183 if (n < 4) {
184 /* Truncated utf16 string, abort */
185 break;
187 ucs = 0x10000 + ((utf16[0] << 10) | ((utf16[1] - 0xD8) << 18)
188 | utf16[2] | ((utf16[3] - 0xDC) << 8));
189 utf16 += 4;
190 n -= 4;
191 } else {
192 ucs = (utf16[0] | (utf16[1] << 8));
193 utf16 += 2;
194 n -= 2;
197 if (*utf8bytes > 6) {
198 newutf8 = utf8encode(ucs, *utf8);
199 *utf8bytes -= (newutf8 - *utf8);
200 *utf8 += (newutf8 - *utf8);
203 /* We have run out of utf16 bytes, read more if available */
204 if ((n == 0) && (utf16bytes > 0)) {
205 n = read(fd, utf16buf, MIN(sizeof(utf16buf), utf16bytes));
206 utf16 = utf16buf;
207 utf16bytes -= n;
211 *utf8[0] = 0;
212 --*utf8bytes;
214 if (utf16bytes > 0) {
215 /* Skip any remaining bytes */
216 lseek(fd, utf16bytes, SEEK_CUR);
218 return;
221 static int asf_parse_header(int fd, struct mp3entry* id3,
222 asf_waveformatex_t* wfx)
224 asf_object_t current;
225 asf_object_t header;
226 uint64_t datalen;
227 int i;
228 int fileprop = 0;
229 uint64_t play_duration;
230 uint16_t flags;
231 uint32_t subobjects;
232 uint8_t utf8buf[512];
233 int id3buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf);
234 unsigned char* id3buf = (unsigned char*)id3->id3v2buf;
236 asf_read_object_header((asf_object_t *) &header, fd);
238 //DEBUGF("header.size=%d\n",(int)header.size);
239 if (header.size < 30) {
240 /* invalid size for header object */
241 return ASF_ERROR_OBJECT_SIZE;
244 read_uint32le(fd, &subobjects);
246 /* Two reserved bytes - do we need to read them? */
247 lseek(fd, 2, SEEK_CUR);
249 //DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects);
251 if (subobjects > 0) {
252 header.datalen = header.size - 30;
254 /* TODO: Check that we have datalen bytes left in the file */
255 datalen = header.datalen;
257 for (i=0; i<(int)subobjects; i++) {
258 //DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen);
259 if (datalen < 24) {
260 //DEBUGF("not enough data for reading object\n");
261 break;
264 asf_read_object_header(&current, fd);
266 if (current.size > datalen || current.size < 24) {
267 //DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen);
268 break;
271 if (asf_guid_match(&current.guid, &asf_guid_file_properties)) {
272 if (current.size < 104)
273 return ASF_ERROR_OBJECT_SIZE;
275 if (fileprop) {
276 /* multiple file properties objects not allowed */
277 return ASF_ERROR_INVALID_OBJECT;
280 fileprop = 1;
281 /* All we want is the play duration - uint64_t at offset 40 */
282 lseek(fd, 40, SEEK_CUR);
284 read_uint64le(fd, &play_duration);
285 id3->length = play_duration / 10000;
287 //DEBUGF("****** length = %lums\n", id3->length);
289 /* Read the packet size - uint32_t at offset 68 */
290 lseek(fd, 20, SEEK_CUR);
291 read_uint32le(fd, &wfx->packet_size);
293 /* Skip bytes remaining in object */
294 lseek(fd, current.size - 24 - 72, SEEK_CUR);
295 } else if (asf_guid_match(&current.guid, &asf_guid_stream_properties)) {
296 guid_t guid;
297 uint32_t propdatalen;
299 if (current.size < 78)
300 return ASF_ERROR_OBJECT_SIZE;
302 #if 0
303 asf_byteio_getGUID(&guid, current->data);
304 datalen = asf_byteio_getDWLE(current->data + 40);
305 flags = asf_byteio_getWLE(current->data + 48);
306 #endif
308 asf_readGUID(fd, &guid);
310 lseek(fd, 24, SEEK_CUR);
311 read_uint32le(fd, &propdatalen);
312 lseek(fd, 4, SEEK_CUR);
313 read_uint16le(fd, &flags);
315 if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) {
316 //DEBUGF("Found stream properties for non audio stream, skipping\n");
317 lseek(fd,current.size - 24 - 50,SEEK_CUR);
318 } else if (wfx->audiostream == -1) {
319 lseek(fd, 4, SEEK_CUR);
320 //DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f);
322 if (propdatalen < 18) {
323 return ASF_ERROR_INVALID_LENGTH;
326 #if 0
327 if (asf_byteio_getWLE(data + 16) > datalen - 16) {
328 return ASF_ERROR_INVALID_LENGTH;
330 #endif
331 read_uint16le(fd, &wfx->codec_id);
332 read_uint16le(fd, &wfx->channels);
333 read_uint32le(fd, &wfx->rate);
334 read_uint32le(fd, &wfx->bitrate);
335 wfx->bitrate *= 8;
336 read_uint16le(fd, &wfx->blockalign);
337 read_uint16le(fd, &wfx->bitspersample);
338 read_uint16le(fd, &wfx->datalen);
340 /* Round bitrate to the nearest kbit */
341 id3->bitrate = (wfx->bitrate + 500) / 1000;
342 id3->frequency = wfx->rate;
344 if (wfx->codec_id == ASF_CODEC_ID_WMAV1) {
345 read(fd, wfx->data, 4);
346 lseek(fd,current.size - 24 - 72 - 4,SEEK_CUR);
347 wfx->audiostream = flags&0x7f;
348 } else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) {
349 read(fd, wfx->data, 6);
350 lseek(fd,current.size - 24 - 72 - 6,SEEK_CUR);
351 wfx->audiostream = flags&0x7f;
352 } else {
353 lseek(fd,current.size - 24 - 72,SEEK_CUR);
357 } else if (asf_guid_match(&current.guid, &asf_guid_content_description)) {
358 /* Object contains five 16-bit string lengths, followed by the five strings:
359 title, artist, copyright, description, rating
361 uint16_t strlength[5];
362 int i;
364 //DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24));
366 /* Read the 5 string lengths - number of bytes included trailing zero */
367 for (i=0; i<5; i++) {
368 read_uint16le(fd, &strlength[i]);
369 //DEBUGF("strlength = %u\n",strlength[i]);
372 if (strlength[0] > 0) { /* 0 - Title */
373 id3->title = id3buf;
374 asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining);
377 if (strlength[1] > 0) { /* 1 - Artist */
378 id3->artist = id3buf;
379 asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining);
382 lseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */
384 if (strlength[3] > 0) { /* 3 - description */
385 id3->comment = id3buf;
386 asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining);
389 lseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */
390 } else if (asf_guid_match(&current.guid, &asf_guid_extended_content_description)) {
391 uint16_t count;
392 int i;
393 int bytesleft = current.size - 24;
394 //DEBUGF("Found GUID_EXTENDED_CONTENT_DESCRIPTION\n");
396 read_uint16le(fd, &count);
397 bytesleft -= 2;
398 //DEBUGF("extended metadata count = %u\n",count);
400 for (i=0; i < count; i++) {
401 uint16_t length, type;
402 unsigned char* utf8 = utf8buf;
403 int utf8length = 512;
405 read_uint16le(fd, &length);
406 asf_utf16LEdecode(fd, length, &utf8, &utf8length);
407 bytesleft -= 2 + length;
409 read_uint16le(fd, &type);
410 read_uint16le(fd, &length);
412 if (!strcmp("WM/TrackNumber",utf8buf)) {
413 if (type == 0) {
414 id3->track_string = id3buf;
415 asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
416 id3->tracknum = atoi(id3->track_string);
417 } else if ((type >=2) && (type <= 5)) {
418 id3->tracknum = asf_intdecode(fd, type, length);
419 } else {
420 lseek(fd, length, SEEK_CUR);
422 } else if ((!strcmp("WM/Genre",utf8buf)) && (type == 0)) {
423 id3->genre_string = id3buf;
424 asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
425 } else if ((!strcmp("WM/AlbumTitle",utf8buf)) && (type == 0)) {
426 id3->album = id3buf;
427 asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
428 } else if ((!strcmp("WM/AlbumArtist",utf8buf)) && (type == 0)) {
429 id3->albumartist = id3buf;
430 asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
431 } else if ((!strcmp("WM/Composer",utf8buf)) && (type == 0)) {
432 id3->composer = id3buf;
433 asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
434 } else if (!strcmp("WM/Year",utf8buf)) {
435 if (type == 0) {
436 id3->year_string = id3buf;
437 asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
438 id3->year = atoi(id3->year_string);
439 } else if ((type >=2) && (type <= 5)) {
440 id3->year = asf_intdecode(fd, type, length);
441 } else {
442 lseek(fd, length, SEEK_CUR);
444 } else {
445 lseek(fd, length, SEEK_CUR);
447 bytesleft -= 4 + length;
450 lseek(fd, bytesleft, SEEK_CUR);
451 } else {
452 //DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24));
453 lseek(fd,current.size - 24,SEEK_CUR);
456 //DEBUGF("Parsed object - size = %d\n",(int)current.size);
457 datalen -= current.size;
460 if (i != (int)subobjects || datalen != 0) {
461 //DEBUGF("header data doesn't match given subobject count\n");
462 return ASF_ERROR_INVALID_VALUE;
465 //DEBUGF("%d subobjects read successfully\n", i);
468 #if 0
469 tmp = asf_parse_header_validate(file, &header);
470 if (tmp < 0) {
471 /* header read ok but doesn't validate correctly */
472 return tmp;
474 #endif
476 //DEBUGF("header validated correctly\n");
478 return 0;
481 bool get_asf_metadata(int fd, struct mp3entry* id3)
483 int res;
484 asf_object_t obj;
485 asf_waveformatex_t wfx;
487 wfx.audiostream = -1;
489 res = asf_parse_header(fd, id3, &wfx);
491 if (res < 0) {
492 DEBUGF("ASF: parsing error - %d\n",res);
493 return false;
496 if (wfx.audiostream == -1) {
497 DEBUGF("ASF: No WMA streams found\n");
498 return false;
501 if (wfx.bitrate < 20000) {
502 DEBUGF("ASF: < 20kbps files not supported\n");
503 return false;
506 asf_read_object_header(&obj, fd);
508 if (!asf_guid_match(&obj.guid, &asf_guid_data)) {
509 DEBUGF("ASF: No data object found\n");
510 return false;
513 /* Store the current file position - no need to parse the header
514 again in the codec. The +26 skips the rest of the data object
515 header.
517 id3->first_frame_offset = lseek(fd, 0, SEEK_CUR) + 26;
519 /* We copy the wfx struct to the MP3 TOC field in the id3 struct so
520 the codec doesn't need to parse the header object again */
521 memcpy(id3->toc, &wfx, sizeof(wfx));
523 return true;