From d4bdd0473d6f43132257c9fb3848d829755167a3 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 5 Nov 2012 17:02:04 +0100 Subject: Rename directories, move files (step 1 of 2) (does not compile) Tis drops the silly lib prefixes, and attempts to organize the tree in a more logical way. Make the top-level directory less cluttered as well. Renames the following directories: libaf -> audio/filter libao2 -> audio/out libvo -> video/out libmpdemux -> demux Split libmpcodecs: vf* -> video/filter vd*, dec_video.* -> video/decode mp_image*, img_format*, ... -> video/ ad*, dec_audio.* -> audio/decode libaf/format.* is moved to audio/ - this is similar to how mp_image.* is located in video/. Move most top-level .c/.h files to core. (talloc.c/.h is left on top- level, because it's external.) Park some of the more annoying files in compat/. Some of these are relicts from the time mplayer used ffmpeg internals. sub/ is not split, because it's too much of a mess (subtitle code is mixed with OSD display and rendering). Maybe the organization of core is not ideal: it mixes playback core (like mplayer.c) and utility helpers (like bstr.c/h). Should the need arise, the playback core will be moved somewhere else, while core contains all helper and common code. --- demux/demux_mkv.c | 2558 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2558 insertions(+) create mode 100644 demux/demux_mkv.c (limited to 'demux/demux_mkv.c') diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c new file mode 100644 index 0000000000..3093fcae0f --- /dev/null +++ b/demux/demux_mkv.c @@ -0,0 +1,2558 @@ +/* + * Matroska demuxer + * Copyright (C) 2004 Aurelien Jacobs + * Based on the one written by Ronald Bultje for gstreamer + * and on demux_mkv.cpp from Moritz Bunkus. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "config.h" + +#if CONFIG_ZLIB +#include +#endif + +#include "talloc.h" +#include "options.h" +#include "bstr.h" +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" +#include "ebml.h" +#include "matroska.h" +//#include "demux_real.h" + +#include "mp_msg.h" + +static const unsigned char sipr_swaps[38][2] = { + {0,63},{1,22},{2,44},{3,90},{5,81},{7,31},{8,86},{9,58},{10,36},{12,68}, + {13,39},{14,73},{15,53},{16,69},{17,57},{19,88},{20,34},{21,71},{24,46}, + {25,94},{26,54},{28,75},{29,50},{32,70},{33,92},{35,74},{38,85},{40,56}, + {42,87},{43,65},{45,59},{48,79},{49,93},{51,89},{55,95},{61,76},{67,83}, + {77,80} +}; + +// Map flavour to bytes per second +#define SIPR_FLAVORS 4 +#define ATRC_FLAVORS 8 +#define COOK_FLAVORS 34 +static const int sipr_fl2bps[SIPR_FLAVORS] = { 813, 1062, 625, 2000 }; +static const int atrc_fl2bps[ATRC_FLAVORS] = { + 8269, 11714, 13092, 16538, 18260, 22050, 33075, 44100 }; +static const int cook_fl2bps[COOK_FLAVORS] = { + 1000, 1378, 2024, 2584, 4005, 5513, 8010, 4005, 750, 2498, + 4048, 5513, 8010, 11973, 8010, 2584, 4005, 2067, 2584, 2584, + 4005, 4005, 5513, 5513, 8010, 12059, 1550, 8010, 12059, 5513, + 12016, 16408, 22911, 33506 +}; + +typedef struct mkv_content_encoding { + uint64_t order, type, scope; + uint64_t comp_algo; + uint8_t *comp_settings; + int comp_settings_len; +} mkv_content_encoding_t; + +typedef struct mkv_track { + int tnum; + char *name; + int id; // -aid / -sid / -vid option value + + char *codec_id; + int ms_compat; + char *language; + + int type; + + uint32_t v_width, v_height, v_dwidth, v_dheight; + double v_frate; + + uint32_t a_formattag; + uint32_t a_channels, a_bps; + float a_sfreq; + float a_osfreq; + + double default_duration; + + int default_track; + + unsigned char *private_data; + unsigned int private_size; + + /* stuff for realmedia */ + int realmedia; + int64_t rv_kf_base; + int rv_kf_pts; + double rv_pts; /* previous video timestamp */ + double ra_pts; /* previous audio timestamp */ + + /** realaudio descrambling */ + int sub_packet_size; ///< sub packet size, per stream + int sub_packet_h; ///< number of coded frames per block + int coded_framesize; ///< coded frame size, per stream + int audiopk_size; ///< audio packet size + unsigned char *audio_buf; ///< place to store reordered audio data + double *audio_timestamp; ///< timestamp for each audio packet + int sub_packet_cnt; ///< number of subpacket already received + int audio_filepos; ///< file position of first audio packet in block + + /* stuff for quicktime */ + int fix_i_bps; + double qt_last_a_pts; + + int subtitle_type; + + /* generic content encoding support */ + mkv_content_encoding_t *encodings; + int num_encodings; + + /* For VobSubs and SSA/ASS */ + sh_sub_t *sh_sub; +} mkv_track_t; + +typedef struct mkv_index { + int tnum; + uint64_t timecode, filepos; +} mkv_index_t; + +typedef struct mkv_demuxer { + off_t segment_start; + + double duration, last_pts; + uint64_t last_filepos; + + mkv_track_t **tracks; + int num_tracks; + + uint64_t tc_scale, cluster_tc; + + uint64_t cluster_start; + uint64_t cluster_size; + uint64_t blockgroup_size; + + mkv_index_t *indexes; + int num_indexes; + + off_t *parsed_pos; + int num_parsed_pos; + bool parsed_info; + bool parsed_tracks; + bool parsed_tags; + bool parsed_chapters; + bool parsed_attachments; + + struct cluster_pos { + uint64_t filepos; + uint64_t timecode; + } *cluster_positions; + int num_cluster_pos; + + uint64_t skip_to_timecode; + int v_skip_to_keyframe, a_skip_to_keyframe; + + int num_audio_tracks; + int num_video_tracks; +} mkv_demuxer_t; + +#define REALHEADER_SIZE 16 +#define RVPROPERTIES_SIZE 34 +#define RAPROPERTIES4_SIZE 56 +#define RAPROPERTIES5_SIZE 70 + +/** + * \brief ensures there is space for at least one additional element + * \param array array to grow + * \param nelem current number of elements in array + * \param elsize size of one array element + */ +static void *grow_array(void *array, int nelem, size_t elsize) +{ + if (!(nelem & 31)) + array = realloc(array, (nelem + 32) * elsize); + return array; +} + +static bool is_parsed_header(struct mkv_demuxer *mkv_d, off_t pos) +{ + int low = 0; + int high = mkv_d->num_parsed_pos; + while (high > low + 1) { + int mid = high + low >> 1; + if (mkv_d->parsed_pos[mid] > pos) + high = mid; + else + low = mid; + } + if (mkv_d->num_parsed_pos && mkv_d->parsed_pos[low] == pos) + return true; + if (!(mkv_d->num_parsed_pos & 31)) + mkv_d->parsed_pos = talloc_realloc(mkv_d, mkv_d->parsed_pos, off_t, + mkv_d->num_parsed_pos + 32); + mkv_d->num_parsed_pos++; + for (int i = mkv_d->num_parsed_pos - 1; i > low; i--) + mkv_d->parsed_pos[i] = mkv_d->parsed_pos[i - 1]; + mkv_d->parsed_pos[low] = pos; + return false; +} + +static mkv_track_t *find_track_by_num(struct mkv_demuxer *d, int n, int type) +{ + for (int i = 0; i < d->num_tracks; i++) + if (d->tracks[i] != NULL && d->tracks[i]->type == type) + if (d->tracks[i]->id == n) + return d->tracks[i]; + + return NULL; +} + +static void add_cluster_position(mkv_demuxer_t *mkv_d, uint64_t filepos, + uint64_t timecode) +{ + if (mkv_d->indexes) + return; + + int n = mkv_d->num_cluster_pos; + if (n > 0 && mkv_d->cluster_positions[n-1].filepos >= filepos) + return; + + mkv_d->cluster_positions = + grow_array(mkv_d->cluster_positions, mkv_d->num_cluster_pos, + sizeof(*mkv_d->cluster_positions)); + mkv_d->cluster_positions[mkv_d->num_cluster_pos++] = (struct cluster_pos){ + .filepos = filepos, + .timecode = timecode, + }; +} + + +#define AAC_SYNC_EXTENSION_TYPE 0x02b7 +static int aac_get_sample_rate_index(uint32_t sample_rate) +{ + static const int srates[] = { + 92017, 75132, 55426, 46009, 37566, 27713, + 23004, 18783, 13856, 11502, 9391, 0 + }; + int i = 0; + while (sample_rate < srates[i]) + i++; + return i; +} + +static void demux_mkv_decode(mkv_track_t *track, uint8_t *src, + uint8_t **dest, uint32_t *size, uint32_t type) +{ + uint8_t *orig_src = src; + + *dest = src; + + for (int i = 0; i < track->num_encodings; i++) { + struct mkv_content_encoding *enc = track->encodings + i; + if (!(enc->scope & type)) + continue; + + if (src != *dest && src != orig_src) + talloc_free(src); + src = *dest; // output from last iteration is new source + + if (enc->comp_algo == 0) { +#if CONFIG_ZLIB + /* zlib encoded track */ + + if (*size == 0) + continue; + + z_stream zstream; + + zstream.zalloc = (alloc_func) 0; + zstream.zfree = (free_func) 0; + zstream.opaque = (voidpf) 0; + if (inflateInit(&zstream) != Z_OK) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] zlib initialization failed.\n"); + goto error; + } + zstream.next_in = (Bytef *) src; + zstream.avail_in = *size; + + *dest = NULL; + zstream.avail_out = *size; + int result; + do { + *size += 4000; + *dest = talloc_realloc_size(NULL, *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_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] zlib decompression failed.\n"); + talloc_free(*dest); + *dest = NULL; + inflateEnd(&zstream); + goto error; + } + zstream.avail_out += 4000; + } while (zstream.avail_out == 4000 && zstream.avail_in != 0 + && result != Z_STREAM_END); + + *size = zstream.total_out; + inflateEnd(&zstream); +#endif + } else if (enc->comp_algo == 2) { + /* lzo encoded track */ + int out_avail; + int dstlen = *size * 3; + + *dest = NULL; + while (1) { + int srclen = *size; + *dest = talloc_realloc_size(NULL, *dest, + dstlen + AV_LZO_OUTPUT_PADDING); + out_avail = dstlen; + int result = av_lzo1x_decode(*dest, &out_avail, src, &srclen); + if (result == 0) + break; + if (!(result & AV_LZO_OUTPUT_FULL)) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] lzo decompression failed.\n"); + talloc_free(*dest); + *dest = NULL; + goto error; + } + mp_msg(MSGT_DEMUX, MSGL_DBG2, + "[mkv] lzo decompression buffer too small.\n"); + dstlen *= 2; + } + *size = dstlen - out_avail; + } else if (enc->comp_algo == 3) { + *dest = talloc_size(NULL, *size + enc->comp_settings_len); + memcpy(*dest, enc->comp_settings, enc->comp_settings_len); + memcpy(*dest + enc->comp_settings_len, src, *size); + *size += enc->comp_settings_len; + } + } + + error: + if (src != *dest && src != orig_src) + talloc_free(src); +} + + +static int demux_mkv_read_info(demuxer_t *demuxer) +{ + mkv_demuxer_t *mkv_d = demuxer->priv; + stream_t *s = demuxer->stream; + int res = 0; + + mkv_d->tc_scale = 1000000; + mkv_d->duration = 0; + + struct ebml_info info = {}; + struct ebml_parse_ctx parse_ctx = {}; + if (ebml_read_element(s, &parse_ctx, &info, &ebml_info_desc) < 0) + return -1; + if (info.n_timecode_scale) { + mkv_d->tc_scale = info.timecode_scale; + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] | + timecode scale: %" PRIu64 "\n", mkv_d->tc_scale); + } + if (info.n_duration) { + mkv_d->duration = info.duration * mkv_d->tc_scale / 1e9; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + duration: %.3fs\n", + mkv_d->duration); + } + if (info.n_segment_uid) { + int len = info.segment_uid.len; + if (len != sizeof(demuxer->matroska_data.segment_uid)) { + mp_msg(MSGT_DEMUX, MSGL_INFO, + "[mkv] segment uid invalid length %d\n", len); + } else { + memcpy(demuxer->matroska_data.segment_uid, info.segment_uid.start, + len); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + segment uid"); + for (int i = 0; i < len; i++) + mp_msg(MSGT_DEMUX, MSGL_V, " %02x", + demuxer->matroska_data.segment_uid[i]); + mp_msg(MSGT_DEMUX, MSGL_V, "\n"); + } + } + if (demuxer->params && demuxer->params->matroska_wanted_uids) { + unsigned char (*uids)[16] = demuxer->params->matroska_wanted_uids; + if (!info.n_segment_uid) + uids = NULL; + for (int i = 0; i < MP_TALLOC_ELEMS(uids); i++) { + if (!memcmp(info.segment_uid.start, uids[i], 16)) + goto out; + } + mp_tmsg(MSGT_DEMUX, MSGL_INFO, + "[mkv] This is not one of the wanted files. " + "Stopping attempt to open.\n"); + res = -2; + } + out: + talloc_free(parse_ctx.talloc_ctx); + return res; +} + +static void parse_trackencodings(struct demuxer *demuxer, + struct mkv_track *track, + struct ebml_content_encodings *encodings) +{ + // initial allocation to be a non-NULL context before realloc + mkv_content_encoding_t *ce = talloc_size(track, 1); + + for (int n_enc = 0; n_enc < encodings->n_content_encoding; n_enc++) { + struct ebml_content_encoding *enc = encodings->content_encoding + n_enc; + struct mkv_content_encoding e = {}; + e.order = enc->content_encoding_order; + if (enc->n_content_encoding_scope) + e.scope = enc->content_encoding_scope; + else + e.scope = 1; + e.type = enc->content_encoding_type; + + if (enc->n_content_compression) { + struct ebml_content_compression *z = &enc->content_compression; + e.comp_algo = z->content_comp_algo; + if (z->n_content_comp_settings) { + int sz = z->content_comp_settings.len; + e.comp_settings = talloc_size(ce, sz); + memcpy(e.comp_settings, z->content_comp_settings.start, sz); + e.comp_settings_len = sz; + } + } + + if (e.type == 1) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, "[mkv] Track " + "number %u has been encrypted and " + "decryption has not yet been\n" + "[mkv] implemented. Skipping track.\n", + track->tnum); + } else if (e.type != 0) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] Unknown content encoding type for " + "track %u. Skipping track.\n", + track->tnum); + } else if (e.comp_algo != 0 && e.comp_algo != 2 && e.comp_algo != 3) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] Track %u has been compressed with " + "an unknown/unsupported compression\n" + "[mkv] algorithm (%" PRIu64 "). Skipping track.\n", + track->tnum, e.comp_algo); + } +#if !CONFIG_ZLIB + else if (e.comp_algo == 0) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] Track %u was compressed with zlib " + "but mpv has not been compiled\n" + "[mkv] with support for zlib compression. " + "Skipping track.\n", + track->tnum); + } +#endif + int i; + for (i = 0; i < n_enc; i++) + if (e.order >= ce[i].order) + break; + ce = talloc_realloc_size(track, ce, (n_enc + 1) * sizeof(*ce)); + memmove(ce + i + 1, ce + i, (n_enc - i) * sizeof(*ce)); + memcpy(ce + i, &e, sizeof(e)); + } + + track->encodings = ce; + track->num_encodings = encodings->n_content_encoding; +} + +static void parse_trackaudio(struct demuxer *demuxer, struct mkv_track *track, + struct ebml_audio *audio) +{ + if (audio->n_sampling_frequency) { + track->a_sfreq = audio->sampling_frequency; + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] | + Sampling frequency: %f\n", track->a_sfreq); + } else + track->a_sfreq = 8000; + if (audio->n_output_sampling_frequency) { + track->a_osfreq = audio->output_sampling_frequency; + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] | + Output sampling frequency: %f\n", track->a_osfreq); + } else + track->a_osfreq = track->a_sfreq; + if (audio->n_bit_depth) { + track->a_bps = audio->bit_depth; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Bit depth: %u\n", + track->a_bps); + } + if (audio->n_channels) { + track->a_channels = audio->channels; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Channels: %u\n", + track->a_channels); + } else + track->a_channels = 1; +} + +static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track, + struct ebml_video *video) +{ + if (video->n_frame_rate) { + track->v_frate = video->frame_rate; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Frame rate: %f\n", + track->v_frate); + if (track->v_frate > 0) + track->default_duration = 1 / track->v_frate; + } + if (video->n_display_width) { + track->v_dwidth = video->display_width; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Display width: %u\n", + track->v_dwidth); + } + if (video->n_display_height) { + track->v_dheight = video->display_height; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Display height: %u\n", + track->v_dheight); + } + if (video->n_pixel_width) { + track->v_width = video->pixel_width; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Pixel width: %u\n", + track->v_width); + } + if (video->n_pixel_height) { + track->v_height = video->pixel_height; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Pixel height: %u\n", + track->v_height); + } +} + +/** + * \brief free any data associated with given track + * \param track track of which to free data + */ +static void demux_mkv_free_trackentry(mkv_track_t *track) +{ + free(track->audio_buf); + free(track->audio_timestamp); + talloc_free(track); +} + +static void parse_trackentry(struct demuxer *demuxer, + struct ebml_track_entry *entry) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + struct mkv_track *track = talloc_zero_size(NULL, sizeof(*track)); + + track->tnum = entry->track_number; + if (track->tnum) + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Track number: %u\n", + track->tnum); + else + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Missing track number!\n"); + + if (entry->n_name) { + track->name = talloc_strndup(track, entry->name.start, + entry->name.len); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Name: %s\n", + track->name); + } + + track->type = entry->track_type; + 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; + } + + if (entry->n_audio) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Audio track\n"); + parse_trackaudio(demuxer, track, &entry->audio); + } + + if (entry->n_video) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Video track\n"); + parse_trackvideo(demuxer, track, &entry->video); + } + + if (entry->n_codec_id) { + track->codec_id = talloc_strndup(track, entry->codec_id.start, + entry->codec_id.len); + if (!strcmp(track->codec_id, MKV_V_MSCOMP) + || !strcmp(track->codec_id, MKV_A_ACM)) + track->ms_compat = 1; + else if (!strcmp(track->codec_id, MKV_S_VOBSUB)) + track->subtitle_type = 'v'; + 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 = 'a'; + else if (!strcmp(track->codec_id, MKV_S_TEXTASCII) + || !strcmp(track->codec_id, MKV_S_TEXTUTF8)) + track->subtitle_type = 't'; + else if (!strcmp(track->codec_id, MKV_S_PGS)) + track->subtitle_type = 'p'; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Codec ID: %s\n", + track->codec_id); + } else + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Missing codec ID!\n"); + + if (entry->n_codec_private) { + int len = entry->codec_private.len; + track->private_data = talloc_size(track, len + AV_LZO_INPUT_PADDING); + memcpy(track->private_data, entry->codec_private.start, len); + track->private_size = len; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + CodecPrivate, length %u\n", + track->private_size); + } + + if (entry->n_language) { + track->language = talloc_strndup(track, entry->language.start, + entry->language.len); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Language: %s\n", + track->language); + } else + track->language = talloc_strdup(track, "eng"); + + if (entry->n_flag_default) { + track->default_track = entry->flag_default; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Default flag: %u\n", + track->default_track); + } else + track->default_track = 1; + + if (entry->n_default_duration) { + track->default_duration = entry->default_duration / 1e9; + if (entry->default_duration == 0) + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Default duration: 0"); + else { + if (!track->v_frate) + track->v_frate = 1e9 / entry->default_duration; + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] | + Default duration: %.3fms ( = %.3f fps)\n", + entry->default_duration / 1000000.0, track->v_frate); + } + } + + if (entry->n_content_encodings) + parse_trackencodings(demuxer, track, &entry->content_encodings); + + mkv_d->tracks[mkv_d->num_tracks++] = track; +} + +static int demux_mkv_read_tracks(demuxer_t *demuxer) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + stream_t *s = demuxer->stream; + + struct ebml_tracks tracks = {}; + struct ebml_parse_ctx parse_ctx = {}; + if (ebml_read_element(s, &parse_ctx, &tracks, &ebml_tracks_desc) < 0) + return -1; + + mkv_d->tracks = talloc_size(mkv_d, + tracks.n_track_entry * sizeof(*mkv_d->tracks)); + for (int i = 0; i < tracks.n_track_entry; i++) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + a track...\n"); + parse_trackentry(demuxer, &tracks.track_entry[i]); + } + talloc_free(parse_ctx.talloc_ctx); + 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; + + if (index_mode == 0 || index_mode == 2) { + ebml_read_skip(s, NULL); + return 0; + } + + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing cues ] -----------\n"); + struct ebml_cues cues = {}; + struct ebml_parse_ctx parse_ctx = {}; + if (ebml_read_element(s, &parse_ctx, &cues, &ebml_cues_desc) < 0) + return -1; + for (int i = 0; i < cues.n_cue_point; i++) { + struct ebml_cue_point *cuepoint = &cues.cue_point[i]; + if (cuepoint->n_cue_time != 1 || !cuepoint->n_cue_track_positions) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Malformed CuePoint element\n"); + continue; + } + uint64_t time = cuepoint->cue_time; + for (int i = 0; i < cuepoint->n_cue_track_positions; i++) { + struct ebml_cue_track_positions *trackpos = + &cuepoint->cue_track_positions[i]; + uint64_t track = trackpos->cue_track; + uint64_t pos = trackpos->cue_cluster_position; + mkv_d->indexes = + grow_array(mkv_d->indexes, mkv_d->num_indexes, + 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 %" PRIu64 + ": timecode %" PRIu64 ", filepos: %" PRIu64 "\n", track, + time, mkv_d->segment_start + pos); + mkv_d->num_indexes++; + } + } + + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing cues ] -----------\n"); + talloc_free(parse_ctx.talloc_ctx); + return 0; +} + +static int demux_mkv_read_chapters(struct demuxer *demuxer) +{ + struct MPOpts *opts = demuxer->opts; + stream_t *s = demuxer->stream; + + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing chapters ] ---------\n"); + struct ebml_chapters file_chapters = {}; + struct ebml_parse_ctx parse_ctx = {}; + if (ebml_read_element(s, &parse_ctx, &file_chapters, + &ebml_chapters_desc) < 0) + return -1; + + int selected_edition = 0; + int num_editions = file_chapters.n_edition_entry; + struct ebml_edition_entry *editions = file_chapters.edition_entry; + if (opts->edition_id >= 0 && opts->edition_id < num_editions) { + selected_edition = opts->edition_id; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] User-specified edition: %d\n", + selected_edition); + } else + for (int i = 0; i < num_editions; i++) + if (editions[i].edition_flag_default) { + selected_edition = i; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Default edition: %d\n", i); + break; + } + struct matroska_chapter *m_chapters = NULL; + if (editions[selected_edition].edition_flag_ordered) { + int count = editions[selected_edition].n_chapter_atom; + m_chapters = talloc_array_ptrtype(demuxer, m_chapters, count); + demuxer->matroska_data.ordered_chapters = m_chapters; + demuxer->matroska_data.num_ordered_chapters = count; + } + + for (int idx = 0; idx < num_editions; idx++) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] New edition %d\n", idx); + int warn_level = idx == selected_edition ? MSGL_WARN : MSGL_V; + if (editions[idx].n_edition_flag_default) + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Default edition flag: %"PRIu64 + "\n", editions[idx].edition_flag_default); + if (editions[idx].n_edition_flag_ordered) + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Ordered chapter flag: %"PRIu64 + "\n", editions[idx].edition_flag_ordered); + for (int i = 0; i < editions[idx].n_chapter_atom; i++) { + struct ebml_chapter_atom *ca = editions[idx].chapter_atom + i; + struct matroska_chapter chapter = { }; + struct bstr name = { "(unnamed)", 9 }; + + if (!ca->n_chapter_time_start) + mp_msg(MSGT_DEMUX, warn_level, + "[mkv] Chapter lacks start time\n"); + chapter.start = ca->chapter_time_start; + chapter.end = ca->chapter_time_end; + + if (ca->n_chapter_display) { + if (ca->n_chapter_display > 1) + mp_msg(MSGT_DEMUX, warn_level, "[mkv] Multiple chapter " + "names not supported, picking first\n"); + if (!ca->chapter_display[0].n_chap_string) + mp_msg(MSGT_DEMUX, warn_level, "[mkv] Malformed chapter " + "name entry\n"); + else + name = ca->chapter_display[0].chap_string; + } + + if (ca->n_chapter_segment_uid) { + chapter.has_segment_uid = true; + int len = ca->chapter_segment_uid.len; + if (len != sizeof(chapter.segment_uid)) + mp_msg(MSGT_DEMUX, warn_level, + "[mkv] Chapter segment uid bad length %d\n", len); + else if (ca->n_chapter_segment_edition_uid) { + mp_tmsg(MSGT_DEMUX, warn_level, "[mkv] Warning: " + "unsupported edition recursion in chapter; " + "will skip on playback!\n"); + } else { + memcpy(chapter.segment_uid, ca->chapter_segment_uid.start, + len); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Chapter segment uid "); + for (int i = 0; i < len; i++) + mp_msg(MSGT_DEMUX, MSGL_V, "%02x ", + chapter.segment_uid[i]); + mp_msg(MSGT_DEMUX, MSGL_V, "\n"); + } + } + + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] Chapter %u from %02d:%02d:%02d.%03d " + "to %02d:%02d:%02d.%03d, %.*s\n", i, + (int) (chapter.start / 60 / 60 / 1000000000), + (int) ((chapter.start / 60 / 1000000000) % 60), + (int) ((chapter.start / 1000000000) % 60), + (int) (chapter.start % 1000000000), + (int) (chapter.end / 60 / 60 / 1000000000), + (int) ((chapter.end / 60 / 1000000000) % 60), + (int) ((chapter.end / 1000000000) % 60), + (int) (chapter.end % 1000000000), + BSTR_P(name)); + + if (idx == selected_edition){ + demuxer_add_chapter(demuxer, name, chapter.start, chapter.end); + if (editions[idx].edition_flag_ordered) { + chapter.name = talloc_strndup(m_chapters, name.start, + name.len); + m_chapters[i] = chapter; + } + } + } + } + if (num_editions > 1) + mp_msg(MSGT_DEMUX, MSGL_INFO, + "[mkv] Found %d editions, will play #%d (first is 0).\n", + num_editions, selected_edition); + + demuxer->num_editions = num_editions; + demuxer->edition = selected_edition; + + talloc_free(parse_ctx.talloc_ctx); + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] \\---- [ parsing chapters ] ---------\n"); + return 0; +} + +static int demux_mkv_read_tags(demuxer_t *demuxer) +{ + stream_t *s = demuxer->stream; + + struct ebml_parse_ctx parse_ctx = {}; + struct ebml_tags tags = {}; + if (ebml_read_element(s, &parse_ctx, &tags, &ebml_tags_desc) < 0) + return -1; + + for (int i = 0; i < tags.n_tag; i++) { + struct ebml_tag tag = tags.tag[i]; + if (tag.targets.target_track_uid || tag.targets.target_edition_uid || + tag.targets.target_chapter_uid || tag.targets.target_attachment_uid) + continue; + + for (int j = 0; j < tag.n_simple_tag; j++) + demux_info_add_bstr(demuxer, tag.simple_tag[j].tag_name, tag.simple_tag[j].tag_string); + } + + talloc_free(parse_ctx.talloc_ctx); + return 0; +} + +static int demux_mkv_read_attachments(demuxer_t *demuxer) +{ + stream_t *s = demuxer->stream; + + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] /---- [ parsing attachments ] ---------\n"); + + struct ebml_attachments attachments = {}; + struct ebml_parse_ctx parse_ctx = {}; + if (ebml_read_element(s, &parse_ctx, &attachments, + &ebml_attachments_desc) < 0) + return -1; + + for (int i = 0; i < attachments.n_attached_file; i++) { + struct ebml_attached_file *attachment = &attachments.attached_file[i]; + if (!attachment->n_file_name || !attachment->n_file_mime_type + || !attachment->n_file_data) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Malformed attachment\n"); + continue; + } + struct bstr name = attachment->file_name; + struct bstr mime = attachment->file_mime_type; + demuxer_add_attachment(demuxer, name, mime, attachment->file_data); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Attachment: %.*s, %.*s, %zu bytes\n", + BSTR_P(name), BSTR_P(mime), attachment->file_data.len); + } + + talloc_free(parse_ctx.talloc_ctx); + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] \\---- [ parsing attachments ] ---------\n"); + return 0; +} + +static int read_header_element(struct demuxer *demuxer, uint32_t id, + off_t at_filepos); + +static int demux_mkv_read_seekhead(demuxer_t *demuxer) +{ + struct mkv_demuxer *mkv_d = demuxer->priv; + struct stream *s = demuxer->stream; + int res = 0; + struct ebml_seek_head seekhead = {}; + struct ebml_parse_ctx parse_ctx = {}; + + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] /---- [ parsing seek head ] ---------\n"); + if (ebml_read_element(s, &parse_ctx, &seekhead, &ebml_seek_head_desc) < 0) { + res = -1; + goto out; + } + /* off now holds the position of the next element after the seek head. */ + off_t off = stream_tell(s); + for (int i = 0; i < seekhead.n_seek; i++) { + struct ebml_seek *seek = &seekhead.seek[i]; + if (seek->n_seek_id != 1 || seek->n_seek_position != 1) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Invalid SeekHead entry\n"); + continue; + } + uint64_t pos = seek->seek_position + mkv_d->segment_start; + if (pos >= demuxer->movi_end) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] SeekHead position beyond " + "end of file - incomplete file?\n"); + continue; + } + int r = read_header_element(demuxer, seek->seek_id, pos); + if (r <= -2) { + res = r; + goto out; + } + } + if (!stream_seek(s, off)) { + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Couldn't seek back after " + "SeekHead??\n"); + res = -1; + } + out: + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] \\---- [ parsing seek head ] ---------\n"); + talloc_free(parse_ctx.talloc_ctx); + return res; +} + +static bool seek_pos_id(struct stream *s, off_t pos, uint32_t id) +{ + if (!stream_seek(s, pos)) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Failed to seek in file\n"); + return false; + } + if (ebml_read_id(s, NULL) != id) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Expected element not found\n"); + return false; + } + return true; +} + +static int read_header_element(struct demuxer *demuxer, uint32_t id, + off_t at_filepos) +{ + struct mkv_demuxer *mkv_d = demuxer->priv; + stream_t *s = demuxer->stream; + off_t pos = stream_tell(s) - 4; + int res = 1; + + switch(id) { + case MATROSKA_ID_INFO: + if (mkv_d->parsed_info) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ segment information...\n"); + mkv_d->parsed_info = true; + return demux_mkv_read_info(demuxer); + + case MATROSKA_ID_TRACKS: + if (mkv_d->parsed_tracks) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_tracks = true; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ segment tracks...\n"); + return demux_mkv_read_tracks(demuxer); + + case MATROSKA_ID_CUES: + if (is_parsed_header(mkv_d, pos)) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + return demux_mkv_read_cues(demuxer); + + case MATROSKA_ID_TAGS: + if (mkv_d->parsed_tags) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_tags = true; + return demux_mkv_read_tags(demuxer); + + case MATROSKA_ID_SEEKHEAD: + if (is_parsed_header(mkv_d, pos)) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + return demux_mkv_read_seekhead(demuxer); + + case MATROSKA_ID_CHAPTERS: + if (mkv_d->parsed_chapters) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_chapters = true; + return demux_mkv_read_chapters(demuxer); + + case MATROSKA_ID_ATTACHMENTS: + if (mkv_d->parsed_attachments) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_attachments = true; + return demux_mkv_read_attachments(demuxer); + + case EBML_ID_VOID: + break; + + default: + res = 2; + } + if (!at_filepos) + ebml_read_skip(s, NULL); + return res; +} + + + +static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track, + int vid); +static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track, + int aid); +static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track, + int sid); + +static void display_create_tracks(demuxer_t *demuxer) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + int i, vid = 0, aid = 0, sid = 0; + + for (i = 0; i < mkv_d->num_tracks; i++) { + char *type = "unknown", str[32]; + *str = '\0'; + switch (mkv_d->tracks[i]->type) { + case MATROSKA_TRACK_VIDEO: + type = "video"; + mkv_d->tracks[i]->id = -1; + if (vid == MAX_V_STREAMS) + break; + mkv_d->tracks[i]->id = vid; + demux_mkv_open_video(demuxer, mkv_d->tracks[i], vid); + if (mkv_d->tracks[i]->name) + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VID_%d_NAME=%s\n", vid, + mkv_d->tracks[i]->name); + sprintf(str, "-vid %u", vid++); + break; + case MATROSKA_TRACK_AUDIO: + type = "audio"; + mkv_d->tracks[i]->id = -1; + if (aid == MAX_A_STREAMS) + break; + mkv_d->tracks[i]->id = aid; + demux_mkv_open_audio(demuxer, mkv_d->tracks[i], aid); + if (mkv_d->tracks[i]->name) + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AID_%d_NAME=%s\n", aid, + mkv_d->tracks[i]->name); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AID_%d_LANG=%s\n", aid, + mkv_d->tracks[i]->language); + sprintf(str, "-aid %u, -alang %.5s", aid++, + mkv_d->tracks[i]->language); + break; + case MATROSKA_TRACK_SUBTITLE: + type = "subtitles"; + mkv_d->tracks[i]->id = -1; + if (sid == MAX_S_STREAMS) + break; + mkv_d->tracks[i]->id = sid; + demux_mkv_open_sub(demuxer, mkv_d->tracks[i], sid); + if (mkv_d->tracks[i]->name) + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SID_%d_NAME=%s\n", sid, + mkv_d->tracks[i]->name); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SID_%d_LANG=%s\n", sid, + mkv_d->tracks[i]->language); + sprintf(str, "-sid %u, -slang %.5s", sid++, + mkv_d->tracks[i]->language); + break; + } + if (mkv_d->tracks[i]->name) + mp_tmsg(MSGT_DEMUX, MSGL_V, + "[mkv] Track ID %u: %s (%s) \"%s\", %s\n", + mkv_d->tracks[i]->tnum, type, mkv_d->tracks[i]->codec_id, + mkv_d->tracks[i]->name, str); + else + mp_tmsg(MSGT_DEMUX, MSGL_V, "[mkv] Track ID %u: %s (%s), %s\n", + mkv_d->tracks[i]->tnum, type, mkv_d->tracks[i]->codec_id, + str); + } + mkv_d->num_audio_tracks = aid; + mkv_d->num_video_tracks = vid; +} + +typedef struct { + char *id; + int fourcc; + int extradata; +} videocodec_info_t; + +static const videocodec_info_t vinfo[] = { + {MKV_V_MJPEG, mmioFOURCC('m', 'j', 'p', 'g'), 1}, + {MKV_V_MPEG1, mmioFOURCC('m', 'p', 'g', '1'), 0}, + {MKV_V_MPEG2, mmioFOURCC('m', 'p', 'g', '2'), 0}, + {MKV_V_MPEG4_SP, mmioFOURCC('m', 'p', '4', 'v'), 1}, + {MKV_V_MPEG4_ASP, mmioFOURCC('m', 'p', '4', 'v'), 1}, + {MKV_V_MPEG4_AP, mmioFOURCC('m', 'p', '4', 'v'), 1}, + {MKV_V_MPEG4_AVC, mmioFOURCC('a', 'v', 'c', '1'), 1}, + {MKV_V_THEORA, mmioFOURCC('t', 'h', 'e', 'o'), 1}, + {MKV_V_VP8, mmioFOURCC('V', 'P', '8', '0'), 0}, + {NULL, 0, 0} +}; + +static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track, + int vid) +{ + BITMAPINFOHEADER *bih; + sh_video_t *sh_v; + + if (track->ms_compat) { /* MS compatibility mode */ + BITMAPINFOHEADER *src; + + if (track->private_data == NULL + || track->private_size < sizeof(*bih)) + return 1; + + src = (BITMAPINFOHEADER *) track->private_data; + bih = calloc(1, 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(bih + 1, + src + 1, + track->private_size - sizeof(*bih)); + + if (track->v_width == 0) + track->v_width = bih->biWidth; + if (track->v_height == 0) + track->v_height = bih->biHeight; + } else { + bih = calloc(1, sizeof(*bih)); + bih->biSize = sizeof(*bih); + 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 >= RVPROPERTIES_SIZE + && (!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; + uint32_t type2; + unsigned int cnt; + + src = (uint8_t *) track->private_data + RVPROPERTIES_SIZE; + + cnt = track->private_size - RVPROPERTIES_SIZE; + bih = realloc(bih, sizeof(*bih) + 8 + cnt); + bih->biSize = 48 + cnt; + bih->biPlanes = 1; + type2 = AV_RB32(src - 4); + 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); + // copy type1 and type2 info from rv properties + memcpy(dst, src - 8, 8 + cnt); + track->realmedia = 1; + + } else { + const videocodec_info_t *vi = vinfo; + while (vi->id && strcmp(vi->id, track->codec_id)) + vi++; + bih->biCompression = vi->fourcc; + if (vi->extradata && track->private_data + && (track->private_size > 0)) { + bih->biSize += track->private_size; + bih = realloc(bih, bih->biSize); + memcpy(bih + 1, track->private_data, track->private_size); + } + if (!vi->id) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, "[mkv] Unknown/unsupported " + "CodecID (%s) or missing/bad CodecPrivate\n" + "[mkv] data (track %u).\n", + track->codec_id, track->tnum); + free(bih); + return 1; + } + } + } + + sh_v = new_sh_video(demuxer, vid); + sh_v->gsh->demuxer_id = track->tnum; + sh_v->demuxer_codecname = track->codec_id; + sh_v->gsh->title = talloc_strdup(sh_v, track->name); + 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; + sh_v->aspect = 0; + if (!track->realmedia) { + sh_v->disp_w = track->v_width; + sh_v->disp_h = track->v_height; + if (track->v_dheight) + sh_v->aspect = (double) track->v_dwidth / 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; + } + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Aspect: %f\n", sh_v->aspect); + + sh_v->ds = demuxer->video; + return 0; +} + +static struct mkv_audio_tag { + char *id; bool prefix; uint32_t formattag; +} mkv_audio_tags[] = { + { MKV_A_MP2, 0, 0x0055 }, + { MKV_A_MP3, 0, 0x0055 }, + { MKV_A_AC3, 1, 0x2000 }, + { MKV_A_EAC3, 1, mmioFOURCC('E', 'A', 'C', '3') }, + { MKV_A_DTS, 0, 0x2001 }, + { MKV_A_PCM, 0, 0x0001 }, + { MKV_A_PCM_BE, 0, 0x0001 }, + { MKV_A_AAC_2MAIN, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_2LC, 1, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_2SSR, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_4MAIN, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_4LC, 1, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_4SSR, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_4LTP, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_VORBIS, 0, mmioFOURCC('v', 'r', 'b', 's') }, + { MKV_A_QDMC, 0, mmioFOURCC('Q', 'D', 'M', 'C') }, + { MKV_A_QDMC2, 0, mmioFOURCC('Q', 'D', 'M', '2') }, + { MKV_A_WAVPACK, 0, mmioFOURCC('W', 'V', 'P', 'K') }, + { MKV_A_TRUEHD, 0, mmioFOURCC('T', 'R', 'H', 'D') }, + { MKV_A_FLAC, 0, mmioFOURCC('f', 'L', 'a', 'C') }, + { MKV_A_REAL28, 0, mmioFOURCC('2', '8', '_', '8') }, + { MKV_A_REALATRC, 0, mmioFOURCC('a', 't', 'r', 'c') }, + { MKV_A_REALCOOK, 0, mmioFOURCC('c', 'o', 'o', 'k') }, + { MKV_A_REALDNET, 0, mmioFOURCC('d', 'n', 'e', 't') }, + { MKV_A_REALSIPR, 0, mmioFOURCC('s', 'i', 'p', 'r') }, + { MKV_A_TTA1, 0, mmioFOURCC('T', 'T', 'A', '1') }, + { NULL }, +}; + + +static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track, + int aid) +{ + sh_audio_t *sh_a = new_sh_audio(demuxer, aid); + if (!sh_a) + return 1; + + if (track->language && (strcmp(track->language, "und") != 0)) + sh_a->lang = talloc_strdup(sh_a, track->language); + sh_a->gsh->demuxer_id = track->tnum; + sh_a->demuxer_codecname = track->codec_id; + sh_a->gsh->title = talloc_strdup(sh_a, track->name); + sh_a->gsh->default_track = track->default_track; + sh_a->ds = demuxer->audio; + if (track->ms_compat) { + if (track->private_size < sizeof(*sh_a->wf)) + goto error; + WAVEFORMATEX *wf = (WAVEFORMATEX *) track->private_data; + sh_a->wf = calloc(1, track->private_size); + sh_a->wf->wFormatTag = le2me_16(wf->wFormatTag); + sh_a->wf->nChannels = le2me_16(wf->nChannels); + sh_a->wf->nSamplesPerSec = le2me_32(wf->nSamplesPerSec); + sh_a->wf->nAvgBytesPerSec = le2me_32(wf->nAvgBytesPerSec); + sh_a->wf->nBlockAlign = le2me_16(wf->nBlockAlign); + sh_a->wf->wBitsPerSample = le2me_16(wf->wBitsPerSample); + sh_a->wf->cbSize = track->private_size - sizeof(*sh_a->wf); + memcpy(sh_a->wf + 1, wf + 1, + track->private_size - sizeof(*sh_a->wf)); + if (track->a_sfreq == 0.0) + track->a_sfreq = sh_a->wf->nSamplesPerSec; + if (track->a_channels == 0) + track->a_channels = sh_a->wf->nChannels; + if (track->a_bps == 0) + track->a_bps = sh_a->wf->wBitsPerSample; + track->a_formattag = sh_a->wf->wFormatTag; + } else { + sh_a->wf = calloc(1, sizeof(*sh_a->wf)); + for (int i = 0; ; i++) { + struct mkv_audio_tag *t = mkv_audio_tags + i; + if (t->id == NULL) + goto error; + if (t->prefix) { + if (!bstr_startswith0(bstr0(track->codec_id), t->id)) + continue; + } else { + if (strcmp(track->codec_id, t->id)) + continue; + } + track->a_formattag = t->formattag; + break; + } + } + + 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->container_out_samplerate = track->a_osfreq; + sh_a->wf->nSamplesPerSec = (uint32_t) track->a_sfreq; + if (track->a_bps == 0) { + sh_a->samplesize = 2; + sh_a->wf->wBitsPerSample = 16; + } else { + sh_a->samplesize = track->a_bps / 8; + sh_a->wf->wBitsPerSample = track->a_bps; + } + if (track->a_formattag == 0x0055) { /* MP3 || MP2 */ + sh_a->wf->nAvgBytesPerSec = 16000; + sh_a->wf->nBlockAlign = 1152; + } else if ((track->a_formattag == 0x2000) /* AC3 */ + || track->a_formattag == mmioFOURCC('E', 'A', 'C', '3') + || (track->a_formattag == 0x2001)) { /* DTS */ + free(sh_a->wf); + sh_a->wf = NULL; + } 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; + 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->nAvgBytesPerSec = 16000; + sh_a->wf->nBlockAlign = 1486; + track->fix_i_bps = 1; + track->qt_last_a_pts = 0.0; + goto copy_private_data; + } else if (track->a_formattag == mmioFOURCC('M', 'P', '4', 'A')) { + int profile, srate_idx; + + sh_a->wf->nAvgBytesPerSec = 16000; + sh_a->wf->nBlockAlign = 1024; + + if (!strcmp(track->codec_id, MKV_A_AAC) && track->private_data) + goto copy_private_data; + + /* 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 = 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 / sh_a->samplerate; + } + } else if (track->a_formattag == mmioFOURCC('v', 'r', 'b', 's')) { + /* VORBIS */ + if (track->private_size == 0 || track->ms_compat && !sh_a->wf->cbSize) + goto error; + if (!track->ms_compat) { + sh_a->wf->cbSize = track->private_size; + sh_a->wf = realloc(sh_a->wf, sizeof(*sh_a->wf) + sh_a->wf->cbSize); + memcpy((unsigned char *) (sh_a->wf + 1), track->private_data, + sh_a->wf->cbSize); + } + } else if (!strncmp(track->codec_id, MKV_A_REALATRC, 7)) { + if (track->private_size < RAPROPERTIES4_SIZE) + goto error; + /* Common initialization for all RealAudio codecs */ + unsigned char *src = track->private_data; + int codecdata_length, version; + int flavor; + + sh_a->wf->nAvgBytesPerSec = 0; /* FIXME !? */ + + version = AV_RB16(src + 4); + flavor = AV_RB16(src + 22); + track->coded_framesize = AV_RB32(src + 24); + track->sub_packet_h = AV_RB16(src + 40); + sh_a->wf->nBlockAlign = track->audiopk_size = AV_RB16(src + 42); + track->sub_packet_size = AV_RB16(src + 44); + if (version == 4) { + src += RAPROPERTIES4_SIZE; + src += src[0] + 1; + src += src[0] + 1; + } else + src += RAPROPERTIES5_SIZE; + + src += 3; + if (version == 5) + src++; + codecdata_length = AV_RB32(src); + src += 4; + sh_a->wf->cbSize = codecdata_length; + sh_a->wf = realloc(sh_a->wf, sizeof(*sh_a->wf) + sh_a->wf->cbSize); + memcpy(((char *) (sh_a->wf + 1)), src, codecdata_length); + + switch (track->a_formattag) { + case mmioFOURCC('a', 't', 'r', 'c'): + sh_a->wf->nAvgBytesPerSec = atrc_fl2bps[flavor]; + sh_a->wf->nBlockAlign = track->sub_packet_size; + goto audiobuf; + case mmioFOURCC('c', 'o', 'o', 'k'): + sh_a->wf->nAvgBytesPerSec = cook_fl2bps[flavor]; + sh_a->wf->nBlockAlign = track->sub_packet_size; + goto audiobuf; + case mmioFOURCC('s', 'i', 'p', 'r'): + sh_a->wf->nAvgBytesPerSec = sipr_fl2bps[flavor]; + sh_a->wf->nBlockAlign = track->coded_framesize; + goto audiobuf; + case mmioFOURCC('2', '8', '_', '8'): + sh_a->wf->nAvgBytesPerSec = 3600; + sh_a->wf->nBlockAlign = track->coded_framesize; + audiobuf: + track->audio_buf = + malloc(track->sub_packet_h * track->audiopk_size); + track->audio_timestamp = + malloc(track->sub_packet_h * sizeof(double)); + break; + } + + 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->ms_compat) { + ptr = track->private_data; + size = track->private_size; + } else { + sh_a->format = mmioFOURCC('f', 'L', 'a', 'C'); + ptr = track->private_data + sizeof(*sh_a->wf); + size = track->private_size - sizeof(*sh_a->wf); + } + if (size < 4 || ptr[0] != 'f' || ptr[1] != 'L' || ptr[2] != 'a' + || ptr[3] != 'C') { + sh_a->codecdata = malloc(4); + sh_a->codecdata_len = 4; + memcpy(sh_a->codecdata, "fLaC", 4); + } else { + sh_a->codecdata = malloc(size); + sh_a->codecdata_len = size; + memcpy(sh_a->codecdata, ptr, size); + } + } else if (track->a_formattag == mmioFOURCC('W', 'V', 'P', 'K') || + track->a_formattag == mmioFOURCC('T', 'R', 'H', 'D')) { + copy_private_data: + if (!track->ms_compat && track->private_size) { + sh_a->codecdata = malloc(track->private_size); + sh_a->codecdata_len = track->private_size; + memcpy(sh_a->codecdata, track->private_data, track->private_size); + } + } else if (track->a_formattag == mmioFOURCC('T', 'T', 'A', '1')) { + sh_a->codecdata_len = 30; + sh_a->codecdata = calloc(1, sh_a->codecdata_len); + if (!sh_a->codecdata) + goto error; + char *data = sh_a->codecdata; + memcpy(data + 0, "TTA1", 4); + AV_WL16(data + 4, 1); + AV_WL16(data + 6, sh_a->channels); + AV_WL16(data + 8, sh_a->wf->wBitsPerSample); + AV_WL32(data + 10, sh_a->samplerate); + // Bogus: last frame won't be played. + AV_WL32(data + 14, 0); + } else if (!track->ms_compat) { + goto error; + } + + return 0; + + error: + mp_tmsg(MSGT_DEMUX, MSGL_WARN, "[mkv] Unknown/unsupported audio " + "codec ID '%s' for track %u or missing/faulty\n[mkv] " + "private codec data.\n", track->codec_id, track->tnum); + return 1; +} + +static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track, + int sid) +{ + if (track->subtitle_type) { + int size; + uint8_t *buffer; + sh_sub_t *sh = new_sh_sub(demuxer, sid); + sh->gsh->demuxer_id = track->tnum; + sh->demuxer_codecname = track->codec_id; + track->sh_sub = sh; + sh->type = track->subtitle_type; + size = track->private_size; + demux_mkv_decode(track, track->private_data, &buffer, &size, 2); + if (buffer && buffer != track->private_data) { + talloc_free(track->private_data); + talloc_steal(track, buffer); + track->private_data = buffer; + track->private_size = size; + } + sh->extradata = malloc(track->private_size); + memcpy(sh->extradata, track->private_data, track->private_size); + sh->extradata_len = track->private_size; + if (track->language && (strcmp(track->language, "und") != 0)) + sh->lang = talloc_strdup(sh, track->language); + sh->gsh->title = talloc_strdup(sh, track->name); + sh->gsh->default_track = track->default_track; + } else { + mp_tmsg(MSGT_DEMUX, MSGL_ERR, + "[mkv] Subtitle type '%s' is not supported.\n", + track->codec_id); + return 1; + } + + return 0; +} + +static void mkv_free(struct demuxer *demuxer) +{ + struct mkv_demuxer *mkv_d = demuxer->priv; + if (!mkv_d) + return; + for (int i = 0; i < mkv_d->num_tracks; i++) + demux_mkv_free_trackentry(mkv_d->tracks[i]); + free(mkv_d->indexes); + free(mkv_d->cluster_positions); +} + +static int demux_mkv_open(demuxer_t *demuxer) +{ + stream_t *s = demuxer->stream; + mkv_demuxer_t *mkv_d; + mkv_track_t *track; + + stream_seek(s, s->start_pos); + if (ebml_read_id(s, NULL) != EBML_ID_EBML) + return 0; + struct ebml_ebml ebml_master = {}; + struct ebml_parse_ctx parse_ctx = { .no_error_messages = true }; + if (ebml_read_element(s, &parse_ctx, &ebml_master, &ebml_ebml_desc) < 0) + return 0; + if (ebml_master.doc_type.start == NULL) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] File has EBML header but no doctype." + " Assuming \"matroska\".\n"); + } else if (bstrcmp(ebml_master.doc_type, bstr0("matroska")) != 0 + && bstrcmp(ebml_master.doc_type, bstr0("webm")) != 0) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] no head found\n"); + talloc_free(parse_ctx.talloc_ctx); + return 0; + } + if (ebml_master.doc_type_read_version > 2) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] This looks like a Matroska file, " + "but we don't support format version %"PRIu64"\n", + ebml_master.doc_type_read_version); + talloc_free(parse_ctx.talloc_ctx); + return 0; + } + if ((ebml_master.n_ebml_read_version + && ebml_master.ebml_read_version != EBML_VERSION) + || (ebml_master.n_ebml_max_size_length + && ebml_master.ebml_max_size_length > 8) + || (ebml_master.n_ebml_max_id_length + && ebml_master.ebml_max_id_length != 4)) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] This looks like a Matroska file, " + "but the header has bad parameters\n"); + talloc_free(parse_ctx.talloc_ctx); + return 0; + } + talloc_free(parse_ctx.talloc_ctx); + + 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 = talloc_zero(demuxer, struct mkv_demuxer); + demuxer->priv = mkv_d; + mkv_d->tc_scale = 1000000; + mkv_d->segment_start = stream_tell(s); + + while (1) { + uint32_t id = ebml_read_id(s, NULL); + if (s->eof) { + mp_tmsg(MSGT_DEMUX, MSGL_ERR, "[mkv] Unexpected end of file\n"); + return 0; + } + if (id == MATROSKA_ID_CLUSTER) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ found cluster, headers are " + "parsed completely :)\n"); + stream_seek(s, stream_tell(s) - 4); + break; + } + int res = read_header_element(demuxer, id, 0); + if (res <= -2) + return 0; + if (res < 0) + break; + } + + display_create_tracks(demuxer); + + /* select video track */ + track = NULL; + if (demuxer->video->id == -1) { /* automatically select a video track */ + /* search for a video track that has the 'default'