summaryrefslogtreecommitdiffstats
path: root/demux/demux_mkv.c
diff options
context:
space:
mode:
Diffstat (limited to 'demux/demux_mkv.c')
-rw-r--r--demux/demux_mkv.c2558
1 files changed, 2558 insertions, 0 deletions
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 <aurel@gnuage.org>
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <libavutil/common.h>
+#include <libavutil/lzo.h>
+#include <libavutil/intreadwrite.h>
+#include <libavutil/avstring.h>
+
+#include "config.h"
+
+#if CONFIG_ZLIB
+#include <zlib.h>
+#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