summaryrefslogtreecommitdiff
path: root/src/demuxers/asfheader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/demuxers/asfheader.c')
-rw-r--r--src/demuxers/asfheader.c806
1 files changed, 806 insertions, 0 deletions
diff --git a/src/demuxers/asfheader.c b/src/demuxers/asfheader.c
new file mode 100644
index 000000000..62220e225
--- /dev/null
+++ b/src/demuxers/asfheader.c
@@ -0,0 +1,806 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#define LOG_MODULE "asfheader"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+#include "bswap.h"
+#include "asfheader.h"
+
+typedef struct asf_header_internal_s asf_header_internal_t;
+struct asf_header_internal_s {
+ asf_header_t pub;
+
+ /* private part */
+ int number_count;
+ uint16_t numbers[ASF_MAX_NUM_STREAMS];
+ iconv_t iconv_cd;
+ uint8_t *bitrate_pointers[ASF_MAX_NUM_STREAMS];
+};
+
+
+typedef struct asf_reader_s asf_reader_t;
+struct asf_reader_s {
+ uint8_t *buffer;
+ size_t pos;
+ size_t size;
+};
+
+
+static void asf_reader_init(asf_reader_t *reader, uint8_t *buffer, int size) {
+ reader->buffer = buffer;
+ reader->pos = 0;
+ reader->size = size;
+}
+
+static int asf_reader_get_8(asf_reader_t *reader, uint8_t *value) {
+ if ((reader->size - reader->pos) < 1)
+ return 0;
+ *value = *(reader->buffer + reader->pos);
+ reader->pos += 1;
+ return 1;
+}
+
+static int asf_reader_get_16(asf_reader_t *reader, uint16_t *value) {
+ if ((reader->size - reader->pos) < 2)
+ return 0;
+ *value = LE_16(reader->buffer + reader->pos);
+ reader->pos += 2;
+ return 1;
+}
+
+static int asf_reader_get_32(asf_reader_t *reader, uint32_t *value) {
+ if ((reader->size - reader->pos) < 4)
+ return 0;
+ *value = LE_32(reader->buffer + reader->pos);
+ reader->pos += 4;
+ return 1;
+}
+
+static int asf_reader_get_64(asf_reader_t *reader, uint64_t *value) {
+ if ((reader->size - reader->pos) < 8)
+ return 0;
+ *value = LE_64(reader->buffer + reader->pos);
+ reader->pos += 8;
+ return 1;
+}
+
+static int asf_reader_get_guid(asf_reader_t *reader, GUID *value) {
+ if ((reader->size - reader->pos) < 16)
+ return 0;
+
+ asf_get_guid(reader->buffer + reader->pos, value);
+ reader->pos += 16;
+ return 1;
+}
+
+static uint8_t *asf_reader_get_bytes(asf_reader_t *reader, size_t size) {
+ uint8_t *buffer;
+
+ if ((reader->size - reader->pos) < size)
+ return NULL;
+ buffer = malloc(size);
+ if (!buffer)
+ return NULL;
+ memcpy(buffer, reader->buffer + reader->pos, size);
+ reader->pos += size;
+ return buffer;
+}
+
+/* get an utf8 string */
+static char *asf_reader_get_string(asf_reader_t *reader, size_t size, iconv_t cd) {
+ char *inbuf, *outbuf;
+ size_t inbytesleft, outbytesleft;
+ char scratch[2048];
+
+ if ((reader->size - reader->pos) < size)
+ return NULL;
+
+ inbuf = (char *)reader->buffer + reader->pos;
+ inbytesleft = size;
+ outbuf = scratch;
+ outbytesleft = sizeof(scratch);
+ reader->pos += size;
+ if (iconv (cd, (char **)&inbuf, &inbytesleft, &outbuf, &outbytesleft) != -1) {
+ return strdup(scratch);
+ } else {
+ lprintf("iconv error\n");
+ return NULL;
+ }
+}
+
+static int asf_reader_skip(asf_reader_t *reader, size_t size) {
+ if ((reader->size - reader->pos) < size) {
+ reader->pos = reader->size;
+ return 0;
+ }
+ reader->pos += size;
+ return size;
+}
+
+static uint8_t *asf_reader_get_buffer(asf_reader_t *reader) {
+ return (reader->buffer + reader->pos);
+}
+
+static int asf_reader_eos(asf_reader_t *reader) {
+ if (reader->pos < reader->size)
+ return 0;
+ else
+ return 1;
+}
+
+static size_t asf_reader_get_size(asf_reader_t *reader) {
+ return reader->size - reader->pos;
+}
+
+void asf_get_guid(uint8_t *buffer, GUID *value) {
+ int i;
+
+ value->Data1 = LE_32(buffer);
+ value->Data2 = LE_16(buffer + 4);
+ value->Data3 = LE_16(buffer + 6);
+ for(i = 0; i < 8; i++) {
+ value->Data4[i] = *(buffer + i + 8);
+ }
+}
+
+int asf_find_object_id (GUID *g) {
+ int i;
+
+ for (i = 1; i < GUID_END; i++) {
+ if (!memcmp(g, &guids[i].guid, sizeof(GUID))) {
+ lprintf ("asf_find_object_id: %s\n", guids[i].name);
+ return i;
+ }
+ }
+ lprintf ("asf_find_object_id: unknown GUID: 0x%04X, 0x%02X, 0x%02X, {0x%01X, 0x%01X, 0x%01X, 0x%01X, 0x%01X, 0x%01X, 0x%01X, 0x%01X}\n",
+ g->Data1, g->Data2, g->Data3, g->Data4[0], g->Data4[1], g->Data4[2], g->Data4[3], g->Data4[4], g->Data4[5], g->Data4[6], g->Data4[7]);
+ return GUID_ERROR;
+}
+
+/* Manage id mapping */
+int asf_header_get_stream_id(asf_header_t *header_pub, uint16_t stream_number) {
+ asf_header_internal_t *header = (asf_header_internal_t *)header_pub;
+ int i;
+
+ /* linear search */
+ for (i = 0; i < header->number_count; i++) {
+ if (stream_number == header->numbers[i]) {
+ lprintf("asf_header_get_stream_id: id found: %d\n", i);
+ return i;
+ }
+ }
+
+ /* not found */
+ if (header->number_count >= ASF_MAX_NUM_STREAMS)
+ return -1;
+
+ header->numbers[header->number_count] = stream_number;
+ header->number_count++;
+ return header->number_count - 1;
+}
+
+int asf_header_parse_file_properties(asf_header_t *header, uint8_t *buffer, int buffer_len) {
+ asf_reader_t reader;
+ asf_file_t *asf_file;
+ uint32_t flags;
+
+ if (buffer_len < 80) {
+ lprintf("invalid asf file properties object\n");
+ return 0;
+ }
+
+ asf_file = malloc(sizeof(asf_file_t));
+ if (!asf_file) {
+ lprintf("cannot allocate asf_file_struct\n");
+ return 0;
+ }
+
+ asf_reader_init(&reader, buffer, buffer_len);
+
+ asf_reader_get_guid(&reader, &asf_file->file_id);
+ asf_reader_get_64(&reader, &asf_file->file_size);
+
+ /* creation date */
+ asf_reader_skip(&reader, 8);
+ asf_reader_get_64(&reader, &asf_file->data_packet_count);
+ asf_reader_get_64(&reader, &asf_file->play_duration);
+ asf_reader_get_64(&reader, &asf_file->send_duration);
+ asf_reader_get_64(&reader, &asf_file->preroll);
+
+ asf_reader_get_32(&reader, &flags);
+ asf_reader_get_32(&reader, &asf_file->packet_size);
+
+ /* duplicated packet size */
+ asf_reader_skip(&reader, 4);
+ asf_reader_get_32(&reader, &asf_file->max_bitrate);
+
+ asf_file->broadcast_flag = flags & 0x1;
+ asf_file->seekable_flag = flags & 0x2;
+
+ header->file = asf_file;
+
+ lprintf("File properties\n");
+ lprintf(" file_id: %04X\n", asf_file->file_id.Data1);
+ lprintf(" file_size: %"PRIu64"\n", asf_file->file_size);
+ lprintf(" data_packet_count: %"PRIu64"\n", asf_file->data_packet_count);
+ lprintf(" play_duration: %"PRIu64"\n", asf_file->play_duration);
+ lprintf(" send_duration: %"PRIu64"\n", asf_file->send_duration);
+ lprintf(" preroll: %"PRIu64"\n", asf_file->preroll);
+ lprintf(" broadcast_flag: %d\n", asf_file->broadcast_flag);
+ lprintf(" seekable_flag: %d\n", asf_file->seekable_flag);
+ lprintf(" packet_size: %"PRIu32"\n", asf_file->packet_size);
+ lprintf(" max_bitrate: %"PRIu32"\n", asf_file->max_bitrate);
+ return 1;
+}
+
+int asf_header_parse_stream_properties(asf_header_t *header, uint8_t *buffer, int buffer_len) {
+ asf_reader_t reader;
+ uint16_t flags;
+ uint32_t junk;
+ GUID guid;
+ asf_stream_t *asf_stream = NULL;
+ int stream_id;
+
+ if (buffer_len < 54)
+ goto exit_error;
+
+ asf_stream = malloc(sizeof(asf_stream_t));
+ if (!asf_stream)
+ goto exit_error;
+
+ asf_reader_init(&reader, buffer, buffer_len);
+
+ asf_reader_get_guid(&reader, &guid);
+ asf_stream->stream_type = asf_find_object_id(&guid);
+ asf_reader_get_guid(&reader, &guid);
+ asf_stream->error_correction_type = asf_find_object_id(&guid);
+
+ asf_reader_get_64(&reader, &asf_stream->time_offset);
+ asf_reader_get_32(&reader, &asf_stream->private_data_length);
+ asf_reader_get_32(&reader, &asf_stream->error_correction_data_length);
+
+ asf_reader_get_16(&reader, &flags);
+ asf_stream->stream_number = flags & 0x7F;
+ asf_stream->encrypted_flag = flags >> 15;
+
+ asf_reader_get_32(&reader, &junk);
+
+ asf_stream->private_data = asf_reader_get_bytes(&reader, asf_stream->private_data_length);
+ if (!asf_stream->private_data)
+ goto exit_error;
+
+ asf_stream->error_correction_data = asf_reader_get_bytes(&reader, asf_stream->error_correction_data_length);
+ if (!asf_stream->error_correction_data)
+ goto exit_error;
+
+ lprintf("Stream_properties\n");
+ lprintf(" stream_number: %d\n", asf_stream->stream_number);
+ lprintf(" stream_type: %s\n", guids[asf_stream->stream_type].name);
+ lprintf(" error_correction_type: %s\n", guids[asf_stream->error_correction_type].name);
+ lprintf(" time_offset: %"PRIu64"\n", asf_stream->time_offset);
+ lprintf(" private_data_length: %"PRIu32"\n", asf_stream->private_data_length);
+ lprintf(" error_correction_data_length: %"PRIu32"\n", asf_stream->error_correction_data_length);
+ lprintf(" encrypted_flag: %d\n", asf_stream->encrypted_flag);
+
+ stream_id = asf_header_get_stream_id(header, asf_stream->stream_number);
+ if (stream_id >= 0) {
+ header->streams[stream_id] = asf_stream;
+ header->stream_count++;
+ }
+ return 1;
+
+exit_error:
+ if (asf_stream) {
+ if (asf_stream->private_data)
+ free(asf_stream->private_data);
+ if (asf_stream->error_correction_data)
+ free(asf_stream->error_correction_data);
+ free(asf_stream);
+ }
+ return 0;
+}
+
+int asf_header_parse_stream_extended_properties(asf_header_t *header, uint8_t *buffer, int buffer_len) {
+ asf_reader_t reader;
+ uint32_t flags;
+ uint16_t stream_number;
+ int i;
+ int stream_id;
+
+ if (buffer_len < 64)
+ return 0;
+
+ asf_stream_extension_t *asf_stream_extension = malloc(sizeof(asf_stream_extension_t));
+ if (!asf_stream_extension)
+ return 0;
+
+ asf_reader_init(&reader, buffer, buffer_len);
+
+ asf_reader_get_64(&reader, &asf_stream_extension->start_time);
+ asf_reader_get_64(&reader, &asf_stream_extension->end_time);
+
+ asf_reader_get_32(&reader, &asf_stream_extension->data_bitrate);
+ asf_reader_get_32(&reader, &asf_stream_extension->buffer_size);
+ asf_reader_get_32(&reader, &asf_stream_extension->initial_buffer_fullness);
+ asf_reader_get_32(&reader, &asf_stream_extension->alternate_data_bitrate);
+ asf_reader_get_32(&reader, &asf_stream_extension->alternate_buffer_size);
+ asf_reader_get_32(&reader, &asf_stream_extension->alternate_initial_buffer_fullness);
+ asf_reader_get_32(&reader, &asf_stream_extension->max_object_size);
+
+ /* 4 flags */
+ asf_reader_get_32(&reader, &flags);
+ asf_stream_extension->reliable_flag = flags & 1;
+ asf_stream_extension->seekable_flag = (flags >> 1) & 1;
+ asf_stream_extension->no_cleanpoints_flag = (flags >> 2) & 1;
+ asf_stream_extension->resend_live_cleanpoints_flag = (flags >> 3) & 1;
+
+ asf_reader_get_16(&reader, &stream_number);
+
+ asf_reader_get_16(&reader, &asf_stream_extension->language_id);
+ asf_reader_get_64(&reader, &asf_stream_extension->average_time_per_frame);
+
+ asf_reader_get_16(&reader, &asf_stream_extension->stream_name_count);
+ asf_reader_get_16(&reader, &asf_stream_extension->payload_extension_system_count);
+
+ /* get stream names */
+ if (asf_stream_extension->stream_name_count) {
+ asf_stream_extension->stream_names = malloc (asf_stream_extension->stream_name_count * sizeof(void*));
+ for (i = 0; i < asf_stream_extension->stream_name_count; i++) {
+ uint16_t lang_index, length;
+ asf_reader_get_16(&reader, &lang_index);
+ asf_reader_get_16(&reader, &length);
+ asf_stream_extension->stream_names[i] = (char*)asf_reader_get_bytes(&reader, length); /* store them */
+ }
+ }
+
+ /* skip payload extensions */
+ if (asf_stream_extension->payload_extension_system_count) {
+ for (i = 0; i < asf_stream_extension->payload_extension_system_count; i++) {
+ GUID guid;
+ uint16_t data_size;
+ uint32_t length;
+ asf_reader_get_guid(&reader, &guid);
+ asf_reader_get_16(&reader, &data_size);
+ asf_reader_get_32(&reader, &length);
+ asf_reader_skip(&reader, length);
+ }
+ }
+
+ stream_id = asf_header_get_stream_id(header, stream_number);
+ if (stream_id >= 0) {
+ header->stream_extensions[stream_id] = asf_stream_extension;
+ }
+
+ /* embeded stream properties */
+ if (asf_reader_get_size(&reader) >= 24) {
+ GUID guid;
+ uint64_t object_length;
+
+ asf_reader_get_guid(&reader, &guid);
+ asf_reader_get_64(&reader, &object_length);
+
+ /* check length validity */
+ if (asf_reader_get_size(&reader) == (object_length - 24)) {
+ int object_id = asf_find_object_id(&guid);
+ switch (object_id) {
+ case GUID_ASF_STREAM_PROPERTIES:
+ asf_header_parse_stream_properties(header, asf_reader_get_buffer(&reader), object_length - 24);
+ break;
+ default:
+ lprintf ("unexpected object\n");
+ break;
+ }
+ } else {
+ lprintf ("invalid object length\n");
+ }
+ }
+
+ lprintf("Stream extension properties\n");
+ lprintf(" stream_number: %"PRIu16"\n", stream_number);
+ lprintf(" start_time: %"PRIu64"\n", asf_stream_extension->start_time);
+ lprintf(" end_time: %"PRIu64"\n", asf_stream_extension->end_time);
+ lprintf(" data_bitrate: %"PRIu32"\n", asf_stream_extension->data_bitrate);
+ lprintf(" buffer_size: %"PRIu32"\n", asf_stream_extension->buffer_size);
+ lprintf(" initial_buffer_fullness: %"PRIu32"\n", asf_stream_extension->initial_buffer_fullness);
+ lprintf(" alternate_data_bitrate: %"PRIu32"\n", asf_stream_extension->alternate_data_bitrate);
+ lprintf(" alternate_buffer_size: %"PRIu32"\n", asf_stream_extension->alternate_buffer_size);
+ lprintf(" alternate_initial_buffer_fullness: %"PRIu32"\n", asf_stream_extension->alternate_initial_buffer_fullness);
+ lprintf(" max_object_size: %"PRIu32"\n", asf_stream_extension->max_object_size);
+ lprintf(" language_id: %"PRIu16"\n", asf_stream_extension->language_id);
+ lprintf(" average_time_per_frame: %"PRIu64"\n", asf_stream_extension->average_time_per_frame);
+ lprintf(" stream_name_count: %"PRIu16"\n", asf_stream_extension->stream_name_count);
+ lprintf(" payload_extension_system_count: %"PRIu16"\n", asf_stream_extension->payload_extension_system_count);
+ lprintf(" reliable_flag: %d\n", asf_stream_extension->reliable_flag);
+ lprintf(" seekable_flag: %d\n", asf_stream_extension->seekable_flag);
+ lprintf(" no_cleanpoints_flag: %d\n", asf_stream_extension->no_cleanpoints_flag);
+ lprintf(" resend_live_cleanpoints_flag: %d\n", asf_stream_extension->resend_live_cleanpoints_flag);
+
+ return 1;
+}
+
+int asf_header_parse_stream_bitrate_properties(asf_header_t *header_pub, uint8_t *buffer, int buffer_len) {
+ asf_header_internal_t *header = (asf_header_internal_t *)header_pub;
+ asf_reader_t reader;
+ uint16_t bitrate_count;
+ int i;
+ int stream_id;
+
+ if (buffer_len < 2)
+ return 0;
+
+ asf_reader_init(&reader, buffer, buffer_len);
+ asf_reader_get_16(&reader, &bitrate_count);
+
+ if (buffer_len < (2 + 6 * bitrate_count))
+ return 0;
+
+ lprintf (" bitrate count: %d\n", bitrate_count);
+
+ for(i = 0; i < bitrate_count; i++) {
+ uint16_t flags;
+ uint32_t bitrate;
+ int stream_number;
+ uint8_t *bitrate_pointer;
+
+ asf_reader_get_16(&reader, &flags);
+ stream_number = flags & 0x7f;
+
+ bitrate_pointer = asf_reader_get_buffer(&reader);
+ asf_reader_get_32(&reader, &bitrate);
+ lprintf (" stream num %d, bitrate %"PRIu32"\n", stream_number, bitrate);
+
+ stream_id = asf_header_get_stream_id(&header->pub, stream_number);
+ if (stream_id >= 0) {
+ header->pub.bitrates[stream_id] = bitrate;
+ header->bitrate_pointers[stream_id] = bitrate_pointer;
+ }
+ }
+ return 1;
+}
+
+int asf_header_parse_header_extension(asf_header_t *header, uint8_t *buffer, int buffer_len) {
+ asf_reader_t reader;
+
+ GUID junk1;
+ uint16_t junk2;
+ uint32_t data_length;
+
+ if (buffer_len < 22)
+ return 0;
+
+ asf_reader_init(&reader, buffer, buffer_len);
+
+ asf_reader_get_guid(&reader, &junk1);
+ asf_reader_get_16(&reader, &junk2);
+ asf_reader_get_32(&reader, &data_length);
+
+ lprintf("parse_asf_header_extension: length: %"PRIu32"\n", data_length);
+
+ while (!asf_reader_eos(&reader)) {
+
+ GUID guid;
+ int object_id;
+ uint64_t object_length, object_data_length;
+
+ if (asf_reader_get_size(&reader) < 24) {
+ printf("invalid buffer size\n");
+ return 0;
+ }
+
+ asf_reader_get_guid(&reader, &guid);
+ asf_reader_get_64(&reader, &object_length);
+
+ object_data_length = object_length - 24;
+ object_id = asf_find_object_id(&guid);
+ switch (object_id) {
+ case GUID_EXTENDED_STREAM_PROPERTIES:
+ asf_header_parse_stream_extended_properties(header, asf_reader_get_buffer(&reader), object_data_length);
+ break;
+ case GUID_ADVANCED_MUTUAL_EXCLUSION:
+ case GUID_GROUP_MUTUAL_EXCLUSION:
+ case GUID_STREAM_PRIORITIZATION:
+ case GUID_BANDWIDTH_SHARING:
+ case GUID_LANGUAGE_LIST:
+ case GUID_METADATA:
+ case GUID_METADATA_LIBRARY:
+ case GUID_INDEX_PARAMETERS:
+ case GUID_MEDIA_OBJECT_INDEX_PARAMETERS:
+ case GUID_TIMECODE_INDEX_PARAMETERS:
+ case GUID_ADVANCED_CONTENT_ENCRYPTION:
+ case GUID_COMPATIBILITY:
+ case GUID_ASF_PADDING:
+ break;
+ default:
+ lprintf ("unexpected object\n");
+ break;
+ }
+ asf_reader_skip(&reader, object_data_length);
+ }
+ return 1;
+}
+
+int asf_header_parse_content_description(asf_header_t *header_pub, uint8_t *buffer, int buffer_len) {
+ asf_header_internal_t *header = (asf_header_internal_t *)header_pub;
+ asf_reader_t reader;
+ asf_content_t *content;
+ uint16_t title_length, author_length, copyright_length, description_length, rating_length;
+
+ if (buffer_len < 10)
+ return 0;
+
+ content = malloc(sizeof(asf_content_t));
+ if (!content)
+ return 0;
+ memset(content, 0, sizeof(asf_content_t));
+
+ asf_reader_init(&reader, buffer, buffer_len);
+ asf_reader_get_16(&reader, &title_length);
+ asf_reader_get_16(&reader, &author_length);
+ asf_reader_get_16(&reader, &copyright_length);
+ asf_reader_get_16(&reader, &description_length);
+ asf_reader_get_16(&reader, &rating_length);
+
+ content->title = asf_reader_get_string(&reader, title_length, header->iconv_cd);
+ content->author = asf_reader_get_string(&reader, author_length, header->iconv_cd);
+ content->copyright = asf_reader_get_string(&reader, copyright_length, header->iconv_cd);
+ content->description = asf_reader_get_string(&reader, description_length, header->iconv_cd);
+ content->rating = asf_reader_get_string(&reader, rating_length, header->iconv_cd);
+
+ header->pub.content = content;
+ return 1;
+}
+
+
+asf_header_t *asf_header_new (uint8_t *buffer, int buffer_len) {
+
+ asf_header_internal_t *asf_header;
+ asf_reader_t reader;
+ uint32_t object_count;
+ uint16_t junk;
+
+ asf_header = malloc(sizeof(asf_header_internal_t));
+ if (!asf_header)
+ return NULL;
+ memset(asf_header, 0, sizeof(asf_header_internal_t));
+
+ lprintf("parsing_asf_header\n");
+ if (buffer_len < 6) {
+ printf("invalid buffer size\n");
+ free(asf_header);
+ return NULL;
+ }
+
+ asf_header->iconv_cd = iconv_open ("UTF-8", "UCS-2LE");
+ if (asf_header->iconv_cd == (iconv_t)-1) {
+ printf("iconv open error\n");
+ free(asf_header);
+ return NULL;
+ }
+
+ asf_reader_init(&reader, buffer, buffer_len);
+ asf_reader_get_32(&reader, &object_count);
+ asf_reader_get_16(&reader, &junk);
+
+ while (!asf_reader_eos(&reader)) {
+
+ GUID guid;
+ int object_id;
+ uint64_t object_length, object_data_length;
+
+ if (asf_reader_get_size(&reader) < 24) {
+ printf("invalid buffer size\n");
+ goto exit_error;
+ }
+
+ asf_reader_get_guid(&reader, &guid);
+ asf_reader_get_64(&reader, &object_length);
+
+ object_data_length = object_length - 24;
+
+ object_id = asf_find_object_id(&guid);
+ switch (object_id) {
+
+ case GUID_ASF_FILE_PROPERTIES:
+ asf_header_parse_file_properties(&asf_header->pub, asf_reader_get_buffer(&reader), object_data_length);
+ break;
+
+ case GUID_ASF_STREAM_PROPERTIES:
+ asf_header_parse_stream_properties(&asf_header->pub, asf_reader_get_buffer(&reader), object_data_length);
+ break;
+
+ case GUID_ASF_STREAM_BITRATE_PROPERTIES:
+ asf_header_parse_stream_bitrate_properties(&asf_header->pub, asf_reader_get_buffer(&reader), object_data_length);
+ break;
+
+ case GUID_ASF_HEADER_EXTENSION:
+ asf_header_parse_header_extension(&asf_header->pub, asf_reader_get_buffer(&reader), object_data_length);
+ break;
+
+ case GUID_ASF_CONTENT_DESCRIPTION:
+ asf_header_parse_content_description(&asf_header->pub, asf_reader_get_buffer(&reader), object_data_length);
+ break;
+
+ case GUID_ASF_CODEC_LIST:
+ case GUID_ASF_SCRIPT_COMMAND:
+ case GUID_ASF_MARKER:
+ case GUID_ASF_BITRATE_MUTUAL_EXCLUSION:
+ case GUID_ASF_ERROR_CORRECTION:
+ case GUID_ASF_EXTENDED_CONTENT_DESCRIPTION:
+ case GUID_ASF_EXTENDED_CONTENT_ENCRYPTION:
+ case GUID_ASF_PADDING:
+ break;
+
+ default:
+ lprintf ("unexpected object\n");
+ break;
+ }
+ asf_reader_skip(&reader, object_data_length);
+ }
+
+ /* basic checks */
+ if (!asf_header->pub.file) {
+ lprintf("no file object present\n");
+ goto exit_error;
+ }
+ if (!asf_header->pub.content) {
+ lprintf("no content object present\n");
+ asf_header->pub.content = malloc(sizeof(asf_content_t));
+ if (!asf_header->pub.content)
+ goto exit_error;
+ memset(asf_header->pub.content, 0, sizeof(asf_content_t));
+ }
+
+ return &asf_header->pub;
+
+exit_error:
+ asf_header_delete(&asf_header->pub);
+ return NULL;
+}
+
+
+void asf_header_delete_file_properties(asf_file_t *asf_file) {
+ free(asf_file);
+}
+
+void asf_header_delete_content(asf_content_t *asf_content) {
+ if (asf_content->title)
+ free(asf_content->title);
+ if (asf_content->author)
+ free(asf_content->author);
+ if (asf_content->copyright)
+ free(asf_content->copyright);
+ if (asf_content->description)
+ free(asf_content->description);
+ if (asf_content->rating)
+ free(asf_content->rating);
+ free(asf_content);
+}
+
+void asf_header_delete_stream_properties(asf_stream_t *asf_stream) {
+ if (asf_stream->private_data)
+ free(asf_stream->private_data);
+ if (asf_stream->error_correction_data)
+ free(asf_stream->error_correction_data);
+ free(asf_stream);
+}
+
+void asf_header_delete_stream_extended_properties(asf_stream_extension_t *asf_stream_extension) {
+ int i;
+
+ if (asf_stream_extension->stream_name_count > 0) {
+ for (i = 0; i < asf_stream_extension->stream_name_count; i++) {
+ free(asf_stream_extension->stream_names[i]);
+ }
+ free(asf_stream_extension->stream_names);
+ }
+ free(asf_stream_extension);
+}
+
+void asf_header_delete (asf_header_t *header_pub) {
+ asf_header_internal_t *header = (asf_header_internal_t *)header_pub;
+ int i;
+
+ if (header->pub.file)
+ asf_header_delete_file_properties(header->pub.file);
+
+ if (header->pub.content)
+ asf_header_delete_content(header->pub.content);
+
+ for (i = 0; i < ASF_MAX_NUM_STREAMS; i++) {
+ if (header->pub.streams[i])
+ asf_header_delete_stream_properties(header->pub.streams[i]);
+ if (header->pub.stream_extensions[i])
+ asf_header_delete_stream_extended_properties(header->pub.stream_extensions[i]);
+ }
+
+ if (header->iconv_cd != (iconv_t)-1)
+ iconv_close (header->iconv_cd);
+
+ free(header);
+}
+
+/* Given a bandwidth, select the best stream */
+static int asf_header_choose_stream (asf_header_internal_t *header, int stream_type,
+ uint32_t bandwidth) {
+ int i;
+ int max_lt, min_gt;
+
+ max_lt = min_gt = -1;
+ for (i = 0; i < header->pub.stream_count; i++) {
+ if (header->pub.streams[i]->stream_type == stream_type) {
+ if (header->pub.bitrates[i] <= bandwidth) {
+ if ((max_lt == -1) || (header->pub.bitrates[i] > header->pub.bitrates[max_lt]))
+ max_lt = i;
+ } else {
+ if ((min_gt == -1) || (header->pub.bitrates[i] < header->pub.bitrates[min_gt]))
+ min_gt = i;
+ }
+ }
+ }
+
+ return (max_lt != -1) ? max_lt : min_gt;
+}
+
+void asf_header_choose_streams (asf_header_t *header_pub, uint32_t bandwidth,
+ int *video_id, int *audio_id) {
+ asf_header_internal_t *header = (asf_header_internal_t *)header_pub;
+ uint32_t bandwidth_left;
+
+ *video_id = *audio_id = -1;
+ bandwidth_left = bandwidth;
+
+ lprintf("%d streams, bandwidth %"PRIu32"\n", header->pub.stream_count, bandwidth_left);
+
+ /* choose a video stream adapted to the user bandwidth */
+ *video_id = asf_header_choose_stream (header, GUID_ASF_VIDEO_MEDIA, bandwidth_left);
+ if (*video_id != -1) {
+ if (header->pub.bitrates[*video_id] < bandwidth_left) {
+ bandwidth_left -= header->pub.bitrates[*video_id];
+ } else {
+ bandwidth_left = 0;
+ }
+ lprintf("selected video stream %d, bandwidth left: %"PRIu32"\n",
+ header->pub.streams[*video_id]->stream_number, bandwidth_left);
+ } else {
+ lprintf("no video stream\n");
+ }
+
+ /* choose a audio stream adapted to the user bandwidth */
+ *audio_id = asf_header_choose_stream (header, GUID_ASF_AUDIO_MEDIA, bandwidth_left);
+ if (*audio_id != -1) {
+ if (header->pub.bitrates[*audio_id] < bandwidth_left) {
+ bandwidth_left -= header->pub.bitrates[*audio_id];
+ } else {
+ bandwidth_left = 0;
+ }
+ lprintf("selected audio stream %d, bandwidth left: %"PRIu32"\n",
+ header->pub.streams[*audio_id]->stream_number, bandwidth_left);
+ } else {
+ lprintf("no audio stream\n");
+ }
+}
+
+void asf_header_disable_streams (asf_header_t *header_pub, int video_id, int audio_id) {
+ asf_header_internal_t *header = (asf_header_internal_t *)header_pub;
+ int i;
+
+ for (i = 0; i < header->pub.stream_count; i++) {
+ int stream_type = header->pub.streams[i]->stream_type;
+
+ if (((stream_type == GUID_ASF_VIDEO_MEDIA) && (i != video_id)) ||
+ ((stream_type == GUID_ASF_AUDIO_MEDIA) && (i != audio_id))) {
+ uint8_t *bitrate_pointer = header->bitrate_pointers[i];
+ /* disable the stream */
+ lprintf("stream %d disabled\n", header->pub.streams[i]->stream_number);
+ *bitrate_pointer++ = 0;
+ *bitrate_pointer++ = 0;
+ *bitrate_pointer++ = 0;
+ *bitrate_pointer = 0;
+ }
+ }
+}