diff options
author | waker <wakeroid@gmail.com> | 2012-12-29 23:21:19 +0100 |
---|---|---|
committer | waker <wakeroid@gmail.com> | 2013-01-12 19:37:05 +0100 |
commit | 94e2ef5704b74ebca17d9add9b6c3587514123e2 (patch) | |
tree | 5cbff3b2be9092522a0e935a3e05d6ff08a3e875 /plugins/wma/asfheader.c | |
parent | 282d6a989995509a3a3281fef43f9ca3fe6dce2a (diff) |
new wma plugin WIP
Diffstat (limited to 'plugins/wma/asfheader.c')
-rw-r--r-- | plugins/wma/asfheader.c | 814 |
1 files changed, 814 insertions, 0 deletions
diff --git a/plugins/wma/asfheader.c b/plugins/wma/asfheader.c new file mode 100644 index 00000000..95dd0299 --- /dev/null +++ b/plugins/wma/asfheader.c @@ -0,0 +1,814 @@ +// this file is a modified version for deadbeef player +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * $Id$ + * + * Copyright (C) 2007 Dave Chapman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <inttypes.h> +#include <unistd.h> +#include "../../deadbeef.h" + +#include <libasf/asf.h> + +#define MIN(x,y) ((x)<(y)?(x):(y)) +#define MAX(x,y) ((x)>(y)?(x):(y)) + +//#define trace(...) { fprintf (stderr, __VA_ARGS__); } +#define trace(fmt,...) +#define DEBUGF trace + +extern DB_functions_t *deadbeef; + +enum { + AFMT_WMAPRO = 1, + AFMT_WMAVOICE = 2, +}; + +static inline uint16_t swap16(uint16_t value) + /* + result[15..8] = value[ 7..0]; + result[ 7..0] = value[15..8]; + */ +{ + return (value >> 8) | (value << 8); +} + + +static inline uint32_t swap32(uint32_t value) + /* + result[31..24] = value[ 7.. 0]; + result[23..16] = value[15.. 8]; + result[15.. 8] = value[23..16]; + result[ 7.. 0] = value[31..24]; + */ +{ + uint32_t hi = swap16(value >> 16); + uint32_t lo = swap16(value & 0xffff); + return (lo << 16) | hi; +} + +#if WORDS_BIGENDIAN +#define read_uint16be(fd,buf) deadbeef->fread((buf), 2, 1, (fd)) +#define read_uint32be(fd,buf) deadbeef->fread((buf), 4, 1, (fd)) +#define read_uint64be(fd,buf) deadbeef->fread((buf), 8, 1, (fd)) +int read_uint16le(DB_FILE *fd, uint16_t* buf); +int read_uint32le(DB_FILE *fd, uint32_t* buf); +int read_uint64le(DB_FILE *fd, uint64_t* buf); +#define letoh16(x) swap16(x) +#define letoh32(x) swap32(x) +#define htole16(x) swap16(x) +#define htole32(x) swap32(x) +#define betoh16(x) (x) +#define betoh32(x) (x) +#define htobe16(x) (x) +#define htobe32(x) (x) +#define swap_odd_even_be32(x) swap_odd_even32(x) +#define swap_odd_even_le32(x) (x) +#else +#define letoh16(x) (x) +#define letoh32(x) (x) +#define htole16(x) (x) +#define htole32(x) (x) +#define betoh16(x) swap16(x) +#define betoh32(x) swap32(x) +//#define htobe16(x) swap16(x) +//#define htobe32(x) swap32(x) +#define swap_odd_even_be32(x) (x) +#define swap_odd_even_le32(x) swap_odd_even32(x) +int read_uint16be(DB_FILE *fd, uint16_t* buf); +int read_uint32be(DB_FILE *fd, uint32_t* buf); +int read_uint64be(DB_FILE *fd, uint64_t* buf); +#define read_uint16le(fd,buf) deadbeef->fread((buf), 2, 1, (fd)) +#define read_uint32le(fd,buf) deadbeef->fread((buf), 4, 1, (fd)) +#define read_uint64le(fd,buf) deadbeef->fread((buf), 8, 1, (fd)) +#endif + + +#if !WORDS_BIGENDIAN +/* Read an unsigned 16-bit integer from a big-endian file. */ +int read_uint16be(DB_FILE *fd, uint16_t* buf) +{ + size_t n; + + n = deadbeef->fread(buf, 1, 2, fd); + *buf = betoh16(*buf); + return n; +} +/* Read an unsigned 32-bit integer from a big-endian file. */ +int read_uint32be(DB_FILE *fd, uint32_t* buf) +{ + size_t n; + + n = deadbeef->fread(buf, 1, 4, fd); + *buf = betoh32(*buf); + return n; +} +/* Read an unsigned 64-bit integer from a big-endian file. */ +int read_uint64be(DB_FILE *fd, uint64_t* buf) +{ + size_t n; + uint8_t data[8]; + int i; + + n = deadbeef->fread(data, 1, 8, fd); + + for (i=0, *buf=0; i<=7; i++) { + *buf <<= 8; + *buf |= data[i]; + } + return n; +} +#else +/* Read unsigned integers from a little-endian file. */ +int read_uint16le(DB_FILE *fd, uint16_t* buf) +{ + size_t n; + + n = deadbeef->fread((char*) buf, 1, 2, fd); + *buf = letoh16(*buf); + return n; +} +int read_uint32le(DB_FILE *fd, uint32_t* buf) +{ + size_t n; + + n = deadbeef->fread(buf, 1, 4, fd); + *buf = letoh32(*buf); + return n; +} +int read_uint64le(DB_FILE *fd, uint64_t* buf) +{ + size_t n; + uint8_t data[8]; + int i; + + n = deadbeef->fread(data, 1, 8, fd); + + for (i=7, *buf=0; i>=0; i--) { + *buf <<= 8; + *buf |= data[i]; + } + + return n; +} +#endif + +/* TODO: Just read the GUIDs into a 16-byte array, and use memcmp to compare */ +struct guid_s { + uint32_t v1; + uint16_t v2; + uint16_t v3; + uint8_t v4[8]; +}; +typedef struct guid_s guid_t; + +struct asf_object_s { + guid_t guid; + uint64_t size; + uint64_t datalen; +}; +typedef struct asf_object_s asf_object_t; + +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) || + (memcmp(guid1->v4, guid2->v4, 8))) { + return 0; + } + + return 1; +} + +/* Read the 16 byte GUID from a file */ +static void asf_readGUID(DB_FILE *fd, guid_t* guid) +{ + read_uint32le(fd, &guid->v1); + read_uint16le(fd, &guid->v2); + read_uint16le(fd, &guid->v3); + deadbeef->fread(guid->v4, 8, 1, fd); +} + +static void asf_read_object_header(asf_object_t *obj, DB_FILE *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(DB_FILE *fd, int type, int length) +{ + uint16_t tmp16; + uint32_t tmp32; + uint64_t tmp64; + + if (type == 3) { + read_uint32le(fd, &tmp32); + deadbeef->fseek(fd,length - 4,SEEK_CUR); + return (int)tmp32; + } else if (type == 4) { + read_uint64le(fd, &tmp64); + deadbeef->fseek(fd,length - 8,SEEK_CUR); + return (int)tmp64; + } else if (type == 5) { + read_uint16le(fd, &tmp16); + deadbeef->fseek(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. +*/ +#define MASK 0xC0 /* 11000000 */ +#define COMP 0x80 /* 10x */ + +static const unsigned char utf8comp[6] = +{ + 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC +}; + +/* Encode a UCS value as UTF-8 and return a pointer after this UTF-8 char. */ +unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8) +{ + int tail = 0; + + if (ucs > 0x7F) + while (ucs >> (5*tail + 6)) + tail++; + + *utf8++ = (ucs >> (6*tail)) | utf8comp[tail]; + while (tail--) + *utf8++ = ((ucs >> (6*tail)) & (MASK ^ 0xFF)) | COMP; + + return utf8; +} + + +static void asf_utf16LEdecode(DB_FILE *fd, + 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 = deadbeef->fread(utf16buf, 1, MIN(sizeof(utf16buf), utf16bytes), fd); + 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 = deadbeef->fread(utf16buf + 2, 1, MIN(sizeof(utf16buf)-2, utf16bytes), fd); + 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 = deadbeef->fread(utf16buf, 1, MIN(sizeof(utf16buf), utf16bytes), fd); + utf16 = utf16buf; + utf16bytes -= n; + } + } + + *utf8[0] = 0; + --*utf8bytes; + + if (utf16bytes > 0) { + /* Skip any remaining bytes */ + deadbeef->fseek(fd, utf16bytes, SEEK_CUR); + } + return; +} + +static int asf_parse_header(DB_FILE *fd, asf_waveformatex_t* wfx, DB_playItem_t *it) +{ + asf_object_t current; + asf_object_t header; + uint64_t datalen; + int i; + int fileprop = 0; + uint64_t play_duration; + uint16_t flags; + uint32_t subobjects; + uint8_t utf8buf[512]; + int codectype = 0; + unsigned char id3buffer[2048]; + unsigned char *id3buf = id3buffer; + int id3buf_remaining = sizeof(id3buffer); + + 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? */ + deadbeef->fseek(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; + + /* Get the number of logical packets - uint64_t at offset 32 + * (Big endian byte order) */ + deadbeef->fseek(fd, 32, SEEK_CUR); + read_uint64le(fd, &wfx->numpackets); + read_uint64le(fd, &wfx->play_duration); + read_uint64le(fd, &wfx->send_duration); + read_uint64le(fd, &wfx->preroll); + read_uint32le(fd, &wfx->flags); + +// DEBUGF("****** length = %lld\n", wfx->play_duration); + + /* Read the packet size - uint32_t at offset 68 */ +// deadbeef->fseek(fd, 20, SEEK_CUR); + read_uint32le(fd, &wfx->packet_size); + read_uint32le(fd, &wfx->max_packet_size); + + /* Skip bytes remaining in object */ + deadbeef->fseek(fd, current.size - 24 - 72 - 4, SEEK_CUR); + } else if (asf_guid_match(¤t.guid, &asf_guid_stream_properties)) { + guid_t guid; + uint32_t propdatalen; + + if (current.size < 78) + return ASF_ERROR_OBJECT_SIZE; + +#if 0 + asf_byteio_getGUID(&guid, current->data); + datalen = asf_byteio_getDWLE(current->data + 40); + flags = asf_byteio_getWLE(current->data + 48); +#endif + + asf_readGUID(fd, &guid); + + deadbeef->fseek(fd, 24, SEEK_CUR); + read_uint32le(fd, &propdatalen); + deadbeef->fseek(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"); + deadbeef->fseek(fd,current.size - 24 - 50,SEEK_CUR); + } else if (wfx->audiostream == -1) { + deadbeef->fseek(fd, 4, SEEK_CUR); + DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f); + + if (propdatalen < 18) { + return ASF_ERROR_INVALID_LENGTH; + } + +#if 0 + if (asf_byteio_getWLE(data + 16) > datalen - 16) { + return ASF_ERROR_INVALID_LENGTH; + } +#endif + 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); + + if (wfx->codec_id == ASF_CODEC_ID_WMAV1) { + deadbeef->fread(wfx->data, 4, 1, fd); + deadbeef->fseek(fd,current.size - 24 - 72 - 4,SEEK_CUR); + wfx->audiostream = flags&0x7f; + } else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) { + deadbeef->fread(wfx->data, 6, 1, fd); + deadbeef->fseek(fd,current.size - 24 - 72 - 6,SEEK_CUR); + wfx->audiostream = flags&0x7f; + } else if (wfx->codec_id == ASF_CODEC_ID_WMAPRO) { + /* wma pro decoder needs the extra-data */ + deadbeef->fread(wfx->data, wfx->datalen, 1, fd); + deadbeef->fseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); + wfx->audiostream = flags&0x7f; + /* Correct codectype to redirect playback to the proper .codec */ + codectype = AFMT_WMAPRO; + } else if (wfx->codec_id == ASF_CODEC_ID_WMAVOICE) { + deadbeef->fread(wfx->data, wfx->datalen, 1, fd); + deadbeef->fseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); + wfx->audiostream = flags&0x7f; + codectype = AFMT_WMAVOICE; + } else { + trace("Unsupported WMA codec (Lossless, Voice, etc)\n"); + deadbeef->fseek(fd,current.size - 24 - 72,SEEK_CUR); + } + } + } else if (it && 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 + */ + 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 */ + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "title", s); + } + + if (strlength[1] > 0) { /* 1 - Artist */ + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "artist", s); + } + + deadbeef->fseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */ + + if (strlength[3] > 0) { /* 3 - description */ + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "comment", s); + } + + deadbeef->fseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */ + } else if (it && asf_guid_match(¤t.guid, &asf_guid_extended_content_description)) { + 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++) { + 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); + trace ("ext md id: %s\n", utf8buf); + + if (!strcmp("WM/TrackNumber",utf8buf)) { + if (type == 0) { + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "track", s); + } else if ((type >=2) && (type <= 5)) { + int tracknum = asf_intdecode(fd, type, length); + char n[100]; + snprintf (n, sizeof (n), "%d", tracknum); + deadbeef->pl_append_meta (it, "track", n); + } else { + deadbeef->fseek(fd, length, SEEK_CUR); + } + } else if (!strcmp("TotalTracks",utf8buf)) { + if (type == 0) { + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "numtracks", s); + } else if ((type >=2) && (type <= 5)) { + int tracknum = asf_intdecode(fd, type, length); + char n[100]; + snprintf (n, sizeof (n), "%d", tracknum); + deadbeef->pl_append_meta (it, "numtracks", n); + } else { + deadbeef->fseek(fd, length, SEEK_CUR); + } + } else if (!strcmp("WM/PartOfSet",utf8buf)) { + if (type == 0) { + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "disc", s); + } else if ((type >=2) && (type <= 5)) { + int tracknum = asf_intdecode(fd, type, length); + char n[100]; + snprintf (n, sizeof (n), "%d", tracknum); + deadbeef->pl_append_meta (it, "disc", n); + } else { + deadbeef->fseek(fd, length, SEEK_CUR); + } + } else if ((!strcmp("WM/Genre", utf8buf)) && (type == 0)) { + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "genre", s); + } else if ((!strcmp("WM/AlbumTitle", utf8buf)) && (type == 0)) { + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "album", s); + } else if ((!strcmp("WM/AlbumArtist", utf8buf)) && (type == 0)) { + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "albumartist", s); + } else if ((!strcmp("WM/Composer", utf8buf)) && (type == 0)) { + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "composer", s); + } else if (!strcmp("WM/Year", utf8buf)) { + if (type == 0) { + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "year", s); + } else if ((type >=2) && (type <= 5)) { + int year = asf_intdecode(fd, type, length); + char n[100]; + snprintf (n, sizeof (n), "%d", year); + deadbeef->pl_append_meta (it, "year", n); + } else { + deadbeef->fseek(fd, length, SEEK_CUR); + } + } else if (!strncmp("replaygain_", utf8buf, 11)) { + char *s = utf8buf; + char *value = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + // parse_replaygain(utf8buf, value, id3); + if (!strncasecmp (s, "replaygain_album_gain", 21)) { + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, atof (value)); + } + else if (!strncasecmp (s, "replaygain_album_peak", 21)) { + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, atof (value)); + } + else if (!strncasecmp (s, "replaygain_track_gain", 21)) { + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, atof (value)); + } + else if (!strncasecmp (s, "replaygain_track_peak", 21)) { + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, atof (value)); + } + } else if (!strcmp("MusicBrainz/Track Id", utf8buf)) { + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, "MusicBrainzId", s); +#ifdef HAVE_ALBUMART + } else if (!strcmp("WM/Picture", utf8buf)) { + uint32_t datalength, strlength; + /* Expected is either "01 00 xx xx 03 yy yy yy yy" or + * "03 yy yy yy yy". xx is the size of the WM/Picture + * container in bytes. yy equals the raw data length of + * the embedded image. */ + deadbeef->fseek(fd, -4, SEEK_CUR); + deadbeef->fread(&type, 1, 1, fd); + if (type == 1) { + deadbeef->fseek(fd, 3, SEEK_CUR); + deadbeef->fread(&type, 1, 1, fd); + /* In case the parsing will fail in the next step we + * might at least be able to skip the whole section. */ + datalength = length - 1; + } + if (type == 3) { + /* Read the raw data length of the embedded image. */ + read_uint32le(fd, &datalength); + + /* Reset utf8 buffer */ + utf8 = utf8buf; + utf8length = 512; + + /* Gather the album art format, this string has a + * double zero-termination. */ + asf_utf16LEdecode(fd, 32, &utf8, &utf8length); + strlength = (strlen(utf8buf) + 2) * 2; + deadbeef->fseek(fd, strlength-32, SEEK_CUR); + if (!strcmp("image/jpeg", utf8buf)) { + id3->albumart.type = AA_TYPE_JPG; + } else if (!strcmp("image/jpg", utf8buf)) { + /* image/jpg is technically invalid, + * but it does occur in the wild */ + id3->albumart.type = AA_TYPE_JPG; + } else if (!strcmp("image/png", utf8buf)) { + id3->albumart.type = AA_TYPE_PNG; + } else { + id3->albumart.type = AA_TYPE_UNKNOWN; + } + + /* Set the album art size and position. */ + if (id3->albumart.type != AA_TYPE_UNKNOWN) { + id3->albumart.pos = deadbeef->fseek(fd, 0, SEEK_CUR); + id3->albumart.size = datalength; + id3->has_embedded_albumart = true; + } + } + + deadbeef->fseek(fd, datalength, SEEK_CUR); +#endif + } else { + if (type == 0) { // FIXME: custom fields -- after others work + unsigned char *s = id3buf; + asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + deadbeef->pl_append_meta (it, utf8buf, s); + } + else { + deadbeef->fseek(fd, length, SEEK_CUR); + } + } + bytesleft -= 4 + length; + } + + deadbeef->fseek(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)); + deadbeef->fseek(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); + } + +#if 0 + tmp = asf_parse_header_validate(file, &header); + if (tmp < 0) { + /* header read ok but doesn't validate correctly */ + return tmp; + } +#endif + + DEBUGF("header validated correctly\n"); + + return 0; +} + +int get_asf_metadata(DB_FILE *fd, DB_playItem_t *it, asf_waveformatex_t *wfx, int64_t *first_frame_offset) +{ + int res; + asf_object_t obj; + + wfx->audiostream = -1; + + res = asf_parse_header(fd, wfx, it); + + if (res < 0) { + DEBUGF("ASF: parsing error - %d\n",res); + return 0; + } + + if (wfx->audiostream == -1) { + DEBUGF("ASF: No WMA streams found\n"); + return 0; + } + + asf_read_object_header(&obj, fd); + + if (!asf_guid_match(&obj.guid, &asf_guid_data)) { + DEBUGF("ASF: No data object found\n"); + return 0; + } + + /* 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. + */ + *first_frame_offset = deadbeef->ftell(fd) + 26; + + return 1; +} |