// 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 #include #include #include #include #include #include "../../deadbeef.h" #include #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 #define SKIP_BYTES(fd,x) {\ if (x > 0) {\ char buf[x];\ deadbeef->fread (buf, x, 1, fd);\ }\ } 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); SKIP_BYTES(fd,length - 4); return (int)tmp32; } else if (type == 4) { read_uint64le(fd, &tmp64); SKIP_BYTES(fd,length - 8); return (int)tmp64; } else if (type == 5) { read_uint16le(fd, &tmp16); SKIP_BYTES(fd,length - 2); 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 */ SKIP_BYTES(fd, utf16bytes); } 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 = 0; int i = 0; int fileprop = 0; uint64_t play_duration = 0; uint16_t flags = 0; uint32_t subobjects = 0; uint8_t utf8buf[512]; int codectype = 0; unsigned char id3buffer[2048]; unsigned char *id3buf = id3buffer; int id3buf_remaining = sizeof(id3buffer); memset (¤t, 0, sizeof (current)); memset (&header, 0, sizeof (header)); memset (&utf8buf, 0, sizeof (utf8buf)); memset (&id3buffer, 0, 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? */ SKIP_BYTES(fd, 2); 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; DEBUGF("datalen: %d\n", 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=%lld, datalen=%lld\n",current.size,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) */ SKIP_BYTES(fd, 32); 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 */ // SKIP_BYTES(fd, 20); read_uint32le(fd, &wfx->packet_size); read_uint32le(fd, &wfx->max_packet_size); /* Skip bytes remaining in object */ SKIP_BYTES(fd, current.size - 24 - 72 - 4); } 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); SKIP_BYTES(fd, 24); read_uint32le(fd, &propdatalen); SKIP_BYTES(fd, 4); read_uint16le(fd, &flags); if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) { DEBUGF("Found stream properties for non audio stream, skipping\n"); SKIP_BYTES(fd,current.size - 24 - 50); } else if (wfx->audiostream == -1) { SKIP_BYTES(fd, 4); 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); SKIP_BYTES(fd,current.size - 24 - 72 - 4); wfx->audiostream = flags&0x7f; } else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) { deadbeef->fread(wfx->data, 6, 1, fd); SKIP_BYTES(fd,current.size - 24 - 72 - 6); 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); SKIP_BYTES(fd,current.size - 24 - 72 - wfx->datalen); 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); SKIP_BYTES(fd,current.size - 24 - 72 - wfx->datalen); wfx->audiostream = flags&0x7f; codectype = AFMT_WMAVOICE; } else { trace("Unsupported WMA codec (Lossless, Voice, etc)\n"); SKIP_BYTES(fd,current.size - 24 - 72); } } } 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); } SKIP_BYTES(fd, strlength[2]); /* 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); } SKIP_BYTES(fd, strlength[4]); /* 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 { SKIP_BYTES(fd, length); } } 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 { SKIP_BYTES(fd, length); } } 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 { SKIP_BYTES(fd, length); } } 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 ((!strcasecmp("foobar2000/cuesheet", utf8buf)) && (type == 0)) { unsigned char *s = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); deadbeef->pl_append_meta (it, "cuesheet", 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 { SKIP_BYTES(fd, length); } } 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. */ SKIP_BYTES(fd, -4); deadbeef->fread(&type, 1, 1, fd); if (type == 1) { SKIP_BYTES(fd, 3); 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; SKIP_BYTES(fd, strlength-32); 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 = SKIP_BYTES(fd, 0); id3->albumart.size = datalength; id3->has_embedded_albumart = true; } } SKIP_BYTES(fd, datalength); #endif } else { if (type == 0) { // FIXME: custom fields -- after others work unsigned char *s = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); if (!strcmp (utf8buf, "MusicBrainz/Track Id")) { strcpy (utf8buf, "musicbrainz_trackid"); } deadbeef->pl_append_meta (it, utf8buf, s); } else { SKIP_BYTES(fd, length); } } bytesleft -= 4 + length; } SKIP_BYTES(fd, bytesleft); } 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)); SKIP_BYTES(fd,current.size - 24); } 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; if (!fd->vfs->is_streaming ()) { // check if we got a fragment if (0 != deadbeef->fseek (fd, 26, SEEK_CUR)) { DEBUGF("ASF: failed to seek to 1st frame\n"); return 0; } int duration = 0; int time = asf_get_timestamp(&duration, fd); if (time != 0) { wfx->first_frame_timestamp = time; // need to scan entire file to find out the duration int pmn = (int)(time * 0.001f / 60); int psc = time * 0.001f - pmn * 60; trace ("wma: the file is a fragment, start time: %d:%02d\n", pmn, psc); trace ("play_duration %lld, numpackets %lld, packet_size %d\n", wfx->play_duration, wfx->numpackets, wfx->packet_size); } if (wfx->play_duration == 0) { wfx->preroll = 0; wfx->numpackets = 1; // calc duration from packets (scan the file) wfx->play_duration += duration * 10000; while (0 == deadbeef->fseek (fd, *first_frame_offset + wfx->packet_size * wfx->numpackets, SEEK_SET)) { time = asf_get_timestamp(&duration, fd); if (time < 0) { break; } wfx->play_duration += duration * 10000; wfx->numpackets++; } } } return 1; }