update on wma decoder

git-svn-id: https://rt-thread.googlecode.com/svn/trunk@41 bbd45198-f89e-11dd-88c7-29a3b14d5316
This commit is contained in:
bernard.xiong 2009-09-07 23:42:35 +00:00
parent 8aa63f3f50
commit f0c313051d
2 changed files with 894 additions and 409 deletions

514
bsp/stm32_radio/asf.c Normal file
View File

@ -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(&current, 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(&current.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(&current.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(&current.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(&current.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(&current.guid, &asf_guid_content_encryption)
|| asf_guid_match(&current.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;
}

View File

@ -1,12 +1,13 @@
/* wma player test */
#include "libwma/asf.h" #include "libwma/asf.h"
#include "libwma/wmadec.h" #include "libwma/wmadec.h"
int packet_count=0;
/* The output buffer containing the decoded samples (channels 0 and 1) /* The output buffer containing the decoded samples (channels 0 and 1)
BLOCK_MAX_SIZE is 2048 (samples) and MAX_CHANNELS is 2. BLOCK_MAX_SIZE is 2048 (samples) and MAX_CHANNELS is 2.
*/ */
static uint32_t decoded[BLOCK_MAX_SIZE * MAX_CHANNELS]; static rt_uint32_t decoded[BLOCK_MAX_SIZE * MAX_CHANNELS];
/* NOTE: WMADecodeContext is 120152 bytes (on x86) */ /* NOTE: WMADecodeContext is 120152 bytes (on x86) */
static WMADecodeContext wmadec; static WMADecodeContext wmadec;
@ -24,84 +25,92 @@ enum asf_error_e {
ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */ ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */
}; };
/* Read an unaligned 32-bit little endian long from buffer. */
static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlength, asf_waveformatex_t* wfx) static unsigned long get_long_le(void* buf)
{ {
uint8_t tmp8, packet_flags, packet_property; 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 stream_id;
int ec_length, opaque_data, ec_length_type; int ec_length, opaque_data, ec_length_type;
int datalen; int datalen;
uint8_t data[18]; rt_uint8_t data[18];
uint8_t* datap; rt_uint8_t* datap;
uint32_t length; rt_uint32_t length;
uint32_t padding_length; rt_uint32_t padding_length;
uint32_t send_time; rt_uint32_t send_time;
uint16_t duration; rt_uint16_t duration;
uint16_t payload_count; rt_uint16_t payload_count;
int payload_length_type; int payload_length_type;
uint32_t payload_hdrlen; rt_uint32_t payload_hdrlen;
int payload_datalen; int payload_datalen;
int multiple; int multiple;
uint32_t replicated_length; rt_uint32_t replicated_length;
uint32_t media_object_number; rt_uint32_t media_object_number;
uint32_t media_object_offset; rt_uint32_t media_object_offset;
uint32_t bytesread = 0; rt_uint32_t bytesread = 0;
uint8_t* buf; rt_uint8_t* buf;
size_t bufsize; size_t bufsize;
int i; int i;
/*DEBUGF("Reading new packet at %d bytes ", (int)ci->curpos);*/
/* rt_kprintf("Reading new packet at %d bytes ", (int)ci->curpos); */ if (ci->read_filebuf(&tmp8, 1) == 0) {
if (ci->read_filebuf(&tmp8, 1) == 0)
{
return ASF_ERROR_EOF; return ASF_ERROR_EOF;
} }
bytesread++; bytesread++;
/* TODO: We need a better way to detect endofstream */ /* TODO: We need a better way to detect endofstream */
if (tmp8 != 0x82) if (tmp8 != 0x82) {
{ DEBUGF("Read failed: packet did not sync\n");
rt_kprintf("Read failed: packet did not sync\n");
return -1; return -1;
} }
if (tmp8 & 0x80)
{ if (tmp8 & 0x80) {
ec_length = tmp8 & 0x0f; ec_length = tmp8 & 0x0f;
opaque_data = (tmp8 >> 4) & 0x01; opaque_data = (tmp8 >> 4) & 0x01;
ec_length_type = (tmp8 >> 5) & 0x03; ec_length_type = (tmp8 >> 5) & 0x03;
if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) {
{ DEBUGF("incorrect error correction flags\n");
rt_kprintf("incorrect error correction flags\n");
return ASF_ERROR_INVALID_VALUE; return ASF_ERROR_INVALID_VALUE;
} }
/* Skip ec_data */ /* Skip ec_data */
ci->advance_buffer(ec_length); ci->advance_buffer(ec_length);
bytesread += ec_length; bytesread += ec_length;
} } else {
else
{
ec_length = 0; ec_length = 0;
} }
if (ci->read_filebuf(&packet_flags, 1) == 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; }
return ASF_ERROR_EOF;
}
if (ci->read_filebuf(&packet_property, 1) == 0)
{
return ASF_ERROR_EOF;
}
bytesread += 2; bytesread += 2;
datalen = GETLEN2b((packet_flags >> 1) & 0x03) + datalen = GETLEN2b((packet_flags >> 1) & 0x03) +
GETLEN2b((packet_flags >> 3) & 0x03) + GETLEN2b((packet_flags >> 3) & 0x03) +
GETLEN2b((packet_flags >> 5) & 0x03) + 6; GETLEN2b((packet_flags >> 5) & 0x03) + 6;
if (ci->read_filebuf(data, datalen) == 0) if (ci->read_filebuf(data, datalen) == 0) {
{
return ASF_ERROR_EOF; return ASF_ERROR_EOF;
} }
@ -119,59 +128,53 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen
datap += 4; datap += 4;
duration = get_short_le(datap); duration = get_short_le(datap);
datap += 2; datap += 2;
/* rt_kprintf("and duration %d ms\n", duration); */ /*DEBUGF("and duration %d ms\n", duration);*/
/* this is really idiotic, packet length can (and often will) be /* 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 * undefined and we just have to use the header packet size as the size
* value */ * value */
if (!((packet_flags >> 5) & 0x03)) if (!((packet_flags >> 5) & 0x03)) {
{
length = wfx->packet_size; length = wfx->packet_size;
} }
/* this is also really idiotic, if packet length is smaller than packet /* this is also really idiotic, if packet length is smaller than packet
* size, we need to manually add the additional bytes into padding length * size, we need to manually add the additional bytes into padding length
*/ */
if (length < wfx->packet_size) if (length < wfx->packet_size) {
{
padding_length += wfx->packet_size - length; padding_length += wfx->packet_size - length;
length = wfx->packet_size; length = wfx->packet_size;
} }
if (length > wfx->packet_size) if (length > wfx->packet_size) {
{ DEBUGF("packet with too big length value\n");
rt_kprintf("packet with too big length value\n");
return ASF_ERROR_INVALID_LENGTH; return ASF_ERROR_INVALID_LENGTH;
} }
/* check if we have multiple payloads */ /* check if we have multiple payloads */
if (packet_flags & 0x01) if (packet_flags & 0x01) {
{ if (ci->read_filebuf(&tmp8, 1) == 0) {
if (ci->read_filebuf(&tmp8, 1) == 0)
{
return ASF_ERROR_EOF; return ASF_ERROR_EOF;
} }
payload_count = tmp8 & 0x3f; payload_count = tmp8 & 0x3f;
payload_length_type = (tmp8 >> 6) & 0x03; payload_length_type = (tmp8 >> 6) & 0x03;
bytesread++; bytesread++;
} } else {
else
{
payload_count = 1; payload_count = 1;
payload_length_type = 0x02; /* not used */ payload_length_type = 0x02; /* not used */
} }
if (length < bytesread) if (length < bytesread) {
{ DEBUGF("header exceeded packet size, invalid file - length=%d, bytesread=%d\n",(int)length,(int)bytesread);
rt_kprintf("header exceeded packet size, invalid file - length=%d, bytesread=%d\n",(int)length,(int)bytesread);
/* FIXME: should this be checked earlier? */ /* FIXME: should this be checked earlier? */
return ASF_ERROR_INVALID_LENGTH; return ASF_ERROR_INVALID_LENGTH;
} }
/* We now parse the individual payloads, and move all payloads /* We now parse the individual payloads, and move all payloads
belonging to our audio stream to a contiguous block, starting at belonging to our audio stream to a contiguous block, starting at
the location of the first payload. the location of the first payload.
*/ */
*audiobuf = NULL; *audiobuf = NULL;
*audiobufsize = 0; *audiobufsize = 0;
*packetlength = length - bytesread; *packetlength = length - bytesread;
@ -179,20 +182,18 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen
buf = ci->request_buffer(&bufsize, length); buf = ci->request_buffer(&bufsize, length);
datap = buf; datap = buf;
if (bufsize != length) if (bufsize != length) {
{
/* This should only happen with packets larger than 32KB (the /* This should only happen with packets larger than 32KB (the
guard buffer size). All the streams I've seen have guard buffer size). All the streams I've seen have
relatively small packets less than about 8KB), but I don't relatively small packets less than about 8KB), but I don't
know what is expected. know what is expected.
*/ */
rt_kprintf("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n", DEBUGF("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n",
(int)length,(int)bufsize,(int)ci->curpos); (int)length,(int)bufsize,(int)ci->curpos);
return -1; return -1;
} }
for (i=0; i<payload_count; i++) for (i=0; i<payload_count; i++) {
{
stream_id = datap[0]&0x7f; stream_id = datap[0]&0x7f;
datap++; datap++;
bytesread++; bytesread++;
@ -201,10 +202,9 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen
GETLEN2b((packet_property >> 2) & 0x03) + GETLEN2b((packet_property >> 2) & 0x03) +
GETLEN2b((packet_property >> 4) & 0x03); GETLEN2b((packet_property >> 4) & 0x03);
// rt_kprintf("payload_hdrlen = %d\n",payload_hdrlen); //DEBUGF("payload_hdrlen = %d\n",payload_hdrlen);
if (payload_hdrlen > sizeof(data)) if (payload_hdrlen > sizeof(data)) {
{ DEBUGF("Unexpectedly long datalen in data - %d\n",datalen);
rt_kprintf("Unexpectedly long datalen in data - %d\n",datalen);
return ASF_ERROR_OUTOFMEM; return ASF_ERROR_OUTOFMEM;
} }
@ -224,14 +224,12 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen
multiple = packet_flags & 0x01; multiple = packet_flags & 0x01;
if (multiple) if (multiple) {
{
int x; int x;
x = GETLEN2b(payload_length_type); x = GETLEN2b(payload_length_type);
if (x != 2) if (x != 2) {
{
/* in multiple payloads datalen should be a word */ /* in multiple payloads datalen should be a word */
return ASF_ERROR_INVALID_VALUE; return ASF_ERROR_INVALID_VALUE;
} }
@ -239,9 +237,7 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen
payload_datalen = GETVALUE2b(payload_length_type, datap); payload_datalen = GETVALUE2b(payload_length_type, datap);
datap += x; datap += x;
bytesread += x; bytesread += x;
} } else {
else
{
payload_datalen = length - bytesread - padding_length; payload_datalen = length - bytesread - padding_length;
} }
@ -250,14 +246,11 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen
if (stream_id == wfx->audiostream) if (stream_id == wfx->audiostream)
{ {
if (*audiobuf == NULL) if (*audiobuf == NULL) {
{
/* The first payload can stay where it is */ /* The first payload can stay where it is */
*audiobuf = datap; *audiobuf = datap;
*audiobufsize = payload_datalen; *audiobufsize = payload_datalen;
} } else {
else
{
/* The second and subsequent payloads in this packet /* The second and subsequent payloads in this packet
that belong to the audio stream need to be moved to be that belong to the audio stream need to be moved to be
contiguous with the first payload. contiguous with the first payload.
@ -277,47 +270,35 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen
} }
/* this is the codec entry point */ /* this is the codec entry point */
void wma_run(void) void wma_codec_main(void)
{ {
uint32_t elapsedtime; rt_uint32_t elapsedtime;
int retval; int retval;
asf_waveformatex_t wfx; asf_waveformatex_t wfx;
size_t resume_offset; size_t resume_offset;
int i; int i;
int wmares, res; int wmares, res;
uint8_t* audiobuf; rt_uint8_t* audiobuf;
int audiobufsize; int audiobufsize;
int packetlength = 0; int packetlength = 0;
int errcount = 0; int errcount = 0;
/* Generic codec initialisation */
next_track:
retval = CODEC_OK;
/* Remember the resume position - when the codec is opened, the /* Remember the resume position - when the codec is opened, the
playback engine will reset it. */ playback engine will reset it. */
resume_offset = ci->id3->offset; 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 /* Copy the format metadata we've stored in the id3 TOC field. This
saves us from parsing it again here. */ saves us from parsing it again here. */
memcpy(&wfx, ci->id3->toc, sizeof(wfx)); memcpy(&wfx, ci->id3->toc, sizeof(wfx));
if (wma_decode_init(&wmadec,&wfx) < 0) if (wma_decode_init(&wmadec,&wfx) < 0) {
{
LOGF("WMA: Unsupported or corrupt file\n"); LOGF("WMA: Unsupported or corrupt file\n");
retval = CODEC_ERROR; retval = CODEC_ERROR;
goto exit; goto exit;
} }
DEBUGF("**************** IN WMA.C ******************\n");
if (resume_offset > ci->id3->first_frame_offset) if (resume_offset > ci->id3->first_frame_offset)
{ {
/* Get start of current packet */ /* Get start of current packet */
@ -335,38 +316,33 @@ restart_track:
} }
resume_offset = 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 */ /* The main decoding loop */
res = 1; res = 1;
while (res >= 0) while (res >= 0)
{ {
/* Deal with any pending seek requests */
errcount = 0; errcount = 0;
new_packet: new_packet:
res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx); res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
if (res < 0) if (res < 0) {
{
/* We'll try to recover from a parse error a certain number of /* We'll try to recover from a parse error a certain number of
* times. If we succeed, the error counter will be reset. * times. If we succeed, the error counter will be reset.
*/ */
errcount++; errcount++;
rt_kprintf("read_packet error %d, errcount %d\n",wmares, errcount); DEBUGF("read_packet error %d, errcount %d\n",wmares, errcount);
if (errcount > 5) if (errcount > 5) {
{
goto done; goto done;
} } else {
else
{
ci->advance_buffer(packetlength); ci->advance_buffer(packetlength);
goto new_packet; goto new_packet;
} }
} } else if (res > 0) {
else if (res > 0)
{
wma_decode_superframe_init(&wmadec, audiobuf, audiobufsize); wma_decode_superframe_init(&wmadec, audiobuf, audiobufsize);
for (i=0; i < wmadec.nb_frames; i++) for (i=0; i < wmadec.nb_frames; i++)
@ -375,35 +351,30 @@ new_packet:
decoded, decoded,
audiobuf, audiobufsize); audiobuf, audiobufsize);
if (wmares < 0) if (wmares < 0) {
{
/* Do the above, but for errors in decode. */ /* Do the above, but for errors in decode. */
errcount++; errcount++;
rt_kprintf("WMA decode error %d, errcount %d\n",wmares, errcount); DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount);
if (errcount > 5) if (errcount > 5) {
{
goto done; goto done;
} } else {
else
{
ci->advance_buffer(packetlength); ci->advance_buffer(packetlength);
goto new_packet; goto new_packet;
} }
} else if (wmares > 0) {
/* write to audio device */
elapsedtime += (wmares*10)/(wfx.rate/100);
ci->set_elapsed(elapsedtime);
} }
else if (wmares > 0) ci->yield();
{
ci->pcmbuf_insert(decoded, NULL, wmares);
} }
} }
ci->advance_buffer(packetlength);
} }
}
retval = CODEC_OK;
done: done:
/*LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000);*/ exit:
return retval; return ;
} }
void wma()
{}
FINSH_FUNCTION_EXPORT(wma, wma test)