Exemple #1
0
void ASFByteIO::asf_byteio_getGUID(asf_guid_t *guid, uint8_t *data)
{
        guid->v1 = asf_byteio_getDWLE(data);
        guid->v2 = asf_byteio_getWLE(data + 4);
        guid->v3 = asf_byteio_getWLE(data + 6);
        memcpy(guid->v4, data + 8, 8);
}
Exemple #2
0
void ASFByteIO::asf_byteio_get_string(uint16_t *string, uint16_t strlen, uint8_t *data)
{
        int i;

        if (!data || !string)
                return;

        for (i=0; i<strlen; i++) {
                string[i] = asf_byteio_getWLE(data + i*2);
        }
}
Exemple #3
0
/**
 * Reads the stream properties object's data into the equivalent
 * data structure, and stores it in asf_stream_t structure
 * with the equivalent stream type. Needs the stream properties
 * object data as its input.
 */
static int
asf_parse_header_stream_properties(asf_stream_t *stream,
                                   uint8_t *objdata,
                                   uint32_t objsize)
{
	asf_guid_t guid;
	guid_type_t type;
	uint32_t datalen;
	uint8_t *data;

	if (objsize < 78) {
		return ASF_ERROR_INVALID_LENGTH;
	}

	asf_byteio_getGUID(&guid, objdata);
	type = asf_guid_get_stream_type(&guid);

	datalen = asf_byteio_getDWLE(objdata + 40);
	if (datalen > objsize - 78) {
		return ASF_ERROR_INVALID_LENGTH;
	}
	data = objdata + 54;

	if (type == GUID_STREAM_TYPE_EXTENDED) {
		/* FIXME: Need to find out what actually is here...
		          but for now we can just skip the extended part */
		if (datalen < 64)
			return ASF_ERROR_INVALID_LENGTH;

		data += 64;
		datalen -= 64;

		/* update the stream type with correct one */
		asf_byteio_getGUID(&guid, objdata);
		type = asf_guid_get_stream_type(&guid);
	}

	switch (type) {
	case GUID_STREAM_TYPE_AUDIO:
	case GUID_STREAM_TYPE_EXTENDED_AUDIO:
	{
		asf_waveformatex_t *wfx;

		stream->type = ASF_STREAM_TYPE_AUDIO;

		if (datalen < 18) {
			return ASF_ERROR_INVALID_LENGTH;
		}
		if (asf_byteio_getWLE(data + 16) > datalen - 16) {
			return ASF_ERROR_INVALID_LENGTH;
		}

		/* this should be freed in asf_close function */
		stream->properties = malloc(sizeof(asf_waveformatex_t));
		if (!stream->properties)
			return ASF_ERROR_OUTOFMEM;
		stream->flags |= ASF_STREAM_FLAG_AVAILABLE;

		wfx = stream->properties;
		wfx->wFormatTag = asf_byteio_getWLE(data);
		wfx->nChannels = asf_byteio_getWLE(data + 2);
		wfx->nSamplesPerSec = asf_byteio_getDWLE(data + 4);
		wfx->nAvgBytesPerSec = asf_byteio_getDWLE(data + 8);
		wfx->nBlockAlign = asf_byteio_getWLE(data + 12);
		wfx->wBitsPerSample = asf_byteio_getWLE(data + 14);
		wfx->cbSize = asf_byteio_getWLE(data + 16);
		wfx->data = data + 18;

		if (wfx->cbSize > datalen - 18) {
			debug_printf("Invalid waveformatex data length, truncating!");
			wfx->cbSize = datalen - 18;
		}

		break;
	}
	case GUID_STREAM_TYPE_VIDEO:
	{
		asf_bitmapinfoheader_t *bmih;
		uint32_t width, height, flags, data_size;

		stream->type = ASF_STREAM_TYPE_VIDEO;

		if (datalen < 51) {
			return ASF_ERROR_INVALID_LENGTH;
		}

		width = asf_byteio_getDWLE(data);
		height = asf_byteio_getDWLE(data + 4);
		flags = data[8];
		data_size = asf_byteio_getWLE(data + 9);

		data += 11;
		datalen -= 11;

		if (asf_byteio_getDWLE(data) != datalen) {
			return ASF_ERROR_INVALID_LENGTH;
		}
		if (width != asf_byteio_getDWLE(data + 4) ||
		    height != asf_byteio_getDWLE(data + 8) ||
		    flags != 2) {
			return ASF_ERROR_INVALID_VALUE;
		}

		/* this should be freed in asf_close function */
		stream->properties = malloc(sizeof(asf_bitmapinfoheader_t));
		if (!stream->properties)
			return ASF_ERROR_OUTOFMEM;
		stream->flags |= ASF_STREAM_FLAG_AVAILABLE;

		bmih = stream->properties;
		bmih->biSize = asf_byteio_getDWLE(data);
		bmih->biWidth = asf_byteio_getDWLE(data + 4);
		bmih->biHeight = asf_byteio_getDWLE(data + 8);
		bmih->biPlanes = asf_byteio_getDWLE(data + 12);
		bmih->biBitCount = asf_byteio_getDWLE(data + 14);
		bmih->biCompression = asf_byteio_getDWLE(data + 16);
		bmih->biSizeImage = asf_byteio_getDWLE(data + 20);
		bmih->biXPelsPerMeter = asf_byteio_getDWLE(data + 24);
		bmih->biYPelsPerMeter = asf_byteio_getDWLE(data + 28);
		bmih->biClrUsed = asf_byteio_getDWLE(data + 32);
		bmih->biClrImportant = asf_byteio_getDWLE(data + 36);
		bmih->data = data + 40;

		if (bmih->biSize > datalen) {
			debug_printf("Invalid bitmapinfoheader data length, truncating!");
			bmih->biSize = datalen;
		}

		break;
	}
	case GUID_STREAM_TYPE_COMMAND:
		stream->type = ASF_STREAM_TYPE_COMMAND;
		break;
	default:
		stream->type = ASF_STREAM_TYPE_UNKNOWN;
		break;
	}

	return 0;
}
Exemple #4
0
/**
 * Allocates a metadata struct and parses the contents from
 * the header object raw data. All strings are in UTF-8 encoded
 * format. The returned struct needs to be freed using the
 * asf_header_metadata_destroy function. Returns NULL on failure.
 */
asf_metadata_t *
asf_header_metadata(asf_object_header_t *header)
{
	asfint_object_t *current;
	asf_metadata_t *ret;

	/* allocate the metadata struct */
	ret = calloc(1, sizeof(asf_metadata_t));
	if (!ret) {
		return NULL;
	}

	current = asf_header_get_object(header, GUID_CONTENT_DESCRIPTION);
	if (current) {
		char *str;
		uint16_t strlen;
		int i, read = 0;

		/* The validity of the object is already checked so we can assume
		 * there's always enough data to read and there are no overflows */
		for (i=0; i<5; i++) {
			strlen = asf_byteio_getWLE(current->data + i*2);
			if (!strlen)
				continue;

			str = asf_utf8_from_utf16le(current->data + 10 + read, strlen);
			read += strlen;

			switch (i) {
			case 0:
				ret->title = str;
				break;
			case 1:
				ret->artist = str;
				break;
			case 2:
				ret->copyright = str;
				break;
			case 3:
				ret->description = str;
				break;
			case 4:
				ret->rating = str;
				break;
			default:
				free(str);
				break;
			}
		}
	}

	current = asf_header_get_object(header, GUID_EXTENDED_CONTENT_DESCRIPTION);
	if (current) {
		int i, j, position;

		ret->extended_count = asf_byteio_getWLE(current->data);
		ret->extended = calloc(ret->extended_count, sizeof(asf_metadata_entry_t));
		if (!ret->extended) {
			/* Clean up the already allocated parts and return */
			free(ret->title);
			free(ret->artist);
			free(ret->copyright);
			free(ret->description);
			free(ret->rating);
			free(ret);

			return NULL;
		}

		position = 2;
		for (i=0; i<ret->extended_count; i++) {
			uint16_t length, type;

			length = asf_byteio_getWLE(current->data + position);
			position += 2;

			ret->extended[i].key = asf_utf8_from_utf16le(current->data + position, length);
			position += length;

			type = asf_byteio_getWLE(current->data + position);
			position += 2;

			length = asf_byteio_getWLE(current->data + position);
			position += 2;

			switch (type) {
			case 0:
				/* type of the value is a string */
				ret->extended[i].value = asf_utf8_from_utf16le(current->data + position, length);
				break;
			case 1:
				/* type of the value is a data block */
				ret->extended[i].value = malloc((length*2 + 1) * sizeof(char));
				for (j=0; j<length; j++) {
					static const char hex[16] = "0123456789ABCDEF";
					ret->extended[i].value[j*2+0] = hex[current->data[position]>>4];
					ret->extended[i].value[j*2+1] = hex[current->data[position]&0x0f];
				}
				ret->extended[i].value[j*2] = '\0';
				break;
			case 2:
				/* type of the value is a boolean */
				ret->extended[i].value = malloc(6 * sizeof(char));
				sprintf(ret->extended[i].value, "%s",
				        *current->data ? "true" : "false");
				break;
			case 3:
				/* type of the value is a signed 32-bit integer */
				ret->extended[i].value = malloc(11 * sizeof(char));
				sprintf(ret->extended[i].value, "%u",
				        asf_byteio_getDWLE(current->data + position));
				break;
			case 4:
				/* FIXME: This doesn't print whole 64-bit integer */
				ret->extended[i].value = malloc(21 * sizeof(char));
				sprintf(ret->extended[i].value, "%u",
				        (uint32_t) asf_byteio_getQWLE(current->data + position));
				break;
			case 5:
				/* type of the value is a signed 16-bit integer */
				ret->extended[i].value = malloc(6 * sizeof(char));
				sprintf(ret->extended[i].value, "%u",
				        asf_byteio_getWLE(current->data + position));
				break;
			default:
				/* Unknown value type... */
				ret->extended[i].value = NULL;
				break;
			}
			position += length;
		}
	}

	return ret;
}
Exemple #5
0
/**
 * Reads the file properties object contents to the asf_file_t structure,
 * parses the useful values from stream properties object to the equivalent
 * stream properties info structure and validates that all known header
 * subobjects have only legal values.
 */
int
asf_parse_header_validate(asf_file_t *file, asf_object_header_t *header)
{
	/* some flags for mandatory subobjects */
	int fileprop = 0, streamprop = 0;
	asfint_object_t *current;

	if (header->first) {
		current = header->first;
		while (current) {
			uint64_t size = current->size;

			switch (current->type) {
			case GUID_FILE_PROPERTIES:
			{
				uint32_t max_packet_size;
				if (size < 104)
					return ASF_ERROR_OBJECT_SIZE;

				if (fileprop) {
					/* multiple file properties objects not allowed */
					return ASF_ERROR_INVALID_OBJECT;
				}

				fileprop = 1;
				asf_byteio_getGUID(&file->file_id, current->data);
				file->file_size = asf_byteio_getQWLE(current->data + 16);
				file->creation_date = asf_byteio_getQWLE(current->data + 24);
				file->data_packets_count = asf_byteio_getQWLE(current->data + 32);
				file->play_duration = asf_byteio_getQWLE(current->data + 40);
				file->send_duration = asf_byteio_getQWLE(current->data + 48);
				file->preroll = asf_byteio_getQWLE(current->data + 56);
				file->flags = asf_byteio_getDWLE(current->data + 64);
				file->packet_size = asf_byteio_getDWLE(current->data + 68);
				file->max_bitrate = asf_byteio_getQWLE(current->data + 76);

				max_packet_size = asf_byteio_getDWLE(current->data + 72);
				if (file->packet_size != max_packet_size) {
					/* in ASF file minimum packet size and maximum
					 * packet size have to be same apparently...
					 * stupid, eh? */
					return ASF_ERROR_INVALID_VALUE;
				}
				break;
			}
			case GUID_STREAM_PROPERTIES:
			{
				uint16_t flags;
				asf_stream_t *stream;
				int ret;

				if (size < 78)
					return ASF_ERROR_OBJECT_SIZE;

				streamprop = 1;
				flags = asf_byteio_getWLE(current->data + 48);
				stream = &file->streams[flags & 0x7f];

				if (stream->type) {
					/* only one stream object per stream allowed */
					return ASF_ERROR_INVALID_OBJECT;
				}

				ret = asf_parse_header_stream_properties(stream,
									 current->data,
									 size);

				if (ret < 0) {
					return ret;
				}
				break;
			}
			case GUID_CONTENT_DESCRIPTION:
			{
				uint32_t stringlen = 0;

				if (size < 34)
					return ASF_ERROR_OBJECT_SIZE;

				stringlen += asf_byteio_getWLE(current->data);
				stringlen += asf_byteio_getWLE(current->data + 2);
				stringlen += asf_byteio_getWLE(current->data + 4);
				stringlen += asf_byteio_getWLE(current->data + 6);
				stringlen += asf_byteio_getWLE(current->data + 8);

				if (size < stringlen + 34) {
					/* invalid string length values */
					return ASF_ERROR_INVALID_LENGTH;
				}
				break;
			}
			case GUID_MARKER:
				break;
			case GUID_CODEC_LIST:
				if (size < 44)
					return ASF_ERROR_OBJECT_SIZE;
				break;
			case GUID_STREAM_BITRATE_PROPERTIES:
				if (size < 26)
					return ASF_ERROR_OBJECT_SIZE;
				break;
			case GUID_PADDING:
				break;
			case GUID_EXTENDED_CONTENT_DESCRIPTION:
				if (size < 26)
					return ASF_ERROR_OBJECT_SIZE;
				break;
			case GUID_UNKNOWN:
				/* unknown guid type */
				break;
			default:
				/* identified type in wrong place */
				return ASF_ERROR_INVALID_OBJECT;
			}

			current = current->next;
		}
	}

	if (header->ext) {
		current = header->ext->first;
		while (current) {
			uint64_t size = current->size;

			switch (current->type) {
			case GUID_METADATA:
				if (size < 26)
					return ASF_ERROR_OBJECT_SIZE;
				break;
			case GUID_LANGUAGE_LIST:
				if (size < 26)
					return ASF_ERROR_OBJECT_SIZE;
				break;
			case GUID_EXTENDED_STREAM_PROPERTIES:
			{
				uint16_t stream_num;
				asf_stream_t *stream;
				int ret;

				if (size < 88)
					return ASF_ERROR_OBJECT_SIZE;

				stream_num = asf_byteio_getWLE(current->data + 48);
				stream = &file->streams[stream_num];

				ret = asf_parse_header_extended_stream_properties(stream,
										  current->data,
										  size);

				if (ret < 0) {
					return ret;
				}
				break;
			}
			case GUID_ADVANCED_MUTUAL_EXCLUSION:
				if (size < 42)
					return ASF_ERROR_OBJECT_SIZE;
				break;
			case GUID_STREAM_PRIORITIZATION:
				if (size < 26)
					return ASF_ERROR_OBJECT_SIZE;
				break;
			case GUID_UNKNOWN:
				/* unknown guid type */
				break;
			default:
				/* identified type in wrong place */
				break;
			}

			current = current->next;
		}
	}

	if (!fileprop || !streamprop || !header->ext) {
		/* mandatory subobject missing */
		return ASF_ERROR_INVALID_OBJECT;
	}

	return 1;
}
Exemple #6
0
static int
asf_parse_header_extended_stream_properties(asf_stream_t *stream,
                                            uint8_t *objdata,
                                            uint32_t objsize)
{
	asf_stream_extended_t ext;
	uint32_t datalen;
	uint8_t *data;
	uint16_t flags;
	int i;

	ext.start_time = asf_byteio_getQWLE(objdata);
	ext.end_time = asf_byteio_getQWLE(objdata + 8);
	ext.data_bitrate = asf_byteio_getDWLE(objdata + 16);
	ext.buffer_size = asf_byteio_getDWLE(objdata + 20);
	ext.initial_buf_fullness = asf_byteio_getDWLE(objdata + 24);
	ext.data_bitrate2 = asf_byteio_getDWLE(objdata + 28);
	ext.buffer_size2 = asf_byteio_getDWLE(objdata + 32);
	ext.initial_buf_fullness2 = asf_byteio_getDWLE(objdata + 36);
	ext.max_obj_size = asf_byteio_getDWLE(objdata + 40);
	ext.flags = asf_byteio_getDWLE(objdata + 44);
	ext.stream_num = asf_byteio_getWLE(objdata + 48);
	ext.lang_idx = asf_byteio_getWLE(objdata + 50);
	ext.avg_time_per_frame = asf_byteio_getQWLE(objdata + 52);
	ext.stream_name_count = asf_byteio_getWLE(objdata + 60);
	ext.num_payload_ext = asf_byteio_getWLE(objdata + 62);

	datalen = objsize - 88;
	data = objdata + 64;

	/* iterate through all name strings */
	for (i=0; i<ext.stream_name_count; i++) {
		uint16_t strlen;

		if (datalen < 4) {
			return ASF_ERROR_INVALID_VALUE;
		}

		strlen = asf_byteio_getWLE(data + 2);
		if (strlen > datalen) {
			return ASF_ERROR_INVALID_LENGTH;
		}

		/* skip the current name string */
		data += 4 + strlen;
		datalen -= 4 + strlen;
	}

	/* iterate through all extension systems */
	for (i=0; i<ext.num_payload_ext; i++) {
		uint32_t extsyslen;

		if (datalen < 22) {
			return ASF_ERROR_INVALID_VALUE;
		}

		extsyslen = asf_byteio_getDWLE(data + 18);
		if (extsyslen > datalen) {
			return ASF_ERROR_INVALID_LENGTH;
		}

		/* skip the current extension system */
		data += 22 + extsyslen;
		datalen -= 22 + extsyslen;
	}

	if (datalen > 0) {
		asf_guid_t guid;

		debug_printf("hidden stream properties object found!");

		/* this is almost same as in stream properties handler */
		if (datalen < 78) {
			return ASF_ERROR_OBJECT_SIZE;
		}

		/* check that we really have a stream properties object */
		asf_byteio_getGUID(&guid, data);
		if (asf_guid_get_type(&guid) != GUID_STREAM_PROPERTIES) {
			return ASF_ERROR_INVALID_OBJECT;
		}
		if (asf_byteio_getQWLE(data + 16) != datalen) {
			return ASF_ERROR_OBJECT_SIZE;
		}

		flags = asf_byteio_getWLE(data + 72);

		if ((flags & 0x7f) != ext.stream_num || stream->type) {
			/* only one stream object per stream allowed and
			 * stream ids have to match with both objects*/
			return ASF_ERROR_INVALID_OBJECT;
		} else {
			int ret;

			stream->flags |= ASF_STREAM_FLAG_HIDDEN;
			ret = asf_parse_header_stream_properties(stream,
								 data + 24,
								 datalen);

			if (ret < 0) {
				return ret;
			}
		}
	}

	stream->extended = malloc(sizeof(asf_stream_extended_t));
	if (!stream->extended) {
		return ASF_ERROR_OUTOFMEM;
	}
	stream->flags |= ASF_STREAM_FLAG_EXTENDED;
	memcpy(stream->extended, &ext, sizeof(ext));

	return 0;
}
Exemple #7
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(&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;
                    
                    /* 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(&current.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(&current.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(&current.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(&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);
    }

#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;
}
Exemple #8
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(&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;
                    
                    /* 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(&current.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(&current.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(&current.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(&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);
    }

#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;
}