summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rwxr-xr-xconfigure46
-rw-r--r--libmpdemux/Makefile4
-rw-r--r--libmpdemux/demux_mkv.cpp1945
-rw-r--r--libmpdemux/demuxer.c33
-rw-r--r--libmpdemux/demuxer.h3
-rw-r--r--libmpdemux/matroska.h21
7 files changed, 2051 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index cf274d2ae4..6a4557e5ab 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@ OBJS_MPLAYER = $(SRCS_MPLAYER:.c=.o)
VO_LIBS = $(AA_LIB) $(X_LIB) $(SDL_LIB) $(GGI_LIB) $(MP1E_LIB) $(MLIB_LIB) $(SVGA_LIB) $(DIRECTFB_LIB)
AO_LIBS = $(ARTS_LIB) $(ESD_LIB) $(NAS_LIB) $(SGIAUDIO_LIB)
-CODEC_LIBS = $(AV_LIB) $(FAME_LIB) $(MAD_LIB) $(VORBIS_LIB) $(FAAD_LIB) $(LIBLZO_LIB) $(DECORE_LIB) $(XVID_LIB) $(PNG_LIB) $(Z_LIB) $(JPEG_LIB) $(ALSA_LIB) $(XMMS_LIB)
+CODEC_LIBS = $(AV_LIB) $(FAME_LIB) $(MAD_LIB) $(VORBIS_LIB) $(FAAD_LIB) $(LIBLZO_LIB) $(DECORE_LIB) $(XVID_LIB) $(PNG_LIB) $(Z_LIB) $(JPEG_LIB) $(ALSA_LIB) $(XMMS_LIB) $(MATROSKA_LIB)
COMMON_LIBS = libmpcodecs/libmpcodecs.a mp3lib/libMP3.a liba52/liba52.a libmpeg2/libmpeg2.a $(W32_LIB) $(DS_LIB) libaf/libaf.a libmpdemux/libmpdemux.a input/libinput.a postproc/libswscale.a osdep/libosdep.a $(CSS_LIB) $(CODEC_LIBS) $(FREETYPE_LIB) $(TERMCAP_LIB) $(CDPARANOIA_LIB) $(STREAMING_LIB) $(WIN32_LIB) $(GIF_LIB) $(MACOSX_FRAMEWORKS) $(SMBSUPPORT_LIB) $(FRIBIDI_LIB)
CFLAGS = $(OPTFLAGS) -Ilibmpdemux -Iloader -Ilibvo $(FREETYPE_INC) $(EXTRA_INC) $(CDPARANOIA_INC) $(SDL_INC) $(FRIBIDI_INC) # -Wall
diff --git a/configure b/configure
index 542a85de56..06a79e924c 100755
--- a/configure
+++ b/configure
@@ -189,6 +189,7 @@ Codecs:
--enable-libfame enable libfame realtime encoder [autodetect]
--enable-vorbis build with OggVorbis support [autodetect]
--enable-tremor build with integer-only OggVorbis support [disabled]
+ --enable-matroska build with Matroska support [autodetect]
--enable-faad build with FAAD2 (MP4/AAC) support [autodetect]
--disable-libdv disable libdv 0.9.5 en/decoding support [autodetect]
--disable-mad disable libmad (mpeg audio) support [autodetect]
@@ -1025,6 +1026,7 @@ _esd=auto
_liblzo=auto
_mad=auto
_vorbis=auto
+_matroska=auto
_tremor=no
_faad=auto
_xmms=no
@@ -1178,6 +1180,8 @@ for ac_option do
--disable-vorbis) _vorbis=no ;;
--enable-tremor) _tremor=yes ;;
--disable-tremor) _tremor=no ;;
+ --enable-matroska) _matroska=yes ;;
+ --disable-matroska) _matroska=no ;;
--enable-faad) _faad=yes ;;
--disable-faad) _faad=no ;;
--enable-xmms) _xmms=yes ;;
@@ -4025,6 +4029,42 @@ fi
echores "$_vorbis"
+echocheck "Matroska support"
+if test "$_matroska" = auto ; then
+ _matroska=no
+ cat > $TMPC << EOF
+#include <EbmlConfig.h>
+int main(void) { return 0; }
+EOF
+ cc_check -lmatroska -lebml && _matroska=yes
+ if test "$_matroska" = no ; then
+ _saved_inc_extra=$_inc_extra
+ _inc_extra="$_inc_extra -I/usr/include/ebml -I/usr/include/matroska"
+ cc_check -lmatroska -lebml && _matroska=yes
+ if test "$_matroska" = no ; then
+ _inc_extra="$_saved_inc_extra -I/usr/local/include/ebml -I/usr/local/include/matroska"
+ cc_check -lmatroska -lebml && _matroska=yes
+ if test "$_matroska" = no ; then
+ _inc_extra=$_saved_inc_extra
+ fi
+ fi
+ fi
+fi
+if test "$_matroska" = yes ; then
+ _def_matroska='#define HAVE_MATROSKA 1'
+ _inputmodules="matroska $_inputmodules"
+ _ld_matroska="-lmatroska -lebml -lstdc++"
+ case $cc_version in
+ 2.*) _def_matroska_gcc2="#define LIBEBML_GCC2" ;;
+ *) _def_matroska_gcc2="/*#define LIBEBML_GCC2*/" ;;
+ esac
+else
+ _def_matroska='#undef HAVE_MATROSKA'
+ _noinputmodules="matroska $_noinputmodules"
+fi
+echores "$_matroska"
+
+
echocheck "faad2 (AAC) support"
if test "$_faad" = auto ; then
_faad=no
@@ -5150,6 +5190,8 @@ CONFIG_PP = yes
CONFIG_RISKY = yes
LIBMENU = $_menu
I18NLIBS = $_i18n_libs
+MATROSKA = $_matroska
+MATROSKA_LIB = $_ld_matroska
OPENDIVX = $_opendivx
@@ -5645,6 +5687,10 @@ $_def_vorbis
/* enable Tremor as vorbis decoder */
$_def_tremor
+/* enable Matroska support */
+$_def_matroska
+$_def_matroska_gcc2
+
/* enable FAAD (AAC) support */
$_def_faad
$_def_faad_version
diff --git a/libmpdemux/Makefile b/libmpdemux/Makefile
index 29115178f4..66050f65cc 100644
--- a/libmpdemux/Makefile
+++ b/libmpdemux/Makefile
@@ -26,7 +26,9 @@ SRCS += dvbin.c
SRCS += dvb_tune.c
endif
-
+ifeq ($(MATROSKA),yes)
+CPLUSPLUSSRCS += demux_mkv.cpp
+endif
OBJS = $(SRCS:.c=.o)
diff --git a/libmpdemux/demux_mkv.cpp b/libmpdemux/demux_mkv.cpp
new file mode 100644
index 0000000000..f0774bd93e
--- /dev/null
+++ b/libmpdemux/demux_mkv.cpp
@@ -0,0 +1,1945 @@
+extern "C" {
+#include "config.h"
+}
+
+#ifdef HAVE_MATROSKA
+
+extern "C" {
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../mp_msg.h"
+#include "../help_mp.h"
+#include "stream.h"
+#include "demuxer.h"
+#include "stheader.h"
+
+#include "../subreader.h"
+#include "../libvo/sub.h"
+}
+
+#include <iostream>
+#include <cassert>
+#include <typeinfo>
+
+#include "EbmlHead.h"
+#include "EbmlSubHead.h"
+#include "EbmlStream.h"
+#include "EbmlContexts.h"
+#include "FileKax.h"
+
+#include "KaxAttachements.h"
+#include "KaxBlock.h"
+#include "KaxBlockData.h"
+#include "KaxChapters.h"
+#include "KaxCluster.h"
+#include "KaxClusterData.h"
+#include "KaxContexts.h"
+#include "KaxCues.h"
+#include "KaxCuesData.h"
+#include "KaxInfo.h"
+#include "KaxInfoData.h"
+#include "KaxSeekHead.h"
+#include "KaxSegment.h"
+#include "KaxTracks.h"
+#include "KaxTrackAudio.h"
+#include "KaxTrackVideo.h"
+
+#include "StdIOCallback.h"
+
+#include "matroska.h"
+
+using namespace LIBMATROSKA_NAMESPACE;
+using namespace std;
+
+// default values for Matroska elements
+#define MKVD_TIMECODESCALE 1000000 // 1000000 = 1ms
+
+class mpstream_io_callback: public IOCallback {
+ private:
+ stream_t *s;
+ public:
+ mpstream_io_callback(stream_t *stream);
+
+ virtual uint32 read(void *buffer, size_t size);
+ virtual void setFilePointer(int64 offset, seek_mode mode = seek_beginning);
+ virtual size_t write(const void *buffer, size_t size);
+ virtual uint64 getFilePointer();
+ virtual void close();
+};
+
+mpstream_io_callback::mpstream_io_callback(stream_t *stream) {
+ s = stream;
+}
+
+uint32 mpstream_io_callback::read(void *buffer, size_t size) {
+ uint32_t result;
+
+ result = stream_read(s, (char *)buffer, size);
+
+ return result;
+}
+
+void mpstream_io_callback::setFilePointer(int64 offset, seek_mode mode) {
+ int64 new_pos;
+
+ if (mode == seek_beginning)
+ new_pos = offset + s->start_pos;
+ else if (mode == seek_end)
+ new_pos = s->end_pos - offset;
+ else
+ new_pos = s->pos + offset;
+
+ if (new_pos > s->end_pos) {
+ mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] seek warning: new_pos %lld > end_pos "
+ "%lld\n", new_pos, s->end_pos);
+ return;
+ }
+
+ stream_seek(s, new_pos);
+}
+
+size_t mpstream_io_callback::write(const void */*buffer*/, size_t /*size*/) {
+ return 0;
+}
+
+uint64 mpstream_io_callback::getFilePointer() {
+ return s->pos - s->buf_len + s->buf_pos;
+}
+
+void mpstream_io_callback::close() {
+}
+
+typedef struct mkv_index_entry {
+ uint64_t timecode, filepos;
+ int is_key;
+} mkv_index_entry_t;
+
+typedef struct mkv_track_index {
+ uint32_t tnum;
+ int num_entries;
+ mkv_index_entry_t *entries;
+} mkv_track_index_t;
+
+typedef struct mkv_track {
+ uint32_t tnum;
+
+ char *codec_id;
+ int ms_compat;
+
+ char type; // 'v' = video, 'a' = audio, 't' = text subs
+
+ char v_fourcc[5];
+ uint32_t v_width, v_height;
+ float v_frate;
+
+ uint16_t a_formattag;
+ uint32_t a_channels, a_bps;
+ float a_sfreq;
+
+ int default_track;
+
+ void *private_data;
+ unsigned int private_size;
+
+ unsigned char *headers[3];
+ uint32_t header_sizes[3];
+
+ int ok;
+} mkv_track_t;
+
+typedef struct mkv_demuxer {
+ float duration, last_pts;
+ uint64_t last_filepos;
+
+ mkv_track_t **tracks;
+ int num_tracks;
+ mkv_track_t *video, *audio, *subs_track;
+
+ uint64_t tc_scale, cluster_tc;
+
+ mpstream_io_callback *in;
+
+ uint64_t clear_subs_at;
+
+ subtitle subs;
+
+ EbmlStream *es;
+ EbmlElement *saved_l1, *saved_l2;
+ KaxSegment *segment;
+ KaxCluster *cluster;
+
+ mkv_track_index_t *index;
+ int num_indexes, cues_found, cues_searched;
+ int64_t *cluster_positions;
+ int num_cluster_pos;
+
+ int skip_to_keyframe;
+} mkv_demuxer_t;
+
+static uint16_t get_uint16(const void *buf) {
+ uint16_t ret;
+ unsigned char *tmp;
+
+ tmp = (unsigned char *) buf;
+
+ ret = tmp[1] & 0xff;
+ ret = (ret << 8) + (tmp[0] & 0xff);
+
+ return ret;
+}
+
+static uint32_t get_uint32(const void *buf) {
+ uint32_t ret;
+ unsigned char *tmp;
+
+ tmp = (unsigned char *) buf;
+
+ ret = tmp[3] & 0xff;
+ ret = (ret << 8) + (tmp[2] & 0xff);
+ ret = (ret << 8) + (tmp[1] & 0xff);
+ ret = (ret << 8) + (tmp[0] & 0xff);
+
+ return ret;
+}
+
+static void handle_subtitles(demuxer_t *d, KaxBlock *block, int64_t duration) {
+ mkv_demuxer_t *mkv_d = (mkv_demuxer_t *)d->priv;
+ int len, line, state;
+ char *s1, *s2, *buffer;
+
+ if (duration == -1) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Warning: No KaxBlockDuration "
+ "element for subtitle track found.\n");
+ return;
+ }
+
+ DataBuffer &data = block->GetBuffer(0);
+ len = data.Size();
+
+ buffer = (char *)data.Buffer();
+ s1 = buffer;
+
+ while (((*s1 == '\n') || (*s1 == '\r')) &&
+ ((unsigned int)(s1 - buffer) <= data.Size()))
+ s1++;
+
+ line = 0;
+ s2 = mkv_d->subs.text[0];
+ mkv_d->subs.lines = 1;
+ state = 0;
+ while ((unsigned int)(s1 - buffer) != data.Size()) {
+ if ((*s1 == '\n') || (*s1 == '\r')) {
+ if (state == 0) { // normal char --> newline
+ if (mkv_d->subs.lines == SUB_MAX_TEXT)
+ break;
+ *s2 = 0;
+ s2 = mkv_d->subs.text[mkv_d->subs.lines];
+ mkv_d->subs.lines++;
+ state = 1;
+ }
+ } else if (*s1 == '<') // skip HTML tags
+ state = 2;
+ else if (*s1 == '>')
+ state = 0;
+ else if (state != 2) { // normal character
+ state = 0;
+ if ((s2 - mkv_d->subs.text[mkv_d->subs.lines - 1]) < 255) {
+ *s2 = *s1;
+ s2++;
+ }
+ }
+ s1++;
+ }
+
+ *s2 = 0;
+
+#ifdef USE_ICONV
+ subcp_recode1(&mkv_d->subs);
+#endif
+
+ vo_sub = &mkv_d->subs;
+ vo_osd_changed(OSDTYPE_SUBTITLE);
+
+ mkv_d->clear_subs_at = block->GlobalTimecode() / 1000000 + duration;
+}
+
+static mkv_track_t *new_mkv_track(mkv_demuxer_t *d) {
+ mkv_track_t *t;
+
+ t = (mkv_track_t *)malloc(sizeof(mkv_track_t));
+ if (t != NULL) {
+ memset(t, 0, sizeof(mkv_track_t));
+ d->tracks = (mkv_track_t **)realloc(d->tracks, (d->num_tracks + 1) *
+ sizeof(mkv_track_t *));
+ if (d->tracks == NULL)
+ return NULL;
+ d->tracks[d->num_tracks] = t;
+ d->num_tracks++;
+ }
+
+ return t;
+}
+
+static mkv_track_t *find_track_by_num(mkv_demuxer_t *d, uint32_t n,
+ mkv_track_t *c) {
+ int i;
+
+ for (i = 0; i < d->num_tracks; i++)
+ if ((d->tracks[i] != NULL) && (d->tracks[i]->tnum == n) &&
+ (d->tracks[i] != c))
+ return d->tracks[i];
+
+ return NULL;
+}
+
+static int check_track_information(mkv_demuxer_t *d) {
+ int i;
+ unsigned char *c;
+ uint32_t u, offset, length;
+ mkv_track_t *t;
+ BITMAPINFOHEADER *bih;
+ WAVEFORMATEX *wfe;
+
+ for (i = 0; i < d->num_tracks; i++) {
+ t = d->tracks[i];
+ switch (t->type) {
+ case 'v': // video track
+ if (t->codec_id == NULL)
+ continue;
+ if (!strcmp(t->codec_id, MKV_V_MSCOMP)) {
+ if ((t->private_data == NULL) ||
+ (t->private_size < sizeof(BITMAPINFOHEADER))) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: CodecID for track "
+ "%u is '" MKV_V_MSCOMP "', but there was no "
+ "BITMAPINFOHEADER struct present. Therefore we don't have "
+ "a FourCC to identify the video codec used.\n", t->tnum);
+ continue;
+ } else {
+ t->ms_compat = 1;
+
+ bih = (BITMAPINFOHEADER *)t->private_data;
+
+ u = get_uint32(&bih->biWidth);
+ if (t->v_width != u) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: (MS "
+ "compatibility mode, track %u) "
+ "Matrosa says video width is %u, but the "
+ "BITMAPINFOHEADER says %u.\n", t->tnum, t->v_width, u);
+ if (t->v_width == 0)
+ t->v_width = u;
+ }
+
+ u = get_uint32(&bih->biHeight);
+ if (t->v_height != u) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: (MS compatibility "
+ "mode, track %u) "
+ "Matrosa video height is %u, but the BITMAPINFOHEADER "
+ "says %u.\n", t->tnum, t->v_height, u);
+ if (t->v_height == 0)
+ t->v_height = u;
+ }
+
+ memcpy(t->v_fourcc, &bih->biCompression, 4);
+
+ if (t->v_frate == 0.0) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] ERROR: (MS compatibility "
+ "mode, track %u) "
+ "No VideoFrameRate element was found.\n", t->tnum);
+ continue;
+ }
+ }
+ } else {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Native CodecIDs for video "
+ "tracks are not supported yet (track %u).\n", t->tnum);
+ continue;
+ }
+
+ if (t->v_width == 0) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] The width for track %u was "
+ "not set.\n", t->tnum);
+ continue;
+ }
+ if (t->v_height == 0) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] The height for track %u was "
+ "not set.\n", t->tnum);
+ continue;
+ }
+
+ // This track seems to be ok.
+ t->ok = 1;
+
+ break;
+
+ case 'a': // audio track
+ if (t->codec_id == NULL)
+ continue;
+ if (!strcmp(t->codec_id, MKV_A_ACM)) {
+ if ((t->private_data == NULL) ||
+ (t->private_size < sizeof(WAVEFORMATEX))) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: CodecID for track "
+ "%u is '" MKV_A_ACM "', "
+ "but there was no WAVEFORMATEX struct present. "
+ "Therefore we don't have a format ID to identify the audio "
+ "codec used.\n", t->tnum);
+ continue;
+ } else {
+ t->ms_compat = 1;
+
+ wfe = (WAVEFORMATEX *)t->private_data;
+ u = get_uint32(&wfe->nSamplesPerSec);
+ if (((uint32_t)t->a_sfreq) != u) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: (MS compatibility "
+ "mode for track %u) "
+ "Matroska says that there are %u samples per second, "
+ "but WAVEFORMATEX says that there are %u.\n", t->tnum,
+ (uint32_t)t->a_sfreq, u);
+ if (t->a_sfreq == 0.0)
+ t->a_sfreq = (float)u;
+ }
+
+ u = get_uint16(&wfe->nChannels);
+ if (t->a_channels != u) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: (MS "
+ "compatibility mode for track %u) "
+ "Matroska says that there are %u channels, but the "
+ "WAVEFORMATEX says that there are %u.\n", t->tnum,
+ t->a_channels, u);
+ if (t->a_channels == 0)
+ t->a_channels = u;
+ }
+
+ u = get_uint16(&wfe->wBitsPerSample);
+ if (t->a_channels != u) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: (MS "
+ "compatibility mode for track %u) "
+ "Matroska says that there are %u bits per sample, "
+ "but the WAVEFORMATEX says that there are %u.\n", t->tnum,
+ t->a_bps, u);
+ if (t->a_bps == 0)
+ t->a_bps = u;
+ }
+
+ t->a_formattag = get_uint16(&wfe->wFormatTag);
+ }
+ } else {
+ if (!strcmp(t->codec_id, MKV_A_MP3))
+ t->a_formattag = 0x0055;
+ else if (!strcmp(t->codec_id, MKV_A_AC3))
+ t->a_formattag = 0x2000;
+ else if (!strcmp(t->codec_id, MKV_A_PCM))
+ t->a_formattag = 0x0001;
+ else if (!strcmp(t->codec_id, MKV_A_VORBIS)) {
+ if (t->private_data == NULL) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] WARNING: CodecID for "
+ "track %u is '" MKV_A_VORBIS
+ "', but there are no header packets present.", t->tnum);
+ continue;
+ }
+
+ c = (unsigned char *)t->private_data;
+ if (c[0] != 2) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Vorbis track does not "
+ "contain valid headers.\n");
+ continue;
+ }
+
+ offset = 1;
+ for (i = 0; i < 2; i++) {
+ length = 0;
+ while ((c[offset] == (unsigned char )255) &&
+ (length < t->private_size)) {
+ length += 255;
+ offset++;
+ }
+ if (offset >= (t->private_size - 1)) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Vorbis track does not "
+ "contain valid headers.\n");
+ continue;
+ }
+ length += c[offset];
+ offset++;
+ t->header_sizes[i] = length;
+ }
+
+ t->headers[0] = &c[offset];
+ t->headers[1] = &c[offset + t->header_sizes[0]];
+ t->headers[2] = &c[offset + t->header_sizes[0] +
+ t->header_sizes[1]];
+ t->header_sizes[2] = t->private_size - offset;
+
+ t->a_formattag = 0xFFFE;
+ } else {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Unknown/unsupported audio "
+ "codec ID '%s' for track %u.\n", t->codec_id, t->tnum);
+ continue;
+ }
+ }
+
+ if (t->a_sfreq == 0.0) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] The sampling frequency was not "
+ "set for track %u.\n", t->tnum);
+ continue;
+ }
+
+ if (t->a_channels == 0) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] The number of channels was not "
+ "set for track %u.\n", t->tnum);
+ continue;
+ }
+
+ if (t->a_formattag == 0) {
+ mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] The audio format tag was not "
+ "set for track %u.\n", t->tnum);
+ continue;
+ }
+
+ // This track seems to be ok.
+ t->ok = 1;
+
+ break;
+
+ case 's': // Text subtitles do not need any data
+ t->ok = 1; // except the CodecID.
+ break;
+
+ default: // unknown track type!? error in demuxer...
+ mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Error in demux_mkv.cpp: unknown "
+ "demuxer type for track %u: '%c'\n", t->tnum, t->type);
+ continue;
+ }
+
+ if (t->ok)
+ mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Track %u seems to be ok.\n", t->tnum);
+ }
+
+ return 1;
+}
+
+static void free_mkv_demuxer(mkv_demuxer_t *d) {
+ int i;
+
+ if (d == NULL)
+ return;
+
+ for (i = 0; i < d->num_tracks; i++)
+ if (d->tracks[i] != NULL) {
+ if (d->tracks[i]->private_data != NULL)
+ free(d->tracks[i]->private_data);
+ free(d->tracks[i]);
+ }
+
+ for (i = 0; i < d->num_indexes; i++)
+ free(d->index[i].entries);
+ free(d->index);
+
+ if (d->es != NULL)
+ delete d->es;
+ if (d->saved_l1 != NULL)
+ delete d->saved_l1;
+ if (d->in != NULL)
+ delete d->in;
+ if (d->segment != NULL)
+ delete d->segment;
+
+ free(d);
+}
+
+static void add_index_entry(mkv_demuxer_t *d, uint32_t tnum, uint64_t filepos,
+ uint64_t timecode, int is_key) {
+ int i, found;
+ mkv_index_entry_t *entry;
+
+ for (i = 0, found = 0; i < d->num_indexes; i++)
+ if (d->index[i].tnum == tnum) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ d->index = (mkv_track_index_t *)realloc(d->index, (d->num_indexes + 1) *
+ sizeof(mkv_track_index_t));
+ if (d->index == NULL)
+ return;
+ i = d->num_indexes;
+ memset(&d->index[i], 0, sizeof(mkv_track_index_t));
+ d->index[i].tnum = tnum;
+ d->num_indexes++;
+ }
+
+ d->index[i].entries =
+ (mkv_index_entry_t *)realloc(d->index[i].entries,
+ (d->index[i].num_entries + 1) *
+ sizeof(mkv_index_entry_t));
+ if (d->index[i].entries == NULL)
+ return;
+ entry = &d->index[i].entries[d->index[i].num_entries];
+ entry->filepos = filepos;
+ entry->timecode = timecode;
+ entry->is_key = is_key;
+ d->index[i].num_entries++;
+}
+
+static void add_cluster_position(mkv_demuxer_t *mkv_d, int64_t position) {
+ mkv_d->cluster_positions = (int64_t *)realloc(mkv_d->cluster_positions,
+ (mkv_d->num_cluster_pos + 1) *
+ sizeof(int64_t));
+ if (mkv_d->cluster_positions != NULL) {
+ mkv_d->cluster_positions[mkv_d->num_cluster_pos] = position;
+ mkv_d->num_cluster_pos++;
+ } else
+ mkv_d->num_cluster_pos = 0;
+}
+
+static int parse_cues(mkv_demuxer_t *mkv_d) {
+ EbmlElement *l1 = NULL, *l2 = NULL, *l3 = NULL, *l4 = NULL, *l5 = NULL;
+ EbmlStream *es;
+ int upper_lvl_el, elements_found, i, k;
+ uint64_t tc_scale, filepos = 0, timecode = 0;
+ uint32_t tnum = 0;
+ mkv_index_entry_t *entry;
+
+ es = mkv_d->es;
+ tc_scale = mkv_d->tc_scale;
+ upper_lvl_el = 0;
+
+ mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing cues ] -----------\n");
+
+ l1 = es->FindNextElement(mkv_d->segment->Generic().Context, upper_lvl_el,
+ 0xFFFFFFFFL, true, 1);
+ if (l1 == NULL)
+ return 0;
+
+ if (!(EbmlId(*l1) == KaxCues::ClassInfos.GlobalId)) {
+ mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] No KaxCues element found but but %s.\n"
+ "[mkv] \\---- [ parsing cues ] -----------\n", typeid(*l1).name());
+
+ return 0;
+ }
+
+ mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ found cues\n");
+
+ l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el, 0xFFFFFFFFL,
+ true, 1);
+ while (l2 != NULL) {
+ if (upper_lvl_el != 0)
+ break;
+
+ if (EbmlId(*l2) == KaxCuePoint::ClassInfos.GlobalId) {
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue point\n");
+
+ elements_found = 0;
+
+ l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
+ 0xFFFFFFFFL, true, 1);
+ while (l3 != NULL) {
+ if (upper_lvl_el != 0)
+ break;
+
+ if (EbmlId(*l3) == KaxCueTime::ClassInfos.GlobalId) {
+ KaxCueTime &cue_time = *static_cast<KaxCueTime *>(l3);
+ cue_time.ReadData(es->I_O());
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue time: %.3fs\n",
+ ((float)uint64(cue_time)) * tc_scale / 1000000000.0);
+
+ timecode = uint64(cue_time) * tc_scale / 1000000;
+ elements_found |= 1;
+
+ } else if (EbmlId(*l3) ==
+ KaxCueTrackPositions::ClassInfos.GlobalId) {
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue track "
+ "positions\n");
+
+ l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el,
+ 0xFFFFFFFFL, true, 1);
+ while (l4 != NULL) {
+ if (upper_lvl_el != 0)
+ break;
+
+ if (EbmlId(*l4) == KaxCueTrack::ClassInfos.GlobalId) {
+ KaxCueTrack &cue_track = *static_cast<KaxCueTrack *>(l4);
+ cue_track.ReadData(es->I_O());
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue track: "
+ "%u\n", uint32(cue_track));
+
+ tnum = uint32(cue_track);
+ elements_found |= 2;
+
+ } else if (EbmlId(*l4) ==
+ KaxCueClusterPosition::ClassInfos.GlobalId) {
+ KaxCueClusterPosition &cue_cp =
+ *static_cast<KaxCueClusterPosition *>(l4);
+ cue_cp.ReadData(es->I_O());
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue cluster "
+ "position: %llu\n", uint64(cue_cp));
+
+ filepos = mkv_d->segment->GetGlobalPosition(uint64_t(cue_cp));
+ elements_found |= 4;
+
+ } else if (EbmlId(*l4) ==
+ KaxCueBlockNumber::ClassInfos.GlobalId) {
+ KaxCueBlockNumber &cue_bn =
+ *static_cast<KaxCueBlockNumber *>(l4);
+ cue_bn.ReadData(es->I_O());
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue block "
+ "number: %llu\n", uint64(cue_bn));
+
+ } else if (EbmlId(*l4) ==
+ KaxCueCodecState::ClassInfos.GlobalId) {
+ KaxCueCodecState &cue_cs =
+ *static_cast<KaxCueCodecState *>(l4);
+ cue_cs.ReadData(es->I_O());
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue codec "
+ "state: %llu\n", uint64(cue_cs));
+
+ } else if (EbmlId(*l4) ==
+ KaxCueReference::ClassInfos.GlobalId) {
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue "
+ "reference\n");
+
+ elements_found |= 8;
+
+ l5 = es->FindNextElement(l4->Generic().Context, upper_lvl_el,
+ 0xFFFFFFFFL, true, 1);
+ while (l5 != NULL) {
+ if (upper_lvl_el != 0)
+ break;
+
+ if (EbmlId(*l5) == KaxCueRefTime::ClassInfos.GlobalId) {
+ KaxCueRefTime &cue_rt =
+ *static_cast<KaxCueRefTime *>(l5);
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue ref "
+ "time: %.3fs\n", ((float)uint64(cue_rt)) * tc_scale /
+ 1000000000.0);
+
+ } else if (EbmlId(*l5) ==
+ KaxCueRefCluster::ClassInfos.GlobalId) {
+ KaxCueRefCluster &cue_rc =
+ *static_cast<KaxCueRefCluster *>(l5);
+ cue_rc.ReadData(es->I_O());
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue ref "
+ "cluster: %llu\n", uint64(cue_rc));
+
+ } else if (EbmlId(*l5) ==
+ KaxCueRefNumber::ClassInfos.GlobalId) {
+ KaxCueRefNumber &cue_rn =
+ *static_cast<KaxCueRefNumber *>(l5);
+ cue_rn.ReadData(es->I_O());
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue ref "
+ "number: %llu\n", uint64(cue_rn));
+
+ } else if (EbmlId(*l5) ==
+ KaxCueRefCodecState::ClassInfos.GlobalId) {
+ KaxCueRefCodecState &cue_rcs =
+ *static_cast<KaxCueRefCodecState *>(l5);
+ cue_rcs.ReadData(es->I_O());
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + found cue ref "
+ "codec state: %llu\n", uint64(cue_rcs));
+
+ } else {
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + unknown "
+ "element, level 5: %s\n", typeid(*l5).name());
+ }
+
+ l5->SkipData(static_cast<EbmlStream &>(*es),
+ l5->Generic().Context);
+ delete l5;
+ l5 = es->FindNextElement(l4->Generic().Context,
+ upper_lvl_el, 0xFFFFFFFFL, true, 1);
+ } // while (l5 != NULL)
+
+ } else
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + unknown element, "
+ "level 4: %s\n", typeid(*l4).name());
+
+ if (upper_lvl_el > 0) { // we're coming from l5
+ upper_lvl_el--;
+ delete l4;
+ l4 = l5;
+ if (upper_lvl_el > 0)
+ break;
+
+ } else {
+ l4->SkipData(static_cast<EbmlStream &>(*es),
+ l4->Generic().Context);
+ delete l4;
+ l4 = es->FindNextElement(l3->Generic().Context, upper_lvl_el,
+ 0xFFFFFFFFL, true, 1);
+ }
+ } // while (l4 != NULL)
+
+ } else
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + unknown element, level 3: "
+ "%s\n", typeid(*l3).name());
+
+ if (upper_lvl_el > 0) { // we're coming from l4
+ upper_lvl_el--;
+ delete l3;
+ l3 = l4;
+ if (upper_lvl_el > 0)
+ break;
+
+ } else {
+ l3->SkipData(static_cast<EbmlStream &>(*es),
+ l3->Generic().Context);
+ delete l3;
+ l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
+ 0xFFFFFFFFL, true, 1);
+ }
+ } // while (l3 != NULL)
+
+ // Three elements must have been found in order for this to be a
+ // correct entry:
+ // 1: cue time (timecode)
+ // 2: cue track (tnum)
+ // 4: cue cluster position (filepos)
+ // If 8 is also set, then there was a reference element, and the
+ // current block is not an I frame. If 8 is not set then this is
+ // an I frame.
+ if ((elements_found & 7) == 7)
+ add_index_entry(mkv_d, tnum, filepos, timecode,
+ (elements_found & 8) ? 0 : 1);
+
+ } else
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] | + unknown element, level 2: "
+ "%s\n", typeid(*l2).name());
+
+ if (upper_lvl_el > 0) { // we're coming from l3
+ upper_lvl_el--;
+ delete l2;
+ l2 = l3;
+ if (upper_lvl_el > 0)
+ break;
+
+ } else {
+ l2->SkipData(static_cast<EbmlStream &>(*es),
+ l2->Generic().Context);
+ delete l2;
+ l2 = es->FindNextElement(l1->Generic().Context, upper_lvl_el,
+ 0xFFFFFFFFL, true, 1);
+ }
+ } // while (l2 != NULL)
+
+ // Debug: dump the index
+ for (i = 0; i < mkv_d->num_indexes; i++) {
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Index for track %u contains %u "
+ "entries.\n", mkv_d->index[i].tnum, mkv_d->index[i].num_entries);
+ for (k = 0; k < mkv_d->index[i].num_entries; k++) {
+ entry = &mkv_d->index[i].entries[k];
+ mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] %d: timecode %llu, filepos %llu, "
+ "is key: %s\n", k, entry->timecode, entry->filepos,
+ entry->is_key ? "yes" : "no");
+ }
+ }
+
+ mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing cues ] -----------\n");
+
+ return 1;
+}
+
+extern "C" int demux_mkv_open(demuxer_t *demuxer) {
+ unsigned char signature[4];
+ stream_t *s;
+ demux_packet_t *dp;
+ mkv_demuxer_t *mkv_d;
+ int upper_lvl_el, exit_loop, i;
+ // Elements for different levels
+ EbmlElement *l0 = NULL, *l1 = NULL, *l2 = NULL, *l3 = NULL, *l4 = NULL;
+ EbmlStream *es;
+ mkv_track_t *track;
+ sh_audio_t *sh_a;
+ sh_video_t *sh_v;
+ uint64_t seek_pos, current_pos;
+ int seek_element_is_cue;
+
+#ifdef USE_ICONV
+ subcp_open();
+#endif
+
+ s = demuxer->stream;
+ stream_seek(s, s->start_pos);
+ memset(signature, 0, 4);
+ stream_read(s, (char *)signature, 4);
+ if ((signature[0] != 0x1A) || (signature[1] != 0x45) ||
+ (signature[2] != 0xDF) || (signature[3] != 0xA3))
+ return 0;
+ stream_seek(s, s->start_pos);
+
+ try {
+ // structure for storing the demuxer's private data
+ mkv_d = (mkv_demuxer_t *)malloc(sizeof(mkv_demuxer_t));
+ if (mkv_d == NULL)
+ return 0;
+ memset(mkv_d, 0, sizeof(mkv_demuxer_t));
+ mkv_d->duration = -1.0;
+
+ // Create the interface between MPlayer's IO system and
+ // libmatroska's IO system.
+ mkv_d->in = new mpstream_io_callback(demuxer->stream);
+ if (mkv_d->in == NULL) {
+ free_mkv_demuxer(mkv_d);
+ return 0;
+ }
+ mpstream_io_callback &io = *static_cast<mpstream_io_callback *>(mkv_d->in);
+ mkv_d->es = new EbmlStream(io);
+ if (mkv_d->es == NULL) {
+ free_mkv_demuxer(mkv_d);
+ return 0;
+ }
+ es = mkv_d->es;
+
+ // Find the EbmlHead element. Must be the first one.
+ l0 = es->FindNextID(EbmlHead::ClassInfos, 0xFFFFFFF