diff options
| author | bernard.xiong <[email protected]> | 2009-09-07 23:42:35 +0000 |
|---|---|---|
| committer | bernard.xiong <[email protected]> | 2009-09-07 23:42:35 +0000 |
| commit | f0c313051d8f22359a2e3cb022ff097cc38ca5b0 (patch) | |
| tree | e735c4f2d1809abad080b53a77ca9579a8a6211a /bsp | |
| parent | 8aa63f3f50a812b3c07d40bf5cb25c8784126ef0 (diff) | |
update on wma decoder
git-svn-id: https://rt-thread.googlecode.com/svn/trunk@41 bbd45198-f89e-11dd-88c7-29a3b14d5316
Diffstat (limited to 'bsp')
| -rw-r--r-- | bsp/stm32_radio/asf.c | 514 | ||||
| -rw-r--r-- | bsp/stm32_radio/wma.c | 789 |
2 files changed, 894 insertions, 409 deletions
diff --git a/bsp/stm32_radio/asf.c b/bsp/stm32_radio/asf.c new file mode 100644 index 0000000000..023db0841c --- /dev/null +++ b/bsp/stm32_radio/asf.c @@ -0,0 +1,514 @@ +#include <rtthread.h> +#include <dfs_posix.h> +#include "libwma/asf.h" + +#define DEBUGF rt_kprintf + +/* always little endian */ +#define read_uint16le(fd,buf) read((fd), (buf), 2) +#define read_uint32le(fd,buf) read((fd), (buf), 4) +#define read_uint64le(fd,buf) read((fd), (buf), 8) + +/* TODO: Just read the GUIDs into a 16-byte array, and use memcmp to compare */ +struct guid_s { + rt_uint32_t v1; + rt_uint16_t v2; + rt_uint16_t v3; + rt_uint8_t v4[8]; +}; +typedef struct guid_s guid_t; +typedef long long rt_uint64_t; + +struct asf_object_s { + guid_t guid; + rt_uint64_t size; + rt_uint64_t datalen; +}; +typedef struct asf_object_s asf_object_t; + +enum asf_error_e { + ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */ + ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */ + ASF_ERROR_EOF = -3, /* unexpected end of file */ + ASF_ERROR_IO = -4, /* error reading or writing to file */ + ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */ + ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */ + ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */ + ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */ + ASF_ERROR_SEEKABLE = -9, /* file not seekable */ + ASF_ERROR_SEEK = -10, /* file is seekable but seeking failed */ + ASF_ERROR_ENCRYPTED = -11 /* file is encrypted */ +}; + +static const guid_t asf_guid_null = +{0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + +/* top level object guids */ + +static const guid_t asf_guid_header = +{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; + +static const guid_t asf_guid_data = +{0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; + +static const guid_t asf_guid_index = +{0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}; + +/* header level object guids */ + +static const guid_t asf_guid_file_properties = +{0x8cabdca1, 0xa947, 0x11cf, {0x8E, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; + +static const guid_t asf_guid_stream_properties = +{0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; + +static const guid_t asf_guid_content_description = +{0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; + +static const guid_t asf_guid_extended_content_description = +{0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}}; + +static const guid_t asf_guid_content_encryption = +{0x2211b3fb, 0xbd23, 0x11d2, {0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e}}; + +static const guid_t asf_guid_extended_content_encryption = +{0x298ae614, 0x2622, 0x4c17, {0xb9, 0x35, 0xda, 0xe0, 0x7e, 0xe9, 0x28, 0x9c}}; + +/* stream type guids */ + +static const guid_t asf_guid_stream_type_audio = +{0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; + +static int asf_guid_match(const guid_t *guid1, const guid_t *guid2) +{ + if((guid1->v1 != guid2->v1) || + (guid1->v2 != guid2->v2) || + (guid1->v3 != guid2->v3) || + (rt_memcmp(guid1->v4, guid2->v4, 8))) + { + return 0; + } + + return 1; +} + +/* Read the 16 byte GUID from a file */ +static void asf_readGUID(int fd, guid_t* guid) +{ + read_uint32le(fd, &guid->v1); + read_uint16le(fd, &guid->v2); + read_uint16le(fd, &guid->v3); + read(fd, guid->v4, 8); +} + +static void asf_read_object_header(asf_object_t *obj, int fd) +{ + asf_readGUID(fd, &obj->guid); + read_uint64le(fd, &obj->size); + obj->datalen = 0; +} + +/* Parse an integer from the extended content object - we always + convert to an int, regardless of native format. +*/ +static int asf_intdecode(int fd, int type, int length) +{ + rt_uint16_t tmp16; + rt_uint32_t tmp32; + rt_uint64_t tmp64; + + if (type==3) { + read_uint32le(fd, &tmp32); + lseek(fd,length - 4,SEEK_CUR); + return (int)tmp32; + } else if (type==4) { + read_uint64le(fd, &tmp64); + lseek(fd,length - 8,SEEK_CUR); + return (int)tmp64; + } else if (type == 5) { + read_uint16le(fd, &tmp16); + lseek(fd,length - 2,SEEK_CUR); + return (int)tmp16; + } + + return 0; +} + +/* Decode a LE utf16 string from a disk buffer into a fixed-sized + utf8 buffer. +*/ +static void asf_utf16LEdecode(int fd, + rt_uint16_t utf16bytes, + unsigned char **utf8, + int* utf8bytes) +{ + unsigned long ucs; + int n; + unsigned char utf16buf[256]; + unsigned char* utf16 = utf16buf; + unsigned char* newutf8; + + n = read(fd, utf16buf, MIN(sizeof(utf16buf), utf16bytes)); + utf16bytes -= n; + + while (n > 0) { + /* Check for a surrogate pair */ + if (utf16[1] >= 0xD8 && utf16[1] < 0xE0) { + if (n < 4) { + /* Run out of utf16 bytes, read some more */ + utf16buf[0] = utf16[0]; + utf16buf[1] = utf16[1]; + + n = read(fd, utf16buf + 2, MIN(sizeof(utf16buf)-2, utf16bytes)); + utf16 = utf16buf; + utf16bytes -= n; + n += 2; + } + + if (n < 4) { + /* Truncated utf16 string, abort */ + break; + } + ucs = 0x10000 + ((utf16[0] << 10) | ((utf16[1] - 0xD8) << 18) + | utf16[2] | ((utf16[3] - 0xDC) << 8)); + utf16 += 4; + n -= 4; + } else { + ucs = (utf16[0] | (utf16[1] << 8)); + utf16 += 2; + n -= 2; + } + + if (*utf8bytes > 6) { + newutf8 = utf8encode(ucs, *utf8); + *utf8bytes -= (newutf8 - *utf8); + *utf8 += (newutf8 - *utf8); + } + + /* We have run out of utf16 bytes, read more if available */ + if ((n == 0) && (utf16bytes > 0)) { + n = read(fd, utf16buf, MIN(sizeof(utf16buf), utf16bytes)); + utf16 = utf16buf; + utf16bytes -= n; + } + } + + *utf8[0] = 0; + --*utf8bytes; + + if (utf16bytes > 0) { + /* Skip any remaining bytes */ + lseek(fd, utf16bytes, SEEK_CUR); + } + return; +} + +static int asf_parse_header(int fd, struct mp3entry* id3, + asf_waveformatex_t* wfx) +{ + asf_object_t current; + asf_object_t header; + rt_uint64_t datalen; + int i; + int fileprop = 0; + rt_uint64_t play_duration; + rt_uint16_t flags; + rt_uint32_t subobjects; + rt_uint8_t utf8buf[512]; + int id3buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); + unsigned char* id3buf = (unsigned char*)id3->id3v2buf; + + asf_read_object_header((asf_object_t *) &header, fd); + + //DEBUGF("header.size=%d\n",(int)header.size); + if (header.size < 30) { + /* invalid size for header object */ + return ASF_ERROR_OBJECT_SIZE; + } + + read_uint32le(fd, &subobjects); + + /* Two reserved bytes - do we need to read them? */ + lseek(fd, 2, SEEK_CUR); + + //DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects); + + if (subobjects > 0) { + header.datalen = header.size - 30; + + /* TODO: Check that we have datalen bytes left in the file */ + datalen = header.datalen; + + for (i=0; i<(int)subobjects; i++) { + //DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen); + if (datalen < 24) { + //DEBUGF("not enough data for reading object\n"); + break; + } + + asf_read_object_header(¤t, fd); + + if (current.size > datalen || current.size < 24) { + //DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen); + break; + } + + if (asf_guid_match(¤t.guid, &asf_guid_file_properties)) { + if (current.size < 104) + return ASF_ERROR_OBJECT_SIZE; + + if (fileprop) { + /* multiple file properties objects not allowed */ + return ASF_ERROR_INVALID_OBJECT; + } + + fileprop = 1; + /* All we want is the play duration - uint64_t at offset 40 */ + lseek(fd, 40, SEEK_CUR); + + read_uint64le(fd, &play_duration); + id3->length = play_duration / 10000; + + //DEBUGF("****** length = %lums\n", id3->length); + + /* Read the packet size - uint32_t at offset 68 */ + lseek(fd, 20, SEEK_CUR); + read_uint32le(fd, &wfx->packet_size); + + /* Skip bytes remaining in object */ + lseek(fd, current.size - 24 - 72, SEEK_CUR); + } else if (asf_guid_match(¤t.guid, &asf_guid_stream_properties)) { + guid_t guid; + rt_uint32_t propdatalen; + + if (current.size < 78) + return ASF_ERROR_OBJECT_SIZE; + + asf_readGUID(fd, &guid); + + lseek(fd, 24, SEEK_CUR); + read_uint32le(fd, &propdatalen); + lseek(fd, 4, SEEK_CUR); + read_uint16le(fd, &flags); + + if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) { + //DEBUGF("Found stream properties for non audio stream, skipping\n"); + lseek(fd,current.size - 24 - 50,SEEK_CUR); + } else if (wfx->audiostream == -1) { + lseek(fd, 4, SEEK_CUR); + //DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f); + + if (propdatalen < 18) { + return ASF_ERROR_INVALID_LENGTH; + } + + read_uint16le(fd, &wfx->codec_id); + read_uint16le(fd, &wfx->channels); + read_uint32le(fd, &wfx->rate); + read_uint32le(fd, &wfx->bitrate); + wfx->bitrate *= 8; + read_uint16le(fd, &wfx->blockalign); + read_uint16le(fd, &wfx->bitspersample); + read_uint16le(fd, &wfx->datalen); + + /* Round bitrate to the nearest kbit */ + id3->bitrate = (wfx->bitrate + 500) / 1000; + id3->frequency = wfx->rate; + + if (wfx->codec_id == ASF_CODEC_ID_WMAV1) { + read(fd, wfx->data, 4); + lseek(fd,current.size - 24 - 72 - 4,SEEK_CUR); + wfx->audiostream = flags&0x7f; + } else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) { + read(fd, wfx->data, 6); + lseek(fd,current.size - 24 - 72 - 6,SEEK_CUR); + wfx->audiostream = flags&0x7f; + } else { + DEBUGF("Unsupported WMA codec (Pro, Lossless, Voice, etc)\n"); + lseek(fd,current.size - 24 - 72,SEEK_CUR); + } + + } + } else if (asf_guid_match(¤t.guid, &asf_guid_content_description)) { + /* Object contains five 16-bit string lengths, followed by the five strings: + title, artist, copyright, description, rating + */ + rt_uint16_t strlength[5]; + int i; + + //DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24)); + + /* Read the 5 string lengths - number of bytes included trailing zero */ + for (i=0; i<5; i++) { + read_uint16le(fd, &strlength[i]); + //DEBUGF("strlength = %u\n",strlength[i]); + } + + if (strlength[0] > 0) { /* 0 - Title */ + id3->title = id3buf; + asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining); + } + + if (strlength[1] > 0) { /* 1 - Artist */ + id3->artist = id3buf; + asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining); + } + + lseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */ + + if (strlength[3] > 0) { /* 3 - description */ + id3->comment = id3buf; + asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining); + } + + lseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */ + } else if (asf_guid_match(¤t.guid, &asf_guid_extended_content_description)) { + rt_uint16_t count; + int i; + int bytesleft = current.size - 24; + //DEBUGF("Found GUID_EXTENDED_CONTENT_DESCRIPTION\n"); + + read_uint16le(fd, &count); + bytesleft -= 2; + //DEBUGF("extended metadata count = %u\n",count); + + for (i=0; i < count; i++) { + rt_uint16_t length, type; + unsigned char* utf8 = utf8buf; + int utf8length = 512; + + read_uint16le(fd, &length); + asf_utf16LEdecode(fd, length, &utf8, &utf8length); + bytesleft -= 2 + length; + + read_uint16le(fd, &type); + read_uint16le(fd, &length); + + if (!strcmp("WM/TrackNumber",utf8buf)) { + if (type == 0) { + id3->track_string = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + id3->tracknum = atoi(id3->track_string); + } else if ((type >=2) && (type <= 5)) { + id3->tracknum = asf_intdecode(fd, type, length); + } else { + lseek(fd, length, SEEK_CUR); + } + } else if ((!strcmp("WM/Genre",utf8buf)) && (type == 0)) { + id3->genre_string = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + } else if ((!strcmp("WM/AlbumTitle",utf8buf)) && (type == 0)) { + id3->album = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + } else if ((!strcmp("WM/AlbumArtist",utf8buf)) && (type == 0)) { + id3->albumartist = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + } else if ((!strcmp("WM/Composer",utf8buf)) && (type == 0)) { + id3->composer = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + } else if (!strcmp("WM/Year",utf8buf)) { + if (type == 0) { + id3->year_string = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + id3->year = atoi(id3->year_string); + } else if ((type >=2) && (type <= 5)) { + id3->year = asf_intdecode(fd, type, length); + } else { + lseek(fd, length, SEEK_CUR); + } + } else if (!strncmp("replaygain_", utf8buf, 11)) { + char* value = id3buf; + int buf_len = id3buf_remaining; + int len; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + len = parse_replaygain(utf8buf, value, id3, + value, buf_len); + + if (len == 0) { + /* Don't need to keep the value */ + id3buf = value; + id3buf_remaining = buf_len; + } + } else if (!strcmp("MusicBrainz/Track Id", utf8buf)) { + id3->mb_track_id = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + } else { + lseek(fd, length, SEEK_CUR); + } + bytesleft -= 4 + length; + } + + lseek(fd, bytesleft, SEEK_CUR); + } else if (asf_guid_match(¤t.guid, &asf_guid_content_encryption) + || asf_guid_match(¤t.guid, &asf_guid_extended_content_encryption)) { + //DEBUGF("File is encrypted\n"); + return ASF_ERROR_ENCRYPTED; + } else { + //DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24)); + lseek(fd,current.size - 24,SEEK_CUR); + } + + //DEBUGF("Parsed object - size = %d\n",(int)current.size); + datalen -= current.size; + } + + if (i != (int)subobjects || datalen != 0) { + //DEBUGF("header data doesn't match given subobject count\n"); + return ASF_ERROR_INVALID_VALUE; + } + + //DEBUGF("%d subobjects read successfully\n", i); + } + + //DEBUGF("header validated correctly\n"); + + return 0; +} + +static off_t filesize(int fd) +{ + struct dfs_stat buf; + + stat(fd,&buf); + return buf.st_size; +} + +rt_bool_t get_asf_metadata(int fd, struct mp3entry* id3) +{ + int res; + asf_object_t obj; + asf_waveformatex_t wfx; + + wfx.audiostream = -1; + + res = asf_parse_header(fd, id3, &wfx); + + if (res < 0) { + DEBUGF("ASF: parsing error - %d\n",res); + return RT_FALSE; + } + + if (wfx.audiostream == -1) { + DEBUGF("ASF: No WMA streams found\n"); + return RT_FALSE; + } + + asf_read_object_header(&obj, fd); + + if (!asf_guid_match(&obj.guid, &asf_guid_data)) { + DEBUGF("ASF: No data object found\n"); + return RT_FALSE; + } + + /* Store the current file position - no need to parse the header + again in the codec. The +26 skips the rest of the data object + header. + */ + id3->first_frame_offset = lseek(fd, 0, SEEK_CUR) + 26; + id3->filesize = filesize(fd); + /* We copy the wfx struct to the MP3 TOC field in the id3 struct so + the codec doesn't need to parse the header object again */ + rt_memcpy(id3->toc, &wfx, sizeof(wfx)); + + return RT_TRUE; +} diff --git a/bsp/stm32_radio/wma.c b/bsp/stm32_radio/wma.c index 90e584d1d4..629b6cf6d8 100644 --- a/bsp/stm32_radio/wma.c +++ b/bsp/stm32_radio/wma.c @@ -1,409 +1,380 @@ -/* wma player test */
-#include "libwma/asf.h"
-#include "libwma/wmadec.h"
-
-/* The output buffer containing the decoded samples (channels 0 and 1)
- BLOCK_MAX_SIZE is 2048 (samples) and MAX_CHANNELS is 2.
- */
-
-static uint32_t decoded[BLOCK_MAX_SIZE * MAX_CHANNELS];
-
-/* NOTE: WMADecodeContext is 120152 bytes (on x86) */
-static WMADecodeContext wmadec;
-
-enum asf_error_e {
- ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */
- ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */
- ASF_ERROR_EOF = -3, /* unexpected end of file */
- ASF_ERROR_IO = -4, /* error reading or writing to file */
- ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */
- ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */
- ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */
- ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */
- ASF_ERROR_SEEKABLE = -9, /* file not seekable */
- ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */
-};
-
-
-static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlength, asf_waveformatex_t* wfx)
-{
- uint8_t tmp8, packet_flags, packet_property;
- int stream_id;
- int ec_length, opaque_data, ec_length_type;
- int datalen;
- uint8_t data[18];
- uint8_t* datap;
- uint32_t length;
- uint32_t padding_length;
- uint32_t send_time;
- uint16_t duration;
- uint16_t payload_count;
- int payload_length_type;
- uint32_t payload_hdrlen;
- int payload_datalen;
- int multiple;
- uint32_t replicated_length;
- uint32_t media_object_number;
- uint32_t media_object_offset;
- uint32_t bytesread = 0;
- uint8_t* buf;
- size_t bufsize;
- int i;
-
- /* rt_kprintf("Reading new packet at %d bytes ", (int)ci->curpos); */
-
- if (ci->read_filebuf(&tmp8, 1) == 0)
- {
- return ASF_ERROR_EOF;
- }
- bytesread++;
-
- /* TODO: We need a better way to detect endofstream */
- if (tmp8 != 0x82)
- {
- rt_kprintf("Read failed: packet did not sync\n");
- return -1;
- }
-
- if (tmp8 & 0x80)
- {
- ec_length = tmp8 & 0x0f;
- opaque_data = (tmp8 >> 4) & 0x01;
- ec_length_type = (tmp8 >> 5) & 0x03;
-
- if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02)
- {
- rt_kprintf("incorrect error correction flags\n");
- return ASF_ERROR_INVALID_VALUE;
- }
-
- /* Skip ec_data */
- ci->advance_buffer(ec_length);
- bytesread += ec_length;
- }
- else
- {
- ec_length = 0;
- }
-
- if (ci->read_filebuf(&packet_flags, 1) == 0)
- {
- return ASF_ERROR_EOF;
- }
- if (ci->read_filebuf(&packet_property, 1) == 0)
- {
- return ASF_ERROR_EOF;
- }
- bytesread += 2;
-
- datalen = GETLEN2b((packet_flags >> 1) & 0x03) +
- GETLEN2b((packet_flags >> 3) & 0x03) +
- GETLEN2b((packet_flags >> 5) & 0x03) + 6;
-
- if (ci->read_filebuf(data, datalen) == 0)
- {
- return ASF_ERROR_EOF;
- }
-
- bytesread += datalen;
-
- datap = data;
- length = GETVALUE2b((packet_flags >> 5) & 0x03, datap);
- datap += GETLEN2b((packet_flags >> 5) & 0x03);
- /* sequence value is not used */
- GETVALUE2b((packet_flags >> 1) & 0x03, datap);
- datap += GETLEN2b((packet_flags >> 1) & 0x03);
- padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap);
- datap += GETLEN2b((packet_flags >> 3) & 0x03);
- send_time = get_long_le(datap);
- datap += 4;
- duration = get_short_le(datap);
- datap += 2;
- /* rt_kprintf("and duration %d ms\n", duration); */
-
- /* this is really idiotic, packet length can (and often will) be
- * undefined and we just have to use the header packet size as the size
- * value */
- if (!((packet_flags >> 5) & 0x03))
- {
- length = wfx->packet_size;
- }
-
- /* this is also really idiotic, if packet length is smaller than packet
- * size, we need to manually add the additional bytes into padding length
- */
- if (length < wfx->packet_size)
- {
- padding_length += wfx->packet_size - length;
- length = wfx->packet_size;
- }
-
- if (length > wfx->packet_size)
- {
- rt_kprintf("packet with too big length value\n");
- return ASF_ERROR_INVALID_LENGTH;
- }
-
- /* check if we have multiple payloads */
- if (packet_flags & 0x01)
- {
- if (ci->read_filebuf(&tmp8, 1) == 0)
- {
- return ASF_ERROR_EOF;
- }
- payload_count = tmp8 & 0x3f;
- payload_length_type = (tmp8 >> 6) & 0x03;
- bytesread++;
- }
- else
- {
- payload_count = 1;
- payload_length_type = 0x02; /* not used */
- }
-
- if (length < bytesread)
- {
- rt_kprintf("header exceeded packet size, invalid file - length=%d, bytesread=%d\n",(int)length,(int)bytesread);
- /* FIXME: should this be checked earlier? */
- return ASF_ERROR_INVALID_LENGTH;
- }
-
- /* We now parse the individual payloads, and move all payloads
- belonging to our audio stream to a contiguous block, starting at
- the location of the first payload.
- */
- *audiobuf = NULL;
- *audiobufsize = 0;
- *packetlength = length - bytesread;
-
- buf = ci->request_buffer(&bufsize, length);
- datap = buf;
-
- if (bufsize != length)
- {
- /* This should only happen with packets larger than 32KB (the
- guard buffer size). All the streams I've seen have
- relatively small packets less than about 8KB), but I don't
- know what is expected.
- */
- rt_kprintf("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n",
- (int)length,(int)bufsize,(int)ci->curpos);
- return -1;
- }
-
- for (i=0; i<payload_count; i++)
- {
- stream_id = datap[0]&0x7f;
- datap++;
- bytesread++;
-
- payload_hdrlen = GETLEN2b(packet_property & 0x03) +
- GETLEN2b((packet_property >> 2) & 0x03) +
- GETLEN2b((packet_property >> 4) & 0x03);
-
- // rt_kprintf("payload_hdrlen = %d\n",payload_hdrlen);
- if (payload_hdrlen > sizeof(data))
- {
- rt_kprintf("Unexpectedly long datalen in data - %d\n",datalen);
- return ASF_ERROR_OUTOFMEM;
- }
-
- bytesread += payload_hdrlen;
- media_object_number = GETVALUE2b((packet_property >> 4) & 0x03, datap);
- datap += GETLEN2b((packet_property >> 4) & 0x03);
- media_object_offset = GETVALUE2b((packet_property >> 2) & 0x03, datap);
- datap += GETLEN2b((packet_property >> 2) & 0x03);
- replicated_length = GETVALUE2b(packet_property & 0x03, datap);
- datap += GETLEN2b(packet_property & 0x03);
-
- /* TODO: Validate replicated_length */
- /* TODO: Is the content of this important for us? */
- datap += replicated_length;
- bytesread += replicated_length;
-
- multiple = packet_flags & 0x01;
-
-
- if (multiple)
- {
- int x;
-
- x = GETLEN2b(payload_length_type);
-
- if (x != 2)
- {
- /* in multiple payloads datalen should be a word */
- return ASF_ERROR_INVALID_VALUE;
- }
-
- payload_datalen = GETVALUE2b(payload_length_type, datap);
- datap += x;
- bytesread += x;
- }
- else
- {
- payload_datalen = length - bytesread - padding_length;
- }
-
- if (replicated_length==1)
- datap++;
-
- if (stream_id == wfx->audiostream)
- {
- if (*audiobuf == NULL)
- {
- /* The first payload can stay where it is */
- *audiobuf = datap;
- *audiobufsize = payload_datalen;
- }
- else
- {
- /* The second and subsequent payloads in this packet
- that belong to the audio stream need to be moved to be
- contiguous with the first payload.
- */
- memmove(*audiobuf + *audiobufsize, datap, payload_datalen);
- *audiobufsize += payload_datalen;
- }
- }
- datap += payload_datalen;
- bytesread += payload_datalen;
- }
-
- if (*audiobuf != NULL)
- return 1;
- else
- return 0;
-}
-
-/* this is the codec entry point */
-void wma_run(void)
-{
- uint32_t elapsedtime;
- int retval;
- asf_waveformatex_t wfx;
- size_t resume_offset;
- int i;
- int wmares, res;
- uint8_t* audiobuf;
- int audiobufsize;
- int packetlength = 0;
- int errcount = 0;
-
- /* Generic codec initialisation */
-next_track:
-
- retval = CODEC_OK;
-
- /* Remember the resume position - when the codec is opened, the
- playback engine will reset it. */
- resume_offset = ci->id3->offset;
-
-restart_track:
- if (codec_init())
- {
- LOGF("WMA: Error initialising codec\n");
- retval = CODEC_ERROR;
- goto exit;
- }
-
- /* Copy the format metadata we've stored in the id3 TOC field. This
- saves us from parsing it again here. */
- memcpy(&wfx, ci->id3->toc, sizeof(wfx));
-
- if (wma_decode_init(&wmadec,&wfx) < 0)
- {
- LOGF("WMA: Unsupported or corrupt file\n");
- retval = CODEC_ERROR;
- goto exit;
- }
-
- if (resume_offset > ci->id3->first_frame_offset)
- {
- /* Get start of current packet */
- int packet_offset = (resume_offset - ci->id3->first_frame_offset)
- % wfx.packet_size;
- ci->seek_buffer(resume_offset - packet_offset);
- elapsedtime = get_timestamp(&i);
- ci->set_elapsed(elapsedtime);
- }
- else
- {
- /* Now advance the file position to the first frame */
- ci->seek_buffer(ci->id3->first_frame_offset);
- elapsedtime = 0;
- }
-
- resume_offset = 0;
-
- /* The main decoding loop */
-
- res = 1;
- while (res >= 0)
- {
- /* Deal with any pending seek requests */
- errcount = 0;
-
-new_packet:
- res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
-
- if (res < 0)
- {
- /* We'll try to recover from a parse error a certain number of
- * times. If we succeed, the error counter will be reset.
- */
-
- errcount++;
- rt_kprintf("read_packet error %d, errcount %d\n",wmares, errcount);
- if (errcount > 5)
- {
- goto done;
- }
- else
- {
- ci->advance_buffer(packetlength);
- goto new_packet;
- }
- }
- else if (res > 0)
- {
- wma_decode_superframe_init(&wmadec, audiobuf, audiobufsize);
-
- for (i=0; i < wmadec.nb_frames; i++)
- {
- wmares = wma_decode_superframe_frame(&wmadec,
- decoded,
- audiobuf, audiobufsize);
-
- if (wmares < 0)
- {
- /* Do the above, but for errors in decode. */
- errcount++;
- rt_kprintf("WMA decode error %d, errcount %d\n",wmares, errcount);
- if (errcount > 5)
- {
- goto done;
- }
- else
- {
- ci->advance_buffer(packetlength);
- goto new_packet;
- }
- }
- else if (wmares > 0)
- {
- ci->pcmbuf_insert(decoded, NULL, wmares);
- }
- }
- }
- }
- retval = CODEC_OK;
-
-done:
- /*LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000);*/
- return retval;
-}
-
-void wma()
-{}
-FINSH_FUNCTION_EXPORT(wma, wma test)
+#include "libwma/asf.h" +#include "libwma/wmadec.h" + +int packet_count=0; + +/* The output buffer containing the decoded samples (channels 0 and 1) + BLOCK_MAX_SIZE is 2048 (samples) and MAX_CHANNELS is 2. + */ + +static rt_uint32_t decoded[BLOCK_MAX_SIZE * MAX_CHANNELS]; + +/* NOTE: WMADecodeContext is 120152 bytes (on x86) */ +static WMADecodeContext wmadec; + +enum asf_error_e { + ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */ + ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */ + ASF_ERROR_EOF = -3, /* unexpected end of file */ + ASF_ERROR_IO = -4, /* error reading or writing to file */ + ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */ + ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */ + ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */ + ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */ + ASF_ERROR_SEEKABLE = -9, /* file not seekable */ + ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */ +}; + +/* Read an unaligned 32-bit little endian long from buffer. */ +static unsigned long get_long_le(void* buf) +{ + unsigned char* p = (unsigned char*) buf; + + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +} + +/* Read an unaligned 16-bit little endian short from buffer. */ +static unsigned short get_short_le(void* buf) +{ + unsigned char* p = (unsigned char*) buf; + + return p[0] | (p[1] << 8); +} + +#define GETLEN2b(bits) (((bits) == 0x03) ? 4 : bits) + +#define GETVALUE2b(bits, data) \ + (((bits) != 0x03) ? ((bits) != 0x02) ? ((bits) != 0x01) ? \ + 0 : *(data) : get_short_le(data) : get_long_le(data)) + +static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packetlength, asf_waveformatex_t* wfx) +{ + rt_uint8_t tmp8, packet_flags, packet_property; + int stream_id; + int ec_length, opaque_data, ec_length_type; + int datalen; + rt_uint8_t data[18]; + rt_uint8_t* datap; + rt_uint32_t length; + rt_uint32_t padding_length; + rt_uint32_t send_time; + rt_uint16_t duration; + rt_uint16_t payload_count; + int payload_length_type; + rt_uint32_t payload_hdrlen; + int payload_datalen; + int multiple; + rt_uint32_t replicated_length; + rt_uint32_t media_object_number; + rt_uint32_t media_object_offset; + rt_uint32_t bytesread = 0; + rt_uint8_t* buf; + size_t bufsize; + int i; + /*DEBUGF("Reading new packet at %d bytes ", (int)ci->curpos);*/ + + if (ci->read_filebuf(&tmp8, 1) == 0) { + return ASF_ERROR_EOF; + } + bytesread++; + + /* TODO: We need a better way to detect endofstream */ + if (tmp8 != 0x82) { + DEBUGF("Read failed: packet did not sync\n"); + return -1; + } + + + if (tmp8 & 0x80) { + ec_length = tmp8 & 0x0f; + opaque_data = (tmp8 >> 4) & 0x01; + ec_length_type = (tmp8 >> 5) & 0x03; + + if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) { + DEBUGF("incorrect error correction flags\n"); + return ASF_ERROR_INVALID_VALUE; + } + + /* Skip ec_data */ + ci->advance_buffer(ec_length); + bytesread += ec_length; + } else { + ec_length = 0; + } + + if (ci->read_filebuf(&packet_flags, 1) == 0) { return ASF_ERROR_EOF; } + if (ci->read_filebuf(&packet_property, 1) == 0) { return ASF_ERROR_EOF; } + bytesread += 2; + + datalen = GETLEN2b((packet_flags >> 1) & 0x03) + + GETLEN2b((packet_flags >> 3) & 0x03) + + GETLEN2b((packet_flags >> 5) & 0x03) + 6; + + if (ci->read_filebuf(data, datalen) == 0) { + return ASF_ERROR_EOF; + } + + bytesread += datalen; + + datap = data; + length = GETVALUE2b((packet_flags >> 5) & 0x03, datap); + datap += GETLEN2b((packet_flags >> 5) & 0x03); + /* sequence value is not used */ + GETVALUE2b((packet_flags >> 1) & 0x03, datap); + datap += GETLEN2b((packet_flags >> 1) & 0x03); + padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap); + datap += GETLEN2b((packet_flags >> 3) & 0x03); + send_time = get_long_le(datap); + datap += 4; + duration = get_short_le(datap); + datap += 2; + /*DEBUGF("and duration %d ms\n", duration);*/ + + /* this is really idiotic, packet length can (and often will) be + * undefined and we just have to use the header packet size as the size + * value */ + if (!((packet_flags >> 5) & 0x03)) { + length = wfx->packet_size; + } + + /* this is also really idiotic, if packet length is smaller than packet + * size, we need to manually add the additional bytes into padding length + */ + if (length < wfx->packet_size) { + padding_length += wfx->packet_size - length; + length = wfx->packet_size; + } + + if (length > wfx->packet_size) { + DEBUGF("packet with too big length value\n"); + return ASF_ERROR_INVALID_LENGTH; + } + + /* check if we have multiple payloads */ + if (packet_flags & 0x01) { + if (ci->read_filebuf(&tmp8, 1) == 0) { + return ASF_ERROR_EOF; + } + payload_count = tmp8 & 0x3f; + payload_length_type = (tmp8 >> 6) & 0x03; + bytesread++; + } else { + payload_count = 1; + payload_length_type = 0x02; /* not used */ + } + + if (length < bytesread) { + DEBUGF("header exceeded packet size, invalid file - length=%d, bytesread=%d\n",(int)length,(int)bytesread); + /* FIXME: should this be checked earlier? */ + return ASF_ERROR_INVALID_LENGTH; + } + + + /* We now parse the individual payloads, and move all payloads + belonging to our audio stream to a contiguous block, starting at + the location of the first payload. + */ + + *audiobuf = NULL; + *audiobufsize = 0; + *packetlength = length - bytesread; + + buf = ci->request_buffer(&bufsize, length); + datap = buf; + + if (bufsize != length) { + /* This should only happen with packets larger than 32KB (the + guard buffer size). All the streams I've seen have + relatively small packets less than about 8KB), but I don't + know what is expected. + */ + DEBUGF("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n", + (int)length,(int)bufsize,(int)ci->curpos); + return -1; + } + + for (i=0; i<payload_count; i++) { + stream_id = datap[0]&0x7f; + datap++; + bytesread++; + + payload_hdrlen = GETLEN2b(packet_property & 0x03) + + GETLEN2b((packet_property >> 2) & 0x03) + + GETLEN2b((packet_property >> 4) & 0x03); + + //DEBUGF("payload_hdrlen = %d\n",payload_hdrlen); + if (payload_hdrlen > sizeof(data)) { + DEBUGF("Unexpectedly long datalen in data - %d\n",datalen); + return ASF_ERROR_OUTOFMEM; + } + + bytesread += payload_hdrlen; + media_object_number = GETVALUE2b((packet_property >> 4) & 0x03, datap); + datap += GETLEN2b((packet_property >> 4) & 0x03); + media_object_offset = GETVALUE2b((packet_property >> 2) & 0x03, datap); + datap += GETLEN2b((packet_property >> 2) & 0x03); + replicated_length = GETVALUE2b(packet_property & 0x03, datap); + datap += GETLEN2b(packet_property & 0x03); + + /* TODO: Validate replicated_length */ + /* TODO: Is the content of this important for us? */ + datap += replicated_length; + bytesread += replicated_length; + + multiple = packet_flags & 0x01; + + + if (multiple) { + int x; + + x = GETLEN2b(payload_length_type); + + if (x != 2) { + /* in multiple payloads datalen should be a word */ + return ASF_ERROR_INVALID_VALUE; + } + + payload_datalen = GETVALUE2b(payload_length_type, datap); + datap += x; + bytesread += x; + } else { + payload_datalen = length - bytesread - padding_length; + } + + if (replicated_length==1) + datap++; + + if (stream_id == wfx->audiostream) + { + if (*audiobuf == NULL) { + /* The first payload can stay where it is */ + *audiobuf = datap; + *audiobufsize = payload_datalen; + } else { + /* The second and subsequent payloads in this packet + that belong to the audio stream need to be moved to be + contiguous with the first payload. + */ + memmove(*audiobuf + *audiobufsize, datap, payload_datalen); + *audiobufsize += payload_datalen; + } + } + datap += payload_datalen; + bytesread += payload_datalen; + } + + if (*audiobuf != NULL) + return 1; + else + return 0; +} + +/* this is the codec entry point */ +void wma_codec_main(void) +{ + rt_uint32_t elapsedtime; + int retval; + asf_waveformatex_t wfx; + size_t resume_offset; + int i; + int wmares, res; + rt_uint8_t* audiobuf; + int audiobufsize; + int packetlength = 0; + int errcount = 0; + + /* Remember the resume position - when the codec is opened, the + playback engine will reset it. */ + resume_offset = ci->id3->offset; + + /* Copy the format metadata we've stored in the id3 TOC field. This + saves us from parsing it again here. */ + memcpy(&wfx, ci->id3->toc, sizeof(wfx)); + + if (wma_decode_init(&wmadec,&wfx) < 0) { + LOGF("WMA: Unsupported or corrupt file\n"); + retval = CODEC_ERROR; + goto exit; + } + + DEBUGF("**************** IN WMA.C ******************\n"); + + if (resume_offset > ci->id3->first_frame_offset) + { + /* Get start of current packet */ + int packet_offset = (resume_offset - ci->id3->first_frame_offset) + % wfx.packet_size; + ci->seek_buffer(resume_offset - packet_offset); + elapsedtime = get_timestamp(&i); + ci->set_elapsed(elapsedtime); + } + else + { + /* Now advance the file position to the first frame */ + ci->seek_buffer(ci->id3->first_frame_offset); + elapsedtime = 0; + } + + resume_offset = 0; + ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate); + ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ? + STEREO_MONO : STEREO_INTERLEAVED); + codec_set_replaygain(ci->id3); + + /* The main decoding loop */ + + res = 1; + while (res >= 0) + { + errcount = 0; +new_packet: + res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx); + + if (res < 0) { + /* We'll try to recover from a parse error a certain number of + * times. If we succeed, the error counter will be reset. + */ + errcount++; + DEBUGF("read_packet error %d, errcount %d\n",wmares, errcount); + if (errcount > 5) { + goto done; + } else { + ci->advance_buffer(packetlength); + goto new_packet; + } + } else if (res > 0) { + wma_decode_superframe_init(&wmadec, audiobuf, audiobufsize); + + for (i=0; i < wmadec.nb_frames; i++) + { + wmares = wma_decode_superframe_frame(&wmadec, + decoded, + audiobuf, audiobufsize); + + if (wmares < 0) { + /* Do the above, but for errors in decode. */ + errcount++; + DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount); + if (errcount > 5) { + goto done; + } else { + ci->advance_buffer(packetlength); + goto new_packet; + } + } else if (wmares > 0) { + /* write to audio device */ + elapsedtime += (wmares*10)/(wfx.rate/100); + ci->set_elapsed(elapsedtime); + } + ci->yield(); + } + } + + ci->advance_buffer(packetlength); + } + +done: +exit: + return ; +} + |
