diff options
Diffstat (limited to 'src/demuxers/ebml.c')
-rw-r--r-- | src/demuxers/ebml.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/src/demuxers/ebml.c b/src/demuxers/ebml.c new file mode 100644 index 000000000..b6f0658fc --- /dev/null +++ b/src/demuxers/ebml.c @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2000-2003 the xine project + * + * This file is part of xine, a free video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * EBML parser + * a lot of ideas from the gstreamer parser + * + * $Id: ebml.c,v 1.1 2004/01/05 00:40:54 tmattern Exp $ + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#define LOG_MODULE "ebml" +#define LOG_VERBOSE +/* +#define LOG +*/ +#include "xine_internal.h" +#include "xineutils.h" +#include "bswap.h" + +#include "ebml.h" + + +ebml_parser_t *new_ebml_parser (xine_t *xine, input_plugin_t *input) { + ebml_parser_t *ebml; + + ebml = malloc(sizeof(ebml_parser_t)); + ebml->xine = xine; + ebml->input = input; + + ebml->version = 0; + ebml->read_version = 0; + ebml->max_id_len = 0; + ebml->max_size_len = 0; + ebml->doctype = NULL; + ebml->doctype_version = 0; + ebml->doctype_read_version = 0; + + ebml->level = 0; + + return ebml; +} + + +void dispose_ebml_parser(ebml_parser_t *ebml) { + free(ebml); +} + + +uint32_t ebml_get_next_level(ebml_parser_t *ebml, ebml_elem_t *elem) { + ebml_elem_t *parent_elem; + + if (ebml->level > 0) { + parent_elem = &ebml->elem_stack[ebml->level - 1]; + while ((elem->start + elem->len) >= (parent_elem->start + parent_elem->len)) { + lprintf("parent: %lld, %lld; elem: %lld, %lld\n", + parent_elem->start, parent_elem->len, elem->start, elem->len); + ebml->level--; + if (ebml->level == 0) break; + parent_elem = &ebml->elem_stack[ebml->level - 1]; + } + } + lprintf("id: 0x%x, len: %lld, next_level: %d\n", elem->id, elem->len, ebml->level); + return ebml->level; +} + + +static int ebml_read_elem_id(ebml_parser_t *ebml, uint32_t *id) { + uint8_t data[4]; + uint32_t mask = 0x80; + uint32_t value; + int size = 1; + int i; + + if (ebml->input->read(ebml->input, data, 1) != 1) { + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: read error\n"); + return 0; + } + value = data[0]; + + /* compute the size of the ID (1-4 bytes)*/ + while (size <= 4 && !(value & mask)) { + size++; + mask >>= 1; + } + if (size > 4) { + off_t pos = ebml->input->get_current_pos(ebml->input); + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: invalid EBML ID size (0x%x) at position %llu\n", data[0], pos); + return 0; + } + + /* read the rest of the id */ + if (ebml->input->read(ebml->input, data + 1, size - 1) != (size - 1)) { + off_t pos = ebml->input->get_current_pos(ebml->input); + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: read error at position %llu\n", pos); + return 0; + } + for(i = 1; i < size; i++) { + value = (value << 8) | data[i]; + } + *id = value; + + return 1; +} + + +static int ebml_read_elem_len(ebml_parser_t *ebml, uint64_t *len) { + uint8_t data[8]; + uint32_t mask = 0x80; + int size = 1; + int ff_bytes; + uint64_t value; + int i; + + if (ebml->input->read(ebml->input, data, 1) != 1) { + off_t pos = ebml->input->get_current_pos(ebml->input); + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: read error at position %llu\n", pos); + return 0; + } + value = data[0]; + + /* compute the size of the "data len" (1-8 bytes) */ + while (size <= 8 && !(value & mask)) { + size++; + mask >>= 1; + } + if (size > 8) { + off_t pos = ebml->input->get_current_pos(ebml->input); + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: Invalid EBML length size (0x%x) at position %llu\n", data[0], pos); + return 0; + } + + /* remove size bits */ + value &= mask - 1; + + /* check if the first byte is full */ + if (value == (mask - 1)) + ff_bytes = 1; + else + ff_bytes = 0; + + /* read the rest of the len */ + if (ebml->input->read(ebml->input, data + 1, size - 1) != (size - 1)) { + off_t pos = ebml->input->get_current_pos(ebml->input); + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: read error at position %llu\n", pos); + return 0; + } + for (i = 1; i < size; i++) { + if (data[i] == 0xff) + ff_bytes++; + value = (value << 8) | data[i]; + } + + if (ff_bytes == size) + *len = -1; + else + *len = value; + + return 1; +} + + +static int ebml_read_elem_data(ebml_parser_t *ebml, int8_t *buf, int64_t len) { + + if (ebml->input->read(ebml->input, buf, len) != len) { + off_t pos = ebml->input->get_current_pos(ebml->input); + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: read error at position %llu\n", pos); + return 0; + } + + return 1; +} + + +int ebml_skip(ebml_parser_t *ebml, ebml_elem_t *elem) { + if (ebml->input->seek(ebml->input, elem->len, SEEK_CUR) < 0) { + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: seek error\n"); + return 0; + } + + return 1; +} + + +int ebml_read_elem_head(ebml_parser_t *ebml, ebml_elem_t *elem) { + + if (!ebml_read_elem_id(ebml, &elem->id)) + return 0; + + if (!ebml_read_elem_len(ebml, &elem->len)) + return 0; + + elem->start = ebml->input->get_current_pos(ebml->input); + return 1; +} + + +int ebml_read_uint(ebml_parser_t *ebml, ebml_elem_t *elem, uint64_t *num) { + uint8_t data[8]; + uint64_t size = elem->len; + + if ((elem->len < 1) || (elem->len > 8)) { + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: Invalid integer element size %lld\n", size); + return 0; + } + + if (!ebml_read_elem_data (ebml, data, size)) + return 0; + + *num = 0; + while (size > 0) { + *num = (*num << 8) | data[elem->len - size]; + size--; + } + + return 1; +} + +int ebml_read_sint (ebml_parser_t *ebml, ebml_elem_t *elem, int64_t *num) { + uint8_t data[8]; + uint64_t size = elem->len; + + if ((elem->len < 1) || (elem->len > 8)) { + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: Invalid integer element size %lld\n", size); + return 0; + } + + if (!ebml_read_elem_data(ebml, data, size)) + return 0; + + /* propagate negative bit */ + if (data[0] & 80) + *num = -1; + else + *num = 0; + + while (size > 0) { + *num = (*num << 8) | data[elem->len - size]; + size--; + } + + return 1; +} + + +int ebml_read_float (ebml_parser_t *ebml, ebml_elem_t *elem, double *num) { + uint8_t data[10]; + uint64_t size = elem->len; + + if ((size != 4) && (size != 8) && (size != 10)) { + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: Invalid float element size %lld\n", size); + return 0; + } + + if (!ebml_read_elem_data(ebml, data, size)) + return 0; + + if (size == 10) { + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: FIXME! 10-byte floats unimplemented\n"); + return 0; + } + + if (size == 4) { + float f; + + *((uint32_t *) &f) = BE_32(data); + *num = f; + } else { + double d; + + *((uint64_t *) &d) = BE_64(data); + *num = d; + } + return 1; +} + +int ebml_read_ascii(ebml_parser_t *ebml, ebml_elem_t *elem, char *str) { + uint64_t size = elem->len; + + if (!ebml_read_elem_data(ebml, str, size)) + return 0; + + return 1; +} + +int ebml_read_utf8 (ebml_parser_t *ebml, ebml_elem_t *elem, char *str) { + return ebml_read_ascii (ebml, elem, str); +} + +int ebml_read_date (ebml_parser_t *ebml, ebml_elem_t *elem, int64_t *date) { + return ebml_read_sint (ebml, elem, date); +} + +int ebml_read_master (ebml_parser_t *ebml, ebml_elem_t *elem) { + ebml_elem_t *top_elem; + + if (ebml->level >= 0) { + top_elem = &ebml->elem_stack[ebml->level]; + top_elem->start = elem->start; + top_elem->len = elem->len; + top_elem->id = elem->id; + + ebml->level++; + lprintf("id: 0x%x, len: %lld, level: %d\n", elem->id, elem->len, ebml->level); + if (ebml->level >= EBML_STACK_SIZE) { + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: max level exceeded\n"); + return 0; + } + return 1; + } else { + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: invalid current level\n"); + return 0; + } +} + +int ebml_read_binary(ebml_parser_t *ebml, ebml_elem_t *elem, uint8_t *binary) { + if (!ebml_read_elem_data(ebml, binary, elem->len)) + return 0; + + return 1; +} + +int ebml_check_header(ebml_parser_t *ebml) { + uint32_t next_level; + ebml_elem_t master; + + if (!ebml_read_elem_head(ebml, &master)) { + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: invalid master element\n"); + return 0; + } + + if (master.id != EBML_ID_EBML) { + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: invalid master element\n"); + return 0; + } + + if (!ebml_read_master (ebml, &master)) + return 0; + + next_level = 1; + while (next_level == 1) { + ebml_elem_t elem; + + if (!ebml_read_elem_head(ebml, &elem)) + return 0; + + switch (elem.id) { + case EBML_ID_EBMLVERSION: { + uint64_t num; + + if (!ebml_read_uint (ebml, &elem, &num)) + return 0; + lprintf("ebml_version: %lld\n", num); + ebml->version = num; + break; + } + + case EBML_ID_EBMLREADVERSION: { + uint64_t num; + + if (!ebml_read_uint (ebml, &elem, &num)) + return 0; + lprintf("ebml_read_version: %lld\n", num); + if (num != EBML_VERSION) + return 0; + ebml->read_version = num; + break; + } + + case EBML_ID_EBMLMAXIDLENGTH: { + uint64_t num; + + if (!ebml_read_uint (ebml, &elem, &num)) + return 0; + lprintf("ebml_max_id_length: %lld\n", num); + ebml->max_id_len = num; + break; + } + + case EBML_ID_EBMLMAXSIZELENGTH: { + uint64_t num; + + if (!ebml_read_uint (ebml, &elem, &num)) + return 0; + lprintf("ebml_max_size_length: %lld\n", num); + ebml->max_size_len = num; + break; + } + + case EBML_ID_DOCTYPE: { + char *text = malloc(elem.len + 1); + + text[elem.len] = '\0'; + if (!ebml_read_ascii (ebml, &elem, text)) + return 0; + + lprintf("doctype: %s\n", text); + if (ebml->doctype) + free (ebml->doctype); + ebml->doctype = text; + break; + } + + case EBML_ID_DOCTYPEVERSION: { + uint64_t num; + + if (!ebml_read_uint (ebml, &elem, &num)) + return 0; + lprintf("doctype_version: %lld\n", num); + ebml->doctype_version = num; + break; + } + + case EBML_ID_DOCTYPEREADVERSION: { + uint64_t num; + + if (!ebml_read_uint (ebml, &elem, &num)) + return 0; + lprintf("doctype_read_version: %lld\n", num); + ebml->doctype_read_version = num; + break; + } + + default: + xprintf(ebml->xine, XINE_VERBOSITY_LOG, + "ebml: Unknown data type 0x%x in EBML header (ignored)\n", elem.id); + } + next_level = ebml_get_next_level(ebml, &elem); + } + + return 1; +} |