changeset 10024:c6fab860ab18

Added Matroska demuxing support.
author mosu
date Wed, 30 Apr 2003 11:39:32 +0000
parents 5a06b5774c00
children 970a95a4d856
files Makefile configure libmpdemux/Makefile libmpdemux/demux_mkv.cpp libmpdemux/demuxer.c libmpdemux/demuxer.h libmpdemux/matroska.h
diffstat 7 files changed, 2051 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Tue Apr 29 19:41:23 2003 +0000
+++ b/Makefile	Wed Apr 30 11:39:32 2003 +0000
@@ -35,7 +35,7 @@
 
 VO_LIBS = $(AA_LIB) $(X_LIB) $(SDL_LIB) $(GGI_LIB) $(MP1E_LIB) $(MLIB_LIB) $(SVGA_LIB) $(DIRECTFB_LIB) 
 AO_LIBS = $(ARTS_LIB) $(ESD_LIB) $(NAS_LIB) $(SGIAUDIO_LIB)
-CODEC_LIBS = $(AV_LIB) $(FAME_LIB) $(MAD_LIB) $(VORBIS_LIB) $(FAAD_LIB) $(LIBLZO_LIB) $(DECORE_LIB) $(XVID_LIB) $(PNG_LIB) $(Z_LIB) $(JPEG_LIB) $(ALSA_LIB) $(XMMS_LIB)
+CODEC_LIBS = $(AV_LIB) $(FAME_LIB) $(MAD_LIB) $(VORBIS_LIB) $(FAAD_LIB) $(LIBLZO_LIB) $(DECORE_LIB) $(XVID_LIB) $(PNG_LIB) $(Z_LIB) $(JPEG_LIB) $(ALSA_LIB) $(XMMS_LIB) $(MATROSKA_LIB)
 COMMON_LIBS = libmpcodecs/libmpcodecs.a mp3lib/libMP3.a liba52/liba52.a libmpeg2/libmpeg2.a $(W32_LIB) $(DS_LIB) libaf/libaf.a libmpdemux/libmpdemux.a input/libinput.a postproc/libswscale.a osdep/libosdep.a $(CSS_LIB) $(CODEC_LIBS) $(FREETYPE_LIB) $(TERMCAP_LIB) $(CDPARANOIA_LIB) $(STREAMING_LIB) $(WIN32_LIB) $(GIF_LIB) $(MACOSX_FRAMEWORKS) $(SMBSUPPORT_LIB) $(FRIBIDI_LIB)
 
 CFLAGS = $(OPTFLAGS) -Ilibmpdemux -Iloader -Ilibvo $(FREETYPE_INC) $(EXTRA_INC) $(CDPARANOIA_INC) $(SDL_INC) $(FRIBIDI_INC) # -Wall
--- a/configure	Tue Apr 29 19:41:23 2003 +0000
+++ b/configure	Wed Apr 30 11:39:32 2003 +0000
@@ -189,6 +189,7 @@
   --enable-libfame       enable libfame realtime encoder [autodetect]
   --enable-vorbis        build with OggVorbis support [autodetect]
   --enable-tremor        build with integer-only OggVorbis support [disabled]
+  --enable-matroska      build with Matroska support [autodetect]
   --enable-faad          build with FAAD2 (MP4/AAC) support [autodetect]
   --disable-libdv        disable libdv 0.9.5 en/decoding support [autodetect]
   --disable-mad          disable libmad (mpeg audio) support [autodetect]
@@ -1025,6 +1026,7 @@
 _liblzo=auto
 _mad=auto
 _vorbis=auto
+_matroska=auto
 _tremor=no
 _faad=auto
 _xmms=no
@@ -1178,6 +1180,8 @@
   --disable-vorbis)	_vorbis=no	;;
   --enable-tremor)	_tremor=yes	;;
   --disable-tremor)	_tremor=no	;;
+  --enable-matroska)    _matroska=yes   ;;
+  --disable-matroska)   _matroska=no    ;;
   --enable-faad)	_faad=yes	;;
   --disable-faad)	_faad=no	;;
   --enable-xmms)	_xmms=yes	;;
@@ -4025,6 +4029,42 @@
 echores "$_vorbis"
 
 
+echocheck "Matroska support"
+if test "$_matroska" = auto ; then
+  _matroska=no
+  cat > $TMPC << EOF
+#include <EbmlConfig.h>
+int main(void) { return 0; }
+EOF
+  cc_check -lmatroska -lebml && _matroska=yes
+  if test "$_matroska" = no ; then
+    _saved_inc_extra=$_inc_extra
+    _inc_extra="$_inc_extra -I/usr/include/ebml -I/usr/include/matroska"
+    cc_check -lmatroska -lebml && _matroska=yes
+    if test "$_matroska" = no ; then
+      _inc_extra="$_saved_inc_extra -I/usr/local/include/ebml -I/usr/local/include/matroska"
+      cc_check -lmatroska -lebml && _matroska=yes
+      if test "$_matroska" = no ; then
+        _inc_extra=$_saved_inc_extra
+      fi
+    fi
+  fi
+fi
+if test "$_matroska" = yes ; then
+  _def_matroska='#define HAVE_MATROSKA 1'
+  _inputmodules="matroska $_inputmodules"
+  _ld_matroska="-lmatroska -lebml -lstdc++"
+  case $cc_version in
+    2.*) _def_matroska_gcc2="#define LIBEBML_GCC2"     ;;
+    *)   _def_matroska_gcc2="/*#define LIBEBML_GCC2*/" ;;
+  esac
+else
+  _def_matroska='#undef HAVE_MATROSKA'
+  _noinputmodules="matroska $_noinputmodules"
+fi
+echores "$_matroska"
+
+
 echocheck "faad2 (AAC) support"
 if test "$_faad" = auto ; then
   _faad=no
@@ -5150,6 +5190,8 @@
 CONFIG_RISKY = yes
 LIBMENU = $_menu
 I18NLIBS = $_i18n_libs
+MATROSKA = $_matroska
+MATROSKA_LIB = $_ld_matroska
 
 OPENDIVX = $_opendivx
 
@@ -5645,6 +5687,10 @@
 /* enable Tremor as vorbis decoder */
 $_def_tremor
 
+/* enable Matroska support */
+$_def_matroska
+$_def_matroska_gcc2
+
 /* enable FAAD (AAC) support */
 $_def_faad
 $_def_faad_version
--- a/libmpdemux/Makefile	Tue Apr 29 19:41:23 2003 +0000
+++ b/libmpdemux/Makefile	Wed Apr 30 11:39:32 2003 +0000
@@ -26,7 +26,9 @@
 SRCS += dvb_tune.c
 endif
 
-
+ifeq ($(MATROSKA),yes)
+CPLUSPLUSSRCS += demux_mkv.cpp
+endif
 
 
 OBJS	= $(SRCS:.c=.o)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/demux_mkv.cpp	Wed Apr 30 11:39:32 2003 +0000
@@ -0,0 +1,1945 @@
+extern "C" {
+#include "config.h"
+}
+
+#ifdef HAVE_MATROSKA
+
+extern "C" {
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../mp_msg.h"
+#include "../help_mp.h"
+#include "stream.h"
+#include "demuxer.h"
+#include "stheader.h"
+
+#include "../subreader.h"
+#include "../libvo/sub.h"
+}
+
+#include <iostream>
+#include <cassert>
+#include <typeinfo>
+
+#include "EbmlHead.h"
+#include "EbmlSubHead.h"
+#include "EbmlStream.h"
+#include "EbmlContexts.h"
+#include "FileKax.h"
+
+#include "KaxAttachements.h"
+#include "KaxBlock.h"
+#include "KaxBlockData.h"
+#include "KaxChapters.h"
+#include "KaxCluster.h"
+#include "KaxClusterData.h"
+#include "KaxContexts.h"
+#include "KaxCues.h"
+#include "KaxCuesData.h"
+#include "KaxInfo.h"
+#include "KaxInfoData.h"
+#include "KaxSeekHead.h"
+#include "KaxSegment.h"
+#include "KaxTracks.h"
+#include "KaxTrackAudio.h"
+#include "KaxTrackVideo.h"
+
+#include "StdIOCallback.h"
+
+#include "matroska.h"
+
+using namespace LIBMATROSKA_NAMESPACE;
+using namespace std;
+
+// default values for Matroska elements
+#define MKVD_TIMECODESCALE 1000000 // 1000000 = 1ms
+
+class mpstream_io_callback: public IOCallback {
+  private:
+    stream_t *s;
+  public:
+    mpstream_io_callback(stream_t *stream);
+
+    virtual uint32 read(void *buffer, size_t size);
+    virtual void setFilePointer(int64 offset, seek_mode mode = seek_beginning);
+    virtual size_t write(const void *buffer, size_t size);
+    virtual uint64 getFilePointer();
+    virtual void close();
+};
+
+mpstream_io_callback::mpstream_io_callback(stream_t *stream) {
+  s = stream;
+}
+
+uint32 mpstream_io_callback::read(void *buffer, size_t size) {
+  uint32_t result;
+
+  result = stream_read(s, (char *)buffer, size);
+
+  return result;
+}
+
+void mpstream_io_callback::setFilePointer(int64 offset, seek_mode mode) {
+  int64 new_pos;
+  
+  if (mode == seek_beginning)
+    new_pos = offset + s->start_pos;
+  else if (mode == seek_end)
+    new_pos = s->end_pos - offset;
+  else
+    new_pos = s->pos + offset;
+
+  if (new_pos > s->end_pos) {
+    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] seek warning: new_pos %lld > end_pos "
+           "%lld\n", new_pos, s->end_pos);
+    return;
+  }
+
+  stream_seek(s, new_pos);
+}
+
+size_t mpstream_io_callback::write(const void */*buffer*/, size_t /*size*/) {
+  return 0;
+}
+
+uint64 mpstream_io_callback::getFilePointer() {
+  return s->pos - s->buf_len + s->buf_pos;
+}
+
+void mpstream_io_callback::close() {
+}
+
+typedef struct mkv_index_entry {
+  uint64_t timecode, filepos;
+  int is_key;
+} mkv_index_entry_t;
+
+typedef struct mkv_track_index {
+  uint32_t tnum;
+  int num_entries;
+  mkv_index_entry_t *entries;
+} mkv_track_index_t;
+
+typedef struct mkv_track {
+  uint32_t tnum;
+  
+  char *codec_id;
+  int ms_compat;
+
+  char type; // 'v' = video, 'a' = audio, 't' = text subs
+  
+  char v_fourcc[5];
+  uint32_t v_width, v_height;
+  float v_frate;
+
+  uint16_t a_formattag;
+  uint32_t a_channels, a_bps;
+  float a_sfreq;
+
+  int default_track;
+
+  void *private_data;
+  unsigned int private_size;
+
+  unsigned char *headers[3];
+  uint32_t header_sizes[3];
+
+  int ok;
+} mkv_track_t;
+
+typedef struct mkv_demuxer {
+  float duration, last_pts;
+  uint64_t last_filepos;
+
+  mkv_track_t **tracks;
+  int num_tracks;
+  mkv_track_t *video, *audio, *subs_track;
+
+  uint64_t tc_scale, cluster_tc;
+
+  mpstream_io_callback *in;
+
+  uint64_t clear_subs_at;
+
+  subtitle subs;
+
+  EbmlStream *es;
+  EbmlElement *saved_l1, *saved_l2;
+  KaxSegment *segment;
+  KaxCluster *cluster;
+
+  mkv_track_index_t *index;
+  int num_indexes, cues_found, cues_searched;
+  int64_t *cluster_positions;
+  int num_cluster_pos;
+
+  int skip_to_keyframe;
+} mkv_demuxer_t;
+
+static uint16_t get_uint16(const void *buf) {
+  uint16_t      ret;
+  unsigned char *tmp;
+
+  tmp = (unsigned char *) buf;
+
+  ret = tmp[1] & 0xff;
+  ret = (ret << 8) + (tmp[0] & 0xff);
+
+  return ret;
+}
+
+static uint32_t get_uint32(const void *buf) {
+  uint32_t      ret;
+  unsigned char *tmp;
+
+  tmp = (unsigned char *) buf;
+
+  ret = tmp[3] & 0xff;
+  ret = (ret << 8) + (tmp[2] & 0xff);
+  ret = (ret << 8) + (tmp[1] & 0xff);
+  ret = (ret << 8) + (tmp[0] & 0xff);
+
+  return ret;
+}
+
+static void handle_subtitles(demuxer_t *d, KaxBlock *block, int64_t duration) {
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *)d->priv;
+  int len, line, state;
+  char *s1, *s2, *buffer;
+
+  if (duration == -1) {
+    mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Warning: No KaxBlockDuration "
+           "element for subtitle track found.\n");
+    return;
+  }
+
+  DataBuffer &data = block->GetBuffer(0);
+  len = data.Size();
+
+  buffer = (char *)data.Buffer();
+  s1 = buffer;
+
+  while (((*s1 == '\n') || (*s1 == '\r')) &&
+         ((unsigned int)(s1 - buffer) <= data.Size()))
+    s1++;
+
+  line = 0;
+  s2 = mkv_d->subs.text[0];
+  mkv_d->subs.lines = 1;
+  state = 0;
+  while ((unsigned int)(s1 - buffer) != data.Size()) {
+    if ((*s1 == '\n') || (*s1 == '\r')) {
+      if (state == 0) {       // normal char --> newline
+        if (mkv_d->subs.lines == SUB_MAX_TEXT)
+          break;
+        *s2 = 0;
+        s2 = mkv_d->subs.text[mkv_d->subs.lines];
+        mkv_d->subs.lines++;
+        state = 1;
+      }
+    } else if (*s1 == '<')    // skip HTML tags
+      state = 2;
+    else if (*s1 == '>')
+      state = 0;
+    else if (state != 2) {      // normal character
+      state = 0;
+      if ((s2 - mkv_d->subs.text[mkv_d->subs.lines - 1]) < 255) {
+        *s2 = *s1;
+        s2++;
+      }
+    }
+    s1++;
+  }
+
+  *s2 = 0;
+
+#ifdef USE_ICONV
+  subcp_recode1(&mkv_d->subs);
+#endif
+
+  vo_sub = &mkv_d->subs;
+  vo_osd_changed(OSDTYPE_SUBTITLE);
+
+  mkv_d->clear_subs_at = block->GlobalTimecode() / 1000000 + duration;
+}
+
+static mkv_track_t *new_mkv_track(mkv_demuxer_t *d) {
+  mkv_track_t *t;
+  
+  t = (mkv_track_t *)malloc(sizeof(mkv_track_t));
+  if (t != NULL) {
+    memset(t, 0, sizeof(mkv_track_t));
+    d->tracks = (mkv_track_t **)realloc(d->tracks, (d->num_tracks + 1) *
+                                        sizeof(mkv_track_t *));
+    if (d->tracks == NULL)
+      return NULL;
+    d->tracks[d->num_tracks] = t;
+    d->num_tracks++;
+  }
+  
+  return t;
+}
+
+static mkv_track_t *find_track_by_num(mkv_demuxer_t *d, uint32_t n,
+                                      mkv_track_t *c) {
+  int i;
+  
+  for (i = 0; i < d->num_tracks; i++)
+    if ((d->tracks[i] != NULL) && (d->tracks[i]->tnum == n) &&
+        (d->tracks[i] != c))
+      return d->tracks[i];
+  
+  return NULL;
+}
+
+static int check_track_information(mkv_demuxer_t *d) {
+  int i;
+  unsigned char *c;
+  uint32_t u, offset, length;
+  mkv_track_t *t;
+  BITMAPINFOHEADER *bih;
+  WAVEFORMATEX *wfe;
+  
+  for (i = 0; i < d->num_tracks; i++) {
+    t = d->tracks[i];
+    switch (t->type) {
+      case 'v':                 // video track
+        if (t->codec_id == NULL)
+          continue;
+        if (!strcmp(t->codec_id, MKV_V_MSCOMP)) {
+          if ((t->private_data == NULL) ||
+              (t->private_size < sizeof(BITMAPINFOHEADER))) {
+            mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: CodecID for track "
+                   "%u is '" MKV_V_MSCOMP "', but there was no "
+                   "BITMAPINFOHEADER struct present. Therefore we don't have "
+                   "a FourCC to identify the video codec used.\n", t->tnum);
+            continue;
+          } else {
+            t->ms_compat = 1;
+
+            bih = (BITMAPINFOHEADER *)t->private_data;
+
+            u = get_uint32(&bih->biWidth);
+            if (t->v_width != u) {
+              mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: (MS "
+                     "compatibility mode, track %u) "
+                     "Matrosa says video width is %u, but the "
+                     "BITMAPINFOHEADER says %u.\n", t->tnum, t->v_width, u);
+              if (t->v_width == 0)
+                t->v_width = u;
+            }
+
+            u = get_uint32(&bih->biHeight);
+            if (t->v_height != u) {
+              mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: (MS compatibility "
+                     "mode, track %u) "
+                     "Matrosa video height is %u, but the BITMAPINFOHEADER "
+                     "says %u.\n", t->tnum, t->v_height, u);
+              if (t->v_height == 0)
+                t->v_height = u;
+            }
+
+            memcpy(t->v_fourcc, &bih->biCompression, 4);
+
+            if (t->v_frate == 0.0) {
+              mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] ERROR: (MS compatibility "
+                     "mode, track %u) "
+                     "No VideoFrameRate element was found.\n", t->tnum);
+              continue;
+            }
+          }
+        } else {
+          mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Native CodecIDs for video "
+                 "tracks are not supported yet (track %u).\n", t->tnum);
+          continue;
+        }
+
+        if (t->v_width == 0) {
+          mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] The width for track %u was "
+                 "not set.\n", t->tnum);
+          continue;
+        }
+        if (t->v_height == 0) {
+          mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] The height for track %u was "
+                 "not set.\n", t->tnum);
+          continue;
+        }
+
+        // This track seems to be ok.
+        t->ok = 1;
+
+        break;
+
+      case 'a':                 // audio track
+        if (t->codec_id == NULL)
+          continue;
+        if (!strcmp(t->codec_id, MKV_A_ACM)) {
+          if ((t->private_data == NULL) ||
+              (t->private_size < sizeof(WAVEFORMATEX))) {
+            mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: CodecID for track "
+                   "%u is '" MKV_A_ACM "', "
+                   "but there was no WAVEFORMATEX struct present. "
+                   "Therefore we don't have a format ID to identify the audio "
+                   "codec used.\n", t->tnum);
+            continue;
+          } else {
+            t->ms_compat = 1;
+
+            wfe = (WAVEFORMATEX *)t->private_data;
+            u = get_uint32(&wfe->nSamplesPerSec);
+            if (((uint32_t)t->a_sfreq) != u) {
+              mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: (MS compatibility "
+                     "mode for track %u) "
+                     "Matroska says that there are %u samples per second, "
+                     "but WAVEFORMATEX says that there are %u.\n", t->tnum,
+                     (uint32_t)t->a_sfreq, u);
+              if (t->a_sfreq == 0.0)
+                t->a_sfreq = (float)u;
+            }
+
+            u = get_uint16(&wfe->nChannels);
+            if (t->a_channels != u) {
+              mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: (MS "
+                     "compatibility mode for track %u) "
+                     "Matroska says that there are %u channels, but the "
+                     "WAVEFORMATEX says that there are %u.\n", t->tnum,
+                     t->a_channels, u);
+              if (t->a_channels == 0)
+                t->a_channels = u;
+            }
+
+            u = get_uint16(&wfe->wBitsPerSample);
+            if (t->a_channels != u) {
+              mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: (MS "
+                     "compatibility mode for track %u) "
+                     "Matroska says that there are %u bits per sample, "
+                     "but the WAVEFORMATEX says that there are %u.\n", t->tnum,
+                     t->a_bps, u);
+              if (t->a_bps == 0)
+                t->a_bps = u;
+            }
+            
+            t->a_formattag = get_uint16(&wfe->wFormatTag);
+          }
+        } else {
+          if (!strcmp(t->codec_id, MKV_A_MP3))
+            t->a_formattag = 0x0055;
+          else if (!strcmp(t->codec_id, MKV_A_AC3))
+            t->a_formattag = 0x2000;
+          else if (!strcmp(t->codec_id, MKV_A_PCM))
+            t->a_formattag = 0x0001;
+          else if (!strcmp(t->codec_id, MKV_A_VORBIS)) {
+            if (t->private_data == NULL) {
+              mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: CodecID for "
+                     "track %u is '" MKV_A_VORBIS
+                     "', but there are no header packets present.", t->tnum);
+              continue;
+            }
+
+            c = (unsigned char *)t->private_data;
+            if (c[0] != 2) {
+              mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Vorbis track does not "
+                     "contain valid headers.\n");
+              continue;
+            }
+
+            offset = 1;
+            for (i = 0; i < 2; i++) {
+              length = 0;
+              while ((c[offset] == (unsigned char )255) &&
+                     (length < t->private_size)) {
+                length += 255;
+                offset++;
+              }
+              if (offset >= (t->private_size - 1)) {
+                mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Vorbis track does not "
+                       "contain valid headers.\n");
+                continue;
+              }
+              length += c[offset];
+              offset++;
+              t->header_sizes[i] = length;
+            }
+
+            t->headers[0] = &c[offset];
+            t->headers[1] = &c[offset + t->header_sizes[0]];
+            t->headers[2] = &c[offset + t->header_sizes[0] +
+                               t->header_sizes[1]];
+            t->header_sizes[2] = t->private_size - offset;
+
+            t->a_formattag = 0xFFFE;
+          } else {
+            mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Unknown/unsupported audio "
+                   "codec ID '%s' for track %u.\n", t->codec_id, t->tnum);
+            continue;
+          }
+        }
+
+        if (t->a_sfreq == 0.0) {
+          mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] The sampling frequency was not "
+                 "set for track %u.\n", t->tnum);
+          continue;
+        }
+
+        if (t->a_channels == 0) {
+          mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] The number of channels was not "
+                 "set for track %u.\n", t->tnum);
+          continue;
+        }
+
+        if (t->a_formattag == 0) {
+          mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] The audio format tag was not "
+                 "set for track %u.\n", t->tnum);
+          continue;
+        }
+
+        // This track seems to be ok.
+        t->ok = 1;
+
+        break;
+
+      case 's':                 // Text subtitles do not need any data
+        t->ok = 1;              // except the CodecID.
+        break;
+
+      default:                  // unknown track type!? error in demuxer...
+        mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Error in demux_mkv.cpp: unknown "
+               "demuxer type for track %u: '%c'\n", t->tnum, t->type);
+        continue;
+    }
+
+    if (t->ok)
+      mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Track %u seems to be ok.\n", t->tnum);
+  }
+
+  return 1;
+}
+
+static void free_mkv_demuxer(mkv_demuxer_t *d) {
+  int i;
+  
+  if (d == NULL)
+    return;
+
+  for (i = 0; i < d->num_tracks; i++)
+    if (d->tracks[i] != NULL) {
+      if (d->tracks[i]->private_data != NULL)
+        free(d->tracks[i]->private_data);
+      free(d->tracks[i]);
+    }
+
+  for (i = 0; i < d->num_indexes; i++)
+    free(d->index[i].entries);
+  free(d->index);
+  
+  if (d->es != NULL)
+    delete d->es;
+  if (d->saved_l1 != NULL)
+    delete d->saved_l1;
+  if (d->in != NULL)
+    delete d->in;
+  if (d->segment != NULL)
+    delete d->segment;
+
+  free(d);
+}
+
+static void add_index_entry(mkv_demuxer_t *d, uint32_t tnum, uint64_t filepos,
+                            uint64_t timecode, int is_key) {
+  int i, found;
+  mkv_index_entry_t *entry;
+
+  for (i = 0, found = 0; i < d->num_indexes; i++)
+    if (d->index[i].tnum == tnum) {
+      found = 1;
+      break;
+    }
+
+  if (!found) {
+    d->index = (mkv_track_index_t *)realloc(d->index, (d->num_indexes + 1) *
+                                            sizeof(mkv_track_index_t));
+    if (d->index == NULL)
+      return;
+    i = d->num_indexes;
+    memset(&d->index[i], 0, sizeof(mkv_track_index_t));
+    d->index[i].tnum = tnum;
+    d->num_indexes++;
+  }
+
+  d->index[i].entries =
+    (mkv_index_entry_t *)realloc(d->index[i].entries,
+                                 (d->index[i].num_entries + 1) *
+                                 sizeof(mkv_index_entry_t));
+  if (d->index[i].entries == NULL)
+    return;
+  entry = &d->index[i].entries[d->index[i].num_entries];
+  entry->filepos = filepos;
+  entry->timecode = timecode;
+  entry->is_key = is_key;
+  d->index[i].num_entries++;
+}
+
+static void add_cluster_position(mkv_demuxer_t *mkv_d, int64_t position) {
+  mkv_d->cluster_positions = (int64_t *)realloc(mkv_d->cluster_positions,
+                                                (mkv_d->num_cluster_pos + 1) *
+                                                sizeof(int64_t));
+  if (mkv_d->cluster_positions != NULL) {
+    mkv_d->cluster_positions[mkv_d->num_cluster_pos] = position;
+    mkv_d->num_cluster_pos++;
+  } else
+    mkv_d->num_cluster_pos = 0;
+}
+
+static int parse_cues(mkv_demuxer_t *mkv_d) {
+  EbmlElement *l1 = NULL, *l2 = NULL, *l3 = NULL, *l4 = NULL, *l5 = NULL;
+  EbmlStream *es;
+  int upper_lvl_el, elements_found, i, k;
+  uint64_t tc_scale, filepos = 0, timecode = 0;
+  uint32_t tnum = 0;
+  mkv_index_entry_t *entry;
+
+  es = mkv_d->es;
+  tc_scale = mkv_d->tc_scale;
+  upper_lvl_el = 0;
+
+  mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing cues ] -----------\n");
+
+  l1 = es->FindNextElement(mkv_d->segment->Generic().Context, upper_lvl_el,
+                           0xFFFFFFFFL, true, 1);
+  if (l1 == NULL)
+    return 0;
+
+  if (!(EbmlId(*l1) == KaxCues::ClassInfos.GlobalId)) {
+    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] No KaxCues element found but but %s.\n"
+           "[mkv] \\---- [ parsing cues ] -----------\n", typeid(*l1).name());
+    
+    return 0;
+  }
+
+  mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ found cues\n");
+
+  l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL,
+                           true, 1);
+  while (l2 != NULL) {
+    if (upper_lvl_el != 0)
+      break;
+
+    if (EbmlId(*l2) == KaxCuePoint::ClassInfos.GlobalId) {
+      mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue point\n");
+
+      elements_found = 0;
+
+      l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
+                               0xFFFFFFFFL, true, 1);
+      while (l3 != NULL) {
+        if (upper_lvl_el != 0)
+          break;
+
+        if (EbmlId(*l3) == KaxCueTime::ClassInfos.GlobalId) {
+          KaxCueTime &cue_time = *static_cast<KaxCueTime *>(l3);
+          cue_time.ReadData(es->I_O());
+          mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |  + found cue time: %.3fs\n",
+                 ((float)uint64(cue_time)) * tc_scale / 1000000000.0);
+
+          timecode = uint64(cue_time) * tc_scale / 1000000;
+          elements_found |= 1;
+
+        } else if (EbmlId(*l3) ==
+                   KaxCueTrackPositions::ClassInfos.GlobalId) {
+          mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |  + found cue track "
+                 "positions\n");
+
+          l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el,
+                                   0xFFFFFFFFL, true, 1);
+          while (l4 != NULL) {
+            if (upper_lvl_el != 0)
+              break;
+
+            if (EbmlId(*l4) == KaxCueTrack::ClassInfos.GlobalId) {
+              KaxCueTrack &cue_track = *static_cast<KaxCueTrack *>(l4);
+              cue_track.ReadData(es->I_O());
+              mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |   + found cue track: "
+                     "%u\n", uint32(cue_track));
+
+              tnum = uint32(cue_track);
+              elements_found |= 2;
+
+            } else if (EbmlId(*l4) ==
+                       KaxCueClusterPosition::ClassInfos.GlobalId) {
+              KaxCueClusterPosition &cue_cp =
+                *static_cast<KaxCueClusterPosition *>(l4);
+              cue_cp.ReadData(es->I_O());
+              mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |   + found cue cluster "
+                     "position: %llu\n", uint64(cue_cp));
+
+              filepos = mkv_d->segment->GetGlobalPosition(uint64_t(cue_cp));
+              elements_found |= 4;
+
+            } else if (EbmlId(*l4) ==
+                       KaxCueBlockNumber::ClassInfos.GlobalId) {
+              KaxCueBlockNumber &cue_bn =
+                *static_cast<KaxCueBlockNumber *>(l4);
+              cue_bn.ReadData(es->I_O());
+              mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |   + found cue block "
+                     "number: %llu\n", uint64(cue_bn));
+
+            } else if (EbmlId(*l4) ==
+                       KaxCueCodecState::ClassInfos.GlobalId) {
+              KaxCueCodecState &cue_cs =
+                *static_cast<KaxCueCodecState *>(l4);
+              cue_cs.ReadData(es->I_O());
+              mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |   + found cue codec "
+                     "state: %llu\n", uint64(cue_cs));
+
+            } else if (EbmlId(*l4) ==
+                       KaxCueReference::ClassInfos.GlobalId) {
+              mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |  + found cue "
+                     "reference\n");
+
+              elements_found |= 8;
+
+              l5 = es->FindNextElement(l4->Generic().Context, upper_lvl_el,
+                                       0xFFFFFFFFL, true, 1);
+              while (l5 != NULL) {
+                if (upper_lvl_el != 0)
+                  break;
+
+                if (EbmlId(*l5) == KaxCueRefTime::ClassInfos.GlobalId) {
+                  KaxCueRefTime &cue_rt =
+                    *static_cast<KaxCueRefTime *>(l5);
+                  mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |    + found cue ref "
+                         "time: %.3fs\n", ((float)uint64(cue_rt)) * tc_scale /
+                         1000000000.0);
+
+                } else if (EbmlId(*l5) ==
+                           KaxCueRefCluster::ClassInfos.GlobalId) {
+                  KaxCueRefCluster &cue_rc =
+                    *static_cast<KaxCueRefCluster *>(l5);
+                  cue_rc.ReadData(es->I_O());
+                  mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |    + found cue ref "
+                         "cluster: %llu\n", uint64(cue_rc));
+
+                } else if (EbmlId(*l5) ==
+                           KaxCueRefNumber::ClassInfos.GlobalId) {
+                  KaxCueRefNumber &cue_rn =
+                    *static_cast<KaxCueRefNumber *>(l5);
+                  cue_rn.ReadData(es->I_O());
+                  mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |    + found cue ref "
+                         "number: %llu\n", uint64(cue_rn));
+
+                } else if (EbmlId(*l5) ==
+                           KaxCueRefCodecState::ClassInfos.GlobalId) {
+                  KaxCueRefCodecState &cue_rcs =
+                    *static_cast<KaxCueRefCodecState *>(l5);
+                  cue_rcs.ReadData(es->I_O());
+                  mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |    + found cue ref "
+                         "codec state: %llu\n", uint64(cue_rcs));
+
+                } else {
+                  mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |    + unknown "
+                         "element, level 5: %s\n", typeid(*l5).name());
+                }
+
+                l5->SkipData(static_cast<EbmlStream &>(*es),
+                             l5->Generic().Context);
+                delete l5;
+                l5 = es->FindNextElement(l4->Generic().Context,
+                                         upper_lvl_el, 0xFFFFFFFFL, true, 1);
+              } // while (l5 != NULL)
+
+            } else
+              mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |  + unknown element, "
+                     "level 4: %s\n", typeid(*l4).name());
+
+            if (upper_lvl_el > 0) {		// we're coming from l5
+              upper_lvl_el--;
+              delete l4;
+              l4 = l5;
+              if (upper_lvl_el > 0)
+                break;
+
+            } else {
+              l4->SkipData(static_cast<EbmlStream &>(*es),
+                           l4->Generic().Context);
+              delete l4;
+              l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el,
+                                       0xFFFFFFFFL, true, 1);
+            }
+          } // while (l4 != NULL)
+
+        } else
+          mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |  + unknown element, level 3: "
+                 "%s\n", typeid(*l3).name());
+
+        if (upper_lvl_el > 0) {		// we're coming from l4
+          upper_lvl_el--;
+          delete l3;
+          l3 = l4;
+          if (upper_lvl_el > 0)
+            break;
+
+        } else {
+          l3->SkipData(static_cast<EbmlStream &>(*es),
+                       l3->Generic().Context);
+          delete l3;
+          l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
+                                   0xFFFFFFFFL, true, 1);
+        }
+      } // while (l3 != NULL)
+
+      // Three elements must have been found in order for this to be a
+      // correct entry:
+      // 1: cue time (timecode)
+      // 2: cue track (tnum)
+      // 4: cue cluster position (filepos)
+      // If 8 is also set, then there was a reference element, and the
+      // current block is not an I frame. If 8 is not set then this is
+      // an I frame.
+      if ((elements_found & 7) == 7)
+        add_index_entry(mkv_d, tnum, filepos, timecode,
+                        (elements_found & 8) ? 0 : 1);
+
+    } else
+      mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] |  + unknown element, level 2: "
+             "%s\n", typeid(*l2).name());
+
+    if (upper_lvl_el > 0) {		// we're coming from l3
+      upper_lvl_el--;
+      delete l2;
+      l2 = l3;
+      if (upper_lvl_el > 0)
+        break;
+
+    } else {
+      l2->SkipData(static_cast<EbmlStream &>(*es),
+                   l2->Generic().Context);
+      delete l2;
+      l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el,
+                               0xFFFFFFFFL, true, 1);
+    }
+  } // while (l2 != NULL)
+
+  // Debug: dump the index
+  for (i = 0; i < mkv_d->num_indexes; i++) {
+    mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Index for track %u contains %u "
+           "entries.\n", mkv_d->index[i].tnum, mkv_d->index[i].num_entries);
+    for (k = 0; k < mkv_d->index[i].num_entries; k++) {
+      entry = &mkv_d->index[i].entries[k];
+      mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv]   %d: timecode %llu, filepos %llu, "
+             "is key: %s\n", k, entry->timecode, entry->filepos,
+             entry->is_key ? "yes" : "no");
+    }
+  }
+
+  mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing cues ] -----------\n");
+
+  return 1;
+}
+
+extern "C" int demux_mkv_open(demuxer_t *demuxer) {
+  unsigned char signature[4];
+  stream_t *s;
+  demux_packet_t *dp;
+  mkv_demuxer_t *mkv_d;
+  int upper_lvl_el, exit_loop, i;
+  // Elements for different levels
+  EbmlElement *l0 = NULL, *l1 = NULL, *l2 = NULL, *l3 = NULL, *l4 = NULL;
+  EbmlStream *es;
+  mkv_track_t *track;
+  sh_audio_t *sh_a;
+  sh_video_t *sh_v;
+  uint64_t seek_pos, current_pos;
+  int seek_element_is_cue;
+
+#ifdef USE_ICONV
+          subcp_open();
+#endif
+
+  s = demuxer->stream;
+  stream_seek(s, s->start_pos);
+  memset(signature, 0, 4);
+  stream_read(s, (char *)signature, 4);
+  if ((signature[0] != 0x1A) || (signature[1] != 0x45) ||
+      (signature[2] != 0xDF) || (signature[3] != 0xA3))
+    return 0;
+  stream_seek(s, s->start_pos);
+  
+  try {
+    // structure for storing the demuxer's private data
+    mkv_d = (mkv_demuxer_t *)malloc(sizeof(mkv_demuxer_t));
+    if (mkv_d == NULL)
+      return 0;
+    memset(mkv_d, 0, sizeof(mkv_demuxer_t));
+    mkv_d->duration = -1.0;
+    
+    // Create the interface between MPlayer's IO system and
+    // libmatroska's IO system.
+    mkv_d->in = new mpstream_io_callback(demuxer->stream);
+    if (mkv_d->in == NULL) {
+      free_mkv_demuxer(mkv_d);
+      return 0;
+    }
+    mpstream_io_callback &io = *static_cast<mpstream_io_callback *>(mkv_d->in);
+    mkv_d->es = new EbmlStream(io);
+    if (mkv_d->es == NULL) {
+      free_mkv_demuxer(mkv_d);
+      return 0;
+    }
+    es = mkv_d->es;
+    
+    // Find the EbmlHead element. Must be the first one.
+    l0 = es->FindNextID(EbmlHead::ClassInfos, 0xFFFFFFFFL);
+    if (l0 == NULL) {
+      mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] no head found\n");
+      free_mkv_demuxer(mkv_d);
+      return 0;
+    }
+    // Don't verify its data for now.
+    l0->SkipData(static_cast<EbmlStream &>(*es), l0->Generic().Context);
+    delete l0;
+    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Found the head...\n");
+    
+    // Next element must be a segment
+    l0 = es->FindNextID(KaxSegment::ClassInfos, 0xFFFFFFFFL);
+    if (l0 == NULL) {
+      mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] but no segment :(\n");
+      free_mkv_demuxer(mkv_d);
+      return 0;
+    }
+    if (!(EbmlId(*l0) == KaxSegment::ClassInfos.GlobalId)) {
+      mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] but no segment :(\n");
+      free_mkv_demuxer(mkv_d);
+      return 0;
+    }
+    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] + a segment...\n");
+    
+    mkv_d->segment = (KaxSegment *)l0;
+
+    upper_lvl_el = 0;
+    exit_loop = 0;
+    // We've got our segment, so let's find the tracks
+    l1 = es->FindNextElement(l0->Generic().Context, upper_lvl_el, 0xFFFFFFFFL,
+                             true, 1);
+    while (l1 != NULL) {
+      if ((upper_lvl_el != 0) || exit_loop)
+        break;
+
+      if (EbmlId(*l1) == KaxInfo::ClassInfos.GlobalId) {
+        // General info about this Matroska file
+        mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ segment information...\n");
+        
+        l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el,
+                                 0xFFFFFFFFL, true, 1);
+        while (l2 != NULL) {
+          if ((upper_lvl_el != 0) || exit_loop)
+            break;
+
+          if (EbmlId(*l2) == KaxTimecodeScale::ClassInfos.GlobalId) {
+            KaxTimecodeScale &tc_scale = *static_cast<KaxTimecodeScale *>(l2);
+            tc_scale.ReadData(es->I_O());
+            mkv_d->tc_scale = uint64(tc_scale);
+            mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + timecode scale: %llu\n",
+                   mkv_d->tc_scale);
+
+          } else if (EbmlId(*l2) == KaxDuration::ClassInfos.GlobalId) {
+            KaxDuration &duration = *static_cast<KaxDuration *>(l2);
+            duration.ReadData(es->I_O());
+            mkv_d->duration = float(duration) * mkv_d->tc_scale / 1000000000.0;
+            mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + duration: %.3fs\n",
+                   mkv_d->duration);
+
+          } else
+            mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + unknown element@2: %s\n",
+                   typeid(*l2).name());
+
+          l2->SkipData(static_cast<EbmlStream &>(*es),
+                       l2->Generic().Context);
+          delete l2;
+          l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el,
+                                   0xFFFFFFFFL, true, 1);
+        }
+
+      } else if (EbmlId(*l1) == KaxTracks::ClassInfos.GlobalId) {
+        // Yep, we've found our KaxTracks element. Now find all tracks
+        // contained in this segment.
+        mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ segment tracks...\n");
+        
+        l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el,
+                                 0xFFFFFFFFL, true, 1);
+        while (l2 != NULL) {
+          if ((upper_lvl_el != 0) || exit_loop)
+            break;
+          
+          if (EbmlId(*l2) == KaxTrackEntry::ClassInfos.GlobalId) {
+            // We actually found a track entry :) We're happy now.
+            mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + a track...\n");
+            
+            track = new_mkv_track(mkv_d);
+            if (track == NULL)
+              return 0;
+            
+            l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
+                                     0xFFFFFFFFL, true, 1);
+            while (l3 != NULL) {
+              if (upper_lvl_el != 0)
+                break;
+              
+              // Now evaluate the data belonging to this track
+              if (EbmlId(*l3) == KaxTrackNumber::ClassInfos.GlobalId) {
+                KaxTrackNumber &tnum = *static_cast<KaxTrackNumber *>(l3);
+                tnum.ReadData(es->I_O());
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + Track number: %u\n",
+                       uint32(tnum));
+                track->tnum = uint32(tnum);
+                if (find_track_by_num(mkv_d, track->tnum, track) != NULL)
+                  mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] |  + WARNING: There's "
+                         "more than one track with the number %u.\n",
+                         track->tnum);
+
+              } else if (EbmlId(*l3) == KaxTrackUID::ClassInfos.GlobalId) {
+                KaxTrackUID &tuid = *static_cast<KaxTrackUID *>(l3);
+                tuid.ReadData(es->I_O());
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + Track UID: %u\n",
+                       uint32(tuid));
+
+              } else if (EbmlId(*l3) == KaxTrackType::ClassInfos.GlobalId) {
+                KaxTrackType &ttype = *static_cast<KaxTrackType *>(l3);
+                ttype.ReadData(es->I_O());
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + Track type: ");
+
+                switch (uint8(ttype)) {
+                  case track_audio:
+                    mp_msg(MSGT_DEMUX, MSGL_V, "Audio\n");
+                    track->type = 'a';
+                    break;
+                  case track_video:
+                    mp_msg(MSGT_DEMUX, MSGL_V, "Video\n");
+                    track->type = 'v';
+                    break;
+                  case track_subtitle:
+                    mp_msg(MSGT_DEMUX, MSGL_V, "Subtitle\n");
+                    track->type = 's';
+                    break;
+                  default:
+                    mp_msg(MSGT_DEMUX, MSGL_V, "unknown\n");
+                    track->type = '?';
+                    break;
+                }
+
+              } else if (EbmlId(*l3) == KaxTrackAudio::ClassInfos.GlobalId) {
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + Audio track\n");
+                l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el,
+                                         0xFFFFFFFFL, true, 1);
+                while (l4 != NULL) {
+                  if (upper_lvl_el != 0)
+                    break;
+                
+                  if (EbmlId(*l4) ==
+                      KaxAudioSamplingFreq::ClassInfos.GlobalId) {
+                    KaxAudioSamplingFreq &freq =
+                      *static_cast<KaxAudioSamplingFreq*>(l4);
+                    freq.ReadData(es->I_O());
+                    track->a_sfreq = float(freq);
+                    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |   + Sampling "
+                           "frequency: %f\n", track->a_sfreq);
+
+                  } else if (EbmlId(*l4) ==
+                             KaxAudioChannels::ClassInfos.GlobalId) {
+                    KaxAudioChannels &channels =
+                      *static_cast<KaxAudioChannels*>(l4);
+                    channels.ReadData(es->I_O());
+                    track->a_channels = uint8(channels);
+                    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |   + Channels: %u\n",
+                           track->a_channels);
+
+                  } else if (EbmlId(*l4) ==
+                             KaxAudioBitDepth::ClassInfos.GlobalId) {
+                    KaxAudioBitDepth &bps =
+                      *static_cast<KaxAudioBitDepth*>(l4);
+                    bps.ReadData(es->I_O());
+                    track->a_bps = uint8(bps);
+                    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |   + Bit depth: %u\n",
+                           track->a_bps);
+
+                  } else
+                    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |   + unknown "
+                           "element@4: %s\n", typeid(*l4).name());
+
+                  l4->SkipData(static_cast<EbmlStream &>(*es),
+                               l4->Generic().Context);
+                  delete l4;
+                  l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el,
+                                           0xFFFFFFFFL, true, 1);
+                } // while (l4 != NULL)
+
+              } else if (EbmlId(*l3) == KaxTrackVideo::ClassInfos.GlobalId) {
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + Video track\n");
+                l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el,
+                                         0xFFFFFFFFL, true, 1);
+                while (l4 != NULL) {
+                  if (upper_lvl_el != 0)
+                    break;
+
+                  if (EbmlId(*l4) == KaxVideoPixelWidth::ClassInfos.GlobalId) {
+                    KaxVideoPixelWidth &width =
+                      *static_cast<KaxVideoPixelWidth *>(l4);
+                    width.ReadData(es->I_O());
+                    track->v_width = uint16(width);
+                    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |   + Pixel width: %u\n",
+                           track->v_width);
+
+                  } else if (EbmlId(*l4) ==
+                             KaxVideoPixelHeight::ClassInfos.GlobalId) {
+                    KaxVideoPixelHeight &height =
+                      *static_cast<KaxVideoPixelHeight *>(l4);
+                    height.ReadData(es->I_O());
+                    track->v_height = uint16(height);
+                    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |   + Pixel height: "
+                           "%u\n", track->v_height);
+
+                  } else if (EbmlId(*l4) ==
+                             KaxVideoFrameRate::ClassInfos.GlobalId) {
+                    KaxVideoFrameRate &framerate =
+                      *static_cast<KaxVideoFrameRate *>(l4);
+                    framerate.ReadData(es->I_O());
+                    track->v_frate = float(framerate);
+                    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |   + Frame rate: %f\n",
+                           float(framerate));
+
+                  } else
+                    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |   + unknown "
+                           "element@4: %s\n", typeid(*l4).name());
+
+                  l4->SkipData(static_cast<EbmlStream &>(*es),
+                               l4->Generic().Context);
+                  delete l4;
+                  l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el,
+                                           0xFFFFFFFFL, true, 1);
+                } // while (l4 != NULL)
+
+              } else if (EbmlId(*l3) == KaxCodecID::ClassInfos.GlobalId) {
+                KaxCodecID &codec_id = *static_cast<KaxCodecID*>(l3);
+                codec_id.ReadData(es->I_O());
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + Codec ID: %s\n",
+                       &binary(codec_id));
+                track->codec_id = strdup((char *)&binary(codec_id));
+
+              } else if (EbmlId(*l3) == KaxCodecPrivate::ClassInfos.GlobalId) {
+                KaxCodecPrivate &c_priv = *static_cast<KaxCodecPrivate*>(l3);
+                c_priv.ReadData(es->I_O());
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + CodecPrivate, length "
+                       "%llu\n", c_priv.GetSize());
+                track->private_size = c_priv.GetSize();
+                if (track->private_size > 0) {
+                  track->private_data = malloc(track->private_size);
+                  if (track->private_data == NULL)
+                    return 0;
+                  memcpy(track->private_data, c_priv.GetBuffer(),
+                         track->private_size);
+                }
+
+              } else if (EbmlId(*l3) ==
+                         KaxTrackFlagDefault::ClassInfos.GlobalId) {
+                KaxTrackFlagDefault &f_default = 
+                  *static_cast<KaxTrackFlagDefault *>(l3);
+                f_default.ReadData(es->I_O());
+                track->default_track = uint32(f_default);
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + Default flag: %u\n",
+                       track->default_track);
+
+              } else if ((EbmlId(*l3) !=
+                          KaxTrackFlagLacing::ClassInfos.GlobalId) &&
+                         (EbmlId(*l3) !=
+                          KaxTrackMinCache::ClassInfos.GlobalId) &&
+                         (EbmlId(*l3) != 
+                          KaxTrackMaxCache::ClassInfos.GlobalId))
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + unknown element@3: "
+                       "%s\n", typeid(*l3).name());
+
+              if (upper_lvl_el > 0) {	// we're coming from l4
+                upper_lvl_el--;
+                delete l3;
+                l3 = l4;
+                if (upper_lvl_el > 0)
+                  break;
+              } else {
+                l3->SkipData(static_cast<EbmlStream &>(*es),
+                             l3->Generic().Context);
+                delete l3;
+                l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
+                                         0xFFFFFFFFL, true, 1);
+              }
+            } // while (l3 != NULL)
+
+          } else
+            mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + unknown element@2: %s\n",
+                   typeid(*l2).name());
+          if (upper_lvl_el > 0) {	// we're coming from l3
+            upper_lvl_el--;
+            delete l2;
+            l2 = l3;
+            if (upper_lvl_el > 0)
+              break;
+          } else {
+            l2->SkipData(static_cast<EbmlStream &>(*es),
+                         l2->Generic().Context);
+            delete l2;
+            l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el,
+                                     0xFFFFFFFFL, true, 1);
+          }
+        } // while (l2 != NULL)
+
+      } else if (EbmlId(*l1) == KaxSeekHead::ClassInfos.GlobalId) {
+        mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ found seek head\n");
+
+        l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el,
+                                 0xFFFFFFFFL, true, 1);
+        while (l2 != NULL) {
+          if (upper_lvl_el != 0)
+            break;
+
+          if (EbmlId(*l2) == KaxSeek::ClassInfos.GlobalId) {
+            mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + seek entry\n");
+
+            seek_pos = 0;
+            seek_element_is_cue = 0;
+
+            l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
+                                     0xFFFFFFFFL, true, 1);
+            while (l3 != NULL) {
+              if (upper_lvl_el != 0)
+                break;
+
+              if (EbmlId(*l3) == KaxSeekID::ClassInfos.GlobalId) {
+                binary *b;
+                int s;
+                KaxSeekID &seek_id = static_cast<KaxSeekID &>(*l3);
+                seek_id.ReadData(es->I_O());
+                b = seek_id.GetBuffer();
+                s = seek_id.GetSize();
+                EbmlId id(b, s);
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |   + seek ID: ");
+                for (i = 0; i < s; i++)
+                  mp_msg(MSGT_DEMUX, MSGL_V, "0x%02x ",
+                         ((unsigned char *)b)[i]);
+                mp_msg(MSGT_DEMUX, MSGL_V, "(%s)\n",
+                        (id == KaxInfo::ClassInfos.GlobalId) ?
+                        "KaxInfo" :
+                        (id == KaxCluster::ClassInfos.GlobalId) ?
+                        "KaxCluster" :
+                        (id == KaxTracks::ClassInfos.GlobalId) ?
+                        "KaxTracks" :
+                        (id == KaxCues::ClassInfos.GlobalId) ?
+                        "KaxCues" :
+                        (id == KaxAttachements::ClassInfos.GlobalId) ?
+                        "KaxAttachements" :
+                        (id == KaxChapters::ClassInfos.GlobalId) ?
+                        "KaxChapters" :
+                        "unknown");
+
+                if (id == KaxCues::ClassInfos.GlobalId)
+                  seek_element_is_cue = 1;
+
+              } else if (EbmlId(*l3) == KaxSeekPosition::ClassInfos.GlobalId) {
+                KaxSeekPosition &kax_seek_pos =
+                  static_cast<KaxSeekPosition &>(*l3);
+                kax_seek_pos.ReadData(es->I_O());
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |   + seek position: %llu\n",
+                        uint64(kax_seek_pos));
+
+                seek_pos = uint64(kax_seek_pos);
+
+              } else
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + unknown element, "
+                       "level 3: %s\n", typeid(*l3).name());
+
+              l3->SkipData(static_cast<EbmlStream &>(*es),
+                           l3->Generic().Context);
+              delete l3;
+              l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
+                                       0xFFFFFFFFL, true, 1);
+            } // while (l3 != NULL)
+
+            if (!mkv_d->cues_found && (seek_pos > 0) &&
+                seek_element_is_cue && (s->end_pos != 0)) {
+              current_pos = io.getFilePointer();
+              io.setFilePointer(mkv_d->segment->GetGlobalPosition(seek_pos));
+              mkv_d->cues_found = parse_cues(mkv_d);
+              if (s->eof)
+                stream_reset(s);
+              io.setFilePointer(current_pos);
+            }
+
+          } else
+            mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |  + unknown element, level 2: "
+                   "%s\n", typeid(*l2).name());
+
+          if (upper_lvl_el > 0) {		// we're coming from l3
+            upper_lvl_el--;
+            delete l2;
+            l2 = l3;
+            if (upper_lvl_el > 0)
+              break;
+
+          } else {
+            l2->SkipData(static_cast<EbmlStream &>(*es),
+                         l2->Generic().Context);
+            delete l2;
+            l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el,
+                                     0xFFFFFFFFL, true, 1);
+          }
+        } // while (l2 != NULL)
+
+      } else if ((EbmlId(*l1) == KaxCues::ClassInfos.GlobalId) &&
+                 !mkv_d->cues_found) {
+        // If the cues are up front then by all means read them now!
+        current_pos = io.getFilePointer();
+        io.setFilePointer(l1->GetElementPosition());
+        mkv_d->cues_found = parse_cues(mkv_d);
+        stream_reset(s);
+        io.setFilePointer(current_pos);
+
+      } else if (EbmlId(*l1) == KaxCluster::ClassInfos.GlobalId) {
+        mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ found cluster, headers are "
+               "parsed completely :)\n");
+        add_cluster_position(mkv_d, l1->GetElementPosition());
+        mkv_d->saved_l1 = l1;
+        exit_loop = 1;
+
+      } else
+        mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ unknown element@1: %s\n",
+               typeid(*l1).name());
+      
+      if (exit_loop)      // we've found the first cluster, so get out
+        break;
+
+      if (upper_lvl_el > 0) {		// we're coming from l2
+        upper_lvl_el--;
+        delete l1;
+        l1 = l2;
+        if (upper_lvl_el > 0)
+          break;
+      } else {
+        l1->SkipData(static_cast<EbmlStream &>(*es), l1->Generic().Context);
+        delete l1;
+        l1 = es->FindNextElement(l0->Generic().Context, upper_lvl_el,
+                                 0xFFFFFFFFL, true, 1);
+      }
+    } // while (l1 != NULL)
+
+    if (!exit_loop) {
+      free_mkv_demuxer(mkv_d);
+      return 0;
+    }
+
+  } catch (exception &ex) {
+    mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] caught exception\n");
+    return 0;
+  }
+
+  if (mkv_d->tc_scale == 0)
+    mkv_d->tc_scale = MKVD_TIMECODESCALE;
+
+  if (!check_track_information(mkv_d)) {
+    free_mkv_demuxer(mkv_d);
+    return 0;
+  }
+
+  track = NULL;
+  if (demuxer->video->id == -1) { // Automatically select a video track.
+    // Search for a video track that has the 'default' flag set.
+    for (i = 0; i < mkv_d->num_tracks; i++)
+      if ((mkv_d->tracks[i]->type == 'v') && mkv_d->tracks[i]->ok &&
+          mkv_d->tracks[i]->default_track) {
+        track = mkv_d->tracks[i];
+        break;
+      }
+
+    if (track == NULL)
+      // No track has the 'default' flag set - let's take the first video
+      // track.
+      for (i = 0; i < mkv_d->num_tracks; i++)
+        if ((mkv_d->tracks[i]->type == 'v') && mkv_d->tracks[i]->ok) {
+          track = mkv_d->tracks[i];
+          break;
+        }
+  } else if (demuxer->video->id != -2) // -2 = no video at all
+    track = find_track_by_num(mkv_d, demuxer->video->id, NULL);
+
+  if (track) {
+    if (track->ms_compat) {         // MS compatibility mode
+      mp_msg(MSGT_DEMUX, MSGL_INFO, "[mkv] Will play video track %u\n",
+             track->tnum);
+      sh_v = new_sh_video(demuxer, track->tnum);
+      sh_v->bih = (BITMAPINFOHEADER *)calloc(1, sizeof(BITMAPINFOHEADER));
+      if (sh_v->bih == NULL) {
+        free_mkv_demuxer(mkv_d);
+        return 0;
+      }
+
+      memcpy(sh_v->bih, track->private_data, sizeof(BITMAPINFOHEADER));
+      sh_v->format = sh_v->bih->biCompression;
+      sh_v->fps = track->v_frate;
+      sh_v->frametime = 1 / track->v_frate;
+      sh_v->disp_w = sh_v->bih->biWidth;
+      sh_v->disp_h = sh_v->bih->biHeight;
+
+      demuxer->video->id = track->tnum;
+      demuxer->video->sh = sh_v;
+      sh_v->ds = demuxer->video;
+
+      mkv_d->video = track;
+    } else {
+      mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Native CodecIDs not supported at "
+             "the moment (track %u).\n", track->tnum);
+      demuxer->video->id = -2;
+    }
+  } else {
+    mp_msg(MSGT_DEMUX, MSGL_INFO, "[mkv] No video track found/wanted.\n");
+    demuxer->video->id = -2;
+  }
+
+  track = NULL;
+  if (demuxer->audio->id == -1) { // Automatically select an audio track.
+    // Search for an audio track that has the 'default' flag set.
+    for (i = 0; i < mkv_d->num_tracks; i++)
+      if ((mkv_d->tracks[i]->type == 'a') && mkv_d->tracks[i]->ok &&
+          mkv_d->tracks[i]->default_track) {
+        track = mkv_d->tracks[i];
+        break;
+      }
+
+    if (track == NULL)
+      // No track has the 'default' flag set - let's take the first audio
+      // track.
+      for (i = 0; i < mkv_d->num_tracks; i++)
+        if ((mkv_d->tracks[i]->type == 'a') && mkv_d->tracks[i]->ok) {
+          track = mkv_d->tracks[i];
+          break;
+        }
+  } else if (demuxer->audio->id != -2) // -2 = no audio at all
+    track = find_track_by_num(mkv_d, demuxer->audio->id, NULL);
+
+  if (track) {
+    mp_msg(MSGT_DEMUX, MSGL_INFO, "[mkv] Will play audio track %u\n",
+           track->tnum);
+    sh_a = new_sh_audio(demuxer, track->tnum);
+
+    demuxer->audio->id = track->tnum;
+    demuxer->audio->sh = sh_a;
+    sh_a->ds = demuxer->audio;
+
+    mkv_d->audio = track;
+
+    if (track->ms_compat) {
+      sh_a->wf = (WAVEFORMATEX *)calloc(1, track->private_size);
+      if (sh_a->wf == NULL) {
+        free_mkv_demuxer(mkv_d);
+        return 0;
+      }
+      memcpy(sh_a->wf, track->private_data, track->private_size);
+    } else {
+      sh_a->wf = (WAVEFORMATEX *)calloc(1, sizeof(WAVEFORMATEX));
+      if (sh_a->wf == NULL) {
+        free_mkv_demuxer(mkv_d);
+        return 0;
+      }
+    }
+    sh_a->format = sh_a->wf->wFormatTag = track->a_formattag;
+    sh_a->channels = sh_a->wf->nChannels = track->a_channels;
+    sh_a->samplerate = sh_a->wf->nSamplesPerSec = (uint32_t)track->a_sfreq;
+    if (!strcmp(track->codec_id, MKV_A_MP3)) {
+      sh_a->wf->nAvgBytesPerSec = 16000;
+      sh_a->wf->nBlockAlign = 1152;
+      sh_a->wf->wBitsPerSample = 0;
+      sh_a->samplesize = 0;
+    } else if (!strcmp(track->codec_id, MKV_A_AC3)) {
+      sh_a->wf->nAvgBytesPerSec = 16000;
+      sh_a->wf->nBlockAlign = 1536;
+      sh_a->wf->wBitsPerSample = 0;
+      sh_a->samplesize = 0;
+    } else if (!strcmp(track->codec_id, MKV_A_PCM)) {
+      sh_a->wf->nAvgBytesPerSec = sh_a->channels * sh_a->samplerate * 2;
+      sh_a->wf->nBlockAlign = sh_a->wf->nAvgBytesPerSec;
+      sh_a->wf->wBitsPerSample = track->a_bps;
+      sh_a->samplesize = track->a_bps / 8;
+    } else if (!strcmp(track->codec_id, MKV_A_VORBIS)) {
+      for (i = 0; i < 3; i++) {
+        dp = new_demux_packet(track->header_sizes[i]);
+        memcpy(dp->buffer, track->headers[i], track->header_sizes[i]);
+        dp->pts = 0;
+        dp->flags = 0;
+        ds_add_packet(demuxer->audio, dp);
+      }
+    }
+  } else {
+    mp_msg(MSGT_DEMUX, MSGL_INFO, "[mkv] No audio track found/wanted.\n");
+    demuxer->audio->id = -2;
+  }
+
+  // DO NOT automatically select a subtitle track and behave like DVD
+  // playback: only show subtitles if the user explicitely wants them.
+  if (demuxer->sub->id >= 0) {
+    track = find_track_by_num(mkv_d, demuxer->sub->id, NULL);
+    if (track) {
+      if (strcmp(track->codec_id, MKV_S_TEXTASCII) &&
+          strcmp(track->codec_id, MKV_S_TEXTUTF8))
+        mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Subtitle type '%s' is not "
+               "supported. Track will not be displayed.\n", track->codec_id);
+      else {
+        mp_msg(MSGT_DEMUX, MSGL_INFO, "[mkv] Will display subtitle track %u\n",
+               track->tnum);
+        mkv_d->subs_track = track;
+        if (!mkv_d->subs.text[0]) {
+          for (i = 0; i < SUB_MAX_TEXT; i++)
+            mkv_d->subs.text[i] = (char *)malloc(256);
+
+          if (!strcmp(track->codec_id, MKV_S_TEXTUTF8))
+            sub_utf8 = 1;       // Force UTF-8 conversion.
+        } else
+          mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] File does not contain a "
+                 "subtitle track with the id %u.\n", demuxer->sub->id);
+      }
+    }
+  }
+
+  if (s->end_pos == 0)
+    demuxer->seekable = 0;
+  else {
+    demuxer->movi_start = s->start_pos;
+    demuxer->movi_end = s->end_pos;
+    demuxer->seekable = 1;
+  }
+
+  demuxer->priv = mkv_d;
+
+  return 1;
+}
+
+extern "C" int demux_mkv_fill_buffer(demuxer_t *d) {
+  demux_packet_t *dp;
+  demux_stream_t *ds;
+  mkv_demuxer_t *mkv_d;
+  int upper_lvl_el, exit_loop, found_data, i, delete_element, elements_found;
+  // Elements for different levels
+  EbmlElement *l0 = NULL, *l1 = NULL, *l2 = NULL, *l3 = NULL, *l4 = NULL;
+  EbmlStream *es;
+  KaxBlock *block;
+  int64_t block_duration, block_ref1, block_ref2;
+
+  mkv_d = (mkv_demuxer_t *)d->priv;
+  es = mkv_d->es;
+  l0 = mkv_d->segment;
+
+  // End of stream
+  if (mkv_d->saved_l1 == NULL)
+    return 0;
+
+  exit_loop = 0;
+  upper_lvl_el = 0;
+  l1 = mkv_d->saved_l1;
+  mkv_d->saved_l1 = NULL;
+  found_data = 0;
+  try {
+    // The idea is not to handle a complete KaxCluster with each call to
+    // demux_mkv_fill_buffer because those might be rather big.
+    while (l1 != NULL)  {
+      if ((upper_lvl_el != 0) || exit_loop)
+        break;
+
+      if (EbmlId(*l1) == KaxCluster::ClassInfos.GlobalId) {
+        mkv_d->cluster = (KaxCluster *)l1;
+        if (found_data) {
+          mkv_d->saved_l1 = l1;
+          break;
+        }
+
+        if (mkv_d->saved_l2 != NULL) {
+          l2 = mkv_d->saved_l2;
+          mkv_d->saved_l2 = NULL;
+        } else
+          l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el,
+                                   0xFFFFFFFFL, true, 1);
+        while (l2 != NULL) {
+          if (upper_lvl_el != 0)
+            break;
+
+          // Handle at at least one data packets in one call to
+          // demux_mkv_fill_buffer - but abort if we have found that.
+          if (found_data >= 1) {
+            mkv_d->saved_l2 = l2;
+            mkv_d->saved_l1 = l1;
+            exit_loop = 1;
+            break;
+          }
+
+          if (EbmlId(*l2) == KaxClusterTimecode::ClassInfos.GlobalId) {
+            KaxClusterTimecode &ctc = *static_cast<KaxClusterTimecode *>(l2);
+            ctc.ReadData(es->I_O());
+            mkv_d->cluster_tc = uint64(ctc);
+            mkv_d->cluster->InitTimecode(mkv_d->cluster_tc);
+
+          } else if (EbmlId(*l2) == KaxBlockGroup::ClassInfos.GlobalId) {
+
+            block = NULL;
+            block_duration = -1;
+            block_ref1 = 0;
+            block_ref2 = 0;
+            elements_found = 0;
+
+            l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
+                                     0xFFFFFFFFL, true, 1);
+            while (l3 != NULL) {
+              delete_element = 1;
+              if (upper_lvl_el > 0)
+                break;
+
+              if (EbmlId(*l3) == KaxBlock::ClassInfos.GlobalId) {
+                block = static_cast<KaxBlock *>(l3);
+                block->SetParent(*mkv_d->cluster);
+                block->ReadData(es->I_O());
+                delete_element = 0;
+                elements_found |= 1;
+
+              } else if (EbmlId(*l3) ==
+                         KaxBlockDuration::ClassInfos.GlobalId) {
+                KaxBlockDuration &duration =
+                  *static_cast<KaxBlockDuration *>(l3);
+                duration.ReadData(es->I_O());
+                block_duration = (int64_t)uint64(duration);
+                elements_found |= 2;
+
+              } else if (EbmlId(*l3) ==
+                         KaxReferenceBlock::ClassInfos.GlobalId) {
+                KaxReferenceBlock &ref =
+                  *static_cast<KaxReferenceBlock *>(l3);
+                ref.ReadData(es->I_O());
+                if (block_ref1 == 0) {
+                  block_ref1 = int64(ref);
+                  elements_found |= 4;
+                } else {
+                  block_ref2 = int64(ref);
+                  elements_found |= 8;
+                }
+
+              }
+
+              if (upper_lvl_el > 0) {		// we're coming from l4
+                upper_lvl_el--;
+                delete l3;
+                l3 = l4;
+                if (upper_lvl_el > 0)
+                  break;
+              } else {
+                l3->SkipData(static_cast<EbmlStream &>(*es),
+                             l3->Generic().Context);
+                if (delete_element)
+                  delete l3;
+                l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
+                                         0xFFFFFFFFL, true, 1);
+              }
+            } // while (l3 != NULL)
+
+            if (block != NULL) {
+              // Clear the subtitles if they're obsolete now.
+              if ((mkv_d->clear_subs_at > 0) &&
+                  (mkv_d->clear_subs_at <=
+                   (block->GlobalTimecode() / 1000000))) {
+                mkv_d->subs.lines = 0;
+                vo_sub = &mkv_d->subs;
+                vo_osd_changed(OSDTYPE_SUBTITLE);
+                mkv_d->clear_subs_at = 0;
+              }
+
+              ds = NULL;
+              if ((mkv_d->video != NULL) &&
+                  (mkv_d->video->tnum == block->TrackNum()))
+                ds = d->video;
+              else if ((mkv_d->audio != NULL) && 
+                       (mkv_d->audio->tnum == block->TrackNum()))
+                  ds = d->audio;
+
+              if (!mkv_d->skip_to_keyframe ||     // Not skipping is ok.
+                  (((elements_found & 4) == 0) && // It's a key frame.
+                   (ds != NULL) &&                // Corresponding track found
+                   (ds == d->video))) {           // track is our video track
+                if (ds != NULL) {
+                  for (i = 0; i < (int)block->NumberFrames(); i++) {
+                    DataBuffer &data = block->GetBuffer(i);
+                    dp = new_demux_packet(data.Size());
+                    memcpy(dp->buffer, data.Buffer(), data.Size());
+                    dp->pts = mkv_d->last_pts;
+                    dp->flags = 0;
+                    ds_add_packet(ds, dp);
+                    found_data++;
+                    mkv_d->skip_to_keyframe = 0;
+                  }
+                } else if ((mkv_d->subs_track != NULL) &&
+                           (mkv_d->subs_track->tnum == block->TrackNum()))
+                  handle_subtitles(d, block, block_duration);
+
+                d->filepos = mkv_d->in->getFilePointer();
+                mkv_d->last_pts = (float)block->GlobalTimecode() /
+                  1000000000.0;
+                mkv_d->last_filepos = d->filepos;
+              }
+
+              delete block;
+            } // block != NULL
+
+          }
+
+          if (upper_lvl_el > 0) {		// we're coming from l3
+            upper_lvl_el--;
+            delete l2;
+            l2 = l3;
+            if (upper_lvl_el > 0)
+              break;
+          } else {
+            l2->SkipData(static_cast<EbmlStream &>(*es),
+                         l2->Generic().Context);
+            delete l2;
+            l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el,
+                                     0xFFFFFFFFL, true, 1);
+          }
+        } // while (l2 != NULL)
+      } else if (EbmlId(*l1) == KaxCues::ClassInfos.GlobalId)
+        return 0;
+      else
+         mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Unknown element@1: %s (stream "
+                "position %llu)\n", typeid(*l1).name(),
+                l1->GetElementPosition());
+
+      if (exit_loop)
+        break;
+
+      if (upper_lvl_el > 0) {		// we're coming from l2
+        upper_lvl_el--;
+        delete l1;
+        l1 = l2;
+        if (upper_lvl_el > 0)
+          break;
+      } else {
+        l1->SkipData(static_cast<EbmlStream &>(*es), l1->Generic().Context);
+        delete l1;
+        l1 = es->FindNextElement(l0->Generic().Context, upper_lvl_el,
+                                 0xFFFFFFFFL, true, 1);
+        if (EbmlId(*l1) == KaxCluster::ClassInfos.GlobalId)
+          add_cluster_position(mkv_d, l1->GetElementPosition());
+      }
+    } // while (l1 != NULL)
+  } catch (exception ex) {
+    mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] exception caught\n");
+    return 0;
+  }
+
+  if (found_data)
+    return 1;
+
+  return 0;
+}
+
+extern "C" void resync_audio_stream(sh_audio_t *sh_audio);
+
+extern "C" void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs,
+                               int flags) {
+  int i, k, upper_lvl_el;
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *)demuxer->priv;
+  int64_t target_timecode, target_filepos = 0, min_diff, diff, current_pos;
+  int64_t cluster_pos;
+  mkv_track_index_t *index;
+  mkv_index_entry_t *entry;
+  EbmlElement *l1;
+
+  mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] SEEK, relss: %.3f, flags: %d\n",
+         rel_seek_secs, flags);
+
+  if (!mkv_d->cues_found && !mkv_d->cues_searched) {
+    // We've not found an index so far. So let's skip over all level 1
+    // elements until we either hit another segment, the end of the file
+    // or - suprise - some cues.
+    current_pos = mkv_d->in->getFilePointer();
+
+    // Skip the data but do not delete the element! This is our current
+    // cluster, and we need it later on in demux_mkv_fill_buffer.
+    l1 = mkv_d->saved_l1;
+    l1->SkipData(static_cast<EbmlStream &>(*mkv_d->es), l1->Generic().Context);
+    l1 = mkv_d->es->FindNextElement(mkv_d->segment->Generic().Context,
+                                    upper_lvl_el, 0xFFFFFFFFL, true, 1);
+    while (l1 != NULL) {
+      if (upper_lvl_el)
+        break;
+
+      if (EbmlId(*l1) == KaxCues::ClassInfos.GlobalId) {
+        mkv_d->in->setFilePointer(l1->GetElementPosition());
+        delete l1;
+        mkv_d->cues_found = parse_cues(mkv_d);
+        break;
+      } else {
+        if (EbmlId(*l1) == KaxCluster::ClassInfos.GlobalId)
+          add_cluster_position(mkv_d, l1->GetElementPosition());
+        l1->SkipData(static_cast<EbmlStream &>(*mkv_d->es),
+                     l1->Generic().Context);
+        delete l1;
+        l1 = mkv_d->es->FindNextElement(mkv_d->segment->Generic().Context,
+                                        upper_lvl_el, 0xFFFFFFFFL, true, 1);
+      }
+    }
+
+    if (demuxer->stream->eof)
+      stream_reset(demuxer->stream);
+    mkv_d->in->setFilePointer(current_pos);
+
+    mkv_d->cues_searched = 1;
+  }
+
+  if (!(flags & 2)) {           // Time in secs
+    if (flags & 1)              // Absolute seek
+      target_timecode = 0;
+    else                        // Relative seek
+      target_timecode = (int64_t)(mkv_d->last_pts * 1000.0);
+    target_timecode += (int64_t)(rel_seek_secs * 1000.0);
+    if (target_timecode < 0)
+      target_timecode = 0;
+
+    min_diff = 0xFFFFFFFL;
+
+    // Let's find the entry in the index with the smallest difference
+    // to the wanted timecode.
+    entry = NULL;
+    for (i = 0; i < mkv_d->num_indexes; i++)
+      if (mkv_d->index[i].tnum == mkv_d->video->tnum) {
+        index = &mkv_d->index[i];
+        for (k = 0; k < index->num_entries; k++) {
+          if (!index->entries[k].is_key)
+            continue;
+          diff = target_timecode - (int64_t)index->entries[k].timecode;
+          if (diff < 0)
+            diff *= -1;
+          if (diff < min_diff) {
+            min_diff = diff;
+            entry = & index->entries[k];
+          }
+        }
+        break;
+      }
+
+    if (mkv_d->saved_l1 != NULL)
+      delete mkv_d->saved_l1;
+
+    if (mkv_d->saved_l2 != NULL) {
+      delete mkv_d->saved_l2;
+      mkv_d->saved_l2 = NULL;
+    }
+
+    if (entry != NULL) {        // We've found an entry.
+      mkv_d->in->setFilePointer(entry->filepos);
+      upper_lvl_el = 0;
+      mkv_d->saved_l1 =
+        mkv_d->es->FindNextElement(mkv_d->segment->Generic().Context,
+                                   upper_lvl_el, 0xFFFFFFFFL, true, 1);
+    } else {                    // We've not found an entry --> no index?
+      target_filepos = (int64_t)(target_timecode * mkv_d->last_filepos /
+        (mkv_d->last_pts * 1000.0));
+      mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] No index entry found. Calculated "
+             "filepos %lld.\n", target_filepos);
+      // Let's find the nearest cluster so that libebml does not have to
+      // do so much work.
+      cluster_pos = 0;
+      min_diff = 0x0FFFFFFFL;
+      for (i = 0; i < mkv_d->num_cluster_pos; i++) {
+        diff = mkv_d->cluster_positions[i] - target_filepos;
+        if (rel_seek_secs < 0) {
+          if ((diff > 0) && (diff < min_diff)) {
+            cluster_pos = mkv_d->cluster_positions[i];
+            min_diff = diff;
+          }
+        } else if (abs(diff) < min_diff) {
+          cluster_pos = mkv_d->cluster_positions[i];
+          min_diff = abs(diff);
+        }
+      }
+      if (min_diff != 0x0FFFFFFFL)
+        target_filepos = cluster_pos;
+      if (target_filepos >= demuxer->movi_end)
+        return;
+      mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] New target_filepos because of cluster:"
+             " %lld.\n", target_filepos);
+      mkv_d->in->setFilePointer(target_filepos);
+      upper_lvl_el = 0;
+      mkv_d->saved_l1 =
+        mkv_d->es->FindNextElement(mkv_d->segment->Generic().Context,
+                                   upper_lvl_el, 0xFFFFFFFFL, false, 1);
+      mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] seek result: target_timecode %lld, "
+             "did not find an entry. Calculated target_filspos: %lld\n",
+             target_timecode, target_filepos);
+      if ((mkv_d->saved_l1 != NULL) &&
+          (EbmlId(*mkv_d->saved_l1) == KaxSegment::ClassInfos.GlobalId))
+        mkv_d->saved_l1 =
+          mkv_d->es->FindNextElement(mkv_d->saved_l1->Generic().Context,
+                                     upper_lvl_el, 0xFFFFFFFFL, true, 1);
+      mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] seek found %p (%s).\n",
+             mkv_d->saved_l1, mkv_d->saved_l1 == NULL ? "null" :
+             typeid(*mkv_d->saved_l1).name());
+    }
+
+    mkv_d->skip_to_keyframe = 1;
+
+    demux_mkv_fill_buffer(demuxer);
+
+    mkv_d->subs.lines = 0;
+    vo_sub = &mkv_d->subs;
+    vo_osd_changed(OSDTYPE_SUBTITLE);
+    mkv_d->clear_subs_at = 0;
+
+    if(demuxer->audio->sh != NULL)
+      resync_audio_stream((sh_audio_t *)demuxer->audio->sh); 
+
+  } else
+    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] seek unsupported flags\n");
+
+}
+
+extern "C" void demux_close_mkv(demuxer_t *demuxer) {
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *)demuxer->priv;
+
+  free_mkv_demuxer(mkv_d);
+
+#ifdef USE_ICONV
+  subcp_close();
+#endif
+}
+
+extern "C" int demux_mkv_control(demuxer_t *demuxer, int cmd, void *arg) {
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *)demuxer->priv;
+  
+  switch (cmd) {
+    case DEMUXER_CTRL_GET_TIME_LENGTH:
+      if (mkv_d->duration == -1.0)
+        return DEMUXER_CTRL_DONTKNOW;
+
+      *((unsigned long *)arg) = (unsigned long)mkv_d->duration;
+      return DEMUXER_CTRL_OK;
+
+    case DEMUXER_CTRL_GET_PERCENT_POS:
+      if (mkv_d->duration == -1.0) {
+        if (demuxer->movi_start == demuxer->movi_end)
+          return DEMUXER_CTRL_DONTKNOW;
+
+        *((int *)arg) =
+          (int)((demuxer->filepos - demuxer->movi_start) /
+                ((demuxer->movi_end - demuxer->movi_start) / 100));
+        return DEMUXER_CTRL_OK;
+      }
+
+      *((int *)arg) = (int)(100 * mkv_d->last_pts / mkv_d->duration);
+      return DEMUXER_CTRL_OK; 
+
+    default:
+      return DEMUXER_CTRL_NOTIMPL;
+  }
+}
+
+#endif /* HAVE_MATROSKA */
--- a/libmpdemux/demuxer.c	Tue Apr 29 19:41:23 2003 +0000
+++ b/libmpdemux/demuxer.c	Wed Apr 30 11:39:32 2003 +0000
@@ -135,6 +135,7 @@
 extern void demux_close_xmms(demuxer_t* demuxer);
 extern void demux_close_gif(demuxer_t* demuxer);
 extern void demux_close_ts(demuxer_t* demuxer);
+extern void demux_close_mkv(demuxer_t* demuxer);
 
 #ifdef USE_TV
 #include "tv.h"
@@ -185,6 +186,10 @@
     case DEMUXER_TYPE_OGG:
       demux_close_ogg(demuxer); break;
 #endif
+#ifdef HAVE_MATROSKA
+    case DEMUXER_TYPE_MATROSKA:
+      demux_close_mkv(demuxer); break;
+#endif
 #ifdef STREAMING_LIVE_DOT_COM
     case DEMUXER_TYPE_RTP:
       demux_close_rtp(demuxer); break;
@@ -292,6 +297,7 @@
 extern int demux_rawaudio_fill_buffer(demuxer_t* demuxer, demux_stream_t *ds);
 extern int demux_rawvideo_fill_buffer(demuxer_t* demuxer, demux_stream_t *ds);
 extern int demux_smjpeg_fill_buffer(demuxer_t* demux);
+extern int demux_mkv_fill_buffer(demuxer_t *d);
 
 int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds){
   // Note: parameter 'ds' can be NULL!
@@ -330,6 +336,9 @@
 #ifdef HAVE_OGGVORBIS
     case DEMUXER_TYPE_OGG: return demux_ogg_fill_buffer(demux);
 #endif
+#ifdef HAVE_MATROSKA
+    case DEMUXER_TYPE_MATROSKA: return demux_mkv_fill_buffer(demux);
+#endif
     case DEMUXER_TYPE_RAWAUDIO: return demux_rawaudio_fill_buffer(demux,ds);
     case DEMUXER_TYPE_RAWVIDEO: return demux_rawvideo_fill_buffer(demux,ds);
 #ifdef STREAMING_LIVE_DOT_COM
@@ -574,6 +583,7 @@
 extern int gif_check_file(demuxer_t *demuxer);
 extern int demux_open_gif(demuxer_t* demuxer);
 extern int ts_check_file(demuxer_t * demuxer);
+extern int demux_open_mkv(demuxer_t *demuxer);
 
 extern demuxer_t* init_avi_with_ogg(demuxer_t* demuxer);
 
@@ -700,6 +710,19 @@
       demuxer = NULL;
   }
 }
+#ifdef HAVE_MATROSKA
+//=============== Try to open as Matroska file: =================
+if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_MATROSKA){
+  demuxer=new_demuxer(stream,DEMUXER_TYPE_MATROSKA,audio_id,video_id,dvdsub_id);
+  if(demux_mkv_open(demuxer)){
+      mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_Detected_XXX_FileFormat,"Matroska");
+      file_format=DEMUXER_TYPE_MATROSKA;
+  } else {
+      free_demuxer(demuxer);
+      demuxer = NULL;
+  }
+}
+#endif
 
 //=============== Try based on filename EXTENSION: =================
 // Ok. We're over the stable detectable fileformats, the next ones are a bit
@@ -1277,6 +1300,7 @@
 extern void demux_rawaudio_seek(demuxer_t *demuxer,float rel_seek_secs,int flags);
 extern void demux_rawvideo_seek(demuxer_t *demuxer,float rel_seek_secs,int flags);
 extern void demux_xmms_seek(demuxer_t *demuxer,float rel_seek_secs,int flags);
+extern void demux_mkv_seek(demuxer_t *demuxer,float rel_seek_secs,int flags);
 
 int demux_seek(demuxer_t *demuxer,float rel_seek_secs,int flags){
     demux_stream_t *d_audio=demuxer->audio;
@@ -1366,6 +1390,10 @@
  case DEMUXER_TYPE_XMMS:
       demux_xmms_seek(demuxer,rel_seek_secs,flags); break;
 #endif
+#ifdef HAVE_MATROSKA
+ case DEMUXER_TYPE_MATROSKA:
+      demux_mkv_seek(demuxer,rel_seek_secs,flags);  break;
+#endif
  case DEMUXER_TYPE_MPEG_TS:
       demux_seek_ts(demuxer,rel_seek_secs,flags); break;
 
@@ -1428,6 +1456,7 @@
 extern int demux_asf_control(demuxer_t *demuxer, int cmd, void *arg);
 extern int demux_avi_control(demuxer_t *demuxer, int cmd, void *arg);
 extern int demux_xmms_control(demuxer_t *demuxer, int cmd, void *arg);
+extern int demux_mkv_control(demuxer_t *demuxer, int cmd, void *arg);
 
 int demux_control(demuxer_t *demuxer, int cmd, void *arg) {
     switch(demuxer->type) {
@@ -1445,6 +1474,10 @@
 	case DEMUXER_TYPE_XMMS:
 	    return demux_xmms_control(demuxer,cmd,arg);
 #endif
+#ifdef HAVE_MATROSKA
+        case DEMUXER_TYPE_MATROSKA:
+	    return demux_mkv_control(demuxer,cmd,arg);
+#endif
 	default:
 	    return DEMUXER_CTRL_NOTIMPL;
     }
--- a/libmpdemux/demuxer.h	Tue Apr 29 19:41:23 2003 +0000
+++ b/libmpdemux/demuxer.h	Wed Apr 30 11:39:32 2003 +0000
@@ -39,11 +39,12 @@
 #define DEMUXER_TYPE_GIF 28
 #define DEMUXER_TYPE_MPEG_TS 29
 #define DEMUXER_TYPE_H264_ES 30
+#define DEMUXER_TYPE_MATROSKA 31
 
 // This should always match the higest demuxer type number.
 // Unless you want to disallow users to force the demuxer to some types
 #define DEMUXER_TYPE_MIN 0
-#define DEMUXER_TYPE_MAX 30
+#define DEMUXER_TYPE_MAX 31
 
 #define DEMUXER_TYPE_DEMUXERS (1<<16)
 // A virtual demuxer type for the network code
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/matroska.h	Wed Apr 30 11:39:32 2003 +0000
@@ -0,0 +1,21 @@
+/*
+ * CodecID definitions for Matroska files
+ *
+ * see http://cvs.corecodec.org/cgi-bin/cvsweb.cgi/~checkout~/matroska/doc/website/specs/codex.html?rev=HEAD&content-type=text/html
+ */
+
+#ifndef __MATROSKA_H
+#define __MATROSKA_H
+
+#define MKV_A_MP3        "A_MPEG/L3"
+#define MKV_A_AC3        "A_AC3"
+#define MKV_A_PCM        "A_PCM/INT/LIT"
+#define MKV_A_VORBIS     "A_VORBIS"
+#define MKV_A_ACM        "A_MS/ACM"
+
+#define MKV_V_MSCOMP     "V_MS/VFW/FOURCC"
+
+#define MKV_S_TEXTASCII  "S_TEXT/ASCII"
+#define MKV_S_TEXTUTF8   "S_TEXT/UTF8"
+
+#endif /* __MATROSKA_H */