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,409 +1,380 @@
/* 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)
BLOCK_MAX_SIZE is 2048 (samples) and MAX_CHANNELS is 2. /* 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];
static rt_uint32_t decoded[BLOCK_MAX_SIZE * MAX_CHANNELS];
/* NOTE: WMADecodeContext is 120152 bytes (on x86) */
static WMADecodeContext wmadec; /* NOTE: WMADecodeContext is 120152 bytes (on x86) */
static WMADecodeContext wmadec;
enum asf_error_e {
ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */ enum asf_error_e {
ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */ ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */
ASF_ERROR_EOF = -3, /* unexpected end of file */ ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */
ASF_ERROR_IO = -4, /* error reading or writing to file */ ASF_ERROR_EOF = -3, /* unexpected end of file */
ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */ ASF_ERROR_IO = -4, /* error reading or writing to file */
ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */ ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */
ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */ ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */
ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */ ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */
ASF_ERROR_SEEKABLE = -9, /* file not seekable */ ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */
ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */ 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) /* Read an unaligned 32-bit little endian long from buffer. */
{ static unsigned long get_long_le(void* buf)
uint8_t tmp8, packet_flags, packet_property; {
int stream_id; unsigned char* p = (unsigned char*) buf;
int ec_length, opaque_data, ec_length_type;
int datalen; return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
uint8_t data[18]; }
uint8_t* datap;
uint32_t length; /* Read an unaligned 16-bit little endian short from buffer. */
uint32_t padding_length; static unsigned short get_short_le(void* buf)
uint32_t send_time; {
uint16_t duration; unsigned char* p = (unsigned char*) buf;
uint16_t payload_count;
int payload_length_type; return p[0] | (p[1] << 8);
uint32_t payload_hdrlen; }
int payload_datalen;
int multiple; #define GETLEN2b(bits) (((bits) == 0x03) ? 4 : bits)
uint32_t replicated_length;
uint32_t media_object_number; #define GETVALUE2b(bits, data) \
uint32_t media_object_offset; (((bits) != 0x03) ? ((bits) != 0x02) ? ((bits) != 0x01) ? \
uint32_t bytesread = 0; 0 : *(data) : get_short_le(data) : get_long_le(data))
uint8_t* buf;
size_t bufsize; static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packetlength, asf_waveformatex_t* wfx)
int i; {
rt_uint8_t tmp8, packet_flags, packet_property;
/* rt_kprintf("Reading new packet at %d bytes ", (int)ci->curpos); */ int stream_id;
int ec_length, opaque_data, ec_length_type;
if (ci->read_filebuf(&tmp8, 1) == 0) int datalen;
{ rt_uint8_t data[18];
return ASF_ERROR_EOF; rt_uint8_t* datap;
} rt_uint32_t length;
bytesread++; rt_uint32_t padding_length;
rt_uint32_t send_time;
/* TODO: We need a better way to detect endofstream */ rt_uint16_t duration;
if (tmp8 != 0x82) rt_uint16_t payload_count;
{ int payload_length_type;
rt_kprintf("Read failed: packet did not sync\n"); rt_uint32_t payload_hdrlen;
return -1; int payload_datalen;
} int multiple;
rt_uint32_t replicated_length;
if (tmp8 & 0x80) rt_uint32_t media_object_number;
{ rt_uint32_t media_object_offset;
ec_length = tmp8 & 0x0f; rt_uint32_t bytesread = 0;
opaque_data = (tmp8 >> 4) & 0x01; rt_uint8_t* buf;
ec_length_type = (tmp8 >> 5) & 0x03; size_t bufsize;
int i;
if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) /*DEBUGF("Reading new packet at %d bytes ", (int)ci->curpos);*/
{
rt_kprintf("incorrect error correction flags\n"); if (ci->read_filebuf(&tmp8, 1) == 0) {
return ASF_ERROR_INVALID_VALUE; return ASF_ERROR_EOF;
} }
bytesread++;
/* Skip ec_data */
ci->advance_buffer(ec_length); /* TODO: We need a better way to detect endofstream */
bytesread += ec_length; if (tmp8 != 0x82) {
} DEBUGF("Read failed: packet did not sync\n");
else return -1;
{ }
ec_length = 0;
}
if (tmp8 & 0x80) {
if (ci->read_filebuf(&packet_flags, 1) == 0) ec_length = tmp8 & 0x0f;
{ opaque_data = (tmp8 >> 4) & 0x01;
return ASF_ERROR_EOF; ec_length_type = (tmp8 >> 5) & 0x03;
}
if (ci->read_filebuf(&packet_property, 1) == 0) if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) {
{ DEBUGF("incorrect error correction flags\n");
return ASF_ERROR_EOF; return ASF_ERROR_INVALID_VALUE;
} }
bytesread += 2;
/* Skip ec_data */
datalen = GETLEN2b((packet_flags >> 1) & 0x03) + ci->advance_buffer(ec_length);
GETLEN2b((packet_flags >> 3) & 0x03) + bytesread += ec_length;
GETLEN2b((packet_flags >> 5) & 0x03) + 6; } else {
ec_length = 0;
if (ci->read_filebuf(data, datalen) == 0) }
{
return ASF_ERROR_EOF; 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;
bytesread += datalen;
datalen = GETLEN2b((packet_flags >> 1) & 0x03) +
datap = data; GETLEN2b((packet_flags >> 3) & 0x03) +
length = GETVALUE2b((packet_flags >> 5) & 0x03, datap); GETLEN2b((packet_flags >> 5) & 0x03) + 6;
datap += GETLEN2b((packet_flags >> 5) & 0x03);
/* sequence value is not used */ if (ci->read_filebuf(data, datalen) == 0) {
GETVALUE2b((packet_flags >> 1) & 0x03, datap); return ASF_ERROR_EOF;
datap += GETLEN2b((packet_flags >> 1) & 0x03); }
padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap);
datap += GETLEN2b((packet_flags >> 3) & 0x03); bytesread += datalen;
send_time = get_long_le(datap);
datap += 4; datap = data;
duration = get_short_le(datap); length = GETVALUE2b((packet_flags >> 5) & 0x03, datap);
datap += 2; datap += GETLEN2b((packet_flags >> 5) & 0x03);
/* rt_kprintf("and duration %d ms\n", duration); */ /* sequence value is not used */
GETVALUE2b((packet_flags >> 1) & 0x03, datap);
/* this is really idiotic, packet length can (and often will) be datap += GETLEN2b((packet_flags >> 1) & 0x03);
* undefined and we just have to use the header packet size as the size padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap);
* value */ datap += GETLEN2b((packet_flags >> 3) & 0x03);
if (!((packet_flags >> 5) & 0x03)) send_time = get_long_le(datap);
{ datap += 4;
length = wfx->packet_size; duration = get_short_le(datap);
} datap += 2;
/*DEBUGF("and duration %d ms\n", duration);*/
/* this is also really idiotic, if packet length is smaller than packet
* size, we need to manually add the additional bytes into padding length /* 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
if (length < wfx->packet_size) * value */
{ if (!((packet_flags >> 5) & 0x03)) {
padding_length += wfx->packet_size - length; length = wfx->packet_size;
length = wfx->packet_size; }
}
/* this is also really idiotic, if packet length is smaller than packet
if (length > wfx->packet_size) * size, we need to manually add the additional bytes into padding length
{ */
rt_kprintf("packet with too big length value\n"); if (length < wfx->packet_size) {
return ASF_ERROR_INVALID_LENGTH; padding_length += wfx->packet_size - length;
} length = wfx->packet_size;
}
/* check if we have multiple payloads */
if (packet_flags & 0x01) if (length > wfx->packet_size) {
{ DEBUGF("packet with too big length value\n");
if (ci->read_filebuf(&tmp8, 1) == 0) return ASF_ERROR_INVALID_LENGTH;
{ }
return ASF_ERROR_EOF;
} /* check if we have multiple payloads */
payload_count = tmp8 & 0x3f; if (packet_flags & 0x01) {
payload_length_type = (tmp8 >> 6) & 0x03; if (ci->read_filebuf(&tmp8, 1) == 0) {
bytesread++; return ASF_ERROR_EOF;
} }
else payload_count = tmp8 & 0x3f;
{ payload_length_type = (tmp8 >> 6) & 0x03;
payload_count = 1; bytesread++;
payload_length_type = 0x02; /* not used */ } 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); if (length < bytesread) {
/* FIXME: should this be checked earlier? */ DEBUGF("header exceeded packet size, invalid file - length=%d, bytesread=%d\n",(int)length,(int)bytesread);
return ASF_ERROR_INVALID_LENGTH; /* 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. /* We now parse the individual payloads, and move all payloads
*/ belonging to our audio stream to a contiguous block, starting at
*audiobuf = NULL; the location of the first payload.
*audiobufsize = 0; */
*packetlength = length - bytesread;
*audiobuf = NULL;
buf = ci->request_buffer(&bufsize, length); *audiobufsize = 0;
datap = buf; *packetlength = length - bytesread;
if (bufsize != length) buf = ci->request_buffer(&bufsize, length);
{ datap = buf;
/* This should only happen with packets larger than 32KB (the
guard buffer size). All the streams I've seen have if (bufsize != length) {
relatively small packets less than about 8KB), but I don't /* This should only happen with packets larger than 32KB (the
know what is expected. guard buffer size). All the streams I've seen have
*/ relatively small packets less than about 8KB), but I don't
rt_kprintf("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n", know what is expected.
(int)length,(int)bufsize,(int)ci->curpos); */
return -1; 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; for (i=0; i<payload_count; i++) {
datap++; stream_id = datap[0]&0x7f;
bytesread++; datap++;
bytesread++;
payload_hdrlen = GETLEN2b(packet_property & 0x03) +
GETLEN2b((packet_property >> 2) & 0x03) + payload_hdrlen = GETLEN2b(packet_property & 0x03) +
GETLEN2b((packet_property >> 4) & 0x03); GETLEN2b((packet_property >> 2) & 0x03) +
GETLEN2b((packet_property >> 4) & 0x03);
// rt_kprintf("payload_hdrlen = %d\n",payload_hdrlen);
if (payload_hdrlen > sizeof(data)) //DEBUGF("payload_hdrlen = %d\n",payload_hdrlen);
{ if (payload_hdrlen > sizeof(data)) {
rt_kprintf("Unexpectedly long datalen in data - %d\n",datalen); DEBUGF("Unexpectedly long datalen in data - %d\n",datalen);
return ASF_ERROR_OUTOFMEM; return ASF_ERROR_OUTOFMEM;
} }
bytesread += payload_hdrlen; bytesread += payload_hdrlen;
media_object_number = GETVALUE2b((packet_property >> 4) & 0x03, datap); media_object_number = GETVALUE2b((packet_property >> 4) & 0x03, datap);
datap += GETLEN2b((packet_property >> 4) & 0x03); datap += GETLEN2b((packet_property >> 4) & 0x03);
media_object_offset = GETVALUE2b((packet_property >> 2) & 0x03, datap); media_object_offset = GETVALUE2b((packet_property >> 2) & 0x03, datap);
datap += GETLEN2b((packet_property >> 2) & 0x03); datap += GETLEN2b((packet_property >> 2) & 0x03);
replicated_length = GETVALUE2b(packet_property & 0x03, datap); replicated_length = GETVALUE2b(packet_property & 0x03, datap);
datap += GETLEN2b(packet_property & 0x03); datap += GETLEN2b(packet_property & 0x03);
/* TODO: Validate replicated_length */ /* TODO: Validate replicated_length */
/* TODO: Is the content of this important for us? */ /* TODO: Is the content of this important for us? */
datap += replicated_length; datap += replicated_length;
bytesread += replicated_length; bytesread += replicated_length;
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 */
{ return ASF_ERROR_INVALID_VALUE;
/* in multiple payloads datalen should be a word */ }
return ASF_ERROR_INVALID_VALUE;
} payload_datalen = GETVALUE2b(payload_length_type, datap);
datap += x;
payload_datalen = GETVALUE2b(payload_length_type, datap); bytesread += x;
datap += x; } else {
bytesread += x; payload_datalen = length - bytesread - padding_length;
} }
else
{ if (replicated_length==1)
payload_datalen = length - bytesread - padding_length; datap++;
}
if (stream_id == wfx->audiostream)
if (replicated_length==1) {
datap++; if (*audiobuf == NULL) {
/* The first payload can stay where it is */
if (stream_id == wfx->audiostream) *audiobuf = datap;
{ *audiobufsize = payload_datalen;
if (*audiobuf == NULL) } else {
{ /* The second and subsequent payloads in this packet
/* The first payload can stay where it is */ that belong to the audio stream need to be moved to be
*audiobuf = datap; contiguous with the first payload.
*audiobufsize = payload_datalen; */
} memmove(*audiobuf + *audiobufsize, datap, payload_datalen);
else *audiobufsize += payload_datalen;
{ }
/* The second and subsequent payloads in this packet }
that belong to the audio stream need to be moved to be datap += payload_datalen;
contiguous with the first payload. bytesread += payload_datalen;
*/ }
memmove(*audiobuf + *audiobufsize, datap, payload_datalen);
*audiobufsize += payload_datalen; if (*audiobuf != NULL)
} return 1;
} else
datap += payload_datalen; return 0;
bytesread += payload_datalen; }
}
/* this is the codec entry point */
if (*audiobuf != NULL) void wma_codec_main(void)
return 1; {
else rt_uint32_t elapsedtime;
return 0; int retval;
} asf_waveformatex_t wfx;
size_t resume_offset;
/* this is the codec entry point */ int i;
void wma_run(void) int wmares, res;
{ rt_uint8_t* audiobuf;
uint32_t elapsedtime; int audiobufsize;
int retval; int packetlength = 0;
asf_waveformatex_t wfx; int errcount = 0;
size_t resume_offset;
int i; /* Remember the resume position - when the codec is opened, the
int wmares, res; playback engine will reset it. */
uint8_t* audiobuf; resume_offset = ci->id3->offset;
int audiobufsize;
int packetlength = 0; /* Copy the format metadata we've stored in the id3 TOC field. This
int errcount = 0; saves us from parsing it again here. */
memcpy(&wfx, ci->id3->toc, sizeof(wfx));
/* Generic codec initialisation */
next_track: if (wma_decode_init(&wmadec,&wfx) < 0) {
LOGF("WMA: Unsupported or corrupt file\n");
retval = CODEC_OK; retval = CODEC_ERROR;
goto exit;
/* Remember the resume position - when the codec is opened, the }
playback engine will reset it. */
resume_offset = ci->id3->offset; DEBUGF("**************** IN WMA.C ******************\n");
restart_track: if (resume_offset > ci->id3->first_frame_offset)
if (codec_init()) {
{ /* Get start of current packet */
LOGF("WMA: Error initialising codec\n"); int packet_offset = (resume_offset - ci->id3->first_frame_offset)
retval = CODEC_ERROR; % wfx.packet_size;
goto exit; ci->seek_buffer(resume_offset - packet_offset);
} elapsedtime = get_timestamp(&i);
ci->set_elapsed(elapsedtime);
/* Copy the format metadata we've stored in the id3 TOC field. This }
saves us from parsing it again here. */ else
memcpy(&wfx, ci->id3->toc, sizeof(wfx)); {
/* Now advance the file position to the first frame */
if (wma_decode_init(&wmadec,&wfx) < 0) ci->seek_buffer(ci->id3->first_frame_offset);
{ elapsedtime = 0;
LOGF("WMA: Unsupported or corrupt file\n"); }
retval = CODEC_ERROR;
goto exit; resume_offset = 0;
} ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate);
ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?
if (resume_offset > ci->id3->first_frame_offset) STEREO_MONO : STEREO_INTERLEAVED);
{ codec_set_replaygain(ci->id3);
/* Get start of current packet */
int packet_offset = (resume_offset - ci->id3->first_frame_offset) /* The main decoding loop */
% wfx.packet_size;
ci->seek_buffer(resume_offset - packet_offset); res = 1;
elapsedtime = get_timestamp(&i); while (res >= 0)
ci->set_elapsed(elapsedtime); {
} errcount = 0;
else new_packet:
{ res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
/* Now advance the file position to the first frame */
ci->seek_buffer(ci->id3->first_frame_offset); if (res < 0) {
elapsedtime = 0; /* We'll try to recover from a parse error a certain number of
} * times. If we succeed, the error counter will be reset.
*/
resume_offset = 0; errcount++;
DEBUGF("read_packet error %d, errcount %d\n",wmares, errcount);
/* The main decoding loop */ if (errcount > 5) {
goto done;
res = 1; } else {
while (res >= 0) ci->advance_buffer(packetlength);
{ goto new_packet;
/* Deal with any pending seek requests */ }
errcount = 0; } else if (res > 0) {
wma_decode_superframe_init(&wmadec, audiobuf, audiobufsize);
new_packet:
res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx); for (i=0; i < wmadec.nb_frames; i++)
{
if (res < 0) wmares = wma_decode_superframe_frame(&wmadec,
{ decoded,
/* We'll try to recover from a parse error a certain number of audiobuf, audiobufsize);
* times. If we succeed, the error counter will be reset.
*/ if (wmares < 0) {
/* Do the above, but for errors in decode. */
errcount++; errcount++;
rt_kprintf("read_packet 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 {
} ci->advance_buffer(packetlength);
else goto new_packet;
{ }
ci->advance_buffer(packetlength); } else if (wmares > 0) {
goto new_packet; /* write to audio device */
} elapsedtime += (wmares*10)/(wfx.rate/100);
} ci->set_elapsed(elapsedtime);
else if (res > 0) }
{ ci->yield();
wma_decode_superframe_init(&wmadec, audiobuf, audiobufsize); }
}
for (i=0; i < wmadec.nb_frames; i++)
{ ci->advance_buffer(packetlength);
wmares = wma_decode_superframe_frame(&wmadec, }
decoded,
audiobuf, audiobufsize); done:
exit:
if (wmares < 0) return ;
{ }
/* 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)