/** * Takes an initialized asf_file_t structure file as a parameter. Allocates * a new asf_object_header_t in file->header and uses the file->iostream to * read all fields and subobjects into it. Finally calls the * asf_parse_header_validate function to validate the values and parse the * commonly used values into the asf_file_t struct itself. */ int asf_parse_header(asf_file_t *file) { asf_object_header_t *header; asf_iostream_t *iostream; uint8_t hdata[30]; int tmp; file->header = NULL; iostream = &file->iostream; /* object minimum is 24 bytes and header needs to have * the subobject count field and two reserved fields */ tmp = asf_byteio_read(iostream, hdata, 30); if (tmp < 0) { /* not enough data to read the header object */ return tmp; } file->header = malloc(sizeof(asf_object_header_t)); header = file->header; if (!header) { return ASF_ERROR_OUTOFMEM; } /* read the object and check its size value */ asf_parse_read_object((asfint_object_t *) header, hdata); if (header->size < 30) { /* invalid size for header object */ return ASF_ERROR_INVALID_OBJECT_SIZE; } /* read header object specific compulsory fields */ header->subobjects = GetDWLE(hdata + 24); header->reserved1 = hdata[28]; header->reserved2 = hdata[29]; /* clear header extension object and subobject list */ header->ext = NULL; header->first = NULL; header->last = NULL; /* the header data needs to be allocated for reading */ header->datalen = header->size - 30; header->data = malloc(header->datalen * sizeof(uint8_t)); if (!header->data) { return ASF_ERROR_OUTOFMEM; } tmp = asf_byteio_read(iostream, header->data, header->datalen); if (tmp < 0) { return tmp; } if (header->subobjects > 0) { uint64_t datalen; uint8_t *data; int i; debug_printf("starting to read subobjects"); /* use temporary variables for use during the read */ datalen = header->datalen; data = header->data; for (i=0; i<header->subobjects; i++) { asfint_object_t *current; if (datalen < 24) { /* not enough data for reading object */ break; } current = malloc(sizeof(asfint_object_t)); if (!current) { return ASF_ERROR_OUTOFMEM; } asf_parse_read_object(current, data); if (current->size > datalen || current->size < 24) { /* invalid object size */ break; } /* Check if the current subobject is a header extension * object or just a normal subobject */ if (current->type == GUID_HEADER_EXTENSION && !header->ext) { int ret; asf_object_headerext_t *headerext; /* we handle header extension separately because it has * some subobjects as well */ current = realloc(current, sizeof(asf_object_headerext_t)); headerext = (asf_object_headerext_t *) current; headerext->first = NULL; headerext->last = NULL; ret = asf_parse_headerext(headerext, data, datalen); if (ret < 0) { /* error parsing header extension */ return ret; } header->ext = headerext; } else { if (current->type == GUID_HEADER_EXTENSION) { debug_printf("WARNING! Second header extension object found, ignoring it!"); } current->datalen = current->size - 24; current->data = data + 24; /* add to list of subobjects */ if (!header->first) { header->first = current; header->last = current; } else { header->last->next = current; header->last = current; } } data += current->size; datalen -= current->size; } if (i != header->subobjects || datalen != 0) { /* header data size doesn't match given subobject count */ return ASF_ERROR_INVALID_VALUE; } debug_printf("%d subobjects read successfully", i); } tmp = asf_parse_header_validate(file, file->header); if (tmp < 0) { /* header read ok but doesn't validate correctly */ return tmp; } debug_printf("header validated correctly"); return header->size; }
/** * Takes an initialized asf_file_t structure file as a parameter. Allocates * a new asf_object_header_t in file->header and uses the file->iostream to * read all fields and subobjects into it. Finally calls the * asf_parse_header_validate function to validate the values and parse the * commonly used values into the asf_file_t struct itself. */ int ASFParser::asf_parse_header() { asf_object_header_t *header = NULL; asf_iostream_t *iostream = NULL; uint8_t hdata[30]; int tmp; file->header = NULL; iostream = &file->iostream; /* object minimum is 24 bytes and header needs to have * the subobject count field and two reserved fields */ memset(hdata, 0, 30); tmp = ASFByteIO::asf_byteio_read(hdata, 30, iostream); if (tmp < 0) { /* not enough data to read the header object */ ALOGE("asf_parse_header:error 1\n"); return tmp; } file->header = (asf_object_header_t*)malloc(sizeof(asf_object_header_t)); if (!file->header) { return ASF_ERROR_OUTOFMEM; } memset(file->header, 0, sizeof(asf_object_header_t)); header = file->header; /* clear header extension object and subobject list */ header->ext = NULL; header->first = NULL; header->last = NULL; /* read the object and check its size value */ asf_parse_read_object((asfint_object_t *) header, hdata); if (header->size < 30) { /* invalid size for header object */ ALOGE("asf_parse_header:error 2\n"); return ASF_ERROR_OBJECT_SIZE; } /* to check not ASF file type */ if (header->type == GUID_UNKNOWN) { /* invalid GUID for header object */ return ASF_ERROR_INVALID_VALUE; } /* read header object specific compulsory fields */ header->subobjects = ASFByteIO::asf_byteio_getDWLE(hdata + 24);//sub header object number header->reserved1 = hdata[28]; header->reserved2 = hdata[29]; header->datalen = header->size - 30; //seek to Data Object and check if Data Object follows by Header Object { file->iostream.seek(iostream->source, header->size); //read 50B from data_object asfint_object_t *data = NULL; uint8_t ddata[50]; int tmp; memset(ddata, 0, 50); /* object minimum is 24 bytes and data object needs to have * 26 additional bytes for its internal fields */ tmp = ASFByteIO::asf_byteio_read(ddata, 50, iostream); if (tmp < 0) { ALOGI("read data 50B error"); return tmp; } data = (asfint_object_t*)calloc(1,sizeof(asfint_object_t)); if (!data) { return ASF_ERROR_OUTOFMEM; } asf_parse_read_object((asfint_object_t *)data, ddata); /* read the object and check its size value */ if (data->size < 50) { /* invalid size for data object */ ALOGI("ASF Data Object Size Error"); return ASF_ERROR_OBJECT_SIZE; } else if (data->type != GUID_DATA) { ALOGE("Data Object Not follow by Header Object"); return ASF_ERROR_INVALID_OBJECT; } } //seek to Header Object's data, and read header->data from iostream file->iostream.seek(iostream->source, 30); header->data = (uint8_t*)malloc(header->datalen * sizeof(uint8_t)); if (!header->data) { return ASF_ERROR_OUTOFMEM; } memset(header->data, 0, header->datalen * sizeof(uint8_t)); tmp = ASFByteIO::asf_byteio_read(header->data, header->datalen, iostream); if (tmp < 0) { ALOGE("asf_parse_header:error 3\n"); return tmp; } if (header->subobjects > 0) { uint64_t datalen; uint8_t *data = NULL; int i; ALOGV("starting to read subobjects\n"); /* use temporary variables for use during the read */ datalen = header->datalen; data = header->data; for (i=0; i<header->subobjects; i++) { void * tmp = NULL; asfint_object_t *current = NULL; if (datalen < 24) {//UUID+Size ==24 /* not enough data for reading object */ break; } //current = (asfint_object_t *)oscl_malloc(sizeof(asfint_object_t)); tmp = (void*)malloc(sizeof(asfint_object_t)); memset(tmp, 0, sizeof(asfint_object_t)); current = (asfint_object_t *)tmp; if (!current) { return ASF_ERROR_OUTOFMEM; } asf_parse_read_object(current, data); if (current->size > datalen || current->size < 24) { /* invalid object size */ ALOGE("invalid object size\n"); break; } /* Check if the current subobject is a header extension * object or just a normal subobject */ if (current->type == GUID_HEADER_EXTENSION && !header->ext) { int ret; asf_object_headerext_t *headerext = NULL; /* we handle header extension separately because it has * some subobjects as well */ //current = (asf_object_headerext_t*)oscl_realloc(current, sizeof(asf_object_headerext_t)); //headerext = (asf_object_headerext_t *) current; //changed by satish to fix compiler conversion problem tmp = (void*)realloc(tmp, sizeof(asf_object_headerext_t)); current = (asfint_object_t *)tmp; headerext = (asf_object_headerext_t *) tmp; headerext->first = NULL; headerext->last = NULL; ret = asf_parse_headerext(headerext, data, datalen); if (ret < 0) { /* error parsing header extension */ return ret; } header->ext = headerext; } else { if (current->type == GUID_HEADER_EXTENSION) { ALOGV("WARNING! Second header extension object found, ignoring it!\n"); } current->datalen = current->size - 24; current->data = data + 24; /* add to list of subobjects */ if (!header->first) { header->first = current; header->last = current; } else { header->last->next = current; header->last = current; } } data += current->size; datalen -= current->size; } if (i != header->subobjects || datalen != 0) { /* header data size doesn't match given subobject count */ return ASF_ERROR_INVALID_VALUE; } ALOGV("%d subobjects read successfully\n", i); } tmp = asf_parse_header_validate(file->header); if (tmp < 0) { /* header read ok but doesn't validate correctly */ return tmp; } ALOGV("header validated correctly\n"); return header->size; }
static int asf_parse_header(int fd, struct mp3entry* id3, asf_waveformatex_t* wfx) { 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 id3buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); unsigned char* id3buf = (unsigned char*)id3->id3v2buf; asf_read_object_header((asf_object_t *) &header, fd); //DEBUGF("header.size=%d\n",(int)header.size); if (header.size < 30) { /* invalid size for header object */ return ASF_ERROR_OBJECT_SIZE; } read_uint32le(fd, &subobjects); /* Two reserved bytes - do we need to read them? */ lseek(fd, 2, SEEK_CUR); //DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects); if (subobjects > 0) { header.datalen = header.size - 30; /* TODO: Check that we have datalen bytes left in the file */ datalen = header.datalen; for (i=0; i<(int)subobjects; i++) { //DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen); if (datalen < 24) { //DEBUGF("not enough data for reading object\n"); break; } asf_read_object_header(¤t, fd); if (current.size > datalen || current.size < 24) { //DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen); break; } if (asf_guid_match(¤t.guid, &asf_guid_file_properties)) { if (current.size < 104) return ASF_ERROR_OBJECT_SIZE; if (fileprop) { /* multiple file properties objects not allowed */ return ASF_ERROR_INVALID_OBJECT; } fileprop = 1; /* Get the number of logical packets - uint64_t at offset 32 * (little endian byte order) */ lseek(fd, 32, SEEK_CUR); read_uint64le(fd, &wfx->numpackets); /*DEBUGF("read packets: %llx %lld\n", wfx->numpackets, wfx->numpackets);*/ /* Now get the play duration - uint64_t at offset 40 */ read_uint64le(fd, &play_duration); id3->length = play_duration / 10000; /*DEBUGF("****** length = %lums\n", id3->length);*/ /* Read the packet size - uint32_t at offset 68 */ lseek(fd, 20, SEEK_CUR); read_uint32le(fd, &wfx->packet_size); /* Skip bytes remaining in object */ lseek(fd, current.size - 24 - 72, SEEK_CUR); } else if (asf_guid_match(¤t.guid, &asf_guid_stream_properties)) { guid_t guid; 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); 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; } #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); /*sanity check the included bitrate by comparing to file size and length*/ unsigned int estimated_bitrate = (wfx->packet_size*wfx->numpackets)/id3->length*8000; /*in theory we could just use the estimated bitrate always, but its safer to underestimate*/ if( wfx->bitrate > estimated_bitrate) { /* Round bitrate to the nearest kbit */ id3->bitrate = (estimated_bitrate + 500) / 1000; } else { /* Round bitrate to the nearest kbit */ id3->bitrate = (wfx->bitrate + 500) / 1000; } /*DEBUGF("bitrate: %d estimated: %d\n", wfx->bitrate, estimated_bitrate);*/ 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 if (wfx->codec_id == ASF_CODEC_ID_WMAPRO) { /* wma pro decoder needs the extra-data */ read(fd, wfx->data, wfx->datalen); lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); wfx->audiostream = flags&0x7f; /* Correct codectype to redirect playback to the proper .codec */ id3->codectype = AFMT_WMAPRO; } else if (wfx->codec_id == ASF_CODEC_ID_WMAVOICE) { read(fd, wfx->data, wfx->datalen); lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); wfx->audiostream = flags&0x7f; id3->codectype = AFMT_WMAVOICE; } else { DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n"); lseek(fd,current.size - 24 - 72,SEEK_CUR); } } } else if (asf_guid_match(¤t.guid, &asf_guid_content_description)) { /* Object contains five 16-bit string lengths, followed by the five strings: title, artist, copyright, description, rating */ uint16_t strlength[5]; int i; //DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24)); /* Read the 5 string lengths - number of bytes included trailing zero */ for (i=0; i<5; i++) { read_uint16le(fd, &strlength[i]); //DEBUGF("strlength = %u\n",strlength[i]); } if (strlength[0] > 0) { /* 0 - Title */ id3->title = id3buf; asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining); } if (strlength[1] > 0) { /* 1 - Artist */ id3->artist = id3buf; asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining); } lseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */ if (strlength[3] > 0) { /* 3 - description */ id3->comment = id3buf; asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining); } lseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */ } else if (asf_guid_match(¤t.guid, &asf_guid_extended_content_description)) { 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); 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; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); parse_replaygain(utf8buf, value, id3); } else if (!strcmp("MusicBrainz/Track Id", utf8buf)) { id3->mb_track_id = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); #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. */ lseek(fd, -4, SEEK_CUR); read(fd, &type, 1); if (type == 1) { lseek(fd, 3, SEEK_CUR); read(fd, &type, 1); /* 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; lseek(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 = lseek(fd, 0, SEEK_CUR); id3->albumart.size = datalength; id3->has_embedded_albumart = true; } } lseek(fd, datalength, SEEK_CUR); #endif } else { lseek(fd, length, SEEK_CUR); } bytesleft -= 4 + length; } lseek(fd, bytesleft, SEEK_CUR); } else if (asf_guid_match(¤t.guid, &asf_guid_content_encryption) || asf_guid_match(¤t.guid, &asf_guid_extended_content_encryption)) { //DEBUGF("File is encrypted\n"); return ASF_ERROR_ENCRYPTED; } else { //DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24)); lseek(fd,current.size - 24,SEEK_CUR); } //DEBUGF("Parsed object - size = %d\n",(int)current.size); datalen -= current.size; } if (i != (int)subobjects || datalen != 0) { //DEBUGF("header data doesn't match given subobject count\n"); return ASF_ERROR_INVALID_VALUE; } //DEBUGF("%d subobjects read successfully\n", i); } #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; }
static int asf_parse_header(int fd, struct mp3entry* id3, asf_waveformatex_t* wfx) { 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 id3buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); unsigned char* id3buf = (unsigned char*)id3->id3v2buf; asf_read_object_header((asf_object_t *) &header, fd); //DEBUGF("header.size=%d\n",(int)header.size); if (header.size < 30) { /* invalid size for header object */ return ASF_ERROR_OBJECT_SIZE; } read_uint32le(fd, &subobjects); /* Two reserved bytes - do we need to read them? */ lseek(fd, 2, SEEK_CUR); //DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects); if (subobjects > 0) { header.datalen = header.size - 30; /* TODO: Check that we have datalen bytes left in the file */ datalen = header.datalen; for (i=0; i<(int)subobjects; i++) { //DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen); if (datalen < 24) { //DEBUGF("not enough data for reading object\n"); break; } asf_read_object_header(¤t, fd); if (current.size > datalen || current.size < 24) { //DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen); break; } if (asf_guid_match(¤t.guid, &asf_guid_file_properties)) { if (current.size < 104) return ASF_ERROR_OBJECT_SIZE; if (fileprop) { /* multiple file properties objects not allowed */ return ASF_ERROR_INVALID_OBJECT; } fileprop = 1; /* Get the number of logical packets - uint16_t at offset 31 * (Big endian byte order) */ lseek(fd, 31, SEEK_CUR); read_uint16be(fd, &wfx->numpackets); /* Now get the play duration - uint64_t at offset 40 */ lseek(fd, 7, SEEK_CUR); read_uint64le(fd, &play_duration); id3->length = play_duration / 10000; //DEBUGF("****** length = %lums\n", id3->length); /* Read the packet size - uint32_t at offset 68 */ lseek(fd, 20, SEEK_CUR); read_uint32le(fd, &wfx->packet_size); /* Skip bytes remaining in object */ lseek(fd, current.size - 24 - 72, SEEK_CUR); } else if (asf_guid_match(¤t.guid, &asf_guid_stream_properties)) { guid_t guid; 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); 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; } #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); /* 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 if (wfx->codec_id == ASF_CODEC_ID_WMAPRO) { /* wma pro decoder needs the extra-data */ read(fd, wfx->data, wfx->datalen); lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); wfx->audiostream = flags&0x7f; /* Correct codectype to redirect playback to the proper .codec */ id3->codectype = AFMT_WMAPRO; } else { DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n"); lseek(fd,current.size - 24 - 72,SEEK_CUR); } } } else if (asf_guid_match(¤t.guid, &asf_guid_content_description)) { /* Object contains five 16-bit string lengths, followed by the five strings: title, artist, copyright, description, rating */ uint16_t strlength[5]; int i; //DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24)); /* Read the 5 string lengths - number of bytes included trailing zero */ for (i=0; i<5; i++) { read_uint16le(fd, &strlength[i]); //DEBUGF("strlength = %u\n",strlength[i]); } if (strlength[0] > 0) { /* 0 - Title */ id3->title = id3buf; asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining); } if (strlength[1] > 0) { /* 1 - Artist */ id3->artist = id3buf; asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining); } lseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */ if (strlength[3] > 0) { /* 3 - description */ id3->comment = id3buf; asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining); } lseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */ } else if (asf_guid_match(¤t.guid, &asf_guid_extended_content_description)) { 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); if (!strcmp("WM/TrackNumber",utf8buf)) { if (type == 0) { id3->track_string = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); id3->tracknum = atoi(id3->track_string); } else if ((type >=2) && (type <= 5)) { id3->tracknum = asf_intdecode(fd, type, length); } else { lseek(fd, length, SEEK_CUR); } } else if ((!strcmp("WM/Genre", utf8buf)) && (type == 0)) { id3->genre_string = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if ((!strcmp("WM/AlbumTitle", utf8buf)) && (type == 0)) { id3->album = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if ((!strcmp("WM/AlbumArtist", utf8buf)) && (type == 0)) { id3->albumartist = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if ((!strcmp("WM/Composer", utf8buf)) && (type == 0)) { id3->composer = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if (!strcmp("WM/Year", utf8buf)) { if (type == 0) { id3->year_string = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); id3->year = atoi(id3->year_string); } else if ((type >=2) && (type <= 5)) { id3->year = asf_intdecode(fd, type, length); } else { lseek(fd, length, SEEK_CUR); } } else if (!strncmp("replaygain_", utf8buf, 11)) { char* value = id3buf; int buf_len = id3buf_remaining; int len; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); len = parse_replaygain(utf8buf, value, id3, value, buf_len); if (len == 0) { /* Don't need to keep the value */ id3buf = value; id3buf_remaining = buf_len; } } else if (!strcmp("MusicBrainz/Track Id", utf8buf)) { id3->mb_track_id = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else { lseek(fd, length, SEEK_CUR); } bytesleft -= 4 + length; } lseek(fd, bytesleft, SEEK_CUR); } else if (asf_guid_match(¤t.guid, &asf_guid_content_encryption) || asf_guid_match(¤t.guid, &asf_guid_extended_content_encryption)) { //DEBUGF("File is encrypted\n"); return ASF_ERROR_ENCRYPTED; } else { //DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24)); lseek(fd,current.size - 24,SEEK_CUR); } //DEBUGF("Parsed object - size = %d\n",(int)current.size); datalen -= current.size; } if (i != (int)subobjects || datalen != 0) { //DEBUGF("header data doesn't match given subobject count\n"); return ASF_ERROR_INVALID_VALUE; } //DEBUGF("%d subobjects read successfully\n", i); } #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; }