Mercurial > mplayer.hg
view libmpdemux/demux_mkv.c @ 17479:92a9690cc3e2
setting codec_tag unconditionally to FMP4 is a bad idea (breaks mov/mp4)
author | michael |
---|---|
date | Tue, 24 Jan 2006 23:55:43 +0000 |
parents | 59452efe579c |
children | 3f20b096782d |
line wrap: on
line source
/* * native Matroska demuxer * Written by Aurelien Jacobs <aurel@gnuage.org> * 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 <stdlib.h> #include <stdio.h> #include <ctype.h> #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 <zlib.h> #endif #ifdef USE_LIBLZO #include <lzo1x.h> #else #include "libmpcodecs/native/minilzo.h" #endif #if !defined(MIN) #define MIN(a, b) ((a)<(b)?(a):(b)) #endif #if !defined(MAX) #define MAX(a, b) ((a)>(b)?(a):(b)) #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; /* 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; /* The timecodes of video frames might have to be reordered if they're in display order (the timecodes, not the frames themselves!). In this case demux packets have to be cached with the help of these variables. */ int reorder_timecodes; demux_packet_t **cached_dps; int num_cached_dps, num_allocated_dps; float max_pts; /* generic content encoding support */ mkv_content_encoding_t *encodings; int num_encodings; /* For VobSubs */ mkv_sh_sub_t sh_sub; } 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; 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 __attribute__((__packed__)) { 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 __attribute__((__packed__)) { 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 __attribute__((__packed__)) { 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; extern int dvdsub_id; extern int demux_aid_vid_mismatch; 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, len; language += strspn(language,","); while((len = strcspn(language,",")) > 0) { for (i=0; i < d->num_tracks; i++) if (d->tracks[i] != NULL && d->tracks[i]->language != NULL && d->tracks[i]->type == type && !strncmp(d->tracks[i]->language, language, len)) return d->tracks[i]; language += len; language += strspn(language,","); } 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 vobsub_parse_size (mkv_track_t *t, const char *start) { if (sscanf(&start[6], "%dx%d", &t->sh_sub.width, &t->sh_sub.height) == 2) { mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] VobSub size: %ux%u\n", t->sh_sub.width, t->sh_sub.height); return 1; } return 0; } static int vobsub_parse_palette (mkv_track_t *t, const char *start) { int i, r, g, b, y, u, v, tmp; start += 8; while (isspace(*start)) start++; for (i = 0; i < 16; i++) { if (sscanf(start, "%06x", &tmp) != 1) break; r = tmp >> 16 & 0xff; g = tmp >> 8 & 0xff; b = tmp & 0xff; y = MIN(MAX((int)(0.1494 * r + 0.6061 * g + 0.2445 * b), 0), 0xff); u = MIN(MAX((int)(0.6066 * r - 0.4322 * g - 0.1744 * b) + 128, 0), 0xff); v = MIN(MAX((int)(-0.08435 * r - 0.3422 * g + 0.4266 * b) + 128, 0), 0xff); t->sh_sub.palette[i] = y << 16 | u << 8 | v; start += 6; while ((*start == ',') || isspace(*start)) start++; } if (i == 16) { mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] VobSub palette: %06x,%06x," "%06x,%06x,%06x,%06x,%06x,%06x,%06x,%06x,%06x,%06x,%06x," "%06x,%06x,%06x\n", t->sh_sub.palette[0], t->sh_sub.palette[1], t->sh_sub.palette[2], t->sh_sub.palette[3], t->sh_sub.palette[4], t->sh_sub.palette[5], t->sh_sub.palette[6], t->sh_sub.palette[7], t->sh_sub.palette[8], t->sh_sub.palette[9], t->sh_sub.palette[10], t->sh_sub.palette[11], t->sh_sub.palette[12], t->sh_sub.palette[13], t->sh_sub.palette[14], t->sh_sub.palette[15]); t->sh_sub.has_palette = 1; return 2; } return 0; } static int vobsub_parse_custom_colors (mkv_track_t *t, const char *start) { int use_custom_colors, i; use_custom_colors = 0; start += 14; while (isspace(*start)) start++; if (!strncasecmp(start, "ON", 2) || (*start == '1')) use_custom_colors = 1; else if (!strncasecmp(start, "OFF", 3) || (*start == '0')) use_custom_colors = 0; mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] VobSub custom colors: %s\n", use_custom_colors ? "ON" : "OFF"); if ((start = strstr(start, "colors:")) != NULL) { start += 7; while (isspace(*start)) start++; for (i = 0; i < 4; i++) { if (sscanf(start, "%06x", &t->sh_sub.colors[i]) != 1) break; start += 6; while ((*start == ',') || isspace(*start)) start++; } if (i == 4) { t->sh_sub.custom_colors = 4; mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] VobSub colors: %06x," "%06x,%06x,%06x\n", t->sh_sub.colors[0], t->sh_sub.colors[1], t->sh_sub.colors[2], t->sh_sub.colors[3]); } } if (!use_custom_colors) t->sh_sub.custom_colors = 0; return 4; } static int vobsub_parse_forced_subs (mkv_track_t *t, const char *start) { start += 12; while (isspace(*start)) start++; if (!strncasecmp(start, "on", 2) || (*start == '1')) t->sh_sub.forced_subs_only = 1; else if (!strncasecmp(start, "off", 3) || (*start == '0')) t->sh_sub.forced_subs_only = 0; else return 0; mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] VobSub forced subs: %d\n", t->sh_sub.forced_subs_only); return 8; } /** \brief Free cached demux packets * * Reordering the timecodes requires caching of demux packets. This function * frees all these cached packets and the memory for the cached pointers * itself. * * \param demuxer The demuxer for which the cache is to be freed. */ static void free_cached_dps (demuxer_t *demuxer) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; mkv_track_t *track; int i, k; for (k = 0; k < mkv_d->num_tracks; k++) { track = mkv_d->tracks[k]; for (i = 0; i < track->num_cached_dps; i++) free_demux_packet (track->cached_dps[i]); free(track->cached_dps); track->cached_dps = NULL; track->num_cached_dps = 0; track->num_allocated_dps = 0; track->max_pts = 0; } } static int demux_mkv_parse_idx (mkv_track_t *t) { int things_found, last; char *buf, *pos, *start; if ((t->private_data == NULL) || (t->private_size == 0)) return 0; things_found = 0; buf = (char *)malloc(t->private_size + 1); if (buf == NULL) return 0; memcpy(buf, t->private_data, t->private_size); buf[t->private_size] = 0; t->sh_sub.type = 'v'; t->sh_sub.has_palette = 0; pos = buf; start = buf; last = 0; do { if ((*pos == 0) || (*pos == '\r') || (*pos == '\n')) { if (*pos == 0) last = 1; *pos = 0; if (!strncasecmp(start, "size: ", 6)) things_found |= vobsub_parse_size(t, start); else if (!strncasecmp(start, "palette:", 8)) things_found |= vobsub_parse_palette(t, start); else if (!strncasecmp(start, "custom colors:", 14)) things_found |= vobsub_parse_custom_colors(t, start); else if (!strncasecmp(start, "forced subs:", 12)) things_found |= vobsub_parse_forced_subs(t, start); if (last) break; do { pos++; } while ((*pos == '\r') || (*pos == '\n')); start = pos; } else pos++; } while (!last && (*start != 0)); free(buf); return (things_found & 3) == 3; } 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; i<track->num_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: %"PRIu64"\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; i<n; i++) if (e.order <= ce[i].order) break; ce = (mkv_content_encoding_t *) realloc (ce, (n+1) *sizeof (*ce)); memmove (ce+i+1, ce+i, (n-i) * sizeof (*ce)); memcpy (ce+i, &e, sizeof (e)); n++; break; } default: ebml_read_skip (s, &l); break; } length -= l + il; } track->encodings = 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); if (track->v_frate > 0) track->default_duration = 1 / 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) || !strcmp (track->codec_id, MKV_A_ACM)) 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; } 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; } 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; track->default_duration = num / 1000000000.0; 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; i<mkv_d->parsed_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 %"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"); 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; i<mkv_d->parsed_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 || ((mkv_d->segment_start + seek_pos) >= (uint64_t)demuxer->movi_end)) 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; 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"; if (identify) mp_msg(MSGT_GLOBAL, MSGL_INFO, "ID_VIDEO_ID=%d\n", vid); sprintf (str, "-vid %u", vid++); break; case MATROSKA_TRACK_AUDIO: type = "audio"; if (identify) { mp_msg(MSGT_GLOBAL, MSGL_INFO, "ID_AUDIO_ID=%d\n", aid); mp_msg(MSGT_GLOBAL, 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"; if (identify) { mp_msg(MSGT_GLOBAL, MSGL_INFO, "ID_SUBTITLE_ID=%d\n", sid); mp_msg(MSGT_GLOBAL, 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; } 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 if (!strcmp(track->codec_id, MKV_V_MPEG1)) { bih->biCompression = mmioFOURCC('m', 'p', 'g', '1'); track->reorder_timecodes = 1; } else if (!strcmp(track->codec_id, MKV_V_MPEG2)) { bih->biCompression = mmioFOURCC('m', 'p', 'g', '2'); track->reorder_timecodes = 1; } else if (!strcmp(track->codec_id, MKV_V_MPEG4_AVC)) { bih->biCompression = mmioFOURCC('a', 'v', 'c', '1'); if (track->private_data && (track->private_size > 0)) { bih->biSize += track->private_size; bih = (BITMAPINFOHEADER *) realloc (bih, bih->biSize); memcpy (bih + 1, track->private_data, track->private_size); } track->reorder_timecodes = 1; } 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; 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 = (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; sh_a->ds = demuxer->audio; sh_a->wf = (WAVEFORMATEX *) malloc (sizeof (WAVEFORMATEX)); if (track->ms_compat && (track->private_size >= sizeof(WAVEFORMATEX))) { WAVEFORMATEX *wf = (WAVEFORMATEX *)track->private_data; sh_a->wf = (WAVEFORMATEX *) realloc(sh_a->wf, 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(WAVEFORMATEX); memcpy(sh_a->wf + 1, wf + 1, track->private_size - sizeof(WAVEFORMATEX)); 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 { 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))) track->a_formattag = 0x2000; else if (!strcmp(track->codec_id, MKV_A_DTS)) track->a_formattag = 0x2001; 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) || !strcmp(track->codec_id, MKV_A_AAC)) track->a_formattag = mmioFOURCC('M', 'P', '4', 'A'); else if (!strcmp(track->codec_id, MKV_A_VORBIS)) { if (track->private_data == NULL) return 1; track->a_formattag = mmioFOURCC('v', 'r', 'b', 's'); } 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; 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 == 0x2001)) /* DTS */ { sh_a->wf->nAvgBytesPerSec = 16000; sh_a->wf->nBlockAlign = 1536; } 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; 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; if (!strcmp (track->codec_id, MKV_A_AAC) && (NULL != track->private_data)) { 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; return 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 == mmioFOURCC('v', 'r', 'b', 's')) /* VORBIS */ { sh_a->wf->cbSize = track->private_size; sh_a->wf = (WAVEFORMATEX*)realloc(sh_a->wf, sizeof(WAVEFORMATEX) + sh_a->wf->cbSize); memcpy((unsigned char *) (sh_a->wf+1), track->private_data, sh_a->wf->cbSize); } 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->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 if (!track->ms_compat || (track->private_size < sizeof(WAVEFORMATEX))) { free_sh_audio (sh_a); return 1; } return 0; } /** \brief Parse the private data for VobSub subtitle tracks. This function tries to parse the private data for all VobSub tracks. The private data contains the normal text from the original .idx file. Things like the palette, subtitle dimensions and custom colors are stored here. \param demuxer The generic demuxer. */ static void demux_mkv_parse_vobsub_data (demuxer_t *demuxer) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; mkv_track_t *track; int i, m, size; uint8_t *buffer; for (i = 0; i < mkv_d->num_tracks; i++) { track = mkv_d->tracks[i]; if ((track->type != MATROSKA_TRACK_SUBTITLE) || (track->subtitle_type != MATROSKA_SUBTYPE_VOBSUB)) continue; size = track->private_size; m = demux_mkv_decode (track,track->private_data,&buffer,&size,2); if (buffer && m) { free (track->private_data); track->private_data = buffer; } if (!demux_mkv_parse_idx (track)) { free (track->private_data); track->private_data = NULL; track->private_size = 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) { if (track->private_data != NULL) { demuxer->sub->sh = malloc(sizeof(mkv_sh_sub_t)); if (demuxer->sub->sh != NULL) memcpy(demuxer->sub->sh, &track->sh_sub, sizeof(mkv_sh_sub_t)); } } } else { mp_msg (MSGT_DEMUX, MSGL_ERR, "[mkv] Subtitle type '%s' is not " "supported.\n", track->codec_id); return 1; } return 0; } static void demux_mkv_seek (demuxer_t *demuxer, float rel_seek_secs, int flags); /** \brief Given a matroska track number and type, find the id that mplayer would ask for. * \param d The demuxer for which the subtitle id should be returned. * \param num The matroska track number we are looking up. * \param type The track type. */ static int demux_mkv_reverse_id(mkv_demuxer_t *d, int num, 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 (d->tracks[i]->tnum == num) return id; id++; } return -1; } static 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(NULL); #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_DBG2, "[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"); demux_aid_vid_mismatch = 1; // don't identify in new_sh_* since ids don't match 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; i<mkv_d->num_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; i<mkv_d->num_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 && !demux_mkv_open_video (demuxer, track)) { 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 && !demux_mkv_open_audio (demuxer, track)) { 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->audio->id = -2; } demux_mkv_parse_vobsub_data (demuxer); /* DO NOT automatically select a subtitle track and behave like DVD */ /* playback: only show subtitles if the user explicitely wants them. */ track = NULL; if (demuxer->sub->id >= 0) track = demux_mkv_find_track_by_num (mkv_d, demuxer->sub->id, MATROSKA_TRACK_SUBTITLE); else if (dvdsub_lang != NULL) track = demux_mkv_find_track_by_language (mkv_d, dvdsub_lang, MATROSKA_TRACK_SUBTITLE); if (track && !demux_mkv_open_sub (demuxer, track)) { mp_msg (MSGT_DEMUX, MSGL_INFO, "[mkv] Will display subtitle track %u\n", track->tnum); dvdsub_id = demux_mkv_reverse_id(mkv_d, track->tnum, MATROSKA_TRACK_SUBTITLE); demuxer->sub->id = track->tnum; } else demuxer->sub->id = -2; if (mkv_d->chapters) { for (i=0; i < (int)mkv_d->num_chapters; i++) { mkv_d->chapters[i].start -= mkv_d->first_tc; mkv_d->chapters[i].end -= mkv_d->first_tc; } if (dvd_last_chapter > 0 && dvd_last_chapter <= mkv_d->num_chapters) { if (mkv_d->chapters[dvd_last_chapter-1].end != 0) mkv_d->stop_timecode = mkv_d->chapters[dvd_last_chapter-1].end; else if (dvd_last_chapter + 1 <= mkv_d->num_chapters) mkv_d->stop_timecode = mkv_d->chapters[dvd_last_chapter].start; } } if (s->end_pos == 0 || (mkv_d->indexes == NULL && index_mode < 0)) demuxer->seekable = 0; else { demuxer->movi_start = s->start_pos; demuxer->movi_end = s->end_pos; demuxer->seekable = 1; if (mkv_d->chapters && dvd_chapter>1 && dvd_chapter<=mkv_d->num_chapters) { if (!mkv_d->has_first_tc) { mkv_d->first_tc = 0; mkv_d->has_first_tc = 1; } demux_mkv_seek (demuxer, mkv_d->chapters[dvd_chapter-1].start/1000.0, 1); } } return DEMUXER_TYPE_MATROSKA; } static void demux_close_mkv (demuxer_t *demuxer) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; if (mkv_d) { int i; #ifdef USE_ICONV subcp_close(); #endif free_cached_dps (demuxer); if (mkv_d->tracks) { for (i=0; i<mkv_d->num_tracks; i++) { if (mkv_d->tracks[i]->codec_id) free (mkv_d->tracks[i]->codec_id); if (mkv_d->tracks[i]->language) free (mkv_d->tracks[i]->language); if (mkv_d->tracks[i]->private_data) free (mkv_d->tracks[i]->private_data); } for (i=0; i < SUB_MAX_TEXT; i++) if (mkv_d->subs.text[i]) free (mkv_d->subs.text[i]); free (mkv_d->tracks); } if (mkv_d->indexes) free (mkv_d->indexes); if (mkv_d->cluster_positions) free (mkv_d->cluster_positions); if (mkv_d->chapters) free (mkv_d->chapters); if (mkv_d->parsed_cues) free (mkv_d->parsed_cues); if (mkv_d->parsed_seekhead) free (mkv_d->parsed_seekhead); free (mkv_d); } } static int demux_mkv_read_block_lacing (uint8_t *buffer, uint64_t *size, uint8_t *laces, uint32_t **all_lace_sizes) { uint32_t total = 0, *lace_size; uint8_t flags; int i; *all_lace_sizes = NULL; lace_size = NULL; /* lacing flags */ flags = *buffer++; (*size)--; switch ((flags & 0x06) >> 1) { case 0: /* no lacing */ *laces = 1; lace_size = (uint32_t *)calloc(*laces, sizeof(uint32_t)); lace_size[0] = *size; break; case 1: /* xiph lacing */ case 2: /* fixed-size lacing */ case 3: /* EBML lacing */ *laces = *buffer++; (*size)--; (*laces)++; lace_size = (uint32_t *)calloc(*laces, sizeof(uint32_t)); switch ((flags & 0x06) >> 1) { case 1: /* xiph lacing */ for (i=0; i < *laces-1; i++) { lace_size[i] = 0; do { lace_size[i] += *buffer; (*size)--; } while (*buffer++ == 0xFF); total += lace_size[i]; } lace_size[i] = *size - total; break; case 2: /* fixed-size lacing */ for (i=0; i < *laces; i++) lace_size[i] = *size / *laces; break; case 3: /* EBML lacing */ { int l; uint64_t num = ebml_read_vlen_uint (buffer, &l); if (num == EBML_UINT_INVALID) { free(lace_size); return 1; } buffer += l; *size -= l; total = lace_size[0] = num; for (i=1; i < *laces-1; i++) { int64_t snum; snum = ebml_read_vlen_int (buffer, &l); if (snum == EBML_INT_INVALID) { free(lace_size); return 1; } buffer += l; *size -= l; lace_size[i] = lace_size[i-1] + snum; total += lace_size[i]; } lace_size[i] = *size - total; break; } } break; } *all_lace_sizes = lace_size; return 0; } static void handle_subtitles(demuxer_t *demuxer, mkv_track_t *track, char *block, int64_t size, uint64_t block_duration, uint64_t timecode) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; char *ptr1, *ptr2; int state, i; if (block_duration == 0) { mp_msg (MSGT_DEMUX, MSGL_WARN, "[mkv] Warning: No BlockDuration " "for subtitle track found.\n"); return; } ptr1 = block; while (ptr1 - block <= size && (*ptr1 == '\n' || *ptr1 == '\r')) ptr1++; ptr2 = block + size - 1; while (ptr2 >= block && (*ptr2 == '\n' || *ptr2 == '\r')) { *ptr2 = 0; ptr2--; } if (mkv_d->subs.lines > SUB_MAX_TEXT - 2) { mp_msg (MSGT_DEMUX, MSGL_WARN, "[mkv] Warning: too many sublines " "to render, skipping\n"); return; } ptr2 = mkv_d->subs.text[mkv_d->subs.lines]; state = 0; if (track->subtitle_type == MATROSKA_SUBTYPE_SSA) { /* Find text section. */ for (i=0; i < 8 && *ptr1 != '\0'; ptr1++) if (*ptr1 == ',') i++; if (*ptr1 == '\0') /* Broken line? */ return; /* Load text. */ while (ptr1 - block < size) { if (*ptr1 == '{') state = 1; else if (*ptr1 == '}' && state == 1) state = 2; if (state == 0) { *ptr2++ = *ptr1; if (ptr2 - mkv_d->subs.text[mkv_d->subs.lines] >= 255) break; } ptr1++; /* Newline */ while (*ptr1 == '\\' && ptr1+1-block < size && (*(ptr1+1)|0x20) == 'n') { mkv_d->clear_subs_at[mkv_d->subs.lines++] = timecode + block_duration; *ptr2 = '\0'; if (mkv_d->subs.lines >= SUB_MAX_TEXT) { mp_msg (MSGT_DEMUX, MSGL_WARN, "\n[mkv] Warning: too many " "sublines to render, skipping after first %i\n", SUB_MAX_TEXT); mkv_d->subs.lines--; ptr1=block+size; break; } ptr2 = mkv_d->subs.text[mkv_d->subs.lines]; ptr1 += 2; } if (state == 2) state = 0; } *ptr2 = '\0'; } else { while (ptr1 - block < size) { if (*ptr1 == '\n' || *ptr1 == '\r') { if (state == 0) /* normal char --> newline */ { *ptr2 = '\0'; mkv_d->clear_subs_at[mkv_d->subs.lines++] = timecode + block_duration; if (mkv_d->subs.lines >= SUB_MAX_TEXT) { mp_msg (MSGT_DEMUX, MSGL_WARN, "\n[mkv] Warning: too many " "sublines to render, skipping after first %i\n", SUB_MAX_TEXT); mkv_d->subs.lines--; ptr1=block+size; break; } ptr2 = mkv_d->subs.text[mkv_d->subs.lines]; state = 1; } } else if (*ptr1 == '<') /* skip HTML tags */ state = 2; else if (*ptr1 == '>') state = 0; else if (state != 2) /* normal character */ { state = 0; if ((ptr2 - mkv_d->subs.text[mkv_d->subs.lines]) < 255) *ptr2++ = *ptr1; } ptr1++; } *ptr2 = '\0'; } mkv_d->clear_subs_at[mkv_d->subs.lines++] = timecode + block_duration; #ifdef USE_ICONV subcp_recode1 (&mkv_d->subs); #endif sub_utf8 = 1; vo_sub = &mkv_d->subs; vo_osd_changed (OSDTYPE_SUBTITLE); } static void clear_subtitles(demuxer_t *demuxer, uint64_t timecode, int clear_all) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; int i, lines_cut = 0; char *tmp; /* Clear all? */ if (clear_all) { lines_cut = mkv_d->subs.lines; mkv_d->subs.lines = 0; if (lines_cut) { vo_sub = &mkv_d->subs; vo_osd_changed (OSDTYPE_SUBTITLE); } return; } /* Clear the subtitles if they're obsolete now. */ for (i=0; i < mkv_d->subs.lines; i++) { if (mkv_d->clear_subs_at[i] <= timecode) { tmp = mkv_d->subs.text[i]; memmove (mkv_d->subs.text+i, mkv_d->subs.text+i+1, (mkv_d->subs.lines-i-1) * sizeof (*mkv_d->subs.text)); memmove (mkv_d->clear_subs_at+i, mkv_d->clear_subs_at+i+1, (mkv_d->subs.lines-i-1) * sizeof (*mkv_d->clear_subs_at)); mkv_d->subs.text[--mkv_d->subs.lines] = tmp; i--; lines_cut = 1; } } if (lines_cut) { vo_sub = &mkv_d->subs; vo_osd_changed (OSDTYPE_SUBTITLE); } } // Taken from demux_real.c. Thanks to the original developpers :) #define SKIP_BITS(n) buffer <<= n #define SHOW_BITS(n) ((buffer) >> (32 - (n))) static float real_fix_timestamp(mkv_track_t *track, unsigned char *s, int timestamp) { float v_pts; uint32_t buffer = (s[0] << 24) + (s[1] << 16) + (s[2] << 8) + s[3]; int kf = timestamp; int pict_type; int orig_kf; if (!strcmp(track->codec_id, MKV_V_REALV30) || !strcmp(track->codec_id, MKV_V_REALV40)) { if (!strcmp(track->codec_id, MKV_V_REALV30)) { SKIP_BITS(3); pict_type = SHOW_BITS(2); SKIP_BITS(2 + 7); }else{ SKIP_BITS(1); pict_type = SHOW_BITS(2); SKIP_BITS(2 + 7 + 3); } kf = SHOW_BITS(13); // kf= 2*SHOW_BITS(12); orig_kf = kf; if (pict_type <= 1) { // I frame, sync timestamps: track->rv_kf_base = timestamp - kf; mp_msg(MSGT_DEMUX, MSGL_V, "\nTS: base=%08X\n", track->rv_kf_base); kf = timestamp; } else { // P/B frame, merge timestamps: int tmp = timestamp - track->rv_kf_base; kf |= tmp & (~0x1fff); // combine with packet timestamp if (kf < (tmp - 4096)) // workaround wrap-around problems kf += 8192; else if (kf > (tmp + 4096)) kf -= 8192; kf += track->rv_kf_base; } if (pict_type != 3) { // P || I frame -> swap timestamps int tmp = kf; kf = track->rv_kf_pts; track->rv_kf_pts = tmp; } mp_msg(MSGT_DEMUX, MSGL_V, "\nTS: %08X -> %08X (%04X) %d %02X %02X %02X " "%02X %5d\n", timestamp, kf, orig_kf, pict_type, s[0], s[1], s[2], s[3], kf - (int)(1000.0 * track->rv_pts)); } v_pts = kf * 0.001f; track->rv_pts = v_pts; return v_pts; } static void handle_realvideo (demuxer_t *demuxer, mkv_track_t *track, uint8_t *buffer, uint32_t size, int block_bref) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; demux_packet_t *dp; dp_hdr_t *hdr; uint8_t chunks; int isize; chunks = *buffer++; isize = --size - (chunks+1)*8; dp = new_demux_packet (sizeof (*hdr) + size); memcpy (dp->buffer + sizeof(*hdr), buffer + (chunks+1)*8, isize); memcpy (dp->buffer + sizeof(*hdr) + isize, buffer, (chunks+1)*8); hdr = (dp_hdr_t *) dp->buffer; hdr->len = isize; hdr->chunks = chunks; hdr->timestamp = mkv_d->last_pts * 1000; hdr->chunktab = sizeof(*hdr) + isize; dp->len = sizeof(*hdr) + size; if (mkv_d->v_skip_to_keyframe) { dp->pts = mkv_d->last_pts; track->rv_kf_base = 0; track->rv_kf_pts = hdr->timestamp; } else dp->pts = real_fix_timestamp (track, dp->buffer + sizeof(*hdr), hdr->timestamp); dp->pos = demuxer->filepos; dp->flags = block_bref ? 0 : 0x10; ds_add_packet(demuxer->video, dp); } static void handle_realaudio (demuxer_t *demuxer, mkv_track_t *track, uint8_t *buffer, uint32_t size, int block_bref) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; demux_packet_t *dp = new_demux_packet (size); memcpy (dp->buffer, buffer, size); if (track->ra_pts == mkv_d->last_pts && !mkv_d->a_skip_to_keyframe) dp->pts = 0; else dp->pts = mkv_d->last_pts; track->ra_pts = mkv_d->last_pts; dp->pos = demuxer->filepos; dp->flags = block_bref ? 0 : 0x10; ds_add_packet (demuxer->audio, dp); } /** Reorder timecodes and add cached demux packets to the queues. * * Timecode reordering is needed if a video track contains B frames that * are timestamped in display order (e.g. MPEG-1, MPEG-2 or "native" MPEG-4). * MPlayer doesn't like timestamps in display order. This function adjusts * the timestamp of cached frames (which are exactly one I/P frame followed * by one or more B frames) so that they are in coding order again. * * Example: The track with 25 FPS contains four frames with the timecodes * I at 0ms, P at 120ms, B at 40ms and B at 80ms. As soon as the next I * or P frame arrives these timecodes can be changed to I at 0ms, P at 40ms, * B at 80ms and B at 120ms. * * This works for simple H.264 B-frame pyramids, but not for arbitrary orders. * * \param demuxer The Matroska demuxer struct for this instance. * \param track The track structure whose cache should be handled. */ static void flush_cached_dps (demuxer_t *demuxer, mkv_track_t *track) { int i, ok; if (track->num_cached_dps == 0) return; do { ok = 1; for (i = 1; i < track->num_cached_dps; i++) if (track->cached_dps[i - 1]->pts > track->cached_dps[i]->pts) { float tmp_pts = track->cached_dps[i - 1]->pts; track->cached_dps[i - 1]->pts = track->cached_dps[i]->pts; track->cached_dps[i]->pts = tmp_pts; ok = 0; } } while (!ok); for (i = 0; i < track->num_cached_dps; i++) ds_add_packet (demuxer->video, track->cached_dps[i]); track->num_cached_dps = 0; } /** Cache video frames if timecodes have to be reordered. * * Timecode reordering is needed if a video track contains B frames that * are timestamped in display order (e.g. MPEG-1, MPEG-2 or "native" MPEG-4). * This function takes in a Matroska block read from the file, allocates a * demux packet for it, fills in its values, allocates space for storing * pointers to the cached demux packets and adds the packet to it. If * the packet contains an I or a P frame then ::flush_cached_dps is called * in order to send the old cached frames downstream. * * \param demuxer The Matroska demuxer struct for this instance. * \param track The packet is meant for this track. * \param buffer The actual frame contents. * \param size The frame size in bytes. * \param block_bref A relative timecode (backward reference). If it is \c 0 * then the frame is an I frame. * \param block_fref A relative timecode (forward reference). If it is \c 0 * then the frame is either an I frame or a P frame depending on the value * of \a block_bref. Otherwise it's a B frame. */ static void handle_video_bframes (demuxer_t *demuxer, mkv_track_t *track, uint8_t *buffer, uint32_t size, int block_bref, int block_fref) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; demux_packet_t *dp; dp = new_demux_packet (size); memcpy(dp->buffer, buffer, size); dp->pos = demuxer->filepos; dp->pts = mkv_d->last_pts; if ((track->num_cached_dps > 0) && (dp->pts < track->max_pts)) block_fref = 1; if (block_fref == 0) /* I or P frame */ flush_cached_dps (demuxer, track); if (block_bref != 0) /* I frame, don't cache it */ dp->flags = 0x10; if ((track->num_cached_dps + 1) > track->num_allocated_dps) { track->cached_dps = (demux_packet_t **) realloc(track->cached_dps, (track->num_cached_dps + 10) * sizeof(demux_packet_t *)); track->num_allocated_dps += 10; } track->cached_dps[track->num_cached_dps] = dp; track->num_cached_dps++; if (dp->pts > track->max_pts) track->max_pts = dp->pts; } static int handle_block (demuxer_t *demuxer, uint8_t *block, uint64_t length, uint64_t block_duration, int64_t block_bref, int64_t block_fref) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; mkv_track_t *track = NULL; demux_stream_t *ds = NULL; uint64_t old_length; int64_t tc; uint32_t *lace_size; uint8_t laces; int i, num, tmp, use_this_block = 1; float current_pts; int16_t time; /* first byte(s): track num */ num = ebml_read_vlen_uint (block, &tmp); block += tmp; /* time (relative to cluster time) */ time = block[0] << 8 | block[1]; block += 2; length -= tmp + 2; old_length = length; if (demux_mkv_read_block_lacing (block, &length, &laces, &lace_size)) return 0; block += old_length - length; tc = ((time*mkv_d->tc_scale+mkv_d->cluster_tc) /1000000.0 - mkv_d->first_tc); if (tc < 0) tc = 0; if (mkv_d->stop_timecode > 0 && tc > mkv_d->stop_timecode) { free(lace_size); return -1; } current_pts = tc / 1000.0; clear_subtitles(demuxer, tc, 0); for (i=0; i<mkv_d->num_tracks; i++) if (mkv_d->tracks[i]->tnum == num) { track = mkv_d->tracks[i]; break; } if (track == NULL) { free(lace_size); return 1; } if (num == demuxer->audio->id) { ds = demuxer->audio; if (mkv_d->a_skip_to_keyframe && block_bref != 0) use_this_block = 0; else if (mkv_d->v_skip_to_keyframe) use_this_block = 0; if (track->fix_i_bps && use_this_block) { sh_audio_t *sh = (sh_audio_t *) ds->sh; if (block_duration != 0) { sh->i_bps = length * 1000 / block_duration; track->fix_i_bps = 0; } else if (track->qt_last_a_pts == 0.0) track->qt_last_a_pts = current_pts; else if(track->qt_last_a_pts != current_pts) { sh->i_bps = length / (current_pts - track->qt_last_a_pts); track->fix_i_bps = 0; } } } else if (tc < mkv_d->skip_to_timecode) use_this_block = 0; else if (num == demuxer->video->id) { ds = demuxer->video; if (mkv_d->v_skip_to_keyframe && (block_bref != 0 || block_fref != 0)) use_this_block = 0; } else if (num == demuxer->sub->id) { ds = demuxer->sub; if (track->subtitle_type != MATROSKA_SUBTYPE_VOBSUB) { if (!mkv_d->v_skip_to_keyframe) handle_subtitles (demuxer, track, block, length, block_duration, tc); use_this_block = 0; } } else use_this_block = 0; if (use_this_block) { mkv_d->last_pts = current_pts; mkv_d->last_filepos = demuxer->filepos; for (i=0; i < laces; i++) { if (ds == demuxer->video && track->realmedia) handle_realvideo (demuxer, track, block, lace_size[i], block_bref); else if (ds == demuxer->audio && track->realmedia) handle_realaudio (demuxer, track, block, lace_size[i], block_bref); else if (ds == demuxer->video && track->reorder_timecodes) handle_video_bframes (demuxer, track, block, lace_size[i], block_bref, block_fref); else { int modified, size = lace_size[i]; demux_packet_t *dp; uint8_t *buffer; modified = demux_mkv_decode (track, block, &buffer, &size, 1); if (buffer) { dp = new_demux_packet (size); memcpy (dp->buffer, buffer, size); if (modified) free (buffer); dp->flags = (block_bref == 0 && block_fref == 0) ? 1 : 0; dp->pts = mkv_d->last_pts + i * track->default_duration; ds_add_packet (ds, dp); } } block += lace_size[i]; } if (ds == demuxer->video) { mkv_d->v_skip_to_keyframe = 0; mkv_d->skip_to_timecode = 0; } else if (ds == demuxer->audio) mkv_d->a_skip_to_keyframe = 0; free(lace_size); return 1; } free(lace_size); return 0; } static int demux_mkv_fill_buffer (demuxer_t *demuxer, demux_stream_t *ds) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; stream_t *s = demuxer->stream; uint64_t l; int il, tmp; while (1) { while (mkv_d->cluster_size > 0) { uint64_t block_duration = 0, block_length = 0; int64_t block_bref = 0, block_fref = 0; uint8_t *block = NULL; while (mkv_d->blockgroup_size > 0) { switch (ebml_read_id (s, &il)) { case MATROSKA_ID_BLOCKDURATION: { block_duration = ebml_read_uint (s, &l); if (block_duration == EBML_UINT_INVALID) return 0; break; } case MATROSKA_ID_BLOCK: block_length = ebml_read_length (s, &tmp); block = (uint8_t *) malloc (block_length); demuxer->filepos = stream_tell (s); if (stream_read (s,block,block_length) != (int) block_length) return 0; l = tmp + block_length; break; case MATROSKA_ID_REFERENCEBLOCK: { int64_t num = ebml_read_int (s, &l); if (num == EBML_INT_INVALID) return 0; if (num <= 0) block_bref = num; else block_fref = num; break; } case EBML_ID_INVALID: return 0; default: ebml_read_skip (s, &l); break; } mkv_d->blockgroup_size -= l + il; mkv_d->cluster_size -= l + il; } if (block) { int res = handle_block (demuxer, block, block_length, block_duration, block_bref, block_fref); free (block); if (res < 0) return 0; if (res) return 1; } if (mkv_d->cluster_size > 0) { switch (ebml_read_id (s, &il)) { case MATROSKA_ID_CLUSTERTIMECODE: { uint64_t num = ebml_read_uint (s, &l); if (num == EBML_UINT_INVALID) return 0; if (!mkv_d->has_first_tc) { mkv_d->first_tc = num * mkv_d->tc_scale / 1000000.0; mkv_d->has_first_tc = 1; } mkv_d->cluster_tc = num * mkv_d->tc_scale; break; } case MATROSKA_ID_BLOCKGROUP: mkv_d->blockgroup_size = ebml_read_length (s, &tmp); l = tmp; break; case EBML_ID_INVALID: return 0; default: ebml_read_skip (s, &l); break; } mkv_d->cluster_size -= l + il; } } if (ebml_read_id (s, &il) != MATROSKA_ID_CLUSTER) return 0; add_cluster_position(mkv_d, stream_tell(s)-il); mkv_d->cluster_size = ebml_read_length (s, NULL); } return 0; } static void demux_mkv_seek (demuxer_t *demuxer, float rel_seek_secs, int flags) { free_cached_dps (demuxer); if (!(flags & 2)) /* time in secs */ { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; stream_t *s = demuxer->stream; int64_t target_timecode = 0, diff, min_diff=0xFFFFFFFL; int i; if (!(flags & 1)) /* relative seek */ target_timecode = (int64_t) (mkv_d->last_pts * 1000.0); target_timecode += (int64_t)(rel_seek_secs * 1000.0); if (target_timecode < 0) target_timecode = 0; if (mkv_d->indexes == NULL) /* no index was found */ { uint64_t target_filepos, cluster_pos, max_pos; target_filepos = (uint64_t) (target_timecode * mkv_d->last_filepos / (mkv_d->last_pts * 1000.0)); max_pos = mkv_d->cluster_positions[mkv_d->num_cluster_pos-1]; if (target_filepos > max_pos) { if ((off_t) max_pos > stream_tell (s)) stream_seek (s, max_pos); else stream_seek (s, stream_tell (s) + mkv_d->cluster_size); /* parse all the clusters upto target_filepos */ while (!s->eof && stream_tell(s) < (off_t) target_filepos) { switch (ebml_read_id (s, &i)) { case MATROSKA_ID_CLUSTER: add_cluster_position(mkv_d, (uint64_t) stream_tell(s)-i); break; case MATROSKA_ID_CUES: demux_mkv_read_cues (demuxer); break; } ebml_read_skip (s, NULL); } if (s->eof) stream_reset(s); } if (mkv_d->indexes == NULL) { cluster_pos = mkv_d->cluster_positions[0]; /* Let's find the nearest cluster */ for (i=0; i < mkv_d->num_cluster_pos; i++) { diff = mkv_d->cluster_positions[i] - target_filepos; if (rel_seek_secs < 0 && diff < 0 && -diff < min_diff) { cluster_pos = mkv_d->cluster_positions[i]; min_diff = -diff; } else if (rel_seek_secs > 0 && (diff < 0 ? -1 * diff : diff) < min_diff) { cluster_pos = mkv_d->cluster_positions[i]; min_diff = diff < 0 ? -1 * diff : diff; } } mkv_d->cluster_size = mkv_d->blockgroup_size = 0; stream_seek (s, cluster_pos); } } else { mkv_index_t *index = NULL; /* let's find the entry in the indexes with the smallest */ /* difference to the wanted timecode. */ for (i=0; i < mkv_d->num_indexes; i++) if (mkv_d->indexes[i].tnum == demuxer->video->id) { diff = target_timecode - (int64_t) mkv_d->indexes[i].timecode; if ((flags & 1 || target_timecode <= mkv_d->last_pts*1000) && diff >= 0 && diff < min_diff) { min_diff = diff; index = mkv_d->indexes + i; } else if (target_timecode > mkv_d->last_pts*1000 && diff < 0 && -diff < min_diff) { min_diff = -diff; index = mkv_d->indexes + i; } } if (index) /* We've found an entry. */ { mkv_d->cluster_size = mkv_d->blockgroup_size = 0; stream_seek (s, index->filepos); } } if (demuxer->video->id >= 0) mkv_d->v_skip_to_keyframe = 1; if (rel_seek_secs > 0.0) mkv_d->skip_to_timecode = target_timecode; mkv_d->a_skip_to_keyframe = 1; /* Clear subtitles. */ if (target_timecode <= mkv_d->last_pts * 1000) clear_subtitles(demuxer, 0, 1); demux_mkv_fill_buffer(demuxer, NULL); } else if ((demuxer->movi_end <= 0) || !(flags & 1)) mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] seek unsupported flags\n"); else { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; stream_t *s = demuxer->stream; uint64_t target_filepos; mkv_index_t *index = NULL; int i; if (mkv_d->indexes == NULL) /* no index was found */ { /* I'm lazy... */ mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] seek unsupported flags\n"); return; } target_filepos = (uint64_t)(demuxer->movi_end * rel_seek_secs); for (i=0; i < mkv_d->num_indexes; i++) if (mkv_d->indexes[i].tnum == demuxer->video->id) if ((index == NULL) || ((mkv_d->indexes[i].filepos >= target_filepos) && ((index->filepos < target_filepos) || (mkv_d->indexes[i].filepos < index->filepos)))) index = &mkv_d->indexes[i]; if (!index) return; mkv_d->cluster_size = mkv_d->blockgroup_size = 0; stream_seek (s, index->filepos); if (demuxer->video->id >= 0) mkv_d->v_skip_to_keyframe = 1; mkv_d->skip_to_timecode = index->timecode; mkv_d->a_skip_to_keyframe = 1; /* Clear subtitles. */ if (index->timecode <= mkv_d->last_pts * 1000) clear_subtitles(demuxer, 0, 1); demux_mkv_fill_buffer(demuxer, NULL); } } static int demux_mkv_control (demuxer_t *demuxer, int cmd, void *arg) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; switch (cmd) { case DEMUXER_CTRL_GET_TIME_LENGTH: if (mkv_d->duration == 0) return DEMUXER_CTRL_DONTKNOW; *((double *)arg) = (double)mkv_d->duration; return DEMUXER_CTRL_OK; case DEMUXER_CTRL_GET_PERCENT_POS: if (mkv_d->duration == 0) { return DEMUXER_CTRL_DONTKNOW; } *((int *) arg) = (int) (100 * mkv_d->last_pts / mkv_d->duration); return DEMUXER_CTRL_OK; case DEMUXER_CTRL_SWITCH_AUDIO: if (demuxer->audio && demuxer->audio->sh) { int i; demux_stream_t *d_audio = demuxer->audio; int idx = d_audio->id - 1; // track ids are 1 based int num = mkv_d->num_tracks; mkv_track_t *otrack = mkv_d->tracks[idx]; mkv_track_t *track = 0; if (*((int*)arg) < 0) for (i = 1; i <= num; i++) { track = mkv_d->tracks[(idx+i)%num]; if ((track->type == MATROSKA_TRACK_AUDIO) && !strcmp(track->codec_id, otrack->codec_id) && (track->private_size == otrack->private_size) && !memcmp(track->private_data, otrack->private_data, track->private_size) && (track->a_channels == otrack->a_channels) && (track->a_bps == otrack->a_bps) && (track->a_sfreq == otrack->a_sfreq)) { break; } } else { track = demux_mkv_find_track_by_num (mkv_d, *((int*)arg), MATROSKA_TRACK_AUDIO); if (track == NULL || strcmp (track->codec_id, otrack->codec_id) || (track->private_size != otrack->private_size) || memcmp(track->private_data, otrack->private_data, track->private_size) || track->a_channels != otrack->a_channels || track->a_bps != otrack->a_bps || track->a_sfreq != otrack->a_sfreq) track = otrack; } if (track != otrack) { d_audio->id = track->tnum; ds_free_packs(d_audio); } } *((int*)arg) = demux_mkv_reverse_id (mkv_d, demuxer->audio->id, MATROSKA_TRACK_AUDIO); return DEMUXER_CTRL_OK; default: return DEMUXER_CTRL_NOTIMPL; } } /** \brief Return the number of subtitle tracks in the file. \param demuxer The demuxer for which the number of subtitle tracks should be returned. */ int demux_mkv_num_subs (demuxer_t *demuxer) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; int i, num; num = 0; for (i = 0; i < mkv_d->num_tracks; i++) if ((mkv_d->tracks[i]->type == MATROSKA_TRACK_SUBTITLE) && (mkv_d->tracks[i]->subtitle_type != MATROSKA_SUBTYPE_UNKNOWN)) num++; return num; } /** \brief Change the current subtitle track and return its ID. Changes the current subtitle track. If the new subtitle track is a VobSub track then the SPU decoder will be re-initialized. \param demuxer The demuxer whose subtitle track will be changed. \param new_num The number of the new subtitle track. The number must be between 0 and demux_mkv_num_subs - 1. \returns The Matroska track number of the newly selected track. */ int demux_mkv_change_subs (demuxer_t *demuxer, int new_num) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; mkv_track_t *track; int i, num; num = 0; track = NULL; for (i = 0; i < mkv_d->num_tracks; i++) { if ((mkv_d->tracks[i]->type == MATROSKA_TRACK_SUBTITLE) && (mkv_d->tracks[i]->subtitle_type != MATROSKA_SUBTYPE_UNKNOWN)) num++; if (num == (new_num + 1)) { track = mkv_d->tracks[i]; break; } } if (track == NULL) return -1; if (demuxer->sub->sh == NULL) demuxer->sub->sh = malloc(sizeof(mkv_sh_sub_t)); if (demuxer->sub->sh != NULL) memcpy(demuxer->sub->sh, &track->sh_sub, sizeof(mkv_sh_sub_t)); return track->tnum; } /** \brief Get the language code for a subtitle track. Retrieves the language code for a subtitle track if it is known. If the language code is "und" then do not copy it ("und" = "undefined"). \param demuxer The demuxer to work on \param track_num The n'th subtitle track to get the language from \param lang Store the language here \param maxlen The maximum number of characters to copy into lang */ void demux_mkv_get_sub_lang(demuxer_t *demuxer, int track_num, char *lang, int maxlen) { mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; mkv_track_t *track; int i, num; num = 0; for (i = 0; i < mkv_d->num_tracks; i++) { track = mkv_d->tracks[i]; if (track->type == MATROSKA_TRACK_SUBTITLE) num++; if (num == (track_num + 1)) { if ((track->language != NULL) && strcmp(track->language, "und")) strncpy(lang, track->language, maxlen); return; } } } demuxer_desc_t demuxer_desc_matroska = { "Matroska demuxer", "mkv", "Matroska", "Aurelien Jacobs", "based on gstreamer demuxer by Ronald Bultje and demux_mkv.cpp by Moritz Bunkus", DEMUXER_TYPE_MATROSKA, 1, // safe autodetect demux_mkv_open, demux_mkv_fill_buffer, NULL, demux_close_mkv, demux_mkv_seek, demux_mkv_control }; #endif /* HAVE_MATROSKA */