From d6fad182c2f6aba9eb07c750052ab0b20d9dbf24 Mon Sep 17 00:00:00 2001 From: mosu Date: Mon, 19 Jan 2004 19:16:10 +0000 Subject: Added the new C based Matroska demuxer by Aurelien Jacobs. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@11808 b3059339-0415-0410-9bf9-f77b7e298cf2 --- libmpdemux/demux_mkv.c | 2850 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2850 insertions(+) create mode 100644 libmpdemux/demux_mkv.c (limited to 'libmpdemux/demux_mkv.c') diff --git a/libmpdemux/demux_mkv.c b/libmpdemux/demux_mkv.c new file mode 100644 index 0000000000..b38e23f90b --- /dev/null +++ b/libmpdemux/demux_mkv.c @@ -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, +