changeset 11807:9a81d7b4c0b6

Added the new C based Matroska demuxer by Aurelien Jacobs.
author mosu
date Mon, 19 Jan 2004 19:16:10 +0000
parents 4f14825fd446
children c4bd86214a20
files configure libmpdemux/Makefile libmpdemux/demux_mkv.c libmpdemux/ebml.c libmpdemux/ebml.h libmpdemux/matroska.h libmpdemux/stream.h
diffstat 7 files changed, 3462 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/configure	Mon Jan 19 18:37:34 2004 +0000
+++ b/configure	Mon Jan 19 19:16:10 2004 +0000
@@ -203,7 +203,8 @@
   --enable-vorbis        build with OggVorbis support [autodetect]
   --enable-tremor        build with integer-only OggVorbis support [disabled]
   --enable-theora        build with OggTheora support [autodetect]
-  --enable-matroska      build with Matroska support [autodetect]
+  --enable-external-matroska  build with external Matroska support [autodetect]
+  --disable-internal-matroska disable internal Matroska support [enabled]
   --enable-external-faad build with external FAAD2 (AAC) support [autodetect]
   --disable-internal-faad disable internal FAAD2 (AAC) support [autodetect]
   --disable-libdv        disable libdv 0.9.5 en/decoding support [autodetect]
@@ -1144,7 +1145,8 @@
 _mad=auto
 _vorbis=auto
 _theora=auto
-_matroska=auto
+_matroska_internal=yes
+_matroska_external=auto
 _tremor=no
 _faad_internal=auto
 _faad_external=auto
@@ -1310,8 +1312,10 @@
   --disable-tremor)	_tremor=no	;;
   --enable-theora)	_theora=yes	;;
   --disable-theora)	_theora=no	;;
-  --enable-matroska)    _matroska=yes   ;;
-  --disable-matroska)   _matroska=no    ;;
+  --enable-internal-matroska)  _matroska_internal=yes _matroska_external=no ;;
+  --disable-internal-matroska) _matroska_internal=no ;;
+  --enable-external-matroska)  _matroska_internal=no  _matroska_external=yes ;;
+  --disable-external-matroska) _matroska_external=no ;;
   --enable-internal-faad)	_faad_internal=yes	_faad_external=no	;;
   --disable-internal-faad)	_faad_internal=no	;;
   --enable-external-faad)	_faad_external=yes	_faad_internal=no	;;
@@ -4494,9 +4498,15 @@
 echores "$_theora"
 
 
-echocheck "Matroska support (0.6.0 or later)"
-if test "$_matroska" != no ; then
-  _matroska=no
+echocheck "Matroska support (external 0.6.0 or later OR internal)"
+_matroska_result="no"
+if test "$_matroska_internal" = yes ; then
+  _matroska_external=no
+  _inputmodules="matroska(internal) $_inputmodules"
+  _matroska_result="yes, internal"
+fi
+if test "$_matroska_external" != no ; then
+  _matroska_external=no
   _TMPC=$TMPC
   TMPC=${TMPC}pp
   cat > $TMPC << EOF
@@ -4513,27 +4523,30 @@
 
 int main(void) { return 0; }
 EOF
-  cc_check -lmatroska -lebml -lstdc++ && _matroska=yes
-  if test "$_matroska" = no ; then
+  cc_check -lmatroska -lebml -lstdc++ && _matroska_external=yes
+  if test "$_matroska_external" = no ; then
     _saved_inc_extra=$_inc_extra
     _inc_extra="$_inc_extra -I/usr/local/include"
-    cc_check -lmatroska -lebml -lstdc++ && _matroska=yes
-    if test "$_matroska" = no ; then
+    cc_check -lmatroska -lebml -lstdc++ && _matroska_external=yes
+    if test "$_matroska_external" = no ; then
       _inc_extra=$_saved_inc_extra
     fi
   fi
   rm ${TMPC} > /dev/null 2> /dev/null
   TMPC=$_TMPC
-fi
-if test "$_matroska" = yes ; then
+  if test "$_matroska_external" = yes ; then
+    _ld_matroska="-lmatroska -lebml -lstdc++"
+    _inputmodules="matroska(external) $_inputmodules"
+    _matroska_result="yes, external"
+  fi
+fi
+echores "$_matroska_result"
+if test "$_matroska_internal" != no -o "$_matroska_external" != no ; then
   _def_matroska='#define HAVE_MATROSKA 1'
-  _inputmodules="matroska $_inputmodules"
-  _ld_matroska="-lmatroska -lebml -lstdc++"
 else
   _def_matroska='#undef HAVE_MATROSKA'
   _noinputmodules="matroska $_noinputmodules"
 fi
-echores "$_matroska"
 
 
 
@@ -5831,7 +5844,8 @@
 CONFIG_MP3LAME = $_mp3lame
 LIBMENU = $_menu
 I18NLIBS = $_i18n_libs
-MATROSKA = $_matroska
+MATROSKA_INTERNAL = $_matroska_internal
+MATROSKA_EXTERNAL = $_matroska_external
 MATROSKA_LIB = $_ld_matroska
 
 OPENDIVX = $_opendivx
--- a/libmpdemux/Makefile	Mon Jan 19 18:37:34 2004 +0000
+++ b/libmpdemux/Makefile	Mon Jan 19 19:16:10 2004 +0000
@@ -26,7 +26,10 @@
 SRCS += dvb_tune.c
 endif
 
-ifeq ($(MATROSKA),yes)
+ifeq ($(MATROSKA_INTERNAL),yes)
+SRCS += demux_mkv.c ebml.c
+endif
+ifeq ($(MATROSKA_EXTERNAL),yes)
 CPLUSPLUSSRCS += demux_mkv.cpp
 endif
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/demux_mkv.c	Mon Jan 19 19:16:10 2004 +0000
@@ -0,0 +1,2850 @@
+/*
+ * native Matroska demuxer
+ * Written by Aurelien Jacobs <aurel@gnuage.org>
+ * Based on the one written by Ronald Bultje for gstreamer
+ *   and on demux_mkv.cpp from Moritz Bunkus.
+ * Licence: GPL
+ */
+
+#include "config.h"
+#ifdef HAVE_MATROSKA
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "stream.h"
+#include "demuxer.h"
+#include "stheader.h"
+#include "ebml.h"
+#include "matroska.h"
+#include "bswap.h"
+
+#include "../subreader.h"
+#include "../libvo/sub.h"
+
+#ifdef USE_QTX_CODECS
+#include "qtx/qtxsdk/components.h"
+#endif
+
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+#ifdef USE_LIBLZO
+#include <lzo1x.h>
+#else
+#include "../libmpcodecs/native/minilzo.h"
+#endif
+
+
+typedef struct
+{
+  uint32_t order, type, scope;
+  uint32_t comp_algo;
+  uint8_t *comp_settings;
+  int comp_settings_len;
+} mkv_content_encoding_t;
+
+typedef struct mkv_track
+{
+  int tnum;
+  
+  char *codec_id;
+  int ms_compat;
+  char *language;
+
+  int type;
+  
+  uint32_t v_width, v_height, v_dwidth, v_dheight;
+  float v_frate;
+
+  uint32_t a_formattag;
+  uint32_t a_channels, a_bps;
+  float a_sfreq;
+
+  float default_duration;
+
+  int default_track;
+
+  void *private_data;
+  unsigned int private_size;
+
+  /* for vorbis audio */
+  unsigned char *headers[3];
+  uint32_t header_sizes[3];
+
+  /* stuff for realmedia */
+  int realmedia;
+  int rv_kf_base, rv_kf_pts;
+  float rv_pts;  /* previous video timestamp */
+  float ra_pts;  /* previous audio timestamp */
+
+  /* stuff for quicktime */
+  int fix_i_bps;
+  float qt_last_a_pts;
+
+  int subtitle_type;
+
+  /* generic content encoding support */
+  mkv_content_encoding_t *encodings;
+  int num_encodings;
+} mkv_track_t;
+
+typedef struct mkv_index
+{
+  int tnum;
+  uint64_t timecode, filepos;
+} mkv_index_t;
+
+typedef struct mkv_chapter
+{
+  uint64_t start, end;
+} mkv_chapter_t;
+
+typedef struct mkv_demuxer
+{
+  off_t segment_start;
+
+  float duration, last_pts;
+  uint64_t last_filepos;
+
+  mkv_track_t **tracks;
+  int num_tracks;
+
+  uint64_t tc_scale, cluster_tc, first_tc;
+  int has_first_tc;
+
+  uint64_t clear_subs_at[SUB_MAX_TEXT];
+  subtitle subs;
+
+  uint64_t cluster_size;
+  uint64_t blockgroup_size;
+
+  mkv_index_t *indexes;
+  int num_indexes;
+
+  off_t *parsed_cues;
+  int parsed_cues_num;
+  off_t *parsed_seekhead;
+  int parsed_seekhead_num;
+
+  uint64_t *cluster_positions;
+  int num_cluster_pos;
+
+  int64_t skip_to_timecode;
+  int v_skip_to_keyframe, a_skip_to_keyframe;
+
+  mkv_chapter_t *chapters;
+  int num_chapters;
+  int64_t stop_timecode;
+} mkv_demuxer_t;
+
+
+#if __GNUC__ == 2
+#pragma pack(2)
+#else
+#pragma pack(push,2)
+#endif
+
+typedef struct
+{
+  uint32_t chunks;              /* number of chunks */
+  uint32_t timestamp;           /* timestamp from packet header */
+  uint32_t len;                 /* length of actual data */
+  uint32_t chunktab;            /* offset to chunk offset array */
+} dp_hdr_t;
+
+typedef struct
+{
+  uint32_t size;
+  uint32_t fourcc1;
+  uint32_t fourcc2;
+  uint16_t width;
+  uint16_t height;
+  uint16_t bpp;
+  uint32_t unknown1;
+  uint32_t fps;
+  uint32_t type1;
+  uint32_t type2;
+} real_video_props_t;
+
+typedef struct
+{
+  uint32_t fourcc1;             /* '.', 'r', 'a', 0xfd */
+  uint16_t version1;            /* 4 or 5 */
+  uint16_t unknown1;            /* 00 000 */
+  uint32_t fourcc2;             /* .ra4 or .ra5 */
+  uint32_t unknown2;            /* ??? */
+  uint16_t version2;            /* 4 or 5 */
+  uint32_t header_size;         /* == 0x4e */
+  uint16_t flavor;              /* codec flavor id */
+  uint32_t coded_frame_size;    /* coded frame size */
+  uint32_t unknown3;            /* big number */
+  uint32_t unknown4;            /* bigger number */
+  uint32_t unknown5;            /* yet another number */
+  uint16_t sub_packet_h;
+  uint16_t frame_size;
+  uint16_t sub_packet_size;
+  uint16_t unknown6;            /* 00 00 */
+  uint16_t sample_rate;
+  uint16_t unknown8;            /* 0 */
+  uint16_t sample_size;
+  uint16_t channels;
+} real_audio_v4_props_t;
+
+typedef struct
+{
+  uint32_t fourcc1;             /* '.', 'r', 'a', 0xfd */
+  uint16_t version1;            /* 4 or 5 */
+  uint16_t unknown1;            /* 00 000 */
+  uint32_t fourcc2;             /* .ra4 or .ra5 */
+  uint32_t unknown2;            /* ??? */
+  uint16_t version2;            /* 4 or 5 */
+  uint32_t header_size;         /* == 0x4e */
+  uint16_t flavor;              /* codec flavor id */
+  uint32_t coded_frame_size;    /* coded frame size */
+  uint32_t unknown3;            /* big number */
+  uint32_t unknown4;            /* bigger number */
+  uint32_t unknown5;            /* yet another number */
+  uint16_t sub_packet_h;
+  uint16_t frame_size;
+  uint16_t sub_packet_size;
+  uint16_t unknown6;            /* 00 00 */
+  uint8_t unknown7[6];          /* 0, srate, 0 */
+  uint16_t sample_rate;
+  uint16_t unknown8;            /* 0 */
+  uint16_t sample_size;
+  uint16_t channels;
+  uint32_t genr;                /* "genr" */
+  uint32_t fourcc3;             /* fourcc */
+} real_audio_v5_props_t;
+
+
+/* for e.g. "-slang ger" */
+extern char *dvdsub_lang;
+extern char *audio_lang;
+
+
+static mkv_track_t *
+demux_mkv_find_track_by_num (mkv_demuxer_t *d, int n, int type)
+{
+  int i, id;
+
+  for (i=0, id=0; i < d->num_tracks; i++)
+    if (d->tracks[i] != NULL && d->tracks[i]->type == type)
+      if (id++ == n)
+        return d->tracks[i];
+  
+  return NULL;
+}
+
+static mkv_track_t *
+demux_mkv_find_track_by_language (mkv_demuxer_t *d, char *language, int type)
+{
+  int i;
+  
+  for (i=0; i < d->num_tracks; i++)
+    if (d->tracks[i] != NULL && d->tracks[i]->language != NULL &&
+        d->tracks[i]->type == type && !strcmp(d->tracks[i]->language,language))
+      return d->tracks[i];
+  
+  return NULL;
+}
+
+static void
+add_cluster_position (mkv_demuxer_t *mkv_d, uint64_t position)
+{
+  int i = mkv_d->num_cluster_pos;
+
+  while (i--)
+    if (mkv_d->cluster_positions[i] == position)
+      return;
+
+  if (!mkv_d->cluster_positions)
+    mkv_d->cluster_positions = (uint64_t *) malloc (32 * sizeof (uint64_t));
+  else if (!(mkv_d->num_cluster_pos % 32))
+    mkv_d->cluster_positions = (uint64_t *) realloc(mkv_d->cluster_positions,
+                                                    (mkv_d->num_cluster_pos+32)
+                                                    * sizeof (uint64_t));
+  mkv_d->cluster_positions[mkv_d->num_cluster_pos++] = position;
+}
+
+
+#define AAC_SYNC_EXTENSION_TYPE 0x02b7
+static int
+aac_get_sample_rate_index (uint32_t sample_rate)
+{
+  if (92017 <= sample_rate)
+    return 0;
+  else if (75132 <= sample_rate)
+    return 1;
+  else if (55426 <= sample_rate)
+    return 2;
+  else if (46009 <= sample_rate)
+    return 3;
+  else if (37566 <= sample_rate)
+    return 4;
+  else if (27713 <= sample_rate)
+    return 5;
+  else if (23004 <= sample_rate)
+    return 6;
+  else if (18783 <= sample_rate)
+    return 7;
+  else if (13856 <= sample_rate)
+    return 8;
+  else if (11502 <= sample_rate)
+    return 9;
+  else if (9391 <= sample_rate)
+    return 10;
+  else
+    return 11;
+}
+
+
+static int
+demux_mkv_decode (mkv_track_t *track, uint8_t *src, uint8_t **dest,
+                  uint32_t *size, uint32_t type)
+{
+  int i, result;
+  int modified = 0;
+
+  *dest = src;
+  if (track->num_encodings <= 0)
+    return 0;
+
+  for (i=0; i<track->num_encodings; i++)
+    {
+      if (!(track->encodings[i].scope & type))
+        continue;
+
+#ifdef HAVE_ZLIB
+      if (track->encodings[i].comp_algo == 0)
+        {
+          /* zlib encoded track */
+          z_stream zstream;
+
+          zstream.zalloc = (alloc_func) 0;
+          zstream.zfree = (free_func) 0;
+          zstream.opaque = (voidpf) 0;
+          if (inflateInit (&zstream) != Z_OK)
+            {
+              mp_msg (MSGT_DEMUX, MSGL_WARN,
+                      "[mkv] zlib initialization failed.\n");
+              return modified;
+            }
+          zstream.next_in = (Bytef *) src;
+          zstream.avail_in = *size;
+
+          modified = 1;
+          *dest = (uint8_t *) malloc (*size);
+          zstream.avail_out = *size;
+          do {
+            *size += 4000;
+            *dest = (uint8_t *) realloc (*dest, *size);
+            zstream.next_out = (Bytef *) (*dest + zstream.total_out);
+            result = inflate (&zstream, Z_NO_FLUSH);
+            if (result != Z_OK && result != Z_STREAM_END)
+              {
+                mp_msg (MSGT_DEMUX, MSGL_WARN,
+                        "[mkv] zlib decompression failed.\n");
+                free(*dest);
+                *dest = NULL;
+                inflateEnd (&zstream);
+                return modified;
+              }
+            zstream.avail_out += 4000;
+          } while (zstream.avail_out == 4000 &&
+                   zstream.avail_in != 0 && result != Z_STREAM_END);
+
+          *size = zstream.total_out;
+          inflateEnd (&zstream);
+        }
+#endif
+      if (track->encodings[i].comp_algo == 2)
+        {
+          /* lzo encoded track */
+          int dstlen = *size * 3;
+
+          if (lzo_init () != LZO_E_OK)
+            {
+              mp_msg (MSGT_DEMUX, MSGL_WARN,
+                      "[mkv] lzo initialization failed.\n");
+              return modified;
+            }
+
+          *dest = (uint8_t *) malloc (dstlen);
+          while (1)
+            {
+              result = lzo1x_decompress_safe (src, *size, *dest, &dstlen,
+                                              NULL);
+              if (result == LZO_E_OK)
+                break;
+              if (result != LZO_E_OUTPUT_OVERRUN)
+                {
+                  mp_msg (MSGT_DEMUX, MSGL_WARN,
+                          "[mkv] lzo decompression failed.\n");
+                  return modified;
+                }
+              mp_msg (MSGT_DEMUX, MSGL_DBG2,
+                      "[mkv] lzo decompression buffer too small.\n");
+              dstlen *= 2;
+              *dest = (uint8_t *) realloc (*dest, dstlen);
+            }
+          *size = dstlen;
+        }
+    }
+
+  return modified;
+}
+
+
+static int
+demux_mkv_read_info (demuxer_t *demuxer)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  stream_t *s = demuxer->stream;
+  uint64_t length, l;
+  int il;
+
+  length = ebml_read_length (s, NULL);
+  while (length > 0)
+    {
+      switch (ebml_read_id (s, &il))
+        {
+        case MATROSKA_ID_TIMECODESCALE:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 1;
+            mkv_d->tc_scale = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] | + timecode scale: %llu\n",
+                    mkv_d->tc_scale);
+            break;
+          }
+
+        case MATROSKA_ID_DURATION:
+          {
+            long double num = ebml_read_float (s, &l);
+            if (num == EBML_FLOAT_INVALID)
+              return 1;
+            mkv_d->duration = num * mkv_d->tc_scale / 1000000000.0;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] | + duration: %.3fs\n",
+                    mkv_d->duration);
+            break;
+          }
+
+        default:
+          ebml_read_skip (s, &l); 
+          break;
+        }
+      length -= l + il;
+    }
+  return 0;
+}
+
+static int
+demux_mkv_read_trackencodings (demuxer_t *demuxer, mkv_track_t *track)
+{
+  stream_t *s = demuxer->stream;
+  mkv_content_encoding_t *ce, e;
+  uint64_t len, length, l;
+  int il, n;
+
+  ce = (mkv_content_encoding_t *) malloc (sizeof (*ce));
+  n = 0;
+
+  len = length = ebml_read_length (s, &il);
+  len += il;
+  while (length > 0)
+    {
+      switch (ebml_read_id (s, &il))
+        {
+        case MATROSKA_ID_CONTENTENCODING:
+          {
+            uint64_t len;
+            int i;
+
+            memset (&e, 0, sizeof (e));
+            e.scope = 1;
+
+            len = ebml_read_length (s, &i);
+            l = len + i;
+
+            while (len > 0)
+              {
+                uint64_t num, l;
+                int il;
+
+                switch (ebml_read_id (s, &il))
+                  {
+                  case MATROSKA_ID_CONTENTENCODINGORDER:
+                    num = ebml_read_uint (s, &l);
+                    if (num == EBML_UINT_INVALID)
+                      return 0;
+                    e.order = num;
+                    break;
+
+                  case MATROSKA_ID_CONTENTENCODINGSCOPE:
+                    num = ebml_read_uint (s, &l);
+                    if (num == EBML_UINT_INVALID)
+                      return 0;
+                    e.scope = num;
+                    break;
+
+                  case MATROSKA_ID_CONTENTENCODINGTYPE:
+                    num = ebml_read_uint (s, &l);
+                    if (num == EBML_UINT_INVALID)
+                      return 0;
+                    e.type = num;
+                    break;
+
+                  case MATROSKA_ID_CONTENTCOMPRESSION:
+                    {
+                      uint64_t le;
+
+                      le = ebml_read_length (s, &i);
+                      l = le + i;
+
+                      while (le > 0)
+                        {
+                          uint64_t l;
+                          int il;
+
+                          switch (ebml_read_id (s, &il))
+                            {
+                            case MATROSKA_ID_CONTENTCOMPALGO:
+                              num = ebml_read_uint (s, &l);
+                              if (num == EBML_UINT_INVALID)
+                                return 0;
+                              e.comp_algo = num;
+                              break;
+
+                            case MATROSKA_ID_CONTENTCOMPSETTINGS:
+                              l = ebml_read_length (s, &i);
+                              e.comp_settings = (uint8_t *) malloc (l);
+                              stream_read (s, e.comp_settings, l);
+                              e.comp_settings_len = l;
+                              l += i;
+                              break;
+
+                            default:
+                              ebml_read_skip (s, &l);
+                              break;
+                            }
+                          le -= l + il;
+                        }
+
+                      if (e.type == 1)
+                        {
+                          mp_msg(MSGT_DEMUX, MSGL_WARN,
+                                 "[mkv] Track number %u has been encrypted "
+                                 "and decryption has not yet been implemented."
+                                 " Skipping track.\n", track->tnum);
+                        }
+                      else if (e.type != 0)
+                        {
+                          mp_msg(MSGT_DEMUX, MSGL_WARN,
+                                 "[mkv] Unknown content encoding type for "
+                                 "track %u. Skipping track.\n", track->tnum);
+                        }
+
+                      if (e.comp_algo != 0 && e.comp_algo != 2)
+                        {
+                          mp_msg (MSGT_DEMUX, MSGL_WARN,
+                                  "[mkv] Track %u has been compressed with an "
+                                  "unknown/unsupported compression algorithm "
+                                  "(%u). Skipping track.\n",
+                                  track->tnum, e.comp_algo);
+                        }
+#ifndef HAVE_ZLIB
+                      else if (e.comp_algo == 0)
+                        {
+                          mp_msg (MSGT_DEMUX, MSGL_WARN,
+                                  "Track %u was compressed with zlib but "
+                                  "mplayer has not been compiled with support "
+                                  "for zlib compression. Skipping track.\n",
+                                  track->tnum);
+                        }
+#endif
+
+                      break;
+                    }
+
+                  default:
+                    ebml_read_skip (s, &l);
+                    break;
+                  }
+                len -= l + il;
+              }
+            for (i=0; i<n; i++)
+              if (e.order <= ce[i].order)
+                break;
+            ce = (mkv_content_encoding_t *) realloc (ce, (n+1) *sizeof (*ce));
+            memmove (ce+i+1, ce+i, (n-i) * sizeof (*ce));
+            memcpy (ce+i, &e, sizeof (e));
+            n++;
+            break;
+          }
+
+        default:
+          ebml_read_skip (s, &l);
+          break;
+        }
+
+      length -= l + il;
+    }
+
+  track->encodings = ce;
+  track->num_encodings = n;
+  return len;
+}
+
+static int
+demux_mkv_read_trackaudio (demuxer_t *demuxer, mkv_track_t *track)
+{
+  stream_t *s = demuxer->stream;
+  uint64_t len, length, l;
+  int il;
+
+  track->a_sfreq = 8000.0;
+  track->a_channels = 1;
+
+  len = length = ebml_read_length (s, &il);
+  len += il;
+  while (length > 0)
+    {
+      switch (ebml_read_id (s, &il))
+        {
+        case MATROSKA_ID_AUDIOSAMPLINGFREQ:
+          {
+            long double num = ebml_read_float (s, &l);
+            if (num == EBML_FLOAT_INVALID)
+              return 0;
+            track->a_sfreq = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |   + Sampling frequency: %f\n",
+                    track->a_sfreq);
+            break;
+          }
+
+        case MATROSKA_ID_AUDIOBITDEPTH:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 0;
+            track->a_bps = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |   + Bit depth: %u\n",
+                    track->a_bps);
+            break;
+          }
+
+        case MATROSKA_ID_AUDIOCHANNELS:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 0;
+            track->a_channels = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |   + Channels: %u\n",
+                    track->a_channels);
+            break;
+          }
+
+        default:
+            ebml_read_skip (s, &l);
+            break;
+        }
+      length -= l + il;
+    }
+  return len;
+}
+
+static int
+demux_mkv_read_trackvideo (demuxer_t *demuxer, mkv_track_t *track)
+{
+  stream_t *s = demuxer->stream;
+  uint64_t len, length, l;
+  int il;
+
+  len = length = ebml_read_length (s, &il);
+  len += il;
+  while (length > 0)
+    {
+      switch (ebml_read_id (s, &il))
+        {
+        case MATROSKA_ID_VIDEOFRAMERATE:
+          {
+            long double num = ebml_read_float (s, &l);
+            if (num == EBML_FLOAT_INVALID)
+              return 0;
+            track->v_frate = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |   + Frame rate: %f\n",
+                    track->v_frate);
+            break;
+          }
+
+        case MATROSKA_ID_VIDEODISPLAYWIDTH:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 0;
+            track->v_dwidth = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |   + Display width: %u\n",
+                    track->v_dwidth);
+            break;
+          }
+
+        case MATROSKA_ID_VIDEODISPLAYHEIGHT:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 0;
+            track->v_dheight = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |   + Display height: %u\n",
+                    track->v_dheight);
+            break;
+          }
+
+        case MATROSKA_ID_VIDEOPIXELWIDTH:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 0;
+            track->v_width = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |   + Pixel width: %u\n",
+                    track->v_width);
+            break;
+          }
+
+        case MATROSKA_ID_VIDEOPIXELHEIGHT:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 0;
+            track->v_height = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |   + Pixel height: %u\n",
+                    track->v_height);
+            break;
+          }
+
+        default:
+            ebml_read_skip (s, &l);
+            break;
+        }
+      length -= l + il;
+    }
+  return len;
+}
+
+static int
+demux_mkv_read_trackentry (demuxer_t *demuxer)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  stream_t *s = demuxer->stream;
+  mkv_track_t *track;
+  uint64_t len, length, l;
+  int il;
+
+  track = (mkv_track_t *) malloc (sizeof (*track));
+  memset(track, 0, sizeof(*track));
+  /* set default values */
+  track->default_track = 1;
+  track->language = strdup("eng");
+
+  len = length = ebml_read_length (s, &il);
+  len += il;
+  while (length > 0)
+    {
+      switch (ebml_read_id (s, &il))
+        {
+        case MATROSKA_ID_TRACKNUMBER:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 0;
+            track->tnum = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |  + Track number: %u\n",
+                    track->tnum);
+            break;
+          }
+
+        case MATROSKA_ID_TRACKTYPE:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 0;
+            track->type = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |  + Track type: ");
+            switch (track->type)
+              {
+              case MATROSKA_TRACK_AUDIO:
+                mp_msg (MSGT_DEMUX, MSGL_V, "Audio\n");
+                break;
+              case MATROSKA_TRACK_VIDEO:
+                mp_msg (MSGT_DEMUX, MSGL_V, "Video\n");
+                break;
+              case MATROSKA_TRACK_SUBTITLE:
+                mp_msg (MSGT_DEMUX, MSGL_V, "Subtitle\n");
+                break;
+              default:
+                mp_msg (MSGT_DEMUX, MSGL_V, "unknown\n");
+                break;
+            }
+            break;
+          }
+
+        case MATROSKA_ID_TRACKAUDIO:
+          mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |  + Audio track\n");
+          l = demux_mkv_read_trackaudio (demuxer, track);
+          if (l == 0)
+            return 0;
+          break;
+
+        case MATROSKA_ID_TRACKVIDEO:
+          mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |  + Video track\n");
+          l = demux_mkv_read_trackvideo (demuxer, track);
+          if (l == 0)
+            return 0;
+          break;
+
+        case MATROSKA_ID_CODECID:
+          track->codec_id = ebml_read_ascii (s, &l);
+          if (track->codec_id == NULL)
+            return 0;
+          if (!strcmp (track->codec_id, MKV_V_MSCOMP))
+            track->ms_compat = 1;
+          else if (!strcmp (track->codec_id, MKV_S_VOBSUB))
+            track->subtitle_type = MATROSKA_SUBTYPE_VOBSUB;
+          else if (!strcmp (track->codec_id, MKV_S_TEXTSSA)
+                   || !strcmp (track->codec_id, MKV_S_TEXTASS)
+                   || !strcmp (track->codec_id, MKV_S_SSA)
+                   || !strcmp (track->codec_id, MKV_S_ASS))
+            {
+              track->subtitle_type = MATROSKA_SUBTYPE_SSA;
+              sub_utf8 = 1;
+            }
+          else if (!strcmp (track->codec_id, MKV_S_TEXTASCII))
+            track->subtitle_type = MATROSKA_SUBTYPE_TEXT;
+          if (!strcmp (track->codec_id, MKV_S_TEXTUTF8))
+            {
+              track->subtitle_type = MATROSKA_SUBTYPE_TEXT;
+              sub_utf8 = 1;
+            }
+          mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |  + Codec ID: %s\n",
+                  track->codec_id);
+          break;
+
+        case MATROSKA_ID_CODECPRIVATE:
+          {
+            int x;
+            uint64_t num = ebml_read_length (s, &x);
+            l = x + num;
+            track->private_data = malloc (num);
+            if (stream_read(s, track->private_data, num) != (int) num)
+              return 0;
+            track->private_size = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |  + CodecPrivate, length "
+                    "%u\n", track->private_size);
+            break;
+          }
+
+        case MATROSKA_ID_TRACKLANGUAGE:
+          track->language = ebml_read_utf8 (s, &l);
+          if (track->language == NULL)
+            return 0;
+          mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |  + Language: %s\n",
+                  track->language);
+          break;
+
+        case MATROSKA_ID_TRACKFLAGDEFAULT:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 0;
+            track->default_track = num;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |  + Default flag: %u\n",
+                    track->default_track);
+            break;
+          }
+
+        case MATROSKA_ID_TRACKDEFAULTDURATION:
+          {
+            uint64_t num = ebml_read_uint (s, &l);
+            if (num == EBML_UINT_INVALID)
+              return 0;
+            if (num == 0)
+              mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |  + Default duration: 0");
+            else
+              {
+                track->v_frate = 1000000000.0 / num;
+                mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |  + Default duration: "
+                        "%.3fms ( = %.3f fps)\n",num/1000000.0,track->v_frate);
+              }
+            break;
+          }
+
+        case MATROSKA_ID_TRACKENCODINGS:
+          l = demux_mkv_read_trackencodings (demuxer, track);
+          if (l == 0)
+            return 0;
+          break;
+
+        default:
+          ebml_read_skip (s, &l);
+          break;
+        }
+      length -= l + il;
+    }
+
+  mkv_d->tracks[mkv_d->num_tracks++] = track;
+  return len;
+}
+
+static int
+demux_mkv_read_tracks (demuxer_t *demuxer)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  stream_t *s = demuxer->stream;
+  uint64_t length, l;
+  int il;
+
+  mkv_d->tracks = (mkv_track_t **) malloc (sizeof (*mkv_d->tracks));
+  mkv_d->num_tracks = 0;
+
+  length = ebml_read_length (s, NULL);
+  while (length > 0)
+    {
+      switch (ebml_read_id (s, &il))
+        {
+        case MATROSKA_ID_TRACKENTRY:
+          mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] | + a track...\n");
+          mkv_d->tracks = (mkv_track_t **) realloc (mkv_d->tracks,
+                                                    (mkv_d->num_tracks+1)
+                                                    *sizeof (*mkv_d->tracks));
+          l = demux_mkv_read_trackentry (demuxer);
+          if (l == 0)
+            return 1;
+          break;
+
+        default:
+            ebml_read_skip (s, &l);
+            break;
+        }
+      length -= l + il;
+    }
+  return 0;
+}
+
+static int
+demux_mkv_read_cues (demuxer_t *demuxer)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  stream_t *s = demuxer->stream;
+  uint64_t length, l, time, track, pos;
+  off_t off;
+  int i, il;
+
+  off = stream_tell (s);
+  for (i=0; i<mkv_d->parsed_cues_num; i++)
+    if (mkv_d->parsed_cues[i] == off)
+      {
+        ebml_read_skip (s, NULL);
+        return 0;
+      }
+  mkv_d->parsed_cues = (off_t *) realloc (mkv_d->parsed_cues, 
+                                          (mkv_d->parsed_cues_num+1)
+                                          * sizeof (off_t));
+  mkv_d->parsed_cues[mkv_d->parsed_cues_num++] = off;
+
+  mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing cues ] -----------\n");
+  length = ebml_read_length (s, NULL);
+
+  while (length > 0)
+    {
+      time = track = pos = EBML_UINT_INVALID;
+
+      switch (ebml_read_id (s, &il))
+        {
+        case MATROSKA_ID_POINTENTRY:
+          {
+            uint64_t len;
+
+            len = ebml_read_length (s, &i);
+            l = len + i;
+
+            while (len > 0)
+              {
+                uint64_t l;
+                int il;
+
+                switch (ebml_read_id (s, &il))
+                  {
+                  case MATROSKA_ID_CUETIME:
+                    time = ebml_read_uint (s, &l);
+                    break;
+
+                  case MATROSKA_ID_CUETRACKPOSITION:
+                    {
+                      uint64_t le;
+
+                      le = ebml_read_length (s, &i);
+                      l = le + i;
+
+                      while (le > 0)
+                        {
+                          uint64_t l;
+                          int il;
+
+                          switch (ebml_read_id (s, &il))
+                            {
+                            case MATROSKA_ID_CUETRACK:
+                              track = ebml_read_uint (s, &l);
+                              break;
+
+                            case MATROSKA_ID_CUECLUSTERPOSITION:
+                              pos = ebml_read_uint (s, &l);
+                              break;
+
+                            default:
+                              ebml_read_skip (s, &l);
+                              break;
+                            }
+                          le -= l + il;
+                        }
+                      break;
+                    }
+
+                  default:
+                    ebml_read_skip (s, &l);
+                    break;
+                  }
+                len -= l + il;
+              }
+            break;
+          }
+
+        default:
+          ebml_read_skip (s, &l);
+          break;
+        }
+
+      length -= l + il;
+
+      if (time != EBML_UINT_INVALID && track != EBML_UINT_INVALID
+          && pos != EBML_UINT_INVALID)
+        {
+          if (mkv_d->indexes == NULL)
+            mkv_d->indexes = (mkv_index_t *) malloc (32*sizeof (mkv_index_t));
+          else if (mkv_d->num_indexes % 32 == 0)
+            mkv_d->indexes = (mkv_index_t *) realloc (mkv_d->indexes,
+                                                      (mkv_d->num_indexes+32)
+                                                      *sizeof (mkv_index_t));
+          mkv_d->indexes[mkv_d->num_indexes].tnum = track;
+          mkv_d->indexes[mkv_d->num_indexes].timecode = time;
+          mkv_d->indexes[mkv_d->num_indexes].filepos =mkv_d->segment_start+pos;
+          mp_msg (MSGT_DEMUX, MSGL_DBG2, "[mkv] |+ found cue point "
+                  "for track %llu: timecode %llu, filepos: %llu\n", 
+                  track, time, mkv_d->segment_start + pos);
+          mkv_d->num_indexes++;
+        }
+    }
+
+  mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing cues ] -----------\n");
+  return 0;
+}
+
+static int
+demux_mkv_read_chapters (demuxer_t *demuxer)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  stream_t *s = demuxer->stream;
+  uint64_t length, l;
+  int il;
+
+  if (mkv_d->chapters)
+    {
+      ebml_read_skip (s, NULL);
+      return 0;
+    }
+
+  mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing chapters ] ---------\n");
+  length = ebml_read_length (s, NULL);
+
+  while (length > 0)
+    {
+      switch (ebml_read_id (s, &il))
+        {
+        case MATROSKA_ID_EDITIONENTRY:
+          {
+            uint64_t len;
+            int i;
+
+            len = ebml_read_length (s, &i);
+            l = len + i;
+
+            while (len > 0)
+              {
+                uint64_t l;
+                int il;
+
+                switch (ebml_read_id (s, &il))
+                  {
+                  case MATROSKA_ID_CHAPTERATOM:
+                    {
+                      uint64_t len, start=0, end=0;
+                      int i;
+
+                      len = ebml_read_length (s, &i);
+                      l = len + i;
+
+                      if (mkv_d->chapters == NULL)
+                        mkv_d->chapters = malloc (32*sizeof(*mkv_d->chapters));
+                      else if (!(mkv_d->num_chapters % 32))
+                        mkv_d->chapters = realloc (mkv_d->chapters,
+                                                   (mkv_d->num_chapters + 32)
+                                                   * sizeof(*mkv_d->chapters));
+
+                      while (len > 0)
+                        {
+                          uint64_t l;
+                          int il;
+
+                          switch (ebml_read_id (s, &il))
+                            {
+                            case MATROSKA_ID_CHAPTERTIMESTART:
+                              start = ebml_read_uint (s, &l) / 1000000;
+                              break;
+
+                            case MATROSKA_ID_CHAPTERTIMEEND:
+                              end = ebml_read_uint (s, &l) / 1000000;
+                              break;
+
+                            default:
+                              ebml_read_skip (s, &l);
+                              break;
+                            }
+                          len -= l + il;
+                        }
+
+                      mkv_d->chapters[mkv_d->num_chapters].start = start;
+                      mkv_d->chapters[mkv_d->num_chapters].end = end;
+                      mp_msg(MSGT_DEMUX, MSGL_V,
+                             "[mkv] Chapter %u from %02d:%02d:%02d."
+                             "%03d to %02d:%02d:%02d.%03d\n",
+                             ++mkv_d->num_chapters,
+                             (int) (start / 60 / 60 / 1000),
+                             (int) ((start / 60 / 1000) % 60),
+                             (int) ((start / 1000) % 60),
+                             (int) (start % 1000),
+                             (int) (end / 60 / 60 / 1000),
+                             (int) ((end / 60 / 1000) % 60),
+                             (int) ((end / 1000) % 60),
+                             (int) (end % 1000));
+                      break;
+                    }
+
+                  default:
+                    ebml_read_skip (s, &l);
+                    break;
+                  }
+                len -= l + il;
+              }
+            break;
+          }
+
+        default:
+          ebml_read_skip (s, &l);
+          break;
+        }
+
+      length -= l + il;
+    }
+
+  mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing chapters ] ---------\n");
+  return 0;
+}
+
+static int
+demux_mkv_read_tags (demuxer_t *demuxer)
+{
+  ebml_read_skip (demuxer->stream, NULL);
+  return 0;
+}
+
+static int
+demux_mkv_read_seekhead (demuxer_t *demuxer)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  stream_t *s = demuxer->stream;
+  uint64_t length, l, seek_pos, saved_pos, num;
+  uint32_t seek_id;
+  int i, il, res = 0;
+  off_t off;
+
+  off = stream_tell (s);
+  for (i=0; i<mkv_d->parsed_seekhead_num; i++)
+    if (mkv_d->parsed_seekhead[i] == off)
+      {
+        ebml_read_skip (s, NULL);
+        return 0;
+      }
+  mkv_d->parsed_seekhead = (off_t *) realloc (mkv_d->parsed_seekhead, 
+                                              (mkv_d->parsed_seekhead_num+1)
+                                              * sizeof (off_t));
+  mkv_d->parsed_seekhead[mkv_d->parsed_seekhead_num++] = off;
+
+  mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing seek head ] ---------\n");
+  length = ebml_read_length (s, NULL);
+  while (length > 0 && !res)
+    {
+
+      seek_id = 0;
+      seek_pos = EBML_UINT_INVALID;
+
+      switch (ebml_read_id (s, &il))
+        {
+        case MATROSKA_ID_SEEKENTRY:
+          {
+            uint64_t len;
+
+            len = ebml_read_length (s, &i);
+            l = len + i;
+
+            while (len > 0)
+              {
+                uint64_t l;
+                int il;
+
+                switch (ebml_read_id (s, &il))
+                  {
+                  case MATROSKA_ID_SEEKID:
+                    num = ebml_read_uint (s, &l);
+                    if (num != EBML_UINT_INVALID)
+                      seek_id = num;
+                    break;
+
+                  case MATROSKA_ID_SEEKPOSITION:
+                    seek_pos = ebml_read_uint (s, &l);
+                    break;
+
+                  default:
+                    ebml_read_skip (s, &l);
+                    break;
+                  }
+                len -= l + il;
+              }
+
+            break;
+          }
+
+        default:
+            ebml_read_skip (s, &l);
+            break;
+        }
+      length -= l + il;
+
+      if (seek_id == 0 || seek_id == MATROSKA_ID_CLUSTER
+          || seek_pos == EBML_UINT_INVALID)
+        continue;
+
+      saved_pos = stream_tell (s);
+      if (!stream_seek (s, mkv_d->segment_start + seek_pos))
+        res = 1;
+      else
+        {
+          if (ebml_read_id (s, &il) != seek_id)
+            res = 1;
+          else
+            switch (seek_id)
+              {
+              case MATROSKA_ID_CUES:
+                if (demux_mkv_read_cues (demuxer))
+                  res = 1;
+                break;
+
+              case MATROSKA_ID_TAGS:
+                if (demux_mkv_read_tags (demuxer))
+                  res = 1;
+                break;
+
+              case MATROSKA_ID_SEEKHEAD:
+                if (demux_mkv_read_seekhead (demuxer))
+                  res = 1;
+                break;
+
+              case MATROSKA_ID_CHAPTERS:
+                if (demux_mkv_read_chapters (demuxer))
+                  res = 1;
+                break;
+              }
+        }
+
+      stream_seek (s, saved_pos);
+    }
+  if (length > 0)
+     stream_seek (s, stream_tell (s) + length);
+  mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing seek head ] ---------\n");
+  return res;
+}
+
+static void
+display_tracks (mkv_demuxer_t *mkv_d)
+{
+  int i, vid=0, aid=0, sid=0;
+
+  for (i=0; i<mkv_d->num_tracks; i++)
+    {
+      char *type = "unknown", str[32];
+      *str = '\0';
+      switch (mkv_d->tracks[i]->type)
+        {
+        case MATROSKA_TRACK_VIDEO:
+          type = "video";
+          sprintf (str, "-vid %u", vid++);
+          break;
+        case MATROSKA_TRACK_AUDIO:
+          type = "audio";
+          sprintf (str, "-aid %u, -alang %s",aid++,mkv_d->tracks[i]->language);
+          break;
+        case MATROSKA_TRACK_SUBTITLE:
+          type = "sutitles";
+          sprintf (str, "-sid %u, -slang %s",sid++,mkv_d->tracks[i]->language);
+          break;
+        }
+      mp_msg(MSGT_DEMUX, MSGL_INFO, "[mkv] Track ID %u: %s (%s), %s\n",
+             mkv_d->tracks[i]->tnum, type, mkv_d->tracks[i]->codec_id, str);
+    }
+}
+
+static int
+demux_mkv_open_video (demuxer_t *demuxer, mkv_track_t *track)
+{
+  BITMAPINFOHEADER *bih;
+  void *ImageDesc = NULL;
+  sh_video_t *sh_v;
+
+  if (track->ms_compat)  /* MS compatibility mode */
+    {
+      BITMAPINFOHEADER *src;
+
+      if (track->private_data == NULL
+          || track->private_size < sizeof (BITMAPINFOHEADER))
+        return 1;
+
+      src = (BITMAPINFOHEADER *) track->private_data;
+      bih = (BITMAPINFOHEADER *) malloc (track->private_size);
+      memset (bih, 0, track->private_size);
+      bih->biSize = le2me_32 (src->biSize);
+      bih->biWidth = le2me_32 (src->biWidth);
+      bih->biHeight = le2me_32 (src->biHeight);
+      bih->biPlanes = le2me_16 (src->biPlanes);
+      bih->biBitCount = le2me_16 (src->biBitCount);
+      bih->biCompression = le2me_32 (src->biCompression);
+      bih->biSizeImage = le2me_32 (src->biSizeImage);
+      bih->biXPelsPerMeter = le2me_32 (src->biXPelsPerMeter);
+      bih->biYPelsPerMeter = le2me_32 (src->biYPelsPerMeter);
+      bih->biClrUsed = le2me_32 (src->biClrUsed);
+      bih->biClrImportant = le2me_32 (src->biClrImportant);
+      memcpy((char *) bih + sizeof (BITMAPINFOHEADER),
+             (char *) src + sizeof (BITMAPINFOHEADER),
+             track->private_size - sizeof (BITMAPINFOHEADER));
+
+      if (track->v_width == 0)
+        track->v_width = bih->biWidth;
+      if (track->v_height == 0)
+        track->v_height = bih->biHeight;
+    }
+  else
+    {
+      bih = (BITMAPINFOHEADER *) malloc (sizeof (BITMAPINFOHEADER));
+      memset (bih, 0, sizeof (BITMAPINFOHEADER));
+      bih->biSize = sizeof (BITMAPINFOHEADER);
+      bih->biWidth = track->v_width;
+      bih->biHeight = track->v_height;
+      bih->biBitCount = 24;
+      bih->biSizeImage = bih->biWidth * bih->biHeight * bih->biBitCount/8;
+
+      if (track->private_size >= sizeof (real_video_props_t)
+          && (!strcmp (track->codec_id, MKV_V_REALV10)
+              || !strcmp (track->codec_id, MKV_V_REALV20)
+              || !strcmp (track->codec_id, MKV_V_REALV30)
+              || !strcmp (track->codec_id, MKV_V_REALV40)))
+        {
+          unsigned char *dst, *src;
+          real_video_props_t *rvp;
+          uint32_t type2;
+
+          rvp = (real_video_props_t *) track->private_data;
+          src = (unsigned char *) (rvp + 1);
+
+          bih = (BITMAPINFOHEADER *) realloc(bih,
+                                             sizeof (BITMAPINFOHEADER)+12);
+          bih->biSize = 48;
+          bih->biPlanes = 1;
+          type2 = be2me_32 (rvp->type2);
+          if (type2 == 0x10003000 || type2 == 0x10003001)
+            bih->biCompression=mmioFOURCC('R','V','1','3');
+          else
+            bih->biCompression=mmioFOURCC('R','V',track->codec_id[9],'0');
+          dst = (unsigned char *) (bih + 1);
+          ((unsigned int *) dst)[0] = be2me_32 (rvp->type1);
+          ((unsigned int *) dst)[1] = type2;
+
+          if (bih->biCompression <= 0x30335652 && type2 >= 0x20200002)
+            {
+              /* read secondary WxH for the cmsg24[] (see vd_realvid.c) */
+              ((unsigned short *)(bih+1))[4] = 4 * (unsigned short) src[0];
+              ((unsigned short *)(bih+1))[5] = 4 * (unsigned short) src[1];
+            }
+          else
+            memset(&dst[8], 0, 4);
+          track->realmedia = 1;
+
+#ifdef USE_QTX_CODECS
+        }
+      else if (track->private_size >= sizeof (ImageDescription)
+               && !strcmp(track->codec_id, MKV_V_QUICKTIME))
+        {
+          ImageDescriptionPtr idesc;
+
+          idesc = (ImageDescriptionPtr) track->private_data;
+          idesc->idSize = be2me_32 (idesc->idSize);
+          idesc->cType = be2me_32 (idesc->cType);
+          idesc->version = be2me_16 (idesc->version);
+          idesc->revisionLevel = be2me_16 (idesc->revisionLevel);
+          idesc->vendor = be2me_32 (idesc->vendor);
+          idesc->temporalQuality = be2me_32 (idesc->temporalQuality);
+          idesc->spatialQuality = be2me_32 (idesc->spatialQuality);
+          idesc->width = be2me_16 (idesc->width);
+          idesc->height = be2me_16 (idesc->height);
+          idesc->hRes = be2me_32 (idesc->hRes);
+          idesc->vRes = be2me_32 (idesc->vRes);
+          idesc->dataSize = be2me_32 (idesc->dataSize);
+          idesc->frameCount = be2me_16 (idesc->frameCount);
+          idesc->depth = be2me_16 (idesc->depth);
+          idesc->clutID = be2me_16 (idesc->clutID);
+          bih->biPlanes = 1;
+          bih->biCompression = idesc->cType;
+          ImageDesc = idesc;
+#endif /* USE_QTX_CODECS */
+
+        }
+      else
+        {
+          mp_msg (MSGT_DEMUX,MSGL_WARN,"[mkv] Unknown/unsupported CodecID "
+                  "(%s) or missing/bad CodecPrivate data (track %u).\n",
+                  track->codec_id, track->tnum);
+          free(bih);
+          return 1;
+        }
+    }
+
+  sh_v = new_sh_video (demuxer, track->tnum);
+  sh_v->bih = bih;
+  sh_v->format = sh_v->bih->biCompression;
+  if (track->v_frate == 0.0)
+    track->v_frate = 25.0;
+  sh_v->fps = track->v_frate;
+  sh_v->frametime = 1 / track->v_frate;
+  if (!track->realmedia)
+    {
+      sh_v->disp_w = track->v_width;
+      sh_v->disp_h = track->v_height;
+      sh_v->aspect = (float)track->v_dwidth / (float)track->v_dheight;
+    }
+  else
+    {
+      // vd_realvid.c will set aspect to disp_w/disp_h and rederive
+      // disp_w and disp_h from the RealVideo stream contents returned
+      // by the Real DLLs. If DisplayWidth/DisplayHeight was not set in
+      // the Matroska file then it has already been set to PixelWidth/Height
+      // by check_track_information.
+      sh_v->disp_w = track->v_dwidth;
+      sh_v->disp_h = track->v_dheight;
+    }
+  sh_v->ImageDesc = ImageDesc;
+  mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] Aspect: %f\n", sh_v->aspect);
+
+  sh_v->ds = demuxer->video;
+  return 0;
+}
+
+static int
+demux_mkv_open_audio (demuxer_t *demuxer, mkv_track_t *track)
+{
+  sh_audio_t *sh_a = new_sh_audio(demuxer, track->tnum);
+  demux_packet_t *dp;
+  int i;
+
+  sh_a->ds = demuxer->audio;
+  sh_a->wf = (WAVEFORMATEX *) malloc (sizeof (WAVEFORMATEX));
+  if (track->ms_compat)
+    {
+      memcpy(sh_a->wf, track->private_data, track->private_size);
+      if (track->a_sfreq == 0.0)
+        track->a_sfreq = le2me_32 (sh_a->wf->nSamplesPerSec);
+      if (track->a_channels == 0)
+        track->a_channels = le2me_16 (sh_a->wf->nChannels);
+      if (track->a_bps == 0)
+        track->a_bps = le2me_16 (sh_a->wf->wBitsPerSample);
+      track->a_formattag = le2me_16 (sh_a->wf->wFormatTag);
+    }
+  else
+    {
+      memset(sh_a->wf, 0, sizeof (WAVEFORMATEX));
+      if (!strcmp(track->codec_id, MKV_A_MP3) ||
+          !strcmp(track->codec_id, MKV_A_MP2))
+        track->a_formattag = 0x0055;
+      else if (!strncmp(track->codec_id, MKV_A_AC3, strlen(MKV_A_AC3)) ||
+               !strcmp(track->codec_id, MKV_A_DTS))
+        track->a_formattag = 0x2000;
+      else if (!strcmp(track->codec_id, MKV_A_PCM) ||
+               !strcmp(track->codec_id, MKV_A_PCM_BE))
+        track->a_formattag = 0x0001;
+      else if (!strcmp(track->codec_id, MKV_A_AAC_2MAIN) ||
+               !strncmp(track->codec_id, MKV_A_AAC_2LC,
+                        strlen(MKV_A_AAC_2LC)) ||
+               !strcmp(track->codec_id, MKV_A_AAC_2SSR) ||
+               !strcmp(track->codec_id, MKV_A_AAC_4MAIN) ||
+               !strncmp(track->codec_id, MKV_A_AAC_4LC,
+                        strlen(MKV_A_AAC_4LC)) ||
+               !strcmp(track->codec_id, MKV_A_AAC_4SSR) ||
+               !strcmp(track->codec_id, MKV_A_AAC_4LTP))
+        track->a_formattag = mmioFOURCC('M', 'P', '4', 'A');
+      else if (!strcmp(track->codec_id, MKV_A_VORBIS))
+        {
+          unsigned char *c;
+          uint32_t offset, length;
+
+          if (track->private_data == NULL)
+            return 1;
+
+          c = (unsigned char *) track->private_data;
+          if (*c != 2)
+            {
+              mp_msg (MSGT_DEMUX, MSGL_WARN, "[mkv] Vorbis track does not "
+                      "contain valid headers.\n");
+              return 1;
+            }
+
+          offset = 1;
+          for (i=0; i < 2; i++)
+            {
+              length = 0;
+              while (c[offset] == (unsigned char) 0xFF
+                     && length < track->private_size)
+                {
+                  length += 255;
+                  offset++;
+                }
+              if (offset >= (track->private_size - 1))
+                {
+                  mp_msg (MSGT_DEMUX, MSGL_WARN, "[mkv] Vorbis track "
+                          "does not contain valid headers.\n");
+                  return 1;
+                }
+              length += c[offset];
+              offset++;
+              track->header_sizes[i] = length;
+            }
+
+          track->headers[0] = &c[offset];
+          track->headers[1] = &c[offset + track->header_sizes[0]];
+          track->headers[2] = &c[offset + track->header_sizes[0] +
+                                 track->header_sizes[1]];
+          track->header_sizes[2] = track->private_size - offset
+            - track->header_sizes[0] - track->header_sizes[1];
+
+          track->a_formattag = 0xFFFE;
+        }
+      else if (!strcmp(track->codec_id, MKV_A_QDMC))
+        track->a_formattag = mmioFOURCC('Q', 'D', 'M', 'C');
+      else if (!strcmp(track->codec_id, MKV_A_QDMC2))
+        track->a_formattag = mmioFOURCC('Q', 'D', 'M', '2');
+      else if (!strcmp(track->codec_id, MKV_A_FLAC))
+        {
+          if (track->private_data == NULL || track->private_size == 0)
+            {
+              mp_msg (MSGT_DEMUX, MSGL_WARN, "[mkv] FLAC track does not "
+                      "contain valid headers.\n");
+              return 1;
+            }
+          track->a_formattag = mmioFOURCC ('f', 'L', 'a', 'C');
+        }
+      else if (track->private_size >= sizeof (real_audio_v4_props_t))
+        {
+          if (!strcmp(track->codec_id, MKV_A_REAL28))
+            track->a_formattag = mmioFOURCC('2', '8', '_', '8');
+          else if (!strcmp(track->codec_id, MKV_A_REALATRC))
+            track->a_formattag = mmioFOURCC('a', 't', 'r', 'c');
+          else if (!strcmp(track->codec_id, MKV_A_REALCOOK))
+            track->a_formattag = mmioFOURCC('c', 'o', 'o', 'k');
+          else if (!strcmp(track->codec_id, MKV_A_REALDNET))
+            track->a_formattag = mmioFOURCC('d', 'n', 'e', 't');
+          else if (!strcmp(track->codec_id, MKV_A_REALSIPR))
+            track->a_formattag = mmioFOURCC('s', 'i', 'p', 'r');
+        }
+      else
+        {
+          mp_msg (MSGT_DEMUX, MSGL_WARN, "[mkv] Unknown/unsupported audio "
+                  "codec ID '%s' for track %u or missing/faulty private "
+                  "codec data.\n", track->codec_id, track->tnum);
+          free_sh_audio (sh_a);
+          return 1;
+        }
+    }
+
+  sh_a->format = track->a_formattag;
+  sh_a->wf->wFormatTag = track->a_formattag;
+  sh_a->channels = track->a_channels;
+  sh_a->wf->nChannels = track->a_channels;
+  sh_a->samplerate = (uint32_t) track->a_sfreq;
+  sh_a->wf->nSamplesPerSec = (uint32_t) track->a_sfreq;
+  sh_a->samplesize = track->a_bps / 8;
+  if (track->a_formattag == 0x0055)  /* MP3 || MP2 */
+    {
+      sh_a->wf->nAvgBytesPerSec = 16000;
+      sh_a->wf->nBlockAlign = 1152;
+      sh_a->wf->wBitsPerSample = 0;
+      sh_a->samplesize = 0;
+    }
+  else if (!strncmp(track->codec_id, MKV_A_AC3, strlen(MKV_A_AC3)))
+    {
+      sh_a->wf->nAvgBytesPerSec = 16000;
+      sh_a->wf->nBlockAlign = 1536;
+      sh_a->wf->wBitsPerSample = 0;
+      sh_a->samplesize = 0;
+    }
+  else if (track->a_formattag == 0x0001)  /* PCM || PCM_BE */
+    {
+      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;
+      if (!strcmp(track->codec_id, MKV_A_PCM_BE))
+        sh_a->format = mmioFOURCC('t', 'w', 'o', 's');
+    }
+  else if (!strcmp(track->codec_id, MKV_A_QDMC) ||
+           !strcmp(track->codec_id, MKV_A_QDMC2))
+    {
+      sh_a->wf->wBitsPerSample = track->a_bps;
+      sh_a->wf->nAvgBytesPerSec = 16000;
+      sh_a->wf->nBlockAlign = 1486;
+      track->fix_i_bps = 1;
+      track->qt_last_a_pts = 0.0;
+      if (track->private_data != NULL)
+        {
+          sh_a->codecdata=(unsigned char *)malloc(track->private_size);
+          memcpy (sh_a->codecdata, track->private_data,
+                  track->private_size);
+          sh_a->codecdata_len = track->private_size;
+        }
+    }
+  else if (track->a_formattag == mmioFOURCC('M', 'P', '4', 'A'))
+    {
+      int profile, srate_idx;
+
+      sh_a->wf->nAvgBytesPerSec = 16000;
+      sh_a->wf->nBlockAlign = 1024;
+      sh_a->wf->wBitsPerSample = 0;
+      sh_a->samplesize = 0;
+
+      /* Recreate the 'private data' */
+      /* which faad2 uses in its initialization */
+      srate_idx = aac_get_sample_rate_index (sh_a->samplerate);
+      if (!strncmp (&track->codec_id[12], "MAIN", 4))
+        profile = 0;
+      else if (!strncmp (&track->codec_id[12], "LC", 2))
+        profile = 1;
+      else if (!strncmp (&track->codec_id[12], "SSR", 3))
+        profile = 2;
+      else
+        profile = 3;
+      sh_a->codecdata = (unsigned char *) malloc (5);
+      sh_a->codecdata[0] = ((profile+1) << 3) | ((srate_idx&0xE) >> 1);
+      sh_a->codecdata[1] = ((srate_idx&0x1)<<7)|(track->a_channels<<3);
+
+      if (strstr(track->codec_id, "SBR") != NULL)
+        {
+          /* HE-AAC (aka SBR AAC) */
+          sh_a->codecdata_len = 5;
+
+          sh_a->samplerate *= 2;
+          sh_a->wf->nSamplesPerSec *= 2;
+          srate_idx = aac_get_sample_rate_index(sh_a->samplerate);
+          sh_a->codecdata[2] = AAC_SYNC_EXTENSION_TYPE >> 3;
+          sh_a->codecdata[3] = ((AAC_SYNC_EXTENSION_TYPE&0x07)<<5) | 5;
+          sh_a->codecdata[4] = (1 << 7) | (srate_idx << 3);
+          track->default_duration = 1024.0 / (sh_a->samplerate / 2);
+        }
+      else
+        {
+          sh_a->codecdata_len = 2;
+          track->default_duration = 1024.0 / (float)sh_a->samplerate;
+        }
+    }
+  else if (track->a_formattag == 0xFFFE)  /* 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 if (track->private_size >= sizeof(real_audio_v4_props_t)
+           && !strncmp (track->codec_id, MKV_A_REALATRC, 7))
+    {
+      /* Common initialization for all RealAudio codecs */
+      real_audio_v4_props_t *ra4p;
+      real_audio_v5_props_t *ra5p;
+      unsigned char *src;
+      int codecdata_length, version;
+
+      ra4p = (real_audio_v4_props_t *) track->private_data;
+      ra5p = (real_audio_v5_props_t *) track->private_data;
+
+      sh_a->wf->wBitsPerSample = sh_a->samplesize * 8;
+      sh_a->wf->nAvgBytesPerSec = 0;  /* FIXME !? */
+      sh_a->wf->nBlockAlign = be2me_16 (ra4p->frame_size);
+
+      version = be2me_16 (ra4p->version1);
+
+      if (version == 4)
+        {
+          src = (unsigned char *) (ra4p + 1);
+          src += src[0] + 1;
+          src += src[0] + 1;
+        }
+      else
+        src = (unsigned char *) (ra5p + 1);
+
+      src += 3;
+      if (version == 5)
+        src++;
+      codecdata_length = be2me_32 (*(uint32_t *)src);
+      src += 4;
+      sh_a->wf->cbSize = 10 + codecdata_length;
+      sh_a->wf = (WAVEFORMATEX *) realloc (sh_a->wf,
+                                           sizeof (WAVEFORMATEX) +
+                                           sh_a->wf->cbSize);
+      ((short *)(sh_a->wf + 1))[0] = be2me_16 (ra4p->sub_packet_size);
+      ((short *)(sh_a->wf + 1))[1] = be2me_16 (ra4p->sub_packet_h);
+      ((short *)(sh_a->wf + 1))[2] = be2me_16 (ra4p->flavor);
+      ((short *)(sh_a->wf + 1))[3] = be2me_32 (ra4p->coded_frame_size);
+      ((short *)(sh_a->wf + 1))[4] = codecdata_length;
+      memcpy(((char *)(sh_a->wf + 1)) + 10, src, codecdata_length);
+
+      track->realmedia = 1;
+    }
+  else if (!strcmp(track->codec_id, MKV_A_FLAC) ||
+           (track->a_formattag == 0xf1ac))
+    {
+      unsigned char *ptr;
+      int size;
+      free(sh_a->wf);
+      sh_a->wf = NULL;
+
+      if (track->a_formattag == mmioFOURCC('f', 'L', 'a', 'C'))
+        {
+          ptr = (unsigned char *)track->private_data;
+          size = track->private_size;
+        }
+      else
+        {
+          sh_a->format = mmioFOURCC('f', 'L', 'a', 'C');
+          ptr = (unsigned char *) track->private_data
+            + sizeof (WAVEFORMATEX);
+          size = track->private_size - sizeof (WAVEFORMATEX);
+        }
+      if (size < 4 || ptr[0] != 'f' || ptr[1] != 'L' ||
+          ptr[2] != 'a' || ptr[3] != 'C')
+        {
+          dp = new_demux_packet (4);
+          memcpy (dp->buffer, "fLaC", 4);
+        }
+      else
+        {
+          dp = new_demux_packet (size);
+          memcpy (dp->buffer, ptr, size);
+        }
+      dp->pts = 0;
+      dp->flags = 0;
+      ds_add_packet (demuxer->audio, dp);
+    }
+  else
+    {
+      free_sh_audio (sh_a);
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+demux_mkv_open_sub (demuxer_t *demuxer, mkv_track_t *track)
+{
+  if (track->subtitle_type != MATROSKA_SUBTYPE_UNKNOWN)
+    {
+      if (track->subtitle_type == MATROSKA_SUBTYPE_VOBSUB)
+        {
+          int m, size = track->private_size;
+          uint8_t *buffer;
+          m = demux_mkv_decode (track,track->private_data,&buffer,&size,2);
+          if (buffer && m)
+            {
+              free (track->private_data);
+              track->private_data = buffer;
+            }
+        }
+    }
+  else
+    {
+      mp_msg (MSGT_DEMUX, MSGL_ERR, "[mkv] Subtitle type '%s' is not "
+              "supported.\n", track->codec_id);
+      return 1;
+    }
+
+  return 0;
+}
+
+void demux_mkv_seek (demuxer_t *demuxer, float rel_seek_secs, int flags);
+
+int
+demux_mkv_open (demuxer_t *demuxer)
+{
+  stream_t *s = demuxer->stream;
+  mkv_demuxer_t *mkv_d;
+  mkv_track_t *track;
+  int i, version, cont = 0;
+  char *str;
+
+#ifdef USE_ICONV
+  subcp_open();
+#endif
+
+  stream_seek(s, s->start_pos);
+  str = ebml_read_header (s, &version);
+  if (str == NULL || strcmp (str, "matroska") || version > 1)
+    {
+      mp_msg (MSGT_DEMUX, MSGL_ERR, "[mkv] no head found\n");
+      return 0;
+    }
+  free (str);
+
+  mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] Found the head...\n");
+
+  if (ebml_read_id (s, NULL) != MATROSKA_ID_SEGMENT)
+    {
+      mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] but no segment :(\n");
+      return 0;
+    }
+  ebml_read_length (s, NULL);  /* return bytes number until EOF */
+
+  mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] + a segment...\n");
+
+  mkv_d = (mkv_demuxer_t *) malloc (sizeof (mkv_demuxer_t));
+  memset (mkv_d, 0, sizeof(mkv_demuxer_t));
+  demuxer->priv = mkv_d;
+  mkv_d->tc_scale = 1000000;
+  mkv_d->segment_start = stream_tell (s);
+  mkv_d->parsed_cues = (off_t *) malloc (sizeof (off_t));
+  mkv_d->parsed_seekhead = (off_t *) malloc (sizeof (off_t));
+
+  for (i=0; i < SUB_MAX_TEXT; i++)
+    mkv_d->subs.text[i] = (char *) malloc (256);
+
+  while (!cont)
+    {
+      switch (ebml_read_id (s, NULL))
+        {
+        case MATROSKA_ID_INFO:
+          mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |+ segment information...\n");
+          cont = demux_mkv_read_info (demuxer);
+          break;
+
+        case MATROSKA_ID_TRACKS:
+          mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |+ segment tracks...\n");
+          cont = demux_mkv_read_tracks (demuxer);
+          break;
+
+        case MATROSKA_ID_CUES:
+          cont = demux_mkv_read_cues (demuxer);
+          break;
+
+        case MATROSKA_ID_TAGS:
+          cont = demux_mkv_read_tags (demuxer);
+          break;
+
+        case MATROSKA_ID_SEEKHEAD:
+          cont = demux_mkv_read_seekhead (demuxer);
+          break;
+
+        case MATROSKA_ID_CHAPTERS:
+          cont = demux_mkv_read_chapters (demuxer);
+          break;
+
+        case MATROSKA_ID_CLUSTER:
+          {
+            int p, l;
+            mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] |+ found cluster, headers are "
+                    "parsed completely :)\n");
+            /* get the first cluster timecode */
+            p = stream_tell(s);
+            l = ebml_read_length (s, NULL);
+            while (ebml_read_id (s, NULL) != MATROSKA_ID_CLUSTERTIMECODE)
+              {
+                ebml_read_skip (s, NULL);
+                if (stream_tell (s) >= p + l)
+                  break;
+              }
+            if (stream_tell (s) < p + l)
+              {
+                uint64_t num = ebml_read_uint (s, NULL);
+                if (num == EBML_UINT_INVALID)
+                  return 0;
+                mkv_d->first_tc = num * mkv_d->tc_scale / 1000000.0;
+                mkv_d->has_first_tc = 1;
+              }
+            stream_seek (s, p - 4);
+            cont = 1;
+            break;
+          }
+
+        default:
+          cont = 1;
+        case MATROSKA_ID_ATTACHMENTS:
+        case EBML_ID_VOID:
+          ebml_read_skip (s, NULL);
+          break;
+        }
+    }
+
+  display_tracks (mkv_d);
+
+  /* select video track */
+  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 == MATROSKA_TRACK_VIDEO
+            && 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 == MATROSKA_TRACK_VIDEO)
+            {
+              track = mkv_d->tracks[i];
+              break;
+            }
+    }
+  else if (demuxer->video->id != -2)  /* -2 = no video at all */
+    track = demux_mkv_find_track_by_num (mkv_d, demuxer->video->id,
+                                         MATROSKA_TRACK_VIDEO);
+
+  if (track)
+    {
+      for (i=0; i < mkv_d->num_tracks; i++)
+        if (mkv_d->tracks[i]->type == MATROSKA_TRACK_VIDEO)
+          {
+            int def = (mkv_d->tracks[i] == track);
+            if (demux_mkv_open_video (demuxer, mkv_d->tracks[i]) && def)
+              demuxer->video->id = -2;
+            else if (def)
+              {
+                mp_msg (MSGT_DEMUX, MSGL_INFO,
+                        "[mkv] Will play video track %u\n", track->tnum);
+                demuxer->video->id = track->tnum;
+                demuxer->video->sh = demuxer->v_streams[track->tnum];
+              }
+          }
+    }
+  else
+    {
+      mp_msg (MSGT_DEMUX, MSGL_INFO, "[mkv] No video track found/wanted.\n");
+      demuxer->video->id = -2;
+    }
+
+  /* select audio track */
+  track = NULL;
+  if (demuxer->audio->id == -1)  /* automatically select an audio track */
+    {
+      /* check if the user specified an audio language */
+      if (audio_lang != NULL)
+        track = demux_mkv_find_track_by_language(mkv_d, audio_lang,
+                                                 MATROSKA_TRACK_AUDIO);
+      if (track == NULL)
+        /* no audio language specified, or language not found */
+        /* 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 == MATROSKA_TRACK_AUDIO
+              && 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 == MATROSKA_TRACK_AUDIO)
+            {
+              track = mkv_d->tracks[i];
+              break;
+            }
+    }
+  else if (demuxer->audio->id != -2)  /* -2 = no audio at all */
+    track = demux_mkv_find_track_by_num (mkv_d, demuxer->audio->id,
+                                         MATROSKA_TRACK_AUDIO);
+
+  if (track)
+    {
+      for (i=0; i < mkv_d->num_tracks; i++)
+        if (mkv_d->tracks[i]->type == MATROSKA_TRACK_AUDIO)
+          {
+            int def = (mkv_d->tracks[i] == track);
+            if (demux_mkv_open_audio (demuxer, mkv_d->tracks[i]) && def)
+              demuxer->audio->id = -2;
+            else if (def)
+              {
+                mp_msg (MSGT_DEMUX, MSGL_INFO,
+                        "[mkv] Will play audio track %u\n", track->tnum);
+                demuxer->audio->id = track->tnum;
+                demuxer->audio->sh = demuxer->a_streams[track->tnum];
+              }
+          }
+    }
+  else
+    {
+      mp_msg (MSGT_DEMUX, MSGL_INFO, "[mkv] No audio track found/wanted.\n");
+      demuxer->video->id = -2;
+    }
+
+  /* DO NOT automatically select a subtitle track and behave like DVD */
+  /* playback: only show subtitles if the user explicitely wants them. */
+  track = NULL;
+  if (demuxer->sub->id >= 0)
+    track = demux_mkv_find_track_by_num (mkv_d, demuxer->sub->id,
+                                         MATROSKA_TRACK_SUBTITLE);
+  else if (dvdsub_lang != NULL)
+    track = demux_mkv_find_track_by_language (mkv_d, dvdsub_lang,
+                                              MATROSKA_TRACK_SUBTITLE);
+
+  for (i=0; i < mkv_d->num_tracks; i++)
+    if (mkv_d->tracks[i]->type == MATROSKA_TRACK_SUBTITLE)
+      {
+        int def = (mkv_d->tracks[i] == track);
+        if (demux_mkv_open_sub (demuxer, mkv_d->tracks[i]) && def)
+          demuxer->sub->id = -2;
+        else if (def)
+          {
+            mp_msg (MSGT_DEMUX, MSGL_INFO,
+                    "[mkv] Will display subtitle track %u\n", track->tnum);
+            demuxer->sub->id = track->tnum;
+          }
+      }
+
+  if (mkv_d->chapters)
+    {
+      for (i=0; i < (int)mkv_d->num_chapters; i++)
+        {
+          mkv_d->chapters[i].start -= mkv_d->first_tc;
+          mkv_d->chapters[i].end -= mkv_d->first_tc;
+        }
+      if (dvd_last_chapter > 0 && dvd_last_chapter <= mkv_d->num_chapters)
+        {
+          if (mkv_d->chapters[dvd_last_chapter-1].end != 0)
+            mkv_d->stop_timecode = mkv_d->chapters[dvd_last_chapter-1].end;
+          else if (dvd_last_chapter + 1 <= mkv_d->num_chapters)
+            mkv_d->stop_timecode = mkv_d->chapters[dvd_last_chapter].start;
+        }
+    }
+
+  if (s->end_pos == 0 || (mkv_d->indexes == NULL && index_mode < 0))
+    demuxer->seekable = 0;
+  else
+    {
+      demuxer->movi_start = s->start_pos;
+      demuxer->movi_end = s->end_pos;
+      demuxer->seekable = 1;
+      if (mkv_d->chapters && dvd_chapter>1 && dvd_chapter<=mkv_d->num_chapters)
+        {
+          if (!mkv_d->has_first_tc)
+            {
+              mkv_d->first_tc = 0;
+              mkv_d->has_first_tc = 1;
+            }
+          demux_mkv_seek (demuxer,
+                          mkv_d->chapters[dvd_chapter-1].start/1000.0, 1);
+        }
+    }
+
+  return 1;
+}
+
+void
+demux_close_mkv (demuxer_t *demuxer)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+
+  if (mkv_d)
+    {
+      int i;
+      if (mkv_d->tracks)
+        {
+          for (i=0; i<mkv_d->num_tracks; i++)
+            {
+              if (mkv_d->tracks[i]->codec_id)
+                free (mkv_d->tracks[i]->codec_id);
+              if (mkv_d->tracks[i]->language)
+                free (mkv_d->tracks[i]->language);
+              if (mkv_d->tracks[i]->private_data)
+                free (mkv_d->tracks[i]->private_data);
+            }
+          for (i=0; i < SUB_MAX_TEXT; i++)
+            if (mkv_d->subs.text[i])
+              free (mkv_d->subs.text[i]);
+          free (mkv_d->tracks);
+        }
+      if (mkv_d->indexes)
+        free (mkv_d->indexes);
+      if (mkv_d->cluster_positions)
+        free (mkv_d->cluster_positions);
+      if (mkv_d->chapters)
+        free (mkv_d->chapters);
+      if (mkv_d->parsed_cues)
+        free (mkv_d->parsed_cues);
+      if (mkv_d->parsed_seekhead)
+        free (mkv_d->parsed_seekhead);
+      free (mkv_d);
+    }
+}
+
+static int
+demux_mkv_read_block_lacing (uint8_t *buffer, uint64_t *size,
+                             uint8_t *laces, uint32_t *lace_size)
+{
+  uint32_t total = 0;
+  uint8_t flags;
+  int i;
+
+  /* lacing flags */
+  flags = *buffer++;
+  (*size)--;
+
+  switch ((flags & 0x06) >> 1)
+    {
+    case 0:  /* no lacing */
+      *laces = 1;
+      lace_size[0] = *size;
+      break;
+
+    case 1:  /* xiph lacing */
+    case 2:  /* fixed-size lacing */
+    case 3:  /* EBML lacing */
+      *laces = *buffer++;
+      (*size)--;
+      if (++*laces > 8)
+        return 1;
+
+      switch ((flags & 0x06) >> 1)
+        {
+        case 1:  /* xiph lacing */
+          for (i=0; i < *laces-1; i++)
+            {
+              lace_size[i] = 0; 
+              do
+                {
+                  lace_size[i] += *buffer;
+                  (*size)--;
+                } while (*buffer++ == 0xFF);
+              total += lace_size[i];
+            }
+          lace_size[i] = *size - total;
+          break;
+
+        case 2:  /* fixed-size lacing */
+          for (i=0; i < *laces; i++)
+            lace_size[i] = *size / *laces;
+          break;
+
+        case 3:  /* EBML lacing */
+          {
+            int l;
+            uint64_t num = ebml_read_vlen_uint (buffer, &l);
+            if (num == EBML_UINT_INVALID)
+              return 1;
+            buffer += l;
+            *size -= l;
+
+            total = lace_size[0] = num;
+            for (i=1; i < *laces-1; i++)
+              {
+                int64_t snum;
+                snum = ebml_read_vlen_int (buffer, &l);
+                if (snum == EBML_INT_INVALID)
+                  return 1;
+                buffer += l;
+                *size -= l;
+                lace_size[i] = lace_size[i-1] + snum;
+                total += lace_size[i];
+              }
+            lace_size[i] = *size - total;
+            break;
+          }
+        }
+      break;
+    }
+  return 0;
+}
+
+static void
+handle_subtitles(demuxer_t *demuxer, mkv_track_t *track, char *block,
+                 int64_t size, uint64_t block_duration, uint64_t timecode)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  char *ptr1, *ptr2;
+  int state, i;
+
+  if (block_duration == 0)
+    {
+      mp_msg (MSGT_DEMUX, MSGL_WARN, "[mkv] Warning: No BlockDuration "
+              "for subtitle track found.\n");
+      return;
+    }
+
+  ptr1 = block;
+  while ((*ptr1 == '\n' || *ptr1 == '\r') && ptr1 - block <= size)
+    ptr1++;
+
+  if (mkv_d->subs.lines > SUB_MAX_TEXT - 2)
+    {
+      mp_msg (MSGT_DEMUX, MSGL_WARN, "[mkv] Warning: too many sublines "
+              "to render, skipping\n");
+      return;
+    }
+  ptr2 = mkv_d->subs.text[mkv_d->subs.lines];
+  state = 0;
+
+  if (track->subtitle_type == MATROSKA_SUBTYPE_SSA)
+    {
+      /* Find text section. */
+      for (i=0; i < 8 && *ptr1 != '\0'; ptr1++)
+        if (*ptr1 == ',')
+          i++;
+      if (*ptr1 == '\0')  /* Broken line? */
+        return;
+
+      /* Load text. */
+      while (ptr1 - block < size)
+        {
+          if (*ptr1 == '{')
+            state = 1;
+          else if (*ptr1 == '}' && state == 1)
+            state = 2;
+
+          if (state == 0)
+            {
+              *ptr2++ = *ptr1;
+              if (ptr2 - mkv_d->subs.text[mkv_d->subs.lines] >= 255)
+                break;
+            }
+          ptr1++;
+      
+          /* Newline */
+          if (*ptr1 == '\\' && ptr1+1-block < size && (*(ptr1+1)|0x20) == 'n')
+            {
+              mkv_d->clear_subs_at[mkv_d->subs.lines++]
+                = timecode + block_duration;
+              *ptr2 = '\0';
+              ptr2 = mkv_d->subs.text[mkv_d->subs.lines];
+              ptr1 += 2;
+            }
+
+          if (state == 2)
+            state = 0;
+        }
+      *ptr2 = '\0';
+    }
+  else
+    {
+      while (ptr1 - block != size)
+        {
+          if (*ptr1 == '\n' || *ptr1 == '\r')
+            {
+              if (state == 0)  /* normal char --> newline */
+                {
+                  if (mkv_d->subs.lines == SUB_MAX_TEXT - 1)
+                    break;
+                  *ptr2 = '\0';
+                  mkv_d->clear_subs_at[mkv_d->subs.lines++]
+                    = timecode + block_duration;
+                  ptr2 = mkv_d->subs.text[mkv_d->subs.lines];
+                  state = 1;
+                }
+            }
+          else if (*ptr1 == '<')  /* skip HTML tags */
+            state = 2;
+          else if (*ptr1 == '>')
+            state = 0;
+          else if (state != 2)  /* normal character */
+            {
+              state = 0;
+              if ((ptr2 - mkv_d->subs.text[mkv_d->subs.lines]) < 255)
+                *ptr2++ = *ptr1;
+            }
+          ptr1++;
+        }
+      *ptr2 = '\0';
+    }
+  mkv_d->clear_subs_at[mkv_d->subs.lines++] = timecode + block_duration;
+
+#ifdef USE_ICONV
+  subcp_recode1 (&mkv_d->subs);
+#endif
+  vo_sub = &mkv_d->subs;
+  vo_osd_changed (OSDTYPE_SUBTITLE);
+}
+
+static void
+clear_subtitles(demuxer_t *demuxer, uint64_t timecode)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  int i, lines_cut = 0;
+  char *tmp;
+
+  /* Clear the subtitles if they're obsolete now. */
+  for (i=0; i < mkv_d->subs.lines; i++)
+    {
+      if (mkv_d->clear_subs_at[i] <= timecode)
+        {
+          tmp = mkv_d->subs.text[i];
+          memmove (mkv_d->subs.text+i, mkv_d->subs.text+i+1,
+                   (mkv_d->subs.lines-i-1) * sizeof (*mkv_d->subs.text));
+          memmove (mkv_d->clear_subs_at+i, mkv_d->clear_subs_at+i+1,
+                   (mkv_d->subs.lines-i-1) * sizeof (*mkv_d->clear_subs_at));
+          mkv_d->subs.text[mkv_d->subs.lines--] = tmp;
+          i--;
+          lines_cut = 1;
+        }
+    }
+  if (lines_cut)
+    {
+      vo_sub = &mkv_d->subs;
+      vo_osd_changed (OSDTYPE_SUBTITLE);
+    }
+}
+
+// Taken from demux_real.c. Thanks to the original developpers :)
+#define SKIP_BITS(n) buffer <<= n
+#define SHOW_BITS(n) ((buffer) >> (32 - (n)))
+
+static float real_fix_timestamp(mkv_track_t *track, unsigned char *s,
+                                int timestamp) {
+  float v_pts;
+  uint32_t buffer = (s[0] << 24) + (s[1] << 16) + (s[2] << 8) + s[3];
+  int kf = timestamp;
+  int pict_type;
+  int orig_kf;
+
+  if (!strcmp(track->codec_id, MKV_V_REALV30) ||
+      !strcmp(track->codec_id, MKV_V_REALV40)) {
+
+    if (!strcmp(track->codec_id, MKV_V_REALV30)) {
+      SKIP_BITS(3);
+      pict_type = SHOW_BITS(2);
+      SKIP_BITS(2 + 7);
+    }else{
+      SKIP_BITS(1);
+      pict_type = SHOW_BITS(2);
+      SKIP_BITS(2 + 7 + 3);
+    }
+    kf = SHOW_BITS(13);         // kf= 2*SHOW_BITS(12);
+    orig_kf = kf;
+    if (pict_type <= 1) {
+      // I frame, sync timestamps:
+      track->rv_kf_base = timestamp - kf;
+      mp_msg(MSGT_DEMUX, MSGL_V, "\nTS: base=%08X\n", track->rv_kf_base);
+      kf = timestamp;
+    } else {
+      // P/B frame, merge timestamps:
+      int tmp = timestamp - track->rv_kf_base;
+      kf |= tmp & (~0x1fff);    // combine with packet timestamp
+      if (kf < (tmp - 4096))    // workaround wrap-around problems
+        kf += 8192;
+      else if (kf > (tmp + 4096))
+        kf -= 8192;
+      kf += track->rv_kf_base;
+    }
+    if (pict_type != 3) {       // P || I  frame -> swap timestamps
+      int tmp = kf;
+      kf = track->rv_kf_pts;
+      track->rv_kf_pts = tmp;
+    }
+    mp_msg(MSGT_DEMUX, MSGL_V, "\nTS: %08X -> %08X (%04X) %d %02X %02X %02X "
+           "%02X %5d\n", timestamp, kf, orig_kf, pict_type, s[0], s[1], s[2],
+           s[3], kf - (int)(1000.0 * track->rv_pts));
+  }
+  v_pts = kf * 0.001f;
+  track->rv_pts = v_pts;
+
+  return v_pts;
+}
+
+static void
+handle_realvideo (demuxer_t *demuxer, mkv_track_t *track, uint8_t *buffer,
+                  uint32_t size, int block_bref)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  demux_packet_t *dp;
+  dp_hdr_t *hdr;
+  uint8_t chunks;
+  int isize;
+
+  chunks = *buffer++;
+  isize = --size - (chunks+1)*8;
+  dp = new_demux_packet (sizeof (*hdr) + size);
+  memcpy (dp->buffer + sizeof(*hdr), buffer + (chunks+1)*8, isize);
+  memcpy (dp->buffer + sizeof(*hdr) + isize, buffer, (chunks+1)*8);
+
+  hdr = (dp_hdr_t *) dp->buffer;
+  hdr->len = isize;
+  hdr->chunks = chunks;
+  hdr->timestamp = mkv_d->last_pts * 1000;
+  hdr->chunktab = sizeof(*hdr) + isize;
+
+  dp->len = sizeof(*hdr) + size;
+  if (mkv_d->v_skip_to_keyframe)
+    {
+      dp->pts = mkv_d->last_pts;
+      track->rv_kf_base = 0;
+      track->rv_kf_pts = hdr->timestamp;
+    }
+  else
+    dp->pts = real_fix_timestamp (track, dp->buffer + sizeof(*hdr),
+                                  hdr->timestamp);
+  dp->pos = demuxer->filepos;
+  dp->flags = block_bref ? 0 : 0x10;
+
+  ds_add_packet(demuxer->video, dp);
+}
+
+static void
+handle_realaudio (demuxer_t *demuxer, mkv_track_t *track, uint8_t *buffer,
+                  uint32_t size, int block_bref)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  demux_packet_t *dp = new_demux_packet (size);
+
+  memcpy (dp->buffer, buffer, size);
+  if (track->ra_pts == mkv_d->last_pts && !mkv_d->a_skip_to_keyframe)
+    dp->pts = 0;
+  else
+    dp->pts = mkv_d->last_pts;
+  track->ra_pts = mkv_d->last_pts;
+
+  dp->pos = demuxer->filepos;
+  dp->flags = block_bref ? 0 : 0x10;
+  ds_add_packet (demuxer->audio, dp);
+}
+
+static int
+handle_block (demuxer_t *demuxer, uint8_t *block, uint64_t length,
+              uint64_t block_duration, int64_t block_bref)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  mkv_track_t *track = NULL;
+  demux_stream_t *ds = NULL;
+  uint64_t old_length;
+  int64_t tc;
+  uint32_t lace_size[8];
+  uint8_t laces;
+  int i, num, tmp, use_this_block = 1;
+  float current_pts;
+  int16_t time;
+
+  /* first byte(s): track num */
+  num = ebml_read_vlen_uint (block, &tmp);
+  block += tmp;
+  /* time (relative to cluster time) */
+  time = be2me_16 (* (int16_t *) block);
+  block += 2;
+  length -= tmp + 2;
+  old_length = length;
+  demux_mkv_read_block_lacing (block, &length, &laces, lace_size);
+  block += old_length - length;
+
+  tc = ((time*mkv_d->tc_scale+mkv_d->cluster_tc) /1000000.0 - mkv_d->first_tc);
+  if (tc < 0)
+    tc = 0;
+  if (mkv_d->stop_timecode > 0 && tc > mkv_d->stop_timecode)
+    return -1;
+  current_pts = tc / 1000.0;
+
+  clear_subtitles(demuxer, tc);
+
+  for (i=0; i<mkv_d->num_tracks; i++)
+    if (mkv_d->tracks[i]->tnum == num)
+      track = mkv_d->tracks[i];
+  if (num == demuxer->audio->id)
+    {
+      ds = demuxer->audio;
+
+      if (mkv_d->a_skip_to_keyframe && block_bref != 0)
+        use_this_block = 0;
+      else if (mkv_d->v_skip_to_keyframe)
+        use_this_block = 0;
+
+      if (track->fix_i_bps && use_this_block)
+        {
+          sh_audio_t *sh = (sh_audio_t *) ds->sh;
+
+          if (block_duration != 0)
+            {
+              sh->i_bps = length * 1000 / block_duration;
+              track->fix_i_bps = 0;
+            }
+          else if (track->qt_last_a_pts == 0.0)
+            track->qt_last_a_pts = current_pts;
+          else if(track->qt_last_a_pts != current_pts)
+            {
+              sh->i_bps = length / (current_pts - track->qt_last_a_pts);
+              track->fix_i_bps = 0;
+            }
+        }
+    }
+  else if (tc < mkv_d->skip_to_timecode)
+    use_this_block = 0;
+  else if (num == demuxer->video->id)
+    {
+      ds = demuxer->video;
+      if (mkv_d->v_skip_to_keyframe && block_bref != 0)
+        use_this_block = 0;
+    }
+  else if (num == demuxer->sub->id)
+    {
+      ds = demuxer->sub;
+      if (track->subtitle_type != MATROSKA_SUBTYPE_VOBSUB)
+        {
+          if (!mkv_d->v_skip_to_keyframe)
+            handle_subtitles (demuxer, track, block, length,
+                              block_duration, tc);
+          use_this_block = 0;
+        }
+    }
+  else
+    use_this_block = 0;
+
+  if (use_this_block)
+    {
+      mkv_d->last_pts = ds->pts = current_pts;
+      mkv_d->last_filepos = demuxer->filepos;
+
+      for (i=0; i < laces; i++)
+        {
+          if (ds == demuxer->video && track->realmedia)
+            handle_realvideo (demuxer, track, block, lace_size[i], block_bref);
+          else if (ds == demuxer->audio && track->realmedia)
+            handle_realaudio (demuxer, track, block, lace_size[i], block_bref);
+          else
+            {
+              int modified, size = lace_size[i];
+              demux_packet_t *dp;
+              uint8_t *buffer;
+              modified = demux_mkv_decode (track, block, &buffer, &size, 1);
+              if (buffer)
+                {
+                  dp = new_demux_packet (size);
+                  memcpy (dp->buffer, buffer, size);
+                  if (modified)
+                    free (buffer);
+                  dp->flags = block_bref == 0 ? 1 : 0;
+                  dp->pts = mkv_d->last_pts + i * track->default_duration;
+                  ds_add_packet (ds, dp);
+                }
+            }
+          block += lace_size[i];
+        }
+
+      if (ds == demuxer->video)
+        {
+          mkv_d->v_skip_to_keyframe = 0;
+          mkv_d->skip_to_timecode = 0;
+        }
+      else if (ds == demuxer->audio)
+        mkv_d->a_skip_to_keyframe = 0;
+
+      return 1;
+    }
+
+  return 0;
+}
+
+int
+demux_mkv_fill_buffer (demuxer_t *demuxer)
+{
+  mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+  stream_t *s = demuxer->stream;
+  uint64_t l;
+  int il, tmp;
+
+  while (1)
+    {
+      while (mkv_d->cluster_size > 0)
+        {
+          uint64_t block_duration = 0,  block_length = 0;
+          int64_t block_bref = 0;
+          uint8_t *block = NULL;
+
+          while (mkv_d->blockgroup_size > 0)
+            {
+              switch (ebml_read_id (s, &il))
+                {
+                case MATROSKA_ID_BLOCKDURATION:
+                  {
+                    block_duration = ebml_read_uint (s, &l);
+                    if (block_duration == EBML_UINT_INVALID)
+                      return 0;
+                    break;
+                  }
+
+                case MATROSKA_ID_BLOCK:
+                  block_length = ebml_read_length (s, &tmp);
+                  block = (uint8_t *) malloc (block_length);
+                  demuxer->filepos = stream_tell (s);
+                  if (stream_read (s,block,block_length) != (int) block_length)
+                    return 0;
+                  l = tmp + block_length;
+                  break;
+
+                case MATROSKA_ID_REFERENCEBLOCK:
+                  {
+                    int64_t num = ebml_read_int (s, &l);
+                    if (num == EBML_INT_INVALID)
+                      return 0;
+                    if (num < 0)
+                      block_bref = num;
+                    break;
+                  }
+
+                default:
+                  ebml_read_skip (s, &l);
+                  break;
+                }
+              mkv_d->blockgroup_size -= l + il;
+              mkv_d->cluster_size -= l + il;
+            }
+
+          if (block)
+            {
+              int res = handle_block (demuxer, block, block_length,
+                                      block_duration, block_bref);
+              free (block);
+              if (res < 0)
+                return 0;
+              if (res)
+                return 1;
+            }
+
+          if (mkv_d->cluster_size > 0)
+            {
+              switch (ebml_read_id (s, &il))
+                {
+                case MATROSKA_ID_CLUSTERTIMECODE:
+                  {
+                    uint64_t num = ebml_read_uint (s, &l);
+                    if (num == EBML_UINT_INVALID)
+                      return 0;
+                    if (!mkv_d->has_first_tc)
+                      {
+                        mkv_d->first_tc = num * mkv_d->tc_scale / 1000000.0;
+                        mkv_d->has_first_tc = 1;
+                      }
+                    mkv_d->cluster_tc = num * mkv_d->tc_scale;
+                    break;
+                  }
+
+                case MATROSKA_ID_BLOCKGROUP:
+                  mkv_d->blockgroup_size = ebml_read_length (s, &tmp);
+                  l = tmp;
+                  break;
+
+                default:
+                  ebml_read_skip (s, &l);
+                  break;
+                }
+              mkv_d->cluster_size -= l + il;
+            }
+        }
+
+      if (ebml_read_id (s, &il) != MATROSKA_ID_CLUSTER)
+        return 0;
+      add_cluster_position(mkv_d, stream_tell(s)-il);
+      mkv_d->cluster_size = ebml_read_length (s, NULL);
+    }
+
+  return 0;
+}
+
+void
+demux_mkv_seek (demuxer_t *demuxer, float rel_seek_secs, int flags)
+{
+  if (!(flags & 2))  /* time in secs */
+    {
+      void resync_audio_stream(sh_audio_t *sh_audio);
+      mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
+      stream_t *s = demuxer->stream;
+      int64_t target_timecode = 0, diff, min_diff=0xFFFFFFFL;
+      int i;
+
+      if (!(flags & 1))  /* 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;
+
+      if (mkv_d->indexes == NULL)  /* no index was found */
+        {
+          uint64_t target_filepos, cluster_pos, max_pos;
+
+          target_filepos = (uint64_t) (target_timecode * mkv_d->last_filepos
+                                       / (mkv_d->last_pts * 1000.0));
+
+          max_pos = mkv_d->cluster_positions[mkv_d->num_cluster_pos-1];
+          if (target_filepos > max_pos)
+            {
+              if ((off_t) max_pos > stream_tell (s))
+                stream_seek (s, max_pos);
+              else
+                stream_seek (s, stream_tell (s) + mkv_d->cluster_size);
+              /* parse all the clusters upto target_filepos */
+              while (!s->eof && stream_tell(s) < (off_t) target_filepos)
+                {
+                  switch (ebml_read_id (s, &i))
+                    {
+                    case MATROSKA_ID_CLUSTER:
+                      add_cluster_position(mkv_d, (uint64_t) stream_tell(s)-i);
+                      break;
+
+                    case MATROSKA_ID_CUES:
+                      demux_mkv_read_cues (demuxer);
+                      break;
+                    }
+                  ebml_read_skip (s, NULL);
+                }
+              if (s->eof)
+                stream_reset(s);
+            }
+
+          if (mkv_d->indexes == NULL)
+            {
+              cluster_pos = mkv_d->cluster_positions[0];
+              /* Let's find the nearest cluster */
+              for (i=0; i < mkv_d->num_cluster_pos; i++)
+                {
+                  diff = mkv_d->cluster_positions[i] - target_filepos;
+                  if (rel_seek_secs < 0 && diff < 0 && -diff < min_diff)
+                    {
+                      cluster_pos = mkv_d->cluster_positions[i];
+                      min_diff = -diff;
+                    }
+                  else if (rel_seek_secs > 0
+                           && (diff < 0 ? -1 * diff : diff) < min_diff)
+                    {
+                      cluster_pos = mkv_d->cluster_positions[i];
+                      min_diff = diff < 0 ? -1 * diff : diff;
+                    }
+                }
+              mkv_d->cluster_size = mkv_d->blockgroup_size = 0;
+              stream_seek (s, cluster_pos);
+            }
+        }
+      else
+        {
+          mkv_index_t *index = NULL;
+
+          /* let's find the entry in the indexes with the smallest */
+          /* difference to the wanted timecode. */
+          for (i=0; i < mkv_d->num_indexes; i++)
+            if (mkv_d->indexes[i].tnum == demuxer->video->id)
+              {
+                diff = target_timecode - (int64_t) mkv_d->indexes[i].timecode;
+
+                if ((flags & 1 || target_timecode <= mkv_d->last_pts*1000)
+                    && diff >= 0 && diff < min_diff)
+                  {
+                    min_diff = diff;
+                    index = mkv_d->indexes + i;
+                  }
+                else if (target_timecode > mkv_d->last_pts*1000
+                         && diff < 0 && -diff < min_diff)
+                  {
+                    min_diff = -diff;
+                    index = mkv_d->indexes + i;
+                  }
+              }
+
+          if (index)  /* We've found an entry. */
+            {
+              mkv_d->cluster_size = mkv_d->blockgroup_size = 0;
+              stream_seek (s, index->filepos);
+            }
+        }
+
+      if (demuxer->video->id >= 0)
+        mkv_d->v_skip_to_keyframe = 1;
+      if (rel_seek_secs > 0.0)
+        mkv_d->skip_to_timecode = target_timecode;
+      mkv_d->a_skip_to_keyframe = 1;
+
+      demux_mkv_fill_buffer(demuxer);
+
+      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");
+}
+
+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 == 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 == 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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/ebml.c	Mon Jan 19 19:16:10 2004 +0000
@@ -0,0 +1,386 @@
+/*
+ * native ebml reader for the Matroska demuxer
+ * Written by Aurelien Jacobs <aurel@gnuage.org>
+ * Based on the one written by Ronald Bultje for gstreamer
+ * Licence: GPL
+ */
+
+#include "config.h"
+#ifdef HAVE_MATROSKA
+
+#include <stdlib.h>
+
+#include "stream.h"
+#include "ebml.h"
+
+
+/*
+ * Read: the element content data ID.
+ * Return: the ID.
+ */
+uint32_t
+ebml_read_id (stream_t *s, int *length)
+{
+  int i, len_mask = 0x80;
+  uint32_t id;
+
+  for (i=0, id=stream_read_char (s); i<4 && !(id & len_mask); i++)
+    len_mask >>= 1;
+  if (i >= 4)
+    return EBML_ID_INVALID;
+  if (length)
+    *length = i + 1;
+  while (i--)
+    id = (id << 8) | stream_read_char (s);
+  return id;
+}
+
+/*
+ * Read a variable length unsigned int.
+ */
+uint64_t
+ebml_read_vlen_uint (uint8_t *buffer, int *length)
+{
+  int i, j, num_ffs = 0, len_mask = 0x80;
+  uint64_t num;
+
+  for (i=0, num=*buffer++; i<8 && !(num & len_mask); i++)
+    len_mask >>= 1;
+  if (i >= 8)
+    return EBML_UINT_INVALID;
+  j = i+1;
+  if (length)
+    *length = j;
+  if (((int)num &= (len_mask - 1)) == len_mask - 1)
+    num_ffs++;
+  while (i--)
+    {
+      num = (num << 8) | *buffer++;
+      if ((num & 0xFF) == 0xFF)
+        num_ffs++;
+    }
+  if (j == num_ffs)
+    return EBML_UINT_INVALID;
+  return num;
+}
+
+/*
+ * Read a variable length signed int.
+ */
+int64_t
+ebml_read_vlen_int (uint8_t *buffer, int *length)
+{
+  uint64_t unum;
+  int l;
+
+  /* read as unsigned number first */
+  unum = ebml_read_vlen_uint (buffer, &l);
+  if (unum == EBML_UINT_INVALID)
+    return EBML_INT_INVALID;
+  if (length)
+    *length = l;
+
+  return unum - ((1 << ((7 * l) - 1)) - 1);
+}
+
+/*
+ * Read: element content length.
+ */
+uint64_t
+ebml_read_length (stream_t *s, int *length)
+{
+  int i, j, num_ffs = 0, len_mask = 0x80;
+  uint64_t len;
+
+  for (i=0, len=stream_read_char (s); i<8 && !(len & len_mask); i++)
+    len_mask >>= 1;
+  if (i >= 8)
+    return EBML_UINT_INVALID;
+  j = i+1;
+  if (length)
+    *length = j;
+  if (((int)len &= (len_mask - 1)) == len_mask - 1)
+    num_ffs++;
+  while (i--)
+    {
+      len = (len << 8) | stream_read_char (s);
+      if ((len & 0xFF) == 0xFF)
+        num_ffs++;
+    }
+  if (j == num_ffs)
+    return EBML_UINT_INVALID;
+  return len;
+}
+
+/*
+ * Read the next element as an unsigned int.
+ */
+uint64_t
+ebml_read_uint (stream_t *s, uint64_t *length)
+{
+  uint64_t len, value = 0;
+  int l;
+
+  len = ebml_read_length (s, &l);
+  if (len == EBML_UINT_INVALID || len < 1 || len > 8)
+    return EBML_UINT_INVALID;
+  if (length)
+    *length = len + l;
+
+  while (len--)
+    value = (value << 8) | stream_read_char (s);
+
+  return value;
+}
+
+/*
+ * Read the next element as a signed int.
+ */
+int64_t
+ebml_read_int (stream_t *s, uint64_t *length)
+{
+  int64_t value = 0;
+  uint64_t len;
+  int l;
+
+  len = ebml_read_length (s, &l);
+  if (len == EBML_UINT_INVALID || len < 1 || len > 8)
+    return EBML_INT_INVALID;
+  if (length)
+    *length = len + l;
+
+  len--;
+  l = stream_read_char (s);
+  if (l & 0x80)
+    value = -1;
+  value = (value << 8) | l;
+  while (len--)
+    value = (value << 8) | stream_read_char (s);
+
+  return value;
+}
+
+/*
+ * Read the next element as a float.
+ */
+long double
+ebml_read_float (stream_t *s, uint64_t *length)
+{
+  long double value;
+  uint64_t len;
+  int l;
+
+  len = ebml_read_length (s, &l);
+  switch (len)
+    {
+    case 4:
+      {
+        uint32_t i;
+        float *f;
+#ifndef WORDS_BIGENDIAN
+        i = stream_read_dword (s);
+#else
+        i = stream_read_dword_le (s);
+#endif
+        f = (float *) (void *) &i;
+        value = *f;
+        break;
+      }
+
+    case 8:
+      {
+        uint64_t i;
+        double *d;
+#ifndef WORDS_BIGENDIAN
+        i = stream_read_qword (s);
+#else
+        i = stream_read_qword_le (s);
+#endif
+        d = (double *) (void *) &i;
+        value = *d;
+        break;
+      }
+
+    case 10:
+      {
+        uint8_t data[10];
+#ifdef WORDS_BIGENDIAN
+        int i = 10;
+#endif
+        if (stream_read (s, data, 10) != 10)
+          return EBML_FLOAT_INVALID;
+#ifndef WORDS_BIGENDIAN
+        value = * (long double *) data;
+#else
+        while (i--)
+          ((uint8_t *) &value)[i] = data[9 - i];
+#endif
+        break;
+      }
+
+    default:
+      return EBML_FLOAT_INVALID;
+    }
+
+  if (length)
+    *length = len + l;
+
+  return value;
+}
+
+/*
+ * Read the next element as an ASCII string.
+ */
+char *
+ebml_read_ascii (stream_t *s, uint64_t *length)
+{
+  uint64_t len;
+  char *str;
+  int l;
+
+  len = ebml_read_length (s, &l);
+  if (len == EBML_UINT_INVALID)
+    return NULL;
+  if (length)
+    *length = len + l;
+
+  str = (char *) malloc (len+1);
+  if (stream_read(s, str, len) != (int) len)
+    {
+      free (str);
+      return NULL;
+    }
+  str[len] = '\0';
+
+  return str;
+}
+
+/*
+ * Read the next element as a UTF-8 string.
+ */
+char *
+ebml_read_utf8 (stream_t *s, uint64_t *length)
+{
+  return ebml_read_ascii (s, length);
+}
+
+/*
+ * Skip the next element.
+ */
+int
+ebml_read_skip (stream_t *s, uint64_t *length)
+{
+  uint64_t len;
+  int l;
+
+  len = ebml_read_length (s, &l);
+  if (len == EBML_UINT_INVALID)
+    return 1;
+  if (length)
+    *length = len + l;
+
+  stream_skip(s, len);
+
+  return 0;
+}
+
+/*
+ * Read the next element, but only the header. The contents
+ * are supposed to be sub-elements which can be read separately.
+ */
+uint32_t
+ebml_read_master (stream_t *s, uint64_t *length)
+{
+  uint64_t len;
+  uint32_t id;
+
+  id = ebml_read_id (s, NULL);
+  if (id == EBML_ID_INVALID)
+    return id;
+
+  len = ebml_read_length (s, NULL);
+  if (len == EBML_UINT_INVALID)
+    return EBML_ID_INVALID;
+  if (length)
+    *length = len;
+
+  return id;
+}
+
+
+/*
+ * Read an EBML header.
+ */
+char *
+ebml_read_header (stream_t *s, int *version)
+{
+  uint64_t length, l, num;
+  uint32_t id;
+  char *str = NULL;
+
+  if (ebml_read_master (s, &length) != EBML_ID_HEADER)
+    return 0;
+
+  if (version)
+    *version = 1;
+
+  while (length > 0)
+    {
+      id = ebml_read_id (s, NULL);
+      if (id == EBML_ID_INVALID)
+        return NULL;
+      length -= 2;
+
+      switch (id)
+        {
+          /* is our read version uptodate? */
+        case EBML_ID_EBMLREADVERSION:
+          num = ebml_read_uint (s, &l);
+          if (num != EBML_VERSION)
+            return NULL;
+          break;
+
+          /* we only handle 8 byte lengths at max */
+        case EBML_ID_EBMLMAXSIZELENGTH:
+          num = ebml_read_uint (s, &l);
+          if (num != sizeof (uint64_t))
+            return NULL;
+          break;
+
+          /* we handle 4 byte IDs at max */
+        case EBML_ID_EBMLMAXIDLENGTH:
+          num = ebml_read_uint (s, &l);
+          if (num != sizeof (uint32_t))
+            return NULL;
+          break;
+
+        case EBML_ID_DOCTYPE:
+          str = ebml_read_ascii (s, &l);
+          if (str == NULL)
+            return NULL;
+          break;
+
+        case EBML_ID_DOCTYPEREADVERSION:
+          num = ebml_read_uint (s, &l);
+          if (num == EBML_UINT_INVALID)
+            return NULL;
+          if (version)
+            *version = num;
+          break;
+
+          /* we ignore these two, they don't tell us anything we care about */
+        case EBML_ID_VOID:
+        case EBML_ID_EBMLVERSION:
+        case EBML_ID_DOCTYPEVERSION:
+        default:
+          if (ebml_read_skip (s, &l))
+            return NULL;
+          break;
+        }
+      length -= l;
+    }
+
+  return str;
+}
+
+#endif /* HAVE_MATROSKA */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/ebml.h	Mon Jan 19 19:16:10 2004 +0000
@@ -0,0 +1,175 @@
+#ifndef __EBML_H
+#define __EBML_H
+
+#include <stdint.h>
+
+
+/* EBML version supported */
+#define EBML_VERSION 1
+
+/*
+ * EBML element IDs. max. 32-bit.
+ */
+
+/* top-level master-IDs */
+#define EBML_ID_HEADER                   0x1A45DFA3
+
+/* IDs in the HEADER master */
+#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
+
+/* general EBML types */
+#define EBML_ID_VOID                     0xEC
+
+/* ID returned in error cases */
+#define EBML_ID_INVALID                  0xFFFFFFFF
+
+
+/*
+ * Matroska element IDs. max. 32-bit.
+ */
+
+/* toplevel segment */
+#define MATROSKA_ID_SEGMENT              0x18538067
+
+/* matroska top-level master IDs */
+#define MATROSKA_ID_INFO                 0x1549A966
+#define MATROSKA_ID_TRACKS               0x1654AE6B
+#define MATROSKA_ID_CUES                 0x1C53BB6B
+#define MATROSKA_ID_TAGS                 0x1254C367
+#define MATROSKA_ID_SEEKHEAD             0x114D9B74
+#define MATROSKA_ID_ATTACHMENTS          0x1941A469
+#define MATROSKA_ID_CHAPTERS             0x1043A770
+#define MATROSKA_ID_CLUSTER              0x1F43B675
+
+/* IDs in the info master */
+#define MATROSKA_ID_TIMECODESCALE        0x2AD7B1
+#define MATROSKA_ID_DURATION             0x4489
+#define MATROSKA_ID_WRITINGAPP           0x5741
+#define MATROSKA_ID_MUXINGAPP            0x4D80
+#define MATROSKA_ID_DATEUTC              0x4461
+
+/* ID in the tracks master */
+#define MATROSKA_ID_TRACKENTRY           0xAE
+
+/* IDs in the trackentry master */
+#define MATROSKA_ID_TRACKNUMBER          0xD7
+#define MATROSKA_ID_TRACKUID             0x73C5
+#define MATROSKA_ID_TRACKTYPE            0x83
+#define MATROSKA_ID_TRACKAUDIO           0xE1
+#define MATROSKA_ID_TRACKVIDEO           0xE0
+#define MATROSKA_ID_CODECID              0x86
+#define MATROSKA_ID_CODECPRIVATE         0x63A2
+#define MATROSKA_ID_CODECNAME            0x258688
+#define MATROSKA_ID_CODECINFOURL         0x3B4040
+#define MATROSKA_ID_CODECDOWNLOADURL     0x26B240
+#define MATROSKA_ID_TRACKNAME            0x536E
+#define MATROSKA_ID_TRACKLANGUAGE        0x22B59C
+#define MATROSKA_ID_TRACKFLAGENABLED     0xB9
+#define MATROSKA_ID_TRACKFLAGDEFAULT     0x88
+#define MATROSKA_ID_TRACKFLAGLACING      0x9C
+#define MATROSKA_ID_TRACKMINCACHE        0x6DE7
+#define MATROSKA_ID_TRACKMAXCACHE        0x6DF8
+#define MATROSKA_ID_TRACKDEFAULTDURATION 0x23E383
+#define MATROSKA_ID_TRACKENCODINGS       0x6D80
+
+/* IDs in the trackaudio master */
+#define MATROSKA_ID_AUDIOSAMPLINGFREQ    0xB5
+#define MATROSKA_ID_AUDIOBITDEPTH        0x6264
+#define MATROSKA_ID_AUDIOCHANNELS        0x9F
+
+/* IDs in the trackvideo master */
+#define MATROSKA_ID_VIDEOFRAMERATE       0x2383E3
+#define MATROSKA_ID_VIDEODISPLAYWIDTH    0x54B0
+#define MATROSKA_ID_VIDEODISPLAYHEIGHT   0x54BA
+#define MATROSKA_ID_VIDEOPIXELWIDTH      0xB0
+#define MATROSKA_ID_VIDEOPIXELHEIGHT     0xBA
+#define MATROSKA_ID_VIDEOFLAGINTERLACED  0x9A
+#define MATROSKA_ID_VIDEOSTEREOMODE      0x53B9
+#define MATROSKA_ID_VIDEODISPLAYUNIT     0x54B2
+#define MATROSKA_ID_VIDEOASPECTRATIO     0x54B3
+#define MATROSKA_ID_VIDEOCOLOURSPACE     0x2EB524
+#define MATROSKA_ID_VIDEOGAMMA           0x2FB523
+
+/* IDs in the trackencodings master */
+#define MATROSKA_ID_CONTENTENCODING      0x6240
+#define MATROSKA_ID_CONTENTENCODINGORDER 0x5031
+#define MATROSKA_ID_CONTENTENCODINGSCOPE 0x5032
+#define MATROSKA_ID_CONTENTENCODINGTYPE  0x5033
+#define MATROSKA_ID_CONTENTCOMPRESSION   0x5034
+#define MATROSKA_ID_CONTENTCOMPALGO      0x4254
+#define MATROSKA_ID_CONTENTCOMPSETTINGS  0x4255
+
+/* ID in the cues master */
+#define MATROSKA_ID_POINTENTRY           0xBB
+
+/* IDs in the pointentry master */
+#define MATROSKA_ID_CUETIME              0xB3
+#define MATROSKA_ID_CUETRACKPOSITION     0xB7
+
+/* IDs in the cuetrackposition master */
+#define MATROSKA_ID_CUETRACK             0xF7
+#define MATROSKA_ID_CUECLUSTERPOSITION   0xF1
+
+/* IDs in the seekhead master */
+#define MATROSKA_ID_SEEKENTRY            0x4DBB
+
+/* IDs in the seekpoint master */
+#define MATROSKA_ID_SEEKID               0x53AB
+#define MATROSKA_ID_SEEKPOSITION         0x53AC
+
+/* IDs in the chapters master */
+#define MATROSKA_ID_EDITIONENTRY         0x45B9
+#define MATROSKA_ID_CHAPTERATOM          0xB6
+#define MATROSKA_ID_CHAPTERTIMESTART     0x91
+#define MATROSKA_ID_CHAPTERTIMEEND       0x92
+
+/* IDs in the cluster master */
+#define MATROSKA_ID_CLUSTERTIMECODE      0xE7
+#define MATROSKA_ID_BLOCKGROUP           0xA0
+
+/* IDs in the blockgroup master */
+#define MATROSKA_ID_BLOCKDURATION        0x9B
+#define MATROSKA_ID_BLOCK                0xA1
+#define MATROSKA_ID_REFERENCEBLOCK       0xFB
+
+
+/* matroska track types */
+#define MATROSKA_TRACK_VIDEO    0x01 /* rectangle-shaped pictures aka video */
+#define MATROSKA_TRACK_AUDIO    0x02 /* anything you can hear */
+#define MATROSKA_TRACK_COMPLEX  0x03 /* audio+video in same track used by DV */
+#define MATROSKA_TRACK_LOGO     0x10 /* overlay-pictures displayed over video*/
+#define MATROSKA_TRACK_SUBTITLE 0x11 /* text-subtitles */
+#define MATROSKA_TRACK_CONTROL  0x20 /* control-codes for menu or other stuff*/
+
+/* matroska subtitle types */
+#define MATROSKA_SUBTYPE_UNKNOWN   0
+#define MATROSKA_SUBTYPE_TEXT      1
+#define MATROSKA_SUBTYPE_SSA       2
+#define MATROSKA_SUBTYPE_VOBSUB    3
+
+
+#define EBML_UINT_INVALID   UINT64_MAX
+#define EBML_INT_INVALID    INT64_MAX
+#define EBML_FLOAT_INVALID  -1000000000.0
+
+
+uint32_t ebml_read_id (stream_t *s, int *length);
+uint64_t ebml_read_vlen_uint (uint8_t *buffer, int *length);
+int64_t ebml_read_vlen_int (uint8_t *buffer, int *length);
+uint64_t ebml_read_length (stream_t *s, int *length);
+uint64_t ebml_read_uint (stream_t *s, uint64_t *length);
+int64_t ebml_read_int (stream_t *s, uint64_t *length);
+long double ebml_read_float (stream_t *s, uint64_t *length);
+char *ebml_read_ascii (stream_t *s, uint64_t *length);
+char *ebml_read_utf8 (stream_t *s, uint64_t *length);
+int ebml_read_skip (stream_t *s, uint64_t *length);
+uint32_t ebml_read_master (stream_t *s, uint64_t *length);
+char *ebml_read_header (stream_t *s, int *version);
+
+#endif /* __EBML_H */
--- a/libmpdemux/matroska.h	Mon Jan 19 18:37:34 2004 +0000
+++ b/libmpdemux/matroska.h	Mon Jan 19 19:16:10 2004 +0000
@@ -47,7 +47,10 @@
 #define MKV_S_TEXTASCII  "S_TEXT/ASCII"
 #define MKV_S_TEXTUTF8   "S_TEXT/UTF8"
 #define MKV_S_TEXTSSA    "S_TEXT/SSA"
+#define MKV_S_TEXTASS    "S_TEXT/ASS"
 #define MKV_S_VOBSUB     "S_VOBSUB"
+#define MKV_S_SSA        "S_SSA" // Deprecated
+#define MKV_S_ASS        "S_ASS" // Deprecated
 
 typedef struct {
   char type;                    // t = text, v = VobSub
--- a/libmpdemux/stream.h	Mon Jan 19 18:37:34 2004 +0000
+++ b/libmpdemux/stream.h	Mon Jan 19 19:16:10 2004 +0000
@@ -166,6 +166,19 @@
   return y;
 }
 
+inline static uint64_t stream_read_qword_le(stream_t *s){
+  uint64_t y;
+  y = stream_read_char(s);
+  y|=stream_read_char(s)<<8;
+  y|=stream_read_char(s)<<16;
+  y|=stream_read_char(s)<<24;
+  y|=(uint64_t)stream_read_char(s)<<32;
+  y|=(uint64_t)stream_read_char(s)<<40;
+  y|=(uint64_t)stream_read_char(s)<<48;
+  y|=(uint64_t)stream_read_char(s)<<56;
+  return y;
+}
+
 inline static unsigned int stream_read_int24(stream_t *s){
   unsigned int y;
   y = stream_read_char(s);