# HG changeset patch # User mosu # Date 1074539770 0 # Node ID 9a81d7b4c0b696890c78d9d5852c3a0b5a54a714 # Parent 4f14825fd4463c22033aded7295ffa152a19e402 Added the new C based Matroska demuxer by Aurelien Jacobs. diff -r 4f14825fd446 -r 9a81d7b4c0b6 configure --- 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 diff -r 4f14825fd446 -r 9a81d7b4c0b6 libmpdemux/Makefile --- 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 diff -r 4f14825fd446 -r 9a81d7b4c0b6 libmpdemux/demux_mkv.c --- /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 + * 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 +#include +#include + +#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 +#endif + +#ifdef USE_LIBLZO +#include +#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; inum_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; iencodings = 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; iparsed_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; iparsed_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; inum_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; inum_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; inum_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; inum_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; inum_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 */ diff -r 4f14825fd446 -r 9a81d7b4c0b6 libmpdemux/ebml.c --- /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 + * Based on the one written by Ronald Bultje for gstreamer + * Licence: GPL + */ + +#include "config.h" +#ifdef HAVE_MATROSKA + +#include + +#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 */ diff -r 4f14825fd446 -r 9a81d7b4c0b6 libmpdemux/ebml.h --- /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 + + +/* 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 */ diff -r 4f14825fd446 -r 9a81d7b4c0b6 libmpdemux/matroska.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 diff -r 4f14825fd446 -r 9a81d7b4c0b6 libmpdemux/stream.h --- 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);