summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/demuxers/Makefile.am7
-rw-r--r--src/demuxers/demux_matroska.c1561
-rw-r--r--src/demuxers/ebml.c468
-rw-r--r--src/demuxers/ebml.h103
-rw-r--r--src/demuxers/matroska.h270
5 files changed, 2408 insertions, 1 deletions
diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am
index 6729fecf4..97c6bb02f 100644
--- a/src/demuxers/Makefile.am
+++ b/src/demuxers/Makefile.am
@@ -45,6 +45,7 @@ lib_LTLIBRARIES = $(ogg_module) $(asf_module) $(mng_module) $(image_module) \
xineplug_dmx_yuv_frames.la \
xineplug_dmx_slave.la \
xineplug_dmx_nsv.la \
+ xineplug_dmx_matroska.la \
xineplug_dmx_iff.la
xineplug_dmx_ogg_la_SOURCES = demux_ogg.c
@@ -138,9 +139,13 @@ xineplug_dmx_nsv_la_SOURCES = demux_nsv.c
xineplug_dmx_nsv_la_LIBADD = $(XINE_LIB)
xineplug_dmx_nsv_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+xineplug_dmx_matroska_la_SOURCES = demux_matroska.c ebml.c
+xineplug_dmx_matroska_la_LIBADD = $(XINE_LIB)
+xineplug_dmx_matroska_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+
xineplug_dmx_iff_la_SOURCES = demux_iff.c
xineplug_dmx_iff_la_LIBADD = $(XINE_LIB)
xineplug_dmx_iff_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
include_HEADERS = demux.h
-noinst_HEADERS = asfheader.h qtpalette.h group_games.h group_audio.h id3.h
+noinst_HEADERS = asfheader.h qtpalette.h group_games.h group_audio.h id3.h ebml.h matroska.h
diff --git a/src/demuxers/demux_matroska.c b/src/demuxers/demux_matroska.c
new file mode 100644
index 000000000..94a0a5dfd
--- /dev/null
+++ b/src/demuxers/demux_matroska.c
@@ -0,0 +1,1561 @@
+/*
+ * 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
+ *
+ * $Id: demux_matroska.c,v 1.1 2004/01/05 00:40:54 tmattern Exp $
+ *
+ * demultiplexer for matroska streams
+ *
+ * TODO:
+ * memory leaks
+ * seeking
+ * subtitles
+ * more codecs
+ * metadata
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define LOG_MODULE "demux_matroska"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "demux.h"
+#include "bswap.h"
+
+#include "ebml.h"
+#include "matroska.h"
+
+#define NUM_PREVIEW_BUFFERS 10
+
+#define MAX_STREAMS 128
+#define MAX_FRAMES 32
+
+typedef struct {
+
+ demux_plugin_t demux_plugin;
+
+ xine_stream_t *stream;
+
+ input_plugin_t *input;
+
+ int status;
+
+ ebml_parser_t *ebml;
+
+ /* segment element */
+ ebml_elem_t segment;
+ uint64_t timecode_scale;
+ int duration; /* in millis */
+ int preview_sent;
+ int preview_mode;
+
+ /* meta seek info */
+ off_t seekhead_pos;
+ off_t info_pos;
+ off_t tracks_pos;
+ off_t chapters_pos;
+ off_t cues_pos;
+ off_t attachments_pos;
+ off_t tags_pos;
+ int has_seekhead;
+ int seekhead_handled;
+
+ /* tracks */
+ int num_tracks;
+ matroska_track_t *tracks[MAX_STREAMS];
+
+ /* block */
+ uint8_t *block_data;
+ int block_data_size;
+
+ /* current tracks */
+ matroska_track_t *video_track;
+ matroska_track_t *audio_track;
+ matroska_track_t *sub_track;
+
+} demux_matroska_t ;
+
+typedef struct {
+
+ demux_class_t demux_class;
+
+ /* class-wide, global variables here */
+
+ xine_t *xine;
+
+} demux_matroska_class_t;
+
+
+static int parse_info(demux_matroska_t *this) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 2;
+ double duration = 0.0; /* in matroska unit */
+
+ while (next_level == 2) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_I_TIMECODESCALE:
+ lprintf("timecode_scale\n");
+ if (!ebml_read_uint(ebml, &elem, &this->timecode_scale))
+ return 0;
+ break;
+ case MATROSKA_ID_I_DURATION: {
+
+ lprintf("duration\n");
+ if (!ebml_read_float(ebml, &elem, &duration))
+ return 0;
+ }
+ break;
+
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ if (this->timecode_scale == 0) {
+ this->timecode_scale = 1000000;
+ }
+ this->duration = (int)(duration * (double)this->timecode_scale / 1000000.0);
+ lprintf("timecode_scale: %lld\n", this->timecode_scale);
+ lprintf("duration: %d\n", this->duration);
+ return 1;
+}
+
+
+static int parse_video_track (demux_matroska_t *this, matroska_video_track_t *vt) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 4;
+ uint64_t val;
+
+ while (next_level == 4) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_TV_FLAGINTERLACED:
+ lprintf("MATROSKA_ID_TV_FLAGINTERLACED\n");
+ if (!ebml_read_uint(ebml, &elem, &val))
+ return 0;
+ vt->flag_interlaced = val;
+ break;
+ case MATROSKA_ID_TV_PIXELWIDTH:
+ lprintf("MATROSKA_ID_TV_PIXELWIDTH\n");
+ if (!ebml_read_uint(ebml, &elem, &val))
+ return 0;
+ vt->pixel_witdh = val;
+ break;
+ case MATROSKA_ID_TV_PIXELHEIGHT:
+ lprintf("MATROSKA_ID_TV_PIXELHEIGHT\n");
+ if (!ebml_read_uint(ebml, &elem, &val))
+ return 0;
+ vt->pixel_height = val;
+ break;
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ return 1;
+}
+
+
+static int parse_audio_track (demux_matroska_t *this, matroska_audio_track_t *at) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 4;
+
+ while (next_level == 4) {
+ ebml_elem_t elem;
+ uint64_t val;
+ double fval;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_TA_SAMPLINGFREQUENCY:
+ lprintf("MATROSKA_ID_TA_SAMPLINGFREQUENCY\n");
+ if (!ebml_read_float(ebml, &elem, &fval))
+ return 0;
+ at->sampling_freq = (int)fval;
+ break;
+ case MATROSKA_ID_TA_OUTPUTSAMPLINGFREQUENCY:
+ lprintf("MATROSKA_ID_TA_OUTPUTSAMPLINGFREQUENCY\n");
+ if (!ebml_read_float(ebml, &elem, &fval))
+ return 0;
+ at->output_sampling_freq = (int)fval;
+ break;
+ case MATROSKA_ID_TA_CHANNELS:
+ lprintf("MATROSKA_ID_TA_CHANNELS\n");
+ if (!ebml_read_uint(ebml, &elem, &val))
+ return 0;
+ at->channels = val;
+ break;
+ case MATROSKA_ID_TA_BITDEPTH:
+ lprintf("MATROSKA_ID_TA_BITDEPTH\n");
+ if (!ebml_read_uint(ebml, &elem, &val))
+ return 0;
+ at->bits_per_sample = val;
+ break;
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ return 1;
+}
+
+
+static void init_codec_video(demux_matroska_t *this, matroska_track_t *track) {
+ buf_element_t *buf;
+
+ buf = track->fifo->buffer_pool_alloc (track->fifo);
+ buf->type = track->buf_type;
+
+ if (track->codec_private_len > buf->max_size) {
+ buf->size = buf->max_size;
+ } else {
+ buf->size = track->codec_private_len;
+ }
+
+ if (buf->size)
+ xine_fast_memcpy (buf->content, track->codec_private, buf->size);
+ else
+ buf->content = NULL;
+
+ buf->decoder_flags = BUF_FLAG_HEADER;
+ buf->type = track->buf_type;
+ buf->pts = 0;
+ track->fifo->put (track->fifo, buf);
+}
+
+
+static void init_codec_audio(demux_matroska_t *this, matroska_track_t *track) {
+ buf_element_t *buf;
+
+ buf = track->fifo->buffer_pool_alloc (track->fifo);
+
+ /* default param */
+ buf->decoder_info[0] = 0;
+ buf->decoder_info[1] = 44100;
+ buf->decoder_info[2] = 16;
+ buf->decoder_info[3] = 2;
+ /* track param */
+ if (track->audio_track) {
+ if (track->audio_track->sampling_freq)
+ buf->decoder_info[1] = track->audio_track->sampling_freq;
+ if (track->audio_track->bits_per_sample)
+ buf->decoder_info[2] = track->audio_track->bits_per_sample;
+ if (track->audio_track->channels)
+ buf->decoder_info[3] = track->audio_track->channels;
+ }
+ lprintf("%d Hz, %d bits, %d channels\n", buf->decoder_info[1],
+ buf->decoder_info[2], buf->decoder_info[3]);
+
+ if (track->codec_private_len > buf->max_size) {
+ buf->size = buf->max_size;
+ } else {
+ buf->size = track->codec_private_len;
+ }
+
+ if (buf->size)
+ xine_fast_memcpy (buf->content, track->codec_private, buf->size);
+ else
+ buf->content = NULL;
+
+ buf->decoder_flags = BUF_FLAG_HEADER;
+ buf->type = track->buf_type;
+ buf->pts = 0;
+ track->fifo->put (track->fifo, buf);
+}
+
+
+static void init_codec_vorbis(demux_matroska_t *this, matroska_track_t *track) {
+ buf_element_t *buf;
+ uint8_t nb_lace;
+ int frame[3];
+ int i;
+ uint8_t *data;
+
+ nb_lace = track->codec_private[0];
+ if (nb_lace != 2)
+ return;
+
+ frame[0] = track->codec_private[1];
+ frame[1] = track->codec_private[2];
+ frame[2] = track->codec_private_len - frame[0] - frame[1] - 3;
+
+ data = track->codec_private + 3;
+ for (i = 0; i < 3; i++) {
+ buf = track->fifo->buffer_pool_alloc (track->fifo);
+ buf->decoder_flags = BUF_FLAG_PREVIEW;
+ buf->type = track->buf_type;
+ buf->pts = 0;
+ buf->size = frame[i];
+
+ xine_fast_memcpy (buf->content, data, buf->size);
+ data += buf->size;
+
+ track->fifo->put (track->fifo, buf);
+ }
+}
+
+
+static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 3;
+
+ while (next_level == 3) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_TR_NUMBER: {
+ uint64_t num;
+ lprintf("TrackNumber\n");
+ if (!ebml_read_uint(ebml, &elem, &num))
+ return 0;
+ track->track_num = num;
+ }
+ break;
+
+ case MATROSKA_ID_TR_TYPE: {
+ uint64_t num;
+ lprintf("TrackType\n");
+ if (!ebml_read_uint(ebml, &elem, &num))
+ return 0;
+ track->track_type = num;
+ }
+ break;
+
+ case MATROSKA_ID_TR_CODECID: {
+ char *codec_id = malloc (elem.len + 1);
+ lprintf("CodecID\n");
+ if (!ebml_read_ascii(ebml, &elem, codec_id))
+ return 0;
+ codec_id[elem.len] = '\0';
+ track->codec_id = codec_id;
+ }
+ break;
+
+ case MATROSKA_ID_TR_CODECPRIVATE: {
+ char *codec_private = malloc (elem.len);
+ lprintf("CodecPrivate\n");
+ if (!ebml_read_binary(ebml, &elem, codec_private))
+ return 0;
+ track->codec_private = codec_private;
+ track->codec_private_len = elem.len;
+ }
+ break;
+
+ case MATROSKA_ID_TR_LANGUAGE: {
+ char *language = malloc (elem.len + 1);
+ lprintf("Language\n");
+ if (!ebml_read_ascii(ebml, &elem, language))
+ return 0;
+ language[elem.len] = '\0';
+ track->language = language;
+ }
+ break;
+
+ case MATROSKA_ID_TV:
+ lprintf("Video\n");
+ if (track->video_track)
+ return 1;
+ track->video_track = (matroska_video_track_t *)xine_xmalloc(sizeof(matroska_video_track_t));
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_video_track(this, track->video_track))
+ return 0;
+ break;
+
+ case MATROSKA_ID_TA:
+ lprintf("Audio\n");
+ if (track->audio_track)
+ return 1;
+ track->audio_track = (matroska_audio_track_t *)xine_xmalloc(sizeof(matroska_audio_track_t));
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_audio_track(this, track->audio_track))
+ return 0;
+ break;
+
+ case MATROSKA_ID_TR_FLAGDEFAULT: {
+ uint64_t val;
+
+ lprintf("Default\n");
+ if (!ebml_read_uint(ebml, &elem, &val))
+ return 0;
+ track->default_flag = (int)val;
+ }
+ break;
+
+ case MATROSKA_ID_TR_UID:
+ case MATROSKA_ID_TR_FLAGENABLED:
+ case MATROSKA_ID_TR_FLAGLACING:
+ case MATROSKA_ID_TR_MINCACHE:
+ case MATROSKA_ID_TR_MAXCACHE:
+ case MATROSKA_ID_TR_DEFAULTDURATION:
+ case MATROSKA_ID_TR_TIMECODESCALE:
+ case MATROSKA_ID_TR_NAME:
+ case MATROSKA_ID_TR_CODECNAME:
+ case MATROSKA_ID_TR_CODECSETTINGS:
+ case MATROSKA_ID_TR_CODECINFOURL:
+ case MATROSKA_ID_TR_CODECDOWNLOADURL:
+ case MATROSKA_ID_TR_CODECDECODEALL:
+ case MATROSKA_ID_TR_OVERLAY:
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem)) {
+ return 0;
+ }
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+
+ switch(track->track_type) {
+ case MATROSKA_TRACK_VIDEO:
+ if (!this->video_track) {
+ this->video_track = track;
+ track->fifo = this->stream->video_fifo;
+ }
+ break;
+ case MATROSKA_TRACK_AUDIO:
+ if (!this->audio_track) {
+ this->audio_track = track;
+ track->fifo = this->stream->audio_fifo;
+ }
+ break;
+ case MATROSKA_TRACK_SUBTITLE:
+ if (!this->sub_track) {
+ this->sub_track = track;
+ track->fifo = this->stream->video_fifo;
+ }
+ break;
+ case MATROSKA_TRACK_COMPLEX:
+ case MATROSKA_TRACK_LOGO:
+ case MATROSKA_TRACK_CONTROL:
+ break;
+ }
+
+ xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_matroska: Track %d, %s %s\n",
+ track->track_num,
+ (track->codec_id ? track->codec_id : ""),
+ (track->language ? track->language : ""));
+ if (track->codec_id && track->fifo) {
+ int init_mode = 0;;
+
+ if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_VFW_FOURCC)) {
+ xine_bmiheader *bih;
+
+ lprintf("MATROSKA_CODEC_ID_V_VFW_FOURCC\n");
+ bih = (xine_bmiheader*)track->codec_private;
+ _x_bmiheader_le2me(bih);
+
+ track->buf_type = _x_fourcc_to_buf_video(bih->biCompression);
+
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_UNCOMPRESSED)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG4_SP)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG4_ASP)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG4_AP)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MSMPEG4V3)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG1)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG2)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_REAL_RV10)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_REAL_RV20)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_REAL_RV30)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_REAL_RV40)) {
+
+ lprintf("MATROSKA_CODEC_ID_V_REAL_RV40\n");
+ /* track->buf_type = BUF_VIDEO_RV40; */
+
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MJPEG)) {
+ } else if ((!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_MPEG1_L1)) ||
+ (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_MPEG1_L2)) ||
+ (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_MPEG1_L3))) {
+ lprintf("MATROSKA_CODEC_ID_A_MPEG1\n");
+ track->buf_type = BUF_AUDIO_MPEG;
+
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_PCM_INT_BE)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_PCM_INT_LE)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_PCM_FLOAT)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_AC3)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_DTS)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_VORBIS)) {
+
+ lprintf("MATROSKA_CODEC_ID_A_VORBIS\n");
+ track->buf_type = BUF_AUDIO_VORBIS;
+ init_mode = 2;
+
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_ACM)) {
+ } else if (!strncmp(track->codec_id, MATROSKA_CODEC_ID_A_AAC,
+ sizeof(MATROSKA_CODEC_ID_A_AAC - 1))) {
+ lprintf("MATROSKA_CODEC_ID_A_AAC\n");
+ track->buf_type = BUF_AUDIO_AAC;
+ init_mode = 1;
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_REAL_14_4)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_REAL_28_8)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_REAL_COOK)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_REAL_SIPR)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_REAL_RALF)) {
+ } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_REAL_ATRC)) {
+ } else {
+ lprintf("unknown codec\n");
+ }
+
+ if (track->buf_type) {
+ switch (init_mode) {
+ case 0:
+ init_codec_video(this, track);
+ break;
+
+ case 1:
+ init_codec_audio(this, track);
+ break;
+
+ case 2:
+ init_codec_vorbis(this, track);
+ break;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+static int parse_tracks(demux_matroska_t *this) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 2;
+
+ while (next_level == 2) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_TR_ENTRY: {
+ this->tracks[this->num_tracks] = xine_xmalloc(sizeof(matroska_track_t));
+ lprintf("TrackEntry\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_track_entry(this, this->tracks[this->num_tracks]))
+ return 0;
+ this->num_tracks++;
+ }
+ break;
+
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ return 1;
+}
+
+
+static int parse_chapters(demux_matroska_t *this) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 2;
+
+ while (next_level == 2) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ return 1;
+}
+
+
+static int parse_cues(demux_matroska_t *this) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 2;
+
+ while (next_level == 2) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ return 1;
+}
+
+
+static int parse_attachments(demux_matroska_t *this) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 2;
+
+ while (next_level == 2) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ return 1;
+}
+
+
+static int parse_tags(demux_matroska_t *this) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 2;
+
+ while (next_level == 2) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ return 1;
+}
+
+
+static int parse_ebml_uint(demux_matroska_t *this, uint8_t *data, uint64_t *num) {
+ uint8_t mask = 0x80;
+ int size = 1;
+ int i;
+
+ /* compute the size of the "data len" (1-8 bytes) */
+ while (size <= 8 && !(data[0] & mask)) {
+ size++;
+ mask >>= 1;
+ }
+ if (size > 8) {
+ off_t pos = this->input->get_current_pos(this->input);
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
+ "demux_matroska: Invalid Track Number at position %llu\n", pos);
+ return 0;
+ }
+
+ *num = data[0];
+ *num &= mask - 1;
+
+ for (i = 1; i < size; i++) {
+ *num = (*num << 8) | data[i];
+ }
+ return size;
+}
+
+
+static int parse_ebml_sint(demux_matroska_t *this, uint8_t *data, int64_t *num) {
+ uint64_t unum;
+ int size;
+
+ size = parse_ebml_uint(this, data, &unum);
+ if (!size)
+ return 0;
+
+ /* formula taken from gstreamer demuxer */
+ if (unum == -1)
+ *num = -1;
+ else
+ *num = unum - ((1 << ((7 * size) - 1)) - 1);
+
+ return size;
+}
+
+static int find_track_by_id(demux_matroska_t *this, int track_num,
+ matroska_track_t **track) {
+ int i;
+
+ *track = NULL;
+ for (i = 0; i < this->num_tracks; i++) {
+ if (this->tracks[i]->track_num == track_num) {
+ *track = this->tracks[i];
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int read_block_data (demux_matroska_t *this, int len) {
+ /* memory management */
+ if (this->block_data_size < len) {
+ if (this->block_data)
+ this->block_data = realloc(this->block_data, len);
+ else
+ this->block_data = malloc(len);
+ this->block_data_size = len;
+ }
+
+ /* block datas */
+ if (this->input->read(this->input, this->block_data, len) != len) {
+ off_t pos = this->input->get_current_pos(this->input);
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
+ "demux_matroska: read error at position %llu\n", pos);
+ return 0;
+ }
+ return 1;
+}
+
+static int parse_block (demux_matroska_t *this, uint64_t block_size,
+ uint64_t timecode, uint64_t duration) {
+ matroska_track_t *track;
+ int64_t track_num;
+ int timecode_diff;
+ uint8_t *data;
+ uint8_t flags;
+ int gap;
+ int lacing;
+ int num_len;
+
+ if (!read_block_data(this, block_size))
+ return 0;
+
+ data = this->block_data;
+ if (!(num_len = parse_ebml_uint(this, data, &track_num)))
+ return 0;
+ data += num_len;
+
+ timecode_diff = (int)BE_16(data);
+ data += 2;
+
+ flags = *data;
+ data += 1;
+
+ lprintf("track_num: %lld, timecode_diff: %d, flags: 0x%x\n", track_num, timecode_diff, flags);
+
+ gap = flags & 1;
+ lacing = (flags >> 1) & 0x3;
+
+ if (!find_track_by_id(this, (int)track_num, &track)) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
+ "demux_matroska: invalid track id: %lld\n", track_num);
+ return 0;
+ }
+
+ if ((track == this->video_track) ||
+ (track == this->audio_track) ||
+ (track == this->sub_track)) {
+ int64_t pts;
+ int decoder_flags = 0;
+ off_t input_pos, input_len;
+
+ pts = ((int64_t)timecode + timecode_diff) *
+ (int64_t)this->timecode_scale * (int64_t)90 /
+ (int64_t)1000000;
+ lprintf("pts: %lld\n", pts);
+
+ if (this->preview_mode) {
+ this->preview_sent++;
+ decoder_flags |= BUF_FLAG_PREVIEW;
+ }
+
+ input_pos = this->input->get_current_pos(this->input);
+ input_len = this->input->get_length(this->input);
+
+ if (lacing == MATROSKA_NO_LACING) {
+ int block_size_left;
+ lprintf("no lacing\n");
+
+ block_size_left = (this->block_data + block_size) - data;
+ lprintf("size: %d, block_size: %lld\n", block_size_left, block_size);
+ _x_demux_send_data(track->fifo, data, block_size_left,
+ pts, track->buf_type, decoder_flags,
+ input_pos, input_len, pts / 90,
+ this->duration, 0);
+ } else {
+
+ int block_size_left;
+ uint8_t lace_num;
+ int frame[MAX_FRAMES];
+ int i;
+
+ /* number of laced frames */
+ lace_num = *data;
+ data++;
+ lprintf("lace_num: %d\n", lace_num);
+ if ((lace_num + 1) > MAX_FRAMES) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
+ "demux_matroska: too many frames: %d\n", lace_num);
+ return 0;
+ }
+ block_size_left = this->block_data + block_size - data;
+
+ switch (lacing) {
+ case MATROSKA_XIPH_LACING: {
+
+ lprintf("xiph lacing\n");
+
+ /* size of each frame */
+ for (i = 0; i < lace_num; i++) {
+ int size = 0;
+ while (*data == 255) {
+ size += *data;
+ data++; block_size_left--;
+ }
+ size += *data;
+ data++; block_size_left--;
+ frame[i] = size;
+ block_size_left -= size;
+ }
+
+ /* last frame */
+ frame[lace_num] = block_size_left;
+ }
+ break;
+
+ case MATROSKA_FIXED_SIZE_LACING:
+ lprintf("fixed size lacing\n");
+ for (i = 0; i < lace_num; i++) {
+ frame[i] = block_size / (lace_num + 1);
+ block_size_left -= frame[i];
+ }
+ frame[lace_num] = block_size_left;
+ break;
+
+ case MATROSKA_EBML_LACING: {
+ int64_t tmp;
+
+ lprintf("ebml lacing\n");
+
+ /* size of each frame */
+ if (!(num_len = parse_ebml_uint(this, data, &tmp)))
+ return 0;
+ data += num_len; block_size_left -= num_len;
+ frame[0] = (int) tmp;
+ lprintf("first frame len: %d\n", frame[0]);
+ block_size_left -= frame[0];
+
+ for (i = 1; i < lace_num; i++) {
+ if (!(num_len = parse_ebml_sint(this, data, &tmp)))
+ return 0;
+
+ data += num_len; block_size_left -= num_len;
+ frame[i] = frame[i-1] + tmp;
+ block_size_left -= frame[i];
+ }
+
+ /* last frame */
+ frame[lace_num] = block_size_left;
+ }
+ break;
+ default:
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
+ "demux_matroska: invalid lacing: %d\n", lacing);
+ return 0;
+ }
+ /* send each frame to the decoder */
+ for (i = 0; i <= lace_num; i++) {
+ _x_demux_send_data(track->fifo, data, frame[i],
+ pts, track->buf_type, decoder_flags,
+ input_pos, input_len, pts / 90,
+ 0, 0);
+ data += frame[i];
+ pts = 0;
+ }
+ }
+ }
+ return 1;
+}
+
+static int parse_block_group(demux_matroska_t *this,
+ uint64_t timecode, uint64_t duration) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 3;
+
+ while (next_level == 3) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_CL_BLOCK:
+ lprintf("block\n");
+ if (!parse_block(this, elem.len, timecode, duration))
+ return 0;
+ break;
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ return 1;
+}
+
+static int parse_cluster(demux_matroska_t *this) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 2;
+ uint64_t timecode = 0;
+ uint64_t duration = 0;
+
+ while (next_level == 2) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_CL_TIMECODE:
+ lprintf("timecode\n");
+ if (!ebml_read_uint(ebml, &elem, &timecode))
+ return 0;
+ break;
+ case MATROSKA_ID_CL_DURATION:
+ lprintf("duration\n");
+ if (!ebml_read_uint(ebml, &elem, &duration))
+ return 0;
+ break;
+ case MATROSKA_ID_CL_BLOCKGROUP:
+ lprintf("blockgroup\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_block_group(this, timecode, duration))
+ return 0;
+ break;
+ case MATROSKA_ID_CL_BLOCK:
+ lprintf("block\n");
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ break;
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ return 1;
+}
+
+
+static int parse_seek_entry(demux_matroska_t *this) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 3;
+ int has_id = 0;
+ int has_position = 0;
+ uint64_t id, pos;
+
+ while (next_level == 3) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_S_ID:
+ lprintf("SeekID\n");
+ if (!ebml_read_uint(ebml, &elem, &id))
+ return 0;
+ has_id = 1;
+ break;
+ case MATROSKA_ID_S_POSITION:
+ lprintf("SeekPosition\n");
+ if (!ebml_read_uint(ebml, &elem, &pos))
+ return 0;
+ has_position = 1;
+ break;
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ if (has_id && has_position) {
+
+ switch (id) {
+ case MATROSKA_ID_INFO:
+ lprintf("Seek Entry: Info: %lld\n", pos);
+ this->info_pos = this->segment.start + pos;
+ break;
+ case MATROSKA_ID_SEEKHEAD:
+ lprintf("Seek Entry: SeekHead: %lld\n", pos);
+ this->seekhead_pos = this->segment.start + pos;
+ break;
+ case MATROSKA_ID_CLUSTER:
+ lprintf("Seek Entry: Cluster: %lld\n", pos);
+ break;
+ case MATROSKA_ID_TRACKS:
+ lprintf("Seek Entry: Tracks: %lld\n", pos);
+ this->tracks_pos = this->segment.start + pos;
+ break;
+ case MATROSKA_ID_CUES:
+ lprintf("Seek Entry: Cues: %lld\n", pos);
+ this->cues_pos = this->segment.start + pos;
+ break;
+ case MATROSKA_ID_ATTACHMENTS:
+ lprintf("Seek Entry: Attachements: %lld\n", pos);
+ this->attachments_pos = this->segment.start + pos;
+ break;
+ case MATROSKA_ID_CHAPTERS:
+ lprintf("Seek Entry: Chapters: %lld\n", pos);
+ this->chapters_pos = this->segment.start + pos;
+ break;
+ default:
+ lprintf("Unhandled Seek Entry ID: 0x%llx\n", id);
+ }
+ return 1;
+ } else {
+ lprintf("incomplete Seek Entry\n");
+ return 1;
+ }
+}
+
+
+static int parse_seekhead(demux_matroska_t *this) {
+ ebml_parser_t *ebml = this->ebml;
+ int next_level = 2;
+
+ this->has_seekhead = 1;
+
+ while (next_level == 2) {
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_S_ENTRY:
+ lprintf("Seek\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_seek_entry(this))
+ return 0;
+ break;
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ next_level = ebml_get_next_level(ebml, &elem);
+ }
+ return 1;
+}
+
+
+static int parse_top_level_head(demux_matroska_t *this, int *next_level) {
+ ebml_parser_t *ebml = this->ebml;
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_SEEKHEAD:
+ lprintf("SeekHead\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_seekhead(this))
+ return 0;
+ this->has_seekhead = 1;
+ break;
+ case MATROSKA_ID_INFO:
+ lprintf("Info\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_info(this))
+ return 0;
+ break;
+ case MATROSKA_ID_TRACKS:
+ lprintf("Tracks\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_tracks(this))
+ return 0;
+ break;
+ case MATROSKA_ID_CHAPTERS:
+ lprintf("Chapters\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_chapters(this))
+ return 0;
+ break;
+ case MATROSKA_ID_CLUSTER:
+ lprintf("Cluster\n");
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ break;
+ case MATROSKA_ID_CUES:
+ lprintf("Cues\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_cues(this))
+ return 0;
+ break;
+ case MATROSKA_ID_ATTACHMENTS:
+ lprintf("Attachments\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_attachments(this))
+ return 0;
+ break;
+ case MATROSKA_ID_TAGS:
+ lprintf("Tags\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_tags(this))
+ return 0;
+ break;
+
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ if (next_level)
+ *next_level = ebml_get_next_level(ebml, &elem);
+ return 1;
+}
+
+static int parse_top_level(demux_matroska_t *this, int *next_level) {
+ ebml_parser_t *ebml = this->ebml;
+ ebml_elem_t elem;
+
+ if (!ebml_read_elem_head(ebml, &elem))
+ return 0;
+
+ switch (elem.id) {
+ case MATROSKA_ID_SEEKHEAD:
+ lprintf("SeekHead\n");
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ this->has_seekhead = 1;
+ break;
+ case MATROSKA_ID_INFO:
+ lprintf("Info\n");
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ break;
+ case MATROSKA_ID_TRACKS:
+ lprintf("Tracks\n");
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ break;
+ case MATROSKA_ID_CHAPTERS:
+ lprintf("Chapters\n");
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ break;
+ case MATROSKA_ID_CLUSTER:
+ lprintf("Cluster\n");
+ if (!ebml_read_master (ebml, &elem))
+ return 0;
+ if (!parse_cluster(this))
+ return 0;
+ break;
+ case MATROSKA_ID_CUES:
+ lprintf("Cues\n");
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ break;
+ case MATROSKA_ID_ATTACHMENTS:
+ lprintf("Attachments\n");
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ break;
+ case MATROSKA_ID_TAGS:
+ lprintf("Tags\n");
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ break;
+
+ default:
+ lprintf("Unhandled ID: 0x%x\n", elem.id);
+ if (!ebml_skip(ebml, &elem))
+ return 0;
+ }
+ if (next_level)
+ *next_level = ebml_get_next_level(ebml, &elem);
+ return 1;
+}
+
+
+static int parse_segment(demux_matroska_t *this) {
+ ebml_parser_t *ebml = this->ebml;
+
+ /* check segment id */
+ if (!ebml_read_elem_head(ebml, &this->segment))
+ return 0;
+
+ if (this->segment.id == MATROSKA_ID_SEGMENT) {
+ int next_level;
+
+ lprintf("Segment\n");
+
+ if (!ebml_read_master (ebml, &this->segment))
+ return 0;
+
+ next_level = 1;
+ while (next_level == 1) {
+ if (!parse_top_level_head(this, &next_level))
+ return 0;
+#if 0
+ if (this->has_seekhead && !this->seekhead_handled) {
+ if (this->seekhead_pos) {
+ if (this->input->seek(this->input, this->seekhead_pos, SEEK_SET) < 0)
+ return 0;
+ this->seekhead_pos = 0;
+ } else {
+ /* parse all top level elements except clusters */
+ if (this->info_pos) {
+ if (this->input->seek(this->input, this->info_pos, SEEK_SET) < 0)
+ return 0;
+ if (!parse_top_level(this, &next_level))
+ return 0;
+ }
+ if (this->tracks_pos) {
+ if (this->input->seek(this->input, this->tracks_pos, SEEK_SET) < 0)
+ return 0;
+ if (!parse_top_level(this, &next_level))
+ return 0;
+ }
+ if (this->chapters_pos) {
+ if (this->input->seek(this->input, this->chapters_pos, SEEK_SET) < 0)
+ return 0;
+ if (!parse_top_level(this, &next_level))
+ return 0;
+ }
+ if (this->cues_pos) {
+ if (this->input->seek(this->input, this->cues_pos, SEEK_SET) < 0)
+ return 0;
+ if (!parse_top_level(this, &next_level))
+ return 0;
+ }
+ if (this->attachments_pos) {
+ if (this->input->seek(this->input, this->attachments_pos, SEEK_SET) < 0)
+ return 0;
+ if (!parse_top_level(this, &next_level))
+ return 0;
+ }
+ if (this->tags_pos) {
+ if (this->input->seek(this->input, this->tags_pos, SEEK_SET) < 0)
+ return 0;
+ if (!parse_top_level(this, &next_level))
+ return 0;
+ }
+ /* this->seekhead_handled = 1; */
+ return 1;
+ }
+ }
+#endif
+ }
+ return 1;
+ } else {
+ /* not a segment */
+ xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_matroska: invalid segment\n");
+ return 0;
+ }
+}
+
+static int demux_matroska_send_chunk (demux_plugin_t *this_gen) {
+
+ demux_matroska_t *this = (demux_matroska_t *) this_gen;
+ int next_level;
+
+ if (!parse_top_level(this, &next_level)) {
+ this->status = DEMUX_FINISHED;
+ }
+ return this->status;
+}
+
+
+static int demux_matroska_get_status (demux_plugin_t *this_gen) {
+ demux_matroska_t *this = (demux_matroska_t *) this_gen;
+
+ return this->status;
+}
+
+
+static void demux_matroska_send_headers (demux_plugin_t *this_gen) {
+
+ demux_matroska_t *this = (demux_matroska_t *) this_gen;
+ int next_level;
+
+ _x_demux_control_start (this->stream);
+
+ if (!parse_segment(this))
+ this->status = DEMUX_FINISHED;
+ else
+ this->status = DEMUX_OK;
+
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, (this->video_track != NULL));
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, (this->audio_track != NULL));
+
+
+ /*
+ * send preview buffers
+ */
+/*
+ for (i = 0; i < NUM_PREVIEW_BUFFERS; i++) {
+ if (!demux_mpgaudio_next (this, BUF_FLAG_PREVIEW)) {
+ break;
+ }
+ }
+ */
+
+ /* enter in the segment */
+ ebml_read_master (this->ebml, &this->segment);
+
+ /* seek to the beginning of the segment */
+ this->input->seek(this->input, this->segment.start, SEEK_SET);
+
+ this->preview_sent = 0;
+ this->preview_mode = 1;
+
+ next_level = 1;
+ while ((this->preview_sent < NUM_PREVIEW_BUFFERS) && (next_level == 1)) {
+ if (!parse_top_level (this, &next_level)) {
+ break;
+ }
+ }
+ this->preview_mode = 0;
+
+ /* seek to the beginning of the segment */
+ this->input->seek(this->input, this->segment.start, SEEK_SET);
+}
+
+
+static int demux_matroska_seek (demux_plugin_t *this_gen,
+ off_t start_pos, int start_time, int playing) {
+
+ demux_matroska_t *this = (demux_matroska_t *) this_gen;
+
+ this->status = DEMUX_OK;
+
+ return this->status;
+}
+
+
+static void demux_matroska_dispose (demux_plugin_t *this) {
+
+ free (this);
+}
+
+
+static int demux_matroska_get_stream_length (demux_plugin_t *this_gen) {
+
+ demux_matroska_t *this = (demux_matroska_t *) this_gen;
+
+ return (int)this->duration;
+}
+
+
+static uint32_t demux_matroska_get_capabilities (demux_plugin_t *this_gen) {
+ return DEMUX_CAP_NOCAP;
+}
+
+
+static int demux_matroska_get_optional_data (demux_plugin_t *this_gen,
+ void *data, int data_type) {
+ return DEMUX_OPTIONAL_UNSUPPORTED;
+}
+
+static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *stream,
+ input_plugin_t *input) {
+
+ demux_matroska_t *this;
+ ebml_parser_t *ebml = NULL;
+
+ lprintf("trying to open %s...\n", input->get_mrl(input));
+
+ switch (stream->content_detection_method) {
+
+ case METHOD_BY_CONTENT: {
+ input->seek(input, 0, SEEK_SET);
+ ebml = new_ebml_parser(stream->xine, input);
+ if (!ebml_check_header(ebml))
+ return NULL;
+ }
+ break;
+
+ case METHOD_BY_EXTENSION: {
+ char *mrl = input->get_mrl(input);
+
+ lprintf ("stage by extension %s\n", mrl);
+
+ char *extensions = class_gen->get_extensions (class_gen);
+
+ if (!_x_demux_check_extension (mrl, extensions))
+ return NULL;
+
+ }
+ break;
+
+ case METHOD_EXPLICIT:
+ break;
+
+ default:
+ return NULL;
+ }
+
+ this = xine_xmalloc (sizeof (demux_matroska_t));
+
+ this->demux_plugin.send_headers = demux_matroska_send_headers;
+ this->demux_plugin.send_chunk = demux_matroska_send_chunk;
+ this->demux_plugin.seek = demux_matroska_seek;
+ this->demux_plugin.dispose = demux_matroska_dispose;
+ this->demux_plugin.get_status = demux_matroska_get_status;
+ this->demux_plugin.get_stream_length = demux_matroska_get_stream_length;
+ this->demux_plugin.get_capabilities = demux_matroska_get_capabilities;
+ this->demux_plugin.get_optional_data = demux_matroska_get_optional_data;
+ this->demux_plugin.demux_class = class_gen;
+
+ this->input = input;
+ this->status = DEMUX_FINISHED;
+ this->stream = stream;
+
+ if (!ebml) {
+ ebml = new_ebml_parser(stream->xine, input);
+ if (!ebml_check_header(ebml))
+ goto error;
+ }
+ this->ebml = ebml;
+
+ /* check header fields */
+ if (ebml->max_id_len > 4)
+ goto error;
+ if (ebml->max_size_len > 8)
+ goto error;
+ if (strcmp(ebml->doctype, "matroska"))
+ goto error;
+
+ return &this->demux_plugin;
+
+error:
+ if (ebml)
+ dispose_ebml_parser(ebml);
+ free(this);
+ return NULL;
+}
+
+
+/*
+ * demux matroska class
+ */
+
+static char *get_description (demux_class_t *this_gen) {
+ return "matroska demux plugin";
+}
+
+
+static char *get_identifier (demux_class_t *this_gen) {
+ return "matroska";
+}
+
+
+static char *get_extensions (demux_class_t *this_gen) {
+ return "mkv";
+}
+
+
+static char *get_mimetypes (demux_class_t *this_gen) {
+ return "video/mkv: mkv: matroska;";
+}
+
+
+static void class_dispose (demux_class_t *this_gen) {
+
+ demux_matroska_class_t *this = (demux_matroska_class_t *) this_gen;
+
+ free (this);
+}
+
+
+static void *init_class (xine_t *xine, void *data) {
+
+ demux_matroska_class_t *this;
+
+ this = xine_xmalloc (sizeof (demux_matroska_class_t));
+ this->xine = xine;
+
+ this->demux_class.open_plugin = open_plugin;
+ this->demux_class.get_description = get_description;
+ this->demux_class.get_identifier = get_identifier;
+ this->demux_class.get_mimetypes = get_mimetypes;
+ this->demux_class.get_extensions = get_extensions;
+ this->demux_class.dispose = class_dispose;
+
+ return this;
+}
+
+/*
+ * exported plugin catalog entry
+ */
+
+plugin_info_t xine_plugin_info[] = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_DEMUX, 23, "matroska", XINE_VERSION_CODE, NULL, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
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;
+}
diff --git a/src/demuxers/ebml.h b/src/demuxers/ebml.h
new file mode 100644
index 000000000..9b3af4efe
--- /dev/null
+++ b/src/demuxers/ebml.h
@@ -0,0 +1,103 @@
+/*
+ * 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.h,v 1.1 2004/01/05 00:40:54 tmattern Exp $
+ *
+ */
+#ifndef EBML_H
+#define EBML_H
+
+#define EBML_STACK_SIZE 10
+#define EBML_VERSION 1
+
+/* EBML IDs */
+#define EBML_ID_EBML 0x1A45DFA3
+#define EBML_ID_EBMLVERSION 0x4286
+#define EBML_ID_EBMLREADVERSION 0x42F7
+#define EBML_ID_EBMLMAXIDLENGTH 0x42F2
+#define EBML_ID_EBMLMAXSIZELENGTH 0x42F3
+#define EBML_ID_DOCTYPE 0x4282
+#define EBML_ID_DOCTYPEVERSION 0x4287
+#define EBML_ID_DOCTYPEREADVERSION 0x4285
+
+
+typedef struct ebml_elem_s {
+ uint32_t id;
+ off_t start;
+ uint64_t len;
+} ebml_elem_t;
+
+typedef struct ebml_parser_s {
+
+ /* xine stuff */
+ xine_t *xine;
+ input_plugin_t *input;
+
+ /* EBML Parser Stack Management */
+ ebml_elem_t elem_stack[EBML_STACK_SIZE];
+ int level;
+
+ /* EBML Header Infos */
+ uint64_t version;
+ uint64_t read_version;
+ uint64_t max_id_len;
+ uint64_t max_size_len;
+ char *doctype;
+ uint64_t doctype_version;
+ uint64_t doctype_read_version;
+
+} ebml_parser_t;
+
+
+ebml_parser_t *new_ebml_parser (xine_t *xine, input_plugin_t *input);
+
+void dispose_ebml_parser (ebml_parser_t *ebml);
+
+/* check EBML header */
+int ebml_check_header(ebml_parser_t *read);
+
+
+/* Element Header */
+int ebml_read_elem_head(ebml_parser_t *ebml, ebml_elem_t *elem);
+
+uint32_t ebml_get_next_level(ebml_parser_t *ebml, ebml_elem_t *elem);
+
+int ebml_skip(ebml_parser_t *ebml, ebml_elem_t *elem);
+
+/* EBML types */
+int ebml_read_uint(ebml_parser_t *ebml, ebml_elem_t *elem, uint64_t *val);
+
+int ebml_read_sint(ebml_parser_t *ebml, ebml_elem_t *elem, int64_t *val);
+
+int ebml_read_float(ebml_parser_t *ebml, ebml_elem_t *elem, double *val);
+
+int ebml_read_ascii(ebml_parser_t *ebml, ebml_elem_t *elem, char *str);
+
+int ebml_read_utf8(ebml_parser_t *ebml, ebml_elem_t *elem, char *str);
+
+int ebml_read_date(ebml_parser_t *ebml, ebml_elem_t *elem, int64_t *date);
+
+int ebml_read_master(ebml_parser_t *ebml, ebml_elem_t *elem);
+
+int ebml_read_binary(ebml_parser_t *ebml, ebml_elem_t *elem, uint8_t *binary);
+
+#endif /* EBML_H */
diff --git a/src/demuxers/matroska.h b/src/demuxers/matroska.h
new file mode 100644
index 000000000..c8f2f4b31
--- /dev/null
+++ b/src/demuxers/matroska.h
@@ -0,0 +1,270 @@
+/*
+ * 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
+ *
+ * $Id: matroska.h,v 1.1 2004/01/05 00:40:54 tmattern Exp $
+ *
+ */
+#ifndef MATROSKA_H
+#define MATROSKA_H
+
+#include "ebml.h"
+
+/*
+ * Matroska Element IDs
+ */
+
+/* Segment */
+#define MATROSKA_ID_SEGMENT 0x18538067
+
+/* Meta Seek Information */
+#define MATROSKA_ID_SEEKHEAD 0x114D9B74
+#define MATROSKA_ID_S_ENTRY 0x4DBB
+#define MATROSKA_ID_S_ID 0x53AB
+#define MATROSKA_ID_S_POSITION 0x53AC
+
+/* Segment Information */
+#define MATROSKA_ID_INFO 0x1549A966
+#define MATROSKA_ID_I_SEGMENTUID 0x73A4
+#define MATROSKA_ID_I_FILENAME 0x7384
+#define MATROSKA_ID_I_PREVUID 0x3CB923
+#define MATROSKA_ID_I_PREVFILENAME 0x3C83AB
+#define MATROSKA_ID_I_NEXTUID 0x3EB923
+#define MATROSKA_ID_I_NEXTFILENAME 0x3E83BB
+#define MATROSKA_ID_I_TIMECODESCALE 0x2AD7B1
+#define MATROSKA_ID_I_DURATION 0x4489
+#define MATROSKA_ID_I_DATEUTC 0x4461
+#define MATROSKA_ID_I_TITLE 0x7BA9
+#define MATROSKA_ID_I_MUXINGAPP 0x4D80
+#define MATROSKA_ID_I_WRITINGAPP 0x5741
+
+/* Cluster */
+#define MATROSKA_ID_CLUSTER 0x1F43B675
+#define MATROSKA_ID_CL_TIMECODE 0xE7
+#define MATROSKA_ID_CL_POSITION 0xA7
+#define MATROSKA_ID_CL_PREVSIZE 0xAB
+#define MATROSKA_ID_CL_BLOCKGROUP 0xA0
+#define MATROSKA_ID_CL_BLOCK 0xA1
+#define MATROSKA_ID_CL_BLOCKVIRTUAL 0xA2
+#define MATROSKA_ID_CL_BLOCKADDITIONS 0x75A1
+#define MATROSKA_ID_CL_BLOCKMORE 0xA6
+#define MATROSKA_ID_CL_BLOCKADDID 0xEE
+#define MATROSKA_ID_CL_BLOCKADDITIONAL 0xA5
+#define MATROSKA_ID_CL_BLOCKDURATION 0x9B
+#define MATROSKA_ID_CL_REFERENCEPRIORITY 0xFA
+#define MATROSKA_ID_CL_REFERENCEBLOCK 0xFB
+#define MATROSKA_ID_CL_REFERENCEVIRTUAL 0xFD
+#define MATROSKA_ID_CL_CODECSTATE 0xA4
+#define MATROSKA_ID_CL_SLICES 0x8E
+#define MATROSKA_ID_CL_TIMESLICE 0xE8
+#define MATROSKA_ID_CL_LACENUMBER 0xCC
+#define MATROSKA_ID_CL_FRAMENUMBER 0xCD
+#define MATROSKA_ID_CL_BLOCKADDITIONID 0xCB
+#define MATROSKA_ID_CL_DELAY 0xCE
+#define MATROSKA_ID_CL_DURATION 0xCF
+
+/* Track */
+#define MATROSKA_ID_TRACKS 0x1654AE6B
+#define MATROSKA_ID_TR_ENTRY 0xAE
+#define MATROSKA_ID_TR_NUMBER 0xD7
+#define MATROSKA_ID_TR_UID 0x73C5
+#define MATROSKA_ID_TR_TYPE 0x83
+#define MATROSKA_ID_TR_FLAGENABLED 0xB9
+#define MATROSKA_ID_TR_FLAGDEFAULT 0x88
+#define MATROSKA_ID_TR_FLAGLACING 0x9C
+#define MATROSKA_ID_TR_MINCACHE 0x6DE7
+#define MATROSKA_ID_TR_MAXCACHE 0x6DF8
+#define MATROSKA_ID_TR_DEFAULTDURATION 0x23E383
+#define MATROSKA_ID_TR_TIMECODESCALE 0x23314F
+#define MATROSKA_ID_TR_NAME 0x536E
+#define MATROSKA_ID_TR_LANGUAGE 0x22B59C
+#define MATROSKA_ID_TR_CODECID 0x86
+#define MATROSKA_ID_TR_CODECPRIVATE 0x63A2
+#define MATROSKA_ID_TR_CODECNAME 0x258688
+#define MATROSKA_ID_TR_CODECSETTINGS 0x3A9697
+#define MATROSKA_ID_TR_CODECINFOURL 0x3B4040
+#define MATROSKA_ID_TR_CODECDOWNLOADURL 0x26B240
+#define MATROSKA_ID_TR_CODECDECODEALL 0xAA
+#define MATROSKA_ID_TR_OVERLAY 0x6FAB
+
+/* Video */
+#define MATROSKA_ID_TV 0xE0
+#define MATROSKA_ID_TV_FLAGINTERLACED 0x9A
+#define MATROSKA_ID_TV_STEREOMODE 0x53B9
+#define MATROSKA_ID_TV_PIXELWIDTH 0xB0
+#define MATROSKA_ID_TV_PIXELHEIGHT 0xBA
+#define MATROSKA_ID_TV_VIDEODISPLAYWIDTH 0x54B0
+#define MATROSKA_ID_TV_VIDEODISPLAYHEIGHT 0x54BA
+#define MATROSKA_ID_TV_VIDEOUNIT 0x54B2
+#define MATROSKA_ID_TV_ASPECTRATIOTYPE 0x54B3
+#define MATROSKA_ID_TV_COLOURSPACE 0x2EB524
+#define MATROSKA_ID_TV_GAMMAVALUE 0x2FB523
+
+/* Audio */
+#define MATROSKA_ID_TA 0xE1
+#define MATROSKA_ID_TA_SAMPLINGFREQUENCY 0xB5
+#define MATROSKA_ID_TA_OUTPUTSAMPLINGFREQUENCY 0x78B5
+#define MATROSKA_ID_TA_CHANNELS 0x9F
+#define MATROSKA_ID_TA_CHANNELPOSITIONS 0x9F
+#define MATROSKA_ID_TA_BITDEPTH 0x6264
+
+/* Content Encoding */
+#define MATROSKA_ID_CONTENTENCODINGS 0x6D80
+#define MATROSKA_ID_CONTENTENCODING 0x6240
+#define MATROSKA_ID_CE_ORDER 0x5031
+#define MATROSKA_ID_CE_SCOPE 0x5032
+#define MATROSKA_ID_CE_TYPE 0x5033
+#define MATROSKA_ID_CE_COMPRESSION 0x5034
+#define MATROSKA_ID_CE_COMPALGO 0x4254
+#define MATROSKA_ID_CE_COMPSETTINGS 0x4255
+#define MATROSKA_ID_CE_ENCRYPTION 0x5035
+#define MATROSKA_ID_CE_ENCALGO 0x47E1
+#define MATROSKA_ID_CE_ENCKEYID 0x47E2
+#define MATROSKA_ID_CE_SIGNATURE 0x47E3
+#define MATROSKA_ID_CE_SIGKEYID 0x47E4
+#define MATROSKA_ID_CE_SIGALGO 0x47E5
+#define MATROSKA_ID_CE_SIGHASHALGO 0x47E6
+
+/* Cueing Data */
+#define MATROSKA_ID_CUES 0x1C53BB6B
+#define MATROSKA_ID_CU_POINT 0xBB
+#define MATROSKA_ID_CU_TIME 0xB3
+#define MATROSKA_ID_CU_TRACKPOSITION 0xB7
+#define MATROSKA_ID_CU_TRACK 0xF7
+#define MATROSKA_ID_CU_CLUSTERPOSITION 0xF1
+#define MATROSKA_ID_CU_BLOCKNUMBER 0x5387
+#define MATROSKA_ID_CU_CODECSTATE 0xEA
+#define MATROSKA_ID_CU_REFERENCE 0xDB
+#define MATROSKA_ID_CU_REFTIME 0x96
+#define MATROSKA_ID_CU_REFCLUSTER 0x97
+#define MATROSKA_ID_CU_REFNUMBER 0x535F
+#define MATROSKA_ID_CU_REFCODECSTATE 0xEB
+
+/* Attachements */
+#define MATROSKA_ID_ATTACHMENTS 0x1941A469
+#define MATROSKA_ID_AT_FILE 0x61A7
+#define MATROSKA_ID_AT_FILEDESCRIPTION 0x467E
+#define MATROSKA_ID_AT_FILENAME 0x466E
+#define MATROSKA_ID_AT_FILEMIMETYPE 0x4660
+#define MATROSKA_ID_AT_FILEDATA 0x465C
+#define MATROSKA_ID_AT_FILEUID 0x46AE
+
+/* Chapters */
+#define MATROSKA_ID_CHAPTERS 0x1043A770
+#define MATROSKA_ID_CH_EDITIONENTRY 0x45B9
+#define MATROSKA_ID_CH_ATOM 0xB6
+#define MATROSKA_ID_CH_UID 0x73C4
+#define MATROSKA_ID_CH_TIMESTART 0x91
+#define MATROSKA_ID_CH_TIMEEND 0x92
+#define MATROSKA_ID_CH_TRACK 0x8F
+#define MATROSKA_ID_CH_TRACKNUMBER 0x89
+#define MATROSKA_ID_CH_DISPLAY 0x80
+#define MATROSKA_ID_CH_STRING 0x85
+#define MATROSKA_ID_CH_LANGUAGE 0x437C
+#define MATROSKA_ID_CH_COUNTRY 0x437E
+
+/* Tags */
+#define MATROSKA_ID_TAGS 0x1254C367
+
+/* Matroska Track */
+typedef struct {
+ int flag_interlaced;
+ int pixel_witdh;
+ int pixel_height;
+} matroska_video_track_t;
+
+typedef struct {
+ int sampling_freq;
+ int output_sampling_freq;
+ int channels;
+ int bits_per_sample;
+} matroska_audio_track_t;
+
+typedef struct {
+ int track_num;
+
+ uint32_t track_type;
+ uint64_t default_duration;
+ char *language;
+ char *codec_id;
+ uint8_t *codec_private;
+ uint32_t codec_private_len;
+ int default_flag;
+
+ uint32_t buf_type;
+ fifo_buffer_t *fifo;
+
+ matroska_video_track_t *video_track;
+ matroska_audio_track_t *audio_track;
+
+} matroska_track_t;
+
+/* IDs in the tags master */
+
+/*
+ * Matroska Codec IDs. Strings.
+ */
+
+#define MATROSKA_CODEC_ID_V_VFW_FOURCC "V_MS/VFW/FOURCC"
+#define MATROSKA_CODEC_ID_V_UNCOMPRESSED "V_UNCOMPRESSED"
+#define MATROSKA_CODEC_ID_V_MPEG4_SP "V_MPEG4/ISO/SP"
+#define MATROSKA_CODEC_ID_V_MPEG4_ASP "V_MPEG4/ISO/ASP"
+#define MATROSKA_CODEC_ID_V_MPEG4_AP "V_MPEG4/ISO/AP"
+#define MATROSKA_CODEC_ID_V_MSMPEG4V3 "V_MPEG4/MS/V3"
+#define MATROSKA_CODEC_ID_V_MPEG1 "V_MPEG1"
+#define MATROSKA_CODEC_ID_V_MPEG2 "V_MPEG2"
+#define MATROSKA_CODEC_ID_V_MPEG2 "V_MPEG2"
+#define MATROSKA_CODEC_ID_V_REAL_RV10 "V_REAL/RV10"
+#define MATROSKA_CODEC_ID_V_REAL_RV20 "V_REAL/RV20"
+#define MATROSKA_CODEC_ID_V_REAL_RV30 "V_REAL/RV30"
+#define MATROSKA_CODEC_ID_V_REAL_RV40 "V_REAL/RV40"
+#define MATROSKA_CODEC_ID_V_MJPEG "V_MJPEG"
+
+#define MATROSKA_CODEC_ID_A_MPEG1_L1 "A_MPEG/L1"
+#define MATROSKA_CODEC_ID_A_MPEG1_L2 "A_MPEG/L2"
+#define MATROSKA_CODEC_ID_A_MPEG1_L3 "A_MPEG/L3"
+#define MATROSKA_CODEC_ID_A_PCM_INT_BE "A_PCM/INT/BIG"
+#define MATROSKA_CODEC_ID_A_PCM_INT_LE "A_PCM/INT/LIT"
+#define MATROSKA_CODEC_ID_A_PCM_FLOAT "A_PCM/FLOAT/IEEE"
+#define MATROSKA_CODEC_ID_A_AC3 "A_AC3"
+#define MATROSKA_CODEC_ID_A_DTS "A_DTS"
+#define MATROSKA_CODEC_ID_A_VORBIS "A_VORBIS"
+#define MATROSKA_CODEC_ID_A_ACM "A_MS/ACM"
+#define MATROSKA_CODEC_ID_A_AAC "A_AAC/"
+#define MATROSKA_CODEC_ID_A_REAL_14_4 "A_REAL/14_4"
+#define MATROSKA_CODEC_ID_A_REAL_28_8 "A_REAL/28_8"
+#define MATROSKA_CODEC_ID_A_REAL_COOK "A_REAL/COOK"
+#define MATROSKA_CODEC_ID_A_REAL_SIPR "A_REAL/SIPR"
+#define MATROSKA_CODEC_ID_A_REAL_RALF "A_REAL/RALF"
+#define MATROSKA_CODEC_ID_A_REAL_ATRC "A_REAL/ATRC"
+
+/* block lacing */
+#define MATROSKA_NO_LACING 0x0
+#define MATROSKA_XIPH_LACING 0x1
+#define MATROSKA_FIXED_SIZE_LACING 0x2
+#define MATROSKA_EBML_LACING 0x3
+
+/* track types */
+#define MATROSKA_TRACK_VIDEO 0x01
+#define MATROSKA_TRACK_AUDIO 0x02
+#define MATROSKA_TRACK_COMPLEX 0x03
+#define MATROSKA_TRACK_LOGO 0x10
+#define MATROSKA_TRACK_SUBTITLE 0x11
+#define MATROSKA_TRACK_CONTROL 0x20
+
+#endif /* MATROSKA_H */