mpv/demux/demux_mkv.c

2834 lines
97 KiB
C
Raw Normal View History

/*
* 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>
demux_mkv: improve robustness against broken files Fixes test7.mkv from the Matroska test file collection, as well as some real broken files I've found in the wild. (Unfortunately, true recovery requires resetting the decoders and playback state with a manual seek, but it's still better than just exiting.) If there are broken EBML elements, try harder to skip them correctly. Do this by searching for the next cluster element. The cluster element intentionally has a long ID, so it's a suitable element for resynchronizing (mkvmerge does something similar). We know that data is corrupt if the ID or length fields of an element are malformed. Additionally, if skipping an unknown element goes past the end of the file, we assume it's corrupt and undo the seek. Do this because it often happens that corrupt data is interpreted as correct EBML elements. Since these elements will have a ridiculous values in their length fields due to the large value range that is possible (0-2^56-2), they will go past the end of the file. So instead of skipping them (which would result in playback termination), try to find the next cluster instead. (We still skip unknown elements that are within the file, as this is needed for correct operation. Also, we first execute the seek, because we don't really know where the file ends. Doing it this way is better for unseekable streams too, because it will still work in the non-error case.) This is done as special case in the packet reading function only. On the other hand, that's the only part of the file that's read after initialization is done.
2013-03-28 00:01:17 +01:00
#include <assert.h>
#include <libavutil/common.h>
#include <libavutil/lzo.h>
#include <libavutil/intreadwrite.h>
#include <libavutil/avstring.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/version.h>
#include "config.h"
#if HAVE_ZLIB
#include <zlib.h>
#endif
#include "talloc.h"
#include "common/av_common.h"
#include "options/options.h"
#include "bstr/bstr.h"
#include "stream/stream.h"
#include "demux.h"
#include "stheader.h"
#include "ebml.h"
#include "matroska.h"
#include "codec_tags.h"
#include "common/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
};
#define IS_LIBAV_FORK (LIBAVCODEC_VERSION_MICRO < 100)
// Both of these versions were bumped by unrelated commits.
#if (IS_LIBAV_FORK && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 7, 1)) || \
(!IS_LIBAV_FORK && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 12, 101))
#define NEED_WAVPACK_PARSE 1
#else
#define NEED_WAVPACK_PARSE 0
#endif
enum {
MAX_NUM_LACES = 256,
};
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;
struct sh_stream *stream;
char *codec_id;
int ms_compat;
char *language;
int type;
uint32_t v_width, v_height, v_dwidth, v_dheight;
bool v_dwidth_set, v_dheight_set;
double v_frate;
uint32_t colorspace;
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;
bool parse;
void *parser_tmp;
AVCodecParserContext *av_parser;
AVCodecContext *av_parser_codec;
/* stuff for realmedia */
int realmedia;
int64_t rv_kf_base;
int rv_kf_pts;
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
/* generic content encoding support */
mkv_content_encoding_t *encodings;
int num_encodings;
/* latest added index entry for this track */
int last_index_entry;
} mkv_track_t;
typedef struct mkv_index {
int tnum;
uint64_t timecode, filepos;
} mkv_index_t;
typedef struct mkv_demuxer {
int64_t segment_start, segment_end;
double duration, last_pts;
uint64_t last_filepos;
mkv_track_t **tracks;
int num_tracks;
struct ebml_tags *tags;
uint64_t tc_scale, cluster_tc;
uint64_t cluster_start;
uint64_t cluster_end;
mkv_index_t *indexes;
int num_indexes;
bool index_complete;
uint64_t deferred_cues;
struct header_elem {
int32_t id;
int64_t pos;
bool parsed;
} *headers;
int num_headers;
uint64_t skip_to_timecode;
int v_skip_to_keyframe, a_skip_to_keyframe;
int subtitle_preroll;
} mkv_demuxer_t;
#define REALHEADER_SIZE 16
#define RVPROPERTIES_SIZE 34
#define RAPROPERTIES4_SIZE 56
#define RAPROPERTIES5_SIZE 70
// Maximum number of subtitle packets that are accepted for pre-roll.
// (Subtitle packets added before first A/V keyframe packet is found with seek.)
#define NUM_SUB_PREROLL_PACKETS 500
/**
* \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;
}
#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 bstr demux_mkv_decode(struct mp_log *log, mkv_track_t *track,
bstr data, uint32_t type)
{
uint8_t *src = data.start;
uint8_t *orig_src = src;
uint8_t *dest = src;
uint32_t size = data.len;
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 HAVE_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_warn(log, "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(track->parser_tmp, 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_warn(log, "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(track->parser_tmp, 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_warn(log, "lzo decompression failed.\n");
talloc_free(dest);
dest = NULL;
goto error;
}
mp_dbg(log, "lzo decompression buffer too small.\n");
dstlen *= 2;
}
size = dstlen - out_avail;
} else if (enc->comp_algo == 3) {
dest = talloc_size(track->parser_tmp, 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);
return (bstr){dest, size};
}
static int demux_mkv_read_info(demuxer_t *demuxer)
{
mkv_demuxer_t *mkv_d = demuxer->priv;
stream_t *s = demuxer->stream;
int res = 0;
MP_VERBOSE(demuxer, "|+ segment information...\n");
mkv_d->tc_scale = 1000000;
mkv_d->duration = 0;
struct ebml_info info = {0};
struct ebml_parse_ctx parse_ctx = {demuxer->log};
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_VERBOSE(demuxer, "| + timecode scale: %" PRIu64 "\n", mkv_d->tc_scale);
}
if (info.n_duration) {
mkv_d->duration = info.duration * mkv_d->tc_scale / 1e9;
MP_VERBOSE(demuxer, "| + duration: %.3fs\n",
mkv_d->duration);
}
if (info.n_title) {
demux_info_add_bstr(demuxer, bstr0("TITLE"), info.title);
}
if (info.n_segment_uid) {
int len = info.segment_uid.len;
if (len != sizeof(demuxer->matroska_data.uid.segment)) {
MP_INFO(demuxer, "segment uid invalid length %d\n", len);
} else {
memcpy(demuxer->matroska_data.uid.segment, info.segment_uid.start,
len);
MP_VERBOSE(demuxer, "| + segment uid");
for (int i = 0; i < len; i++)
MP_VERBOSE(demuxer, " %02x",
demuxer->matroska_data.uid.segment[i]);
MP_VERBOSE(demuxer, "\n");
}
}
if (demuxer->params && demuxer->params->matroska_wanted_uids) {
if (info.n_segment_uid) {
for (int i = 0; i < demuxer->params->matroska_num_wanted_uids; i++) {
struct matroska_segment_uid *uid = demuxer->params->matroska_wanted_uids + i;
if (!memcmp(info.segment_uid.start, uid->segment, 16)) {
demuxer->matroska_data.uid.edition = uid->edition;
goto out;
}
}
}
MP_INFO(demuxer, "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 = {0};
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_WARN(demuxer, "Track "
"number %u has been encrypted and "
"decryption has not yet been\n"
"implemented. Skipping track.\n",
track->tnum);
} else if (e.type != 0) {
MP_WARN(demuxer, "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_WARN(demuxer, "Track %u has been compressed with "
"an unknown/unsupported compression\n"
"algorithm (%" PRIu64 "). Skipping track.\n",
track->tnum, e.comp_algo);
}
#if !HAVE_ZLIB
else if (e.comp_algo == 0) {
MP_WARN(demuxer, "Track %u was compressed with zlib "
"but mpv has not been compiled\n"
"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_VERBOSE(demuxer, "| + 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_VERBOSE(demuxer, "| + 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_VERBOSE(demuxer, "| + Bit depth: %u\n",
track->a_bps);
}
if (audio->n_channels) {
track->a_channels = audio->channels;
MP_VERBOSE(demuxer, "| + 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_VERBOSE(demuxer, "| + 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;
track->v_dwidth_set = true;
MP_VERBOSE(demuxer, "| + Display width: %u\n",
track->v_dwidth);
}
if (video->n_display_height) {
track->v_dheight = video->display_height;
track->v_dheight_set = true;
MP_VERBOSE(demuxer, "| + Display height: %u\n",
track->v_dheight);
}
if (video->n_pixel_width) {
track->v_width = video->pixel_width;
MP_VERBOSE(demuxer, "| + Pixel width: %u\n",
track->v_width);
}
if (video->n_pixel_height) {
track->v_height = video->pixel_height;
MP_VERBOSE(demuxer, "| + Pixel height: %u\n",
track->v_height);
}
if (video->n_colour_space && video->colour_space.len == 4) {
uint8_t *d = (uint8_t *)&video->colour_space.start[0];
track->colorspace = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
MP_VERBOSE(demuxer, "| + Colorspace: %#x\n",
(unsigned int)track->colorspace);
}
}
/**
* \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)
{
talloc_free(track->parser_tmp);
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->last_index_entry = -1;
track->parser_tmp = talloc_new(track);
track->tnum = entry->track_number;
if (track->tnum) {
MP_VERBOSE(demuxer, "| + Track number: %u\n",
track->tnum);
} else {
MP_ERR(demuxer, "Missing track number!\n");
}
if (entry->n_name) {
track->name = talloc_strndup(track, entry->name.start,
entry->name.len);
MP_VERBOSE(demuxer, "| + Name: %s\n",
track->name);
}
track->type = entry->track_type;
MP_VERBOSE(demuxer, "| + Track type: ");
switch (track->type) {
case MATROSKA_TRACK_AUDIO:
MP_VERBOSE(demuxer, "Audio\n");
break;
case MATROSKA_TRACK_VIDEO:
MP_VERBOSE(demuxer, "Video\n");
break;
case MATROSKA_TRACK_SUBTITLE:
MP_VERBOSE(demuxer, "Subtitle\n");
break;
default:
MP_VERBOSE(demuxer, "unknown\n");
break;
}
if (entry->n_audio) {
MP_VERBOSE(demuxer, "| + Audio track\n");
parse_trackaudio(demuxer, track, &entry->audio);
}
if (entry->n_video) {
MP_VERBOSE(demuxer, "| + 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;
MP_VERBOSE(demuxer, "| + Codec ID: %s\n",
track->codec_id);
} else {
MP_ERR(demuxer, "Missing codec ID!\n");
track->codec_id = "";
}
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_VERBOSE(demuxer, "| + CodecPrivate, length %u\n",
track->private_size);
}
if (entry->n_language) {
track->language = talloc_strndup(track, entry->language.start,
entry->language.len);
MP_VERBOSE(demuxer, "| + Language: %s\n",
track->language);
} else {
track->language = talloc_strdup(track, "eng");
}
if (entry->n_flag_default) {
track->default_track = entry->flag_default;
MP_VERBOSE(demuxer, "| + 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_VERBOSE(demuxer, "| + Default duration: 0");
} else {
if (!track->v_frate)
track->v_frate = 1e9 / entry->default_duration;
MP_VERBOSE(demuxer, "| + 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;
MP_VERBOSE(demuxer, "|+ segment tracks...\n");
struct ebml_tracks tracks = {0};
struct ebml_parse_ctx parse_ctx = {demuxer->log};
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_VERBOSE(demuxer, "| + a track...\n");
parse_trackentry(demuxer, &tracks.track_entry[i]);
}
talloc_free(parse_ctx.talloc_ctx);
return 0;
}
static void cue_index_add(demuxer_t *demuxer, int track_id, uint64_t filepos,
uint64_t timecode)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
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_id;
mkv_d->indexes[mkv_d->num_indexes].timecode = timecode;
mkv_d->indexes[mkv_d->num_indexes].filepos = filepos;
mkv_d->num_indexes++;
}
static void add_block_position(demuxer_t *demuxer, struct mkv_track *track,
uint64_t filepos, uint64_t timecode)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
if (mkv_d->index_complete || !track)
return;
if (track->last_index_entry >= 0) {
mkv_index_t *index = &mkv_d->indexes[track->last_index_entry];
// filepos is always the cluster position, which can contain multiple
// blocks with different timecodes - one is enough.
// Also, never add block which are already covered by the index.
if (index->filepos == filepos || index->timecode >= timecode)
return;
}
cue_index_add(demuxer, track->tnum, filepos, timecode);
track->last_index_entry = mkv_d->num_indexes - 1;
}
static int demux_mkv_read_cues(demuxer_t *demuxer)
{
struct MPOpts *opts = demuxer->opts;
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
stream_t *s = demuxer->stream;
mkv_d->deferred_cues = 0;
if (opts->index_mode == 0 || opts->index_mode == 2) {
ebml_read_skip(demuxer->log, -1, s);
return 0;
}
MP_VERBOSE(demuxer, "/---- [ parsing cues ] -----------\n");
struct ebml_cues cues = {0};
struct ebml_parse_ctx parse_ctx = {demuxer->log};
if (ebml_read_element(s, &parse_ctx, &cues, &ebml_cues_desc) < 0)
return -1;
mkv_d->num_indexes = 0;
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_WARN(demuxer, "Malformed CuePoint element\n");
continue;
}
uint64_t time = cuepoint->cue_time;
for (int c = 0; c < cuepoint->n_cue_track_positions; c++) {
struct ebml_cue_track_positions *trackpos =
&cuepoint->cue_track_positions[c];
uint64_t pos = mkv_d->segment_start + trackpos->cue_cluster_position;
cue_index_add(demuxer, trackpos->cue_track, pos, time);
MP_DBG(demuxer, "|+ found cue point for track %" PRIu64
": timecode %" PRIu64 ", filepos: %" PRIu64 "\n",
trackpos->cue_track, time, pos);
}
}
// Do not attempt to create index on the fly.
mkv_d->index_complete = true;
MP_VERBOSE(demuxer, "\\---- [ parsing cues ] -----------\n");
talloc_free(parse_ctx.talloc_ctx);
return 0;
}
static void read_deferred_cues(demuxer_t *demuxer)
{
mkv_demuxer_t *mkv_d = demuxer->priv;
stream_t *s = demuxer->stream;
if (mkv_d->deferred_cues) {
int64_t pos = mkv_d->deferred_cues;
mkv_d->deferred_cues = 0;
if (!stream_seek(s, pos)) {
MP_WARN(demuxer, "Failed to seek to cues\n");
return;
}
if (ebml_read_id(s) != MATROSKA_ID_CUES) {
MP_WARN(demuxer, "Expected element not found\n");
return;
}
demux_mkv_read_cues(demuxer);
}
}
static int demux_mkv_read_chapters(struct demuxer *demuxer)
{
struct MPOpts *opts = demuxer->opts;
stream_t *s = demuxer->stream;
int wanted_edition = opts->edition_id;
uint64_t wanted_edition_uid = demuxer->matroska_data.uid.edition;
/* A specific edition UID was requested; ignore the user option which is
* only applicable to the top-level file. */
if (wanted_edition_uid)
wanted_edition = -1;
MP_VERBOSE(demuxer, "/---- [ parsing chapters ] ---------\n");
struct ebml_chapters file_chapters = {0};
struct ebml_parse_ctx parse_ctx = {demuxer->log};
if (ebml_read_element(s, &parse_ctx, &file_chapters,
&ebml_chapters_desc) < 0)
return -1;
int selected_edition = -1;
int num_editions = file_chapters.n_edition_entry;
struct ebml_edition_entry *editions = file_chapters.edition_entry;
for (int i = 0; i < num_editions; i++) {
struct demux_edition new = {
.demuxer_id = editions[i].edition_uid,
.default_edition = editions[i].edition_flag_default,
.metadata = talloc_zero(demuxer, struct mp_tags),
};
MP_TARRAY_APPEND(demuxer, demuxer->editions, demuxer->num_editions, new);
}
if (wanted_edition >= 0 && wanted_edition < num_editions) {
selected_edition = wanted_edition;
MP_VERBOSE(demuxer, "User-specified edition: %d\n",
selected_edition);
} else {
for (int i = 0; i < num_editions; i++) {
if (wanted_edition_uid &&
editions[i].edition_uid == wanted_edition_uid) {
selected_edition = i;
break;
} else if (editions[i].edition_flag_default) {
selected_edition = i;
MP_VERBOSE(demuxer, "Default edition: %d\n", i);
break;
}
}
}
if (selected_edition < 0) {
if (wanted_edition_uid) {
MP_ERR(demuxer, "Unable to find expected edition uid: %"PRIu64"\n",
wanted_edition_uid);
return -1;
} else {
selected_edition = 0;
}
}
for (int idx = 0; idx < num_editions; idx++) {
MP_VERBOSE(demuxer, "New edition %d\n", idx);
int warn_level = idx == selected_edition ? MSGL_WARN : MSGL_V;
if (editions[idx].n_edition_flag_default)
MP_VERBOSE(demuxer, "Default edition flag: %"PRIu64
"\n", editions[idx].edition_flag_default);
if (editions[idx].n_edition_flag_ordered)
MP_VERBOSE(demuxer, "Ordered chapter flag: %"PRIu64
"\n", editions[idx].edition_flag_ordered);
int chapter_count = editions[idx].n_chapter_atom;
struct matroska_chapter *m_chapters = NULL;
if (idx == selected_edition && editions[idx].edition_flag_ordered) {
m_chapters = talloc_array_ptrtype(demuxer, m_chapters, chapter_count);
demuxer->matroska_data.ordered_chapters = m_chapters;
demuxer->matroska_data.num_ordered_chapters = chapter_count;
}
for (int i = 0; i < chapter_count; i++) {
struct ebml_chapter_atom *ca = editions[idx].chapter_atom + i;
struct matroska_chapter chapter = {0};
struct bstr name = { "(unnamed)", 9 };
if (!ca->n_chapter_time_start)
MP_MSG(demuxer, warn_level,
"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(demuxer, warn_level, "Multiple chapter "
"names not supported, picking first\n");
if (!ca->chapter_display[0].n_chap_string)
MP_MSG(demuxer, warn_level, "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.uid.segment))
MP_MSG(demuxer, warn_level,
"Chapter segment uid bad length %d\n", len);
else {
memcpy(chapter.uid.segment, ca->chapter_segment_uid.start,
len);
if (ca->n_chapter_segment_edition_uid)
chapter.uid.edition = ca->chapter_segment_edition_uid;
else
chapter.uid.edition = 0;
MP_VERBOSE(demuxer, "Chapter segment uid ");
for (int n = 0; n < len; n++)
MP_VERBOSE(demuxer, "%02x ",
chapter.uid.segment[n]);
MP_VERBOSE(demuxer, "\n");
}
}
MP_VERBOSE(demuxer, "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,
ca->chapter_uid);
}
if (m_chapters) {
chapter.name = talloc_strndup(m_chapters, name.start, name.len);
m_chapters[i] = chapter;
}
}
}
demuxer->num_editions = num_editions;
demuxer->edition = selected_edition;
talloc_free(parse_ctx.talloc_ctx);
MP_VERBOSE(demuxer, "\\---- [ parsing chapters ] ---------\n");
return 0;
}
static int demux_mkv_read_tags(demuxer_t *demuxer)
{
struct mkv_demuxer *mkv_d = demuxer->priv;
2010-03-28 14:21:42 +02:00
stream_t *s = demuxer->stream;
struct ebml_parse_ctx parse_ctx = {demuxer->log};
struct ebml_tags tags = {0};
2010-03-28 14:21:42 +02:00
if (ebml_read_element(s, &parse_ctx, &tags, &ebml_tags_desc) < 0)
return -1;
2010-03-28 14:21:42 +02:00
mkv_d->tags = talloc_memdup(mkv_d, &tags, sizeof(tags));
talloc_steal(mkv_d->tags, parse_ctx.talloc_ctx);
return 0;
}
static void process_tags(demuxer_t *demuxer)
{
struct mkv_demuxer *mkv_d = demuxer->priv;
struct ebml_tags *tags = mkv_d->tags;
if (!tags)
return;
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_attachment_uid)
2010-03-28 14:21:42 +02:00
continue;
if (tag.targets.target_chapter_uid) {
for (int j = 0; j < tag.n_simple_tag; j++) {
demuxer_add_chapter_info(demuxer, tag.targets.target_chapter_uid,
tag.simple_tag[j].tag_name,
tag.simple_tag[j].tag_string);
}
} else if (tag.targets.target_edition_uid) {
struct demux_edition *edition = NULL;
for (int n = 0; n < demuxer->num_editions; n++) {
if (demuxer->editions[n].demuxer_id ==
tag.targets.target_edition_uid)
{
edition = &demuxer->editions[n];
break;
}
}
if (edition) {
for (int j = 0; j < tag.n_simple_tag; j++) {
mp_tags_set_bstr(edition->metadata,
tag.simple_tag[j].tag_name,
tag.simple_tag[j].tag_string);
}
}
} else {
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);
}
}
2010-03-28 14:21:42 +02:00
}
}
static int demux_mkv_read_attachments(demuxer_t *demuxer)
{
stream_t *s = demuxer->stream;
MP_VERBOSE(demuxer, "/---- [ parsing attachments ] ---------\n");
struct ebml_attachments attachments = {0};
struct ebml_parse_ctx parse_ctx = {demuxer->log};
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_WARN(demuxer, "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_VERBOSE(demuxer, "Attachment: %.*s, %.*s, %zu bytes\n",
BSTR_P(name), BSTR_P(mime), attachment->file_data.len);
}
talloc_free(parse_ctx.talloc_ctx);
MP_VERBOSE(demuxer, "\\---- [ parsing attachments ] ---------\n");
return 0;
}
static int read_header_element(struct demuxer *demuxer, uint32_t id,
int64_t at_filepos);
static struct header_elem *get_header_element(struct demuxer *demuxer,
uint32_t id,
int64_t element_filepos)
{
struct mkv_demuxer *mkv_d = demuxer->priv;
// Note that some files in fact contain a SEEKHEAD with a list of all
// clusters - we have no use for that.
if (!ebml_is_mkv_level1_id(id) || id == MATROSKA_ID_CLUSTER)
return NULL;
for (int n = 0; n < mkv_d->num_headers; n++) {
struct header_elem *elem = &mkv_d->headers[n];
// SEEKHEAD is the only element that can happen multiple times.
// Other elements might be duplicated (or attempted to be read twice,
// even if it's only once in the file), but only the first is used.
if (elem->id == id && (id != MATROSKA_ID_SEEKHEAD ||
elem->pos == element_filepos))
return elem;
}
struct header_elem elem = { .id = id, .pos = element_filepos };
MP_TARRAY_APPEND(mkv_d, mkv_d->headers, mkv_d->num_headers, elem);
return &mkv_d->headers[mkv_d->num_headers - 1];
}
// Mark the level 1 element with the given id as read. Return whether it
// was marked read before (e.g. for checking whether it was already read).
// element_filepos refers to the file position of the element ID.
static bool test_header_element(struct demuxer *demuxer, uint32_t id,
int64_t element_filepos)
{
struct header_elem *elem = get_header_element(demuxer, id, element_filepos);
if (!elem)
return false;
if (elem->parsed)
return true;
elem->parsed = true;
return false;
}
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 = {0};
struct ebml_parse_ctx parse_ctx = {demuxer->log};
MP_VERBOSE(demuxer, "/---- [ parsing seek head ] ---------\n");
if (ebml_read_element(s, &parse_ctx, &seekhead, &ebml_seek_head_desc) < 0) {
res = -1;
goto out;
}
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_WARN(demuxer, "Invalid SeekHead entry\n");
continue;
}
uint64_t pos = seek->seek_position + mkv_d->segment_start;
MP_VERBOSE(demuxer, "Element 0x%x at %"PRIu64".\n",
(unsigned)seek->seek_id, pos);
get_header_element(demuxer, seek->seek_id, pos);
// This is nice to warn against incomplete files.
if (pos >= s->end_pos)
MP_WARN(demuxer, "SeekHead position beyond "
"end of file - incomplete file?\n");
}
out:
MP_VERBOSE(demuxer, "\\---- [ parsing seek head ] ---------\n");
talloc_free(parse_ctx.talloc_ctx);
return res;
}
static int read_header_element(struct demuxer *demuxer, uint32_t id,
int64_t start_filepos)
{
if (id == EBML_ID_INVALID)
return 0;
if (test_header_element(demuxer, id, start_filepos))
goto skip;
switch(id) {
case MATROSKA_ID_INFO:
return demux_mkv_read_info(demuxer);
case MATROSKA_ID_TRACKS:
return demux_mkv_read_tracks(demuxer);
case MATROSKA_ID_CUES:
return demux_mkv_read_cues(demuxer);
case MATROSKA_ID_TAGS:
return demux_mkv_read_tags(demuxer);
case MATROSKA_ID_SEEKHEAD:
return demux_mkv_read_seekhead(demuxer);
case MATROSKA_ID_CHAPTERS:
return demux_mkv_read_chapters(demuxer);
case MATROSKA_ID_ATTACHMENTS:
return demux_mkv_read_attachments(demuxer);
}
skip:
ebml_read_skip(demuxer->log, -1, demuxer->stream);
return 0;
}
static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track);
static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track);
static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track);
static void display_create_tracks(demuxer_t *demuxer)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
for (int i = 0; i < mkv_d->num_tracks; i++) {
switch (mkv_d->tracks[i]->type) {
case MATROSKA_TRACK_VIDEO:
demux_mkv_open_video(demuxer, mkv_d->tracks[i]);
break;
case MATROSKA_TRACK_AUDIO:
demux_mkv_open_audio(demuxer, mkv_d->tracks[i]);
break;
case MATROSKA_TRACK_SUBTITLE:
demux_mkv_open_sub(demuxer, mkv_d->tracks[i]);
break;
}
}
}
typedef struct {
char *id;
int fourcc;
int extradata;
} videocodec_info_t;
static const videocodec_info_t vinfo[] = {
{MKV_V_MJPEG, MP_FOURCC('m', 'j', 'p', 'g'), 1},
{MKV_V_MPEG1, MP_FOURCC('m', 'p', 'g', '1'), 0},
{MKV_V_MPEG2, MP_FOURCC('m', 'p', 'g', '2'), 0},
{MKV_V_MPEG4_SP, MP_FOURCC('m', 'p', '4', 'v'), 1},
{MKV_V_MPEG4_ASP, MP_FOURCC('m', 'p', '4', 'v'), 1},
{MKV_V_MPEG4_AP, MP_FOURCC('m', 'p', '4', 'v'), 1},
{MKV_V_MPEG4_AVC, MP_FOURCC('a', 'v', 'c', '1'), 1},
{MKV_V_THEORA, MP_FOURCC('t', 'h', 'e', 'o'), 1},
{MKV_V_VP8, MP_FOURCC('V', 'P', '8', '0'), 0},
{MKV_V_VP9, MP_FOURCC('V', 'P', '9', '0'), 0},
{MKV_V_DIRAC, MP_FOURCC('d', 'r', 'a', 'c'), 0},
{MKV_V_PRORES, MP_FOURCC('p', 'r', '0', '0'), 0},
{MKV_V_HEVC, MP_FOURCC('H', 'E', 'V', 'C'), 1},
{NULL, 0, 0}
};
static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
{
MP_BITMAPINFOHEADER *bih = &(MP_BITMAPINFOHEADER){0};
unsigned char *extradata;
unsigned int extradata_size = 0;
struct sh_stream *sh;
sh_video_t *sh_v;
bool raw = false;
if (track->ms_compat) { /* MS compatibility mode */
MP_BITMAPINFOHEADER *src;
if (track->private_data == NULL
cleanup: malloc+memset->calloc, sizeof(TYPE)->sizeof(*ptr) Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32181 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32182 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32183 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace some sizeof(type) by sizeof(*pointer) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32184 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32186 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32187 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32188 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizoef(type) by sizeof(*ptrvar). Besides being consistent with FFmpeg style, this reduces the size of a patch to rename these types to not conflict with the windows.h definitions. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32189 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32191 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32192 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(type) by sizeof(*ptrvar) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32193 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove a useless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32194 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(type) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32195 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove a useless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32196 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace several sizeof(WAVEFORMATEX) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32197 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace one more instance of sizeof(WAVEFORMATEX); fix compilation. patch by Clément Bœsch, ubitux gmail com git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32199 b3059339-0415-0410-9bf9-f77b7e298cf2 Avoid some pointless uses of sizeof() and one related cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32200 b3059339-0415-0410-9bf9-f77b7e298cf2 Merge one malloc() + memset() invocation into calloc(). git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32202 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32203 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(WAVEFORMATEX) occurrences. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32205 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32206 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(BITMAPINFOHEADER) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32207 b3059339-0415-0410-9bf9-f77b7e298cf2
2010-09-12 13:44:42 +02:00
|| track->private_size < sizeof(*bih))
return 1;
src = (MP_BITMAPINFOHEADER *) track->private_data;
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);
extradata = track->private_data + sizeof(*bih);
extradata_size = track->private_size - sizeof(*bih);
if (track->v_width == 0)
track->v_width = bih->biWidth;
if (track->v_height == 0)
track->v_height = bih->biHeight;
} else {
cleanup: malloc+memset->calloc, sizeof(TYPE)->sizeof(*ptr) Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32181 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32182 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32183 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace some sizeof(type) by sizeof(*pointer) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32184 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32186 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32187 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32188 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizoef(type) by sizeof(*ptrvar). Besides being consistent with FFmpeg style, this reduces the size of a patch to rename these types to not conflict with the windows.h definitions. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32189 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32191 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32192 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(type) by sizeof(*ptrvar) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32193 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove a useless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32194 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(type) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32195 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove a useless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32196 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace several sizeof(WAVEFORMATEX) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32197 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace one more instance of sizeof(WAVEFORMATEX); fix compilation. patch by Clément Bœsch, ubitux gmail com git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32199 b3059339-0415-0410-9bf9-f77b7e298cf2 Avoid some pointless uses of sizeof() and one related cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32200 b3059339-0415-0410-9bf9-f77b7e298cf2 Merge one malloc() + memset() invocation into calloc(). git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32202 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32203 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(WAVEFORMATEX) occurrences. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32205 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32206 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(BITMAPINFOHEADER) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32207 b3059339-0415-0410-9bf9-f77b7e298cf2
2010-09-12 13:44:42 +02:00
bih->biSize = sizeof(*bih);
bih->biWidth = track->v_width;
bih->biHeight = track->v_height;
bih->biBitCount = 24;
bih->biSizeImage = bih->biWidth * bih->biHeight * bih->biBitCount / 8;
if (track->private_size >= RVPROPERTIES_SIZE
&& (!strcmp(track->codec_id, MKV_V_REALV10)
|| !strcmp(track->codec_id, MKV_V_REALV20)
|| !strcmp(track->codec_id, MKV_V_REALV30)
|| !strcmp(track->codec_id, MKV_V_REALV40))) {
unsigned char *src;
uint32_t type2;
unsigned int cnt;
src = (uint8_t *) track->private_data + RVPROPERTIES_SIZE;
cnt = track->private_size - RVPROPERTIES_SIZE;
bih->biSize = 48 + cnt;
bih->biPlanes = 1;
type2 = AV_RB32(src - 4);
if (type2 == 0x10003000 || type2 == 0x10003001)
bih->biCompression = MP_FOURCC('R', 'V', '1', '3');
else
bih->biCompression =
MP_FOURCC('R', 'V', track->codec_id[9], '0');
// copy type1 and type2 info from rv properties
extradata_size = cnt + 8;
extradata = src - 8;
track->realmedia = 1;
} else if (strcmp(track->codec_id, MKV_V_UNCOMPRESSED) == 0) {
// raw video, "like AVI" - this is a FourCC
bih->biCompression = track->colorspace;
raw = true;
} else {
const videocodec_info_t *vi = vinfo;
while (vi->id && strcmp(vi->id, track->codec_id))
vi++;
bih->biCompression = vi->fourcc;
if (vi->extradata && track->private_data
&& (track->private_size > 0)) {
bih->biSize += track->private_size;
extradata = track->private_data;
extradata_size = track->private_size;
}
if (!vi->id) {
MP_WARN(demuxer, "Unknown/unsupported "
"CodecID (%s) or missing/bad CodecPrivate\n"
"data (track %u).\n",
track->codec_id, track->tnum);
return 1;
}
}
}
sh = new_sh_stream(demuxer, STREAM_VIDEO);
if (!sh)
return 1;
track->stream = sh;
sh_v = sh->video;
sh->demuxer_id = track->tnum;
sh->title = talloc_strdup(sh_v, track->name);
sh_v->bih = talloc_size(sh_v, sizeof(MP_BITMAPINFOHEADER) + extradata_size);
if (!sh_v->bih) {
MP_FATAL(demuxer, "Memory allocation failure!\n");
abort();
}
*sh_v->bih = *bih;
if (extradata_size)
memcpy(sh_v->bih + 1, extradata, extradata_size);
sh->format = sh_v->bih->biCompression;
if (raw) {
sh->codec = "rawvideo";
core: redo how codecs are mapped, remove codecs.conf Use codec names instead of FourCCs to identify codecs. Rewrite how codecs are selected and initialized. Now each decoder exports a list of decoders (and the codec it supports) via add_decoders(). The order matters, and the first decoder for a given decoder is preferred over the other decoders. E.g. all ad_mpg123 decoders are preferred over ad_lavc, because it comes first in the mpcodecs_ad_drivers array. Likewise, decoders within ad_lavc that are enumerated first by libavcodec (using av_codec_next()) are preferred. (This is actually critical to select h264 software decoding by default instead of vdpau. libavcodec and ffmpeg/avconv use the same method to select decoders by default, so we hope this is sane.) The codec names follow libavcodec's codec names as defined by AVCodecDescriptor.name (see libavcodec/codec_desc.c). Some decoders have names different from the canonical codec name. The AVCodecDescriptor API is relatively new, so we need a compatibility layer for older libavcodec versions for codec names that are referenced internally, and which are different from the decoder name. (Add a configure check for that, because checking versions is getting way too messy.) demux/codec_tags.c is generated from the former codecs.conf (minus "special" decoders like vdpau, and excluding the mappings that are the same as the mappings libavformat's exported RIFF tables). It contains all the mappings from FourCCs to codec name. This is needed for demux_mkv, demux_mpg, demux_avi and demux_asf. demux_lavf will set the codec as determined by libavformat, while the other demuxers have to do this on their own, using the mp_set_audio/video_codec_from_tag() functions. Note that the sh_audio/video->format members don't uniquely identify the codec anymore, and sh->codec takes over this role. Replace the --ac/--vc/--afm/--vfm with new --vd/--ad options, which provide cover the functionality of the removed switched. Note: there's no CODECS_FLAG_FLIP flag anymore. This means some obscure container/video combinations (e.g. the sample Film_200_zygo_pro.mov) are played flipped. ffplay/avplay doesn't handle this properly either, so we don't care and blame ffmeg/libav instead.
2013-02-09 15:15:19 +01:00
} else {
mp_set_codec_from_tag(sh);
sh->format = mp_video_fourcc_alias(sh->format);
}
if (track->v_frate == 0.0)
track->v_frate = 25.0;
sh_v->fps = track->v_frate;
sh_v->aspect = 0;
if (!track->realmedia) {
sh_v->disp_w = track->v_width;
sh_v->disp_h = track->v_height;
uint32_t dw = track->v_dwidth_set ? track->v_dwidth : track->v_width;
uint32_t dh = track->v_dheight_set ? track->v_dheight : track->v_height;
if (dw && dh)
sh_v->aspect = (double) dw / dh;
} else {
// vd_realvid.c will set aspect to disp_w/disp_h and rederive
// disp_w and disp_h from the RealVideo stream contents returned
// by the Real DLLs. If DisplayWidth/DisplayHeight was not set in
// the Matroska file then it has already been set to PixelWidth/Height
// by check_track_information.
sh_v->disp_w = track->v_dwidth;
sh_v->disp_h = track->v_dheight;
}
MP_VERBOSE(demuxer, "Aspect: %f\n", sh_v->aspect);
video: add insane hack to work around FFmpeg/Libav insanity So, FFmpeg/Libav requires us to figure out video timestamps ourselves (see last 10 commits or so), but the methods it provides for this aren't even sufficient. In particular, everything that uses AVI-style DTS (avi, vfw-muxed mkv, possibly mpeg4-in-ogm) with a codec that has an internal frame delay is broken. In this case, libavcodec will shift the packet- to-image correspondence by the codec delay, meaning that with a delay=1, the first AVFrame.pkt_dts is not 0, but that of the second packet. All timestamps will appear shifted. The start time (e.g. the time displayed when doing "mpv file.avi --pause") will not be exactly 0. (According to Libav developers, this is how it's supposed to work; just that the first DTS values are normally negative with formats that use DTS "properly". Who cares if it doesn't work at all with very common video formats? There's no indication that they'll fix this soon, either. An elegant workaround is missing too.) Add a hack to re-enable the old PTS code for AVI and vfw-muxed MKV. Since these timestamps are not reorderd, we wouldn't need to sort them, but it's less code this way (and possibly more robust, should a demuxer unexpectedly output PTS). The original intention of all the timestamp changes recently was actually to get rid of demuxer-specific hacks and the old timestamp sorting code, but it looks like this didn't work out. Yet another case where trying to replace native MPlayer functionality with FFmpeg/Libav led to disadvantages and bugs. (Note that the old PTS sorting code doesn't and can't handle frame dropping correctly, though.) Bug reports: https://trac.ffmpeg.org/ticket/3178 https://bugzilla.libav.org/show_bug.cgi?id=600
2013-11-28 15:10:45 +01:00
sh_v->avi_dts = track->ms_compat;
return 0;
}
static struct mkv_audio_tag {
char *id; bool prefix; uint32_t formattag;
bool parse;
} mkv_audio_tags[] = {
{ MKV_A_MP2, 0, 0x0055 },
{ MKV_A_MP3, 0, 0x0055 },
{ MKV_A_AC3, 1, 0x2000 },
{ MKV_A_EAC3, 1, MP_FOURCC('E', 'A', 'C', '3') },
{ MKV_A_DTS, 0, 0x2001 },
{ MKV_A_PCM, 0, 0x0001 },
{ MKV_A_PCM_BE, 0, 0x0001 },
{ MKV_A_PCM_FLT, 0, 0x0003 },
{ MKV_A_AAC_2MAIN, 0, MP_FOURCC('M', 'P', '4', 'A') },
{ MKV_A_AAC_2LC, 1, MP_FOURCC('M', 'P', '4', 'A') },
{ MKV_A_AAC_2SSR, 0, MP_FOURCC('M', 'P', '4', 'A') },
{ MKV_A_AAC_4MAIN, 0, MP_FOURCC('M', 'P', '4', 'A') },
{ MKV_A_AAC_4LC, 1, MP_FOURCC('M', 'P', '4', 'A') },
{ MKV_A_AAC_4SSR, 0, MP_FOURCC('M', 'P', '4', 'A') },
{ MKV_A_AAC_4LTP, 0, MP_FOURCC('M', 'P', '4', 'A') },
{ MKV_A_AAC, 0, MP_FOURCC('M', 'P', '4', 'A') },
{ MKV_A_VORBIS, 0, MP_FOURCC('v', 'r', 'b', 's') },
{ MKV_A_OPUS, 0, MP_FOURCC('O', 'p', 'u', 's') },
{ MKV_A_OPUS_EXP, 0, MP_FOURCC('O', 'p', 'u', 's') },
{ MKV_A_QDMC, 0, MP_FOURCC('Q', 'D', 'M', 'C') },
{ MKV_A_QDMC2, 0, MP_FOURCC('Q', 'D', 'M', '2') },
{ MKV_A_WAVPACK, 0, MP_FOURCC('W', 'V', 'P', 'K') },
{ MKV_A_TRUEHD, 0, MP_FOURCC('T', 'R', 'H', 'D'), true },
{ MKV_A_FLAC, 0, MP_FOURCC('f', 'L', 'a', 'C') },
{ MKV_A_ALAC, 0, MP_FOURCC('a', 'L', 'a', 'C') },
{ MKV_A_REAL28, 0, MP_FOURCC('2', '8', '_', '8') },
{ MKV_A_REALATRC, 0, MP_FOURCC('a', 't', 'r', 'c') },
{ MKV_A_REALCOOK, 0, MP_FOURCC('c', 'o', 'o', 'k') },
{ MKV_A_REALDNET, 0, MP_FOURCC('d', 'n', 'e', 't') },
{ MKV_A_REALSIPR, 0, MP_FOURCC('s', 'i', 'p', 'r') },
{ MKV_A_TTA1, 0, MP_FOURCC('T', 'T', 'A', '1') },
{ NULL },
};
static void copy_audio_private_data(sh_audio_t *sh, mkv_track_t *track)
{
if (!track->ms_compat && track->private_size) {
sh->codecdata = talloc_size(sh, track->private_size);
sh->codecdata_len = track->private_size;
memcpy(sh->codecdata, track->private_data, track->private_size);
}
}
static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
{
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_AUDIO);
if (!sh)
return 1;
track->stream = sh;
sh_audio_t *sh_a = sh->audio;
if (track->language && (strcmp(track->language, "und") != 0))
sh->lang = talloc_strdup(sh_a, track->language);
sh->demuxer_id = track->tnum;
sh->title = talloc_strdup(sh_a, track->name);
sh->default_track = track->default_track;
if (!track->a_osfreq)
track->a_osfreq = track->a_sfreq;
if (track->ms_compat) {
if (track->private_size < sizeof(*sh_a->wf))
goto error;
MP_VERBOSE(demuxer, "track with MS compat audio.\n");
MP_WAVEFORMATEX *wf = (MP_WAVEFORMATEX *) track->private_data;
sh_a->wf = talloc_zero_size(sh_a, 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);
cleanup: malloc+memset->calloc, sizeof(TYPE)->sizeof(*ptr) Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32181 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32182 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32183 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace some sizeof(type) by sizeof(*pointer) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32184 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32186 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32187 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32188 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizoef(type) by sizeof(*ptrvar). Besides being consistent with FFmpeg style, this reduces the size of a patch to rename these types to not conflict with the windows.h definitions. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32189 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32191 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32192 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(type) by sizeof(*ptrvar) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32193 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove a useless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32194 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(type) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32195 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove a useless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32196 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace several sizeof(WAVEFORMATEX) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32197 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace one more instance of sizeof(WAVEFORMATEX); fix compilation. patch by Clément Bœsch, ubitux gmail com git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32199 b3059339-0415-0410-9bf9-f77b7e298cf2 Avoid some pointless uses of sizeof() and one related cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32200 b3059339-0415-0410-9bf9-f77b7e298cf2 Merge one malloc() + memset() invocation into calloc(). git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32202 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32203 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(WAVEFORMATEX) occurrences. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32205 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32206 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(BITMAPINFOHEADER) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32207 b3059339-0415-0410-9bf9-f77b7e298cf2
2010-09-12 13:44:42 +02:00
sh_a->wf->cbSize = track->private_size - sizeof(*sh_a->wf);
memcpy(sh_a->wf + 1, wf + 1,
cleanup: malloc+memset->calloc, sizeof(TYPE)->sizeof(*ptr) Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32181 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32182 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32183 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace some sizeof(type) by sizeof(*pointer) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32184 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32186 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32187 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32188 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizoef(type) by sizeof(*ptrvar). Besides being consistent with FFmpeg style, this reduces the size of a patch to rename these types to not conflict with the windows.h definitions. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32189 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32191 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32192 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(type) by sizeof(*ptrvar) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32193 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove a useless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32194 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(type) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32195 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove a useless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32196 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace several sizeof(WAVEFORMATEX) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32197 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace one more instance of sizeof(WAVEFORMATEX); fix compilation. patch by Clément Bœsch, ubitux gmail com git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32199 b3059339-0415-0410-9bf9-f77b7e298cf2 Avoid some pointless uses of sizeof() and one related cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32200 b3059339-0415-0410-9bf9-f77b7e298cf2 Merge one malloc() + memset() invocation into calloc(). git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32202 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32203 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(WAVEFORMATEX) occurrences. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32205 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32206 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(BITMAPINFOHEADER) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32207 b3059339-0415-0410-9bf9-f77b7e298cf2
2010-09-12 13:44:42 +02:00
track->private_size - sizeof(*sh_a->wf));
if (track->a_osfreq == 0.0)
track->a_osfreq = sh_a->wf->nSamplesPerSec;
if (track->a_channels == 0)
track->a_channels = sh_a->wf->nChannels;
if (track->a_bps == 0)
track->a_bps = sh_a->wf->wBitsPerSample;
track->a_formattag = sh_a->wf->wFormatTag;
} else {
sh_a->wf = talloc_zero(sh_a, MP_WAVEFORMATEX);
for (int i = 0; ; i++) {
struct mkv_audio_tag *t = mkv_audio_tags + i;
if (t->id == NULL)
goto error;
if (t->prefix) {
if (!bstr_startswith0(bstr0(track->codec_id), t->id))
continue;
} else {
if (strcmp(track->codec_id, t->id))
continue;
}
track->a_formattag = t->formattag;
track->parse = t->parse;
break;
}
}
sh->format = track->a_formattag;
sh_a->wf->wFormatTag = track->a_formattag;
mp_chmap_from_channels(&sh_a->channels, track->a_channels);
sh_a->wf->nChannels = track->a_channels;
sh_a->samplerate = (uint32_t) track->a_osfreq;
sh_a->wf->nSamplesPerSec = (uint32_t) track->a_osfreq;
if (track->a_bps == 0)
sh_a->wf->wBitsPerSample = 16;
else
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 == MP_FOURCC('E', 'A', 'C', '3')
|| (track->a_formattag == 0x2001)) { /* DTS */
talloc_free(sh_a->wf);
sh_a->wf = NULL;
} else if (track->a_formattag == 0x0001) { /* PCM || PCM_BE */
if (!strcmp(track->codec_id, MKV_A_PCM_BE))
sh->format = MP_FOURCC('t', 'w', 'o', 's');
} else if (track->a_formattag == 0x0003) { /* PCM_FLT */
/* ok */
} 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;
copy_audio_private_data(sh_a, track);
} else if (track->a_formattag == MP_FOURCC('M', 'P', '4', 'A')) {
int profile, srate_idx;
sh_a->wf->nAvgBytesPerSec = 16000;
sh_a->wf->nBlockAlign = 1024;
if (!strcmp(track->codec_id, MKV_A_AAC) && track->private_data) {
copy_audio_private_data(sh_a, track);
} else {
/* Recreate the 'private data' */
/* which faad2 uses in its initialization */
srate_idx = aac_get_sample_rate_index(track->a_sfreq);
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 = talloc_size(sh_a, 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 = sh_a->wf->nSamplesPerSec = track->a_osfreq;
srate_idx = aac_get_sample_rate_index(sh_a->samplerate);
sh_a->codecdata[2] = AAC_SYNC_EXTENSION_TYPE >> 3;
sh_a->codecdata[3] = ((AAC_SYNC_EXTENSION_TYPE & 0x07) << 5) | 5;
sh_a->codecdata[4] = (1 << 7) | (srate_idx << 3);
track->default_duration = 1024.0 / (sh_a->samplerate / 2);
} else {
sh_a->codecdata_len = 2;
track->default_duration = 1024.0 / sh_a->samplerate;
}
}
} else if (track->a_formattag == MP_FOURCC('v', 'r', 'b', 's')) {
/* VORBIS */
if (track->private_size == 0 || track->ms_compat && !sh_a->wf->cbSize)
goto error;
if (!track->ms_compat) {
sh_a->wf->cbSize = track->private_size;
sh_a->wf = talloc_realloc_size(sh_a, sh_a->wf,
sizeof(*sh_a->wf) + sh_a->wf->cbSize);
memcpy((unsigned char *) (sh_a->wf + 1), track->private_data,
sh_a->wf->cbSize);
}
} else if (!strcmp(track->codec_id, MKV_A_OPUS)
|| !strcmp(track->codec_id, MKV_A_OPUS_EXP)) {
sh->format = MP_FOURCC('O', 'p', 'u', 's');
copy_audio_private_data(sh_a, track);
} else if (!strncmp(track->codec_id, MKV_A_REALATRC, 7)) {
if (track->private_size < RAPROPERTIES4_SIZE)
goto error;
/* Common initialization for all RealAudio codecs */
unsigned char *src = track->private_data;
int codecdata_length, version;
int flavor;
sh_a->wf->nAvgBytesPerSec = 0; /* FIXME !? */
version = AV_RB16(src + 4);
flavor = AV_RB16(src + 22);
track->coded_framesize = AV_RB32(src + 24);
track->sub_packet_h = AV_RB16(src + 40);
sh_a->wf->nBlockAlign = track->audiopk_size = AV_RB16(src + 42);
track->sub_packet_size = AV_RB16(src + 44);
if (version == 4) {
src += RAPROPERTIES4_SIZE;
src += src[0] + 1;
src += src[0] + 1;
} else {
src += RAPROPERTIES5_SIZE;
}
src += 3;
if (version == 5)
src++;
codecdata_length = AV_RB32(src);
src += 4;
sh_a->wf->cbSize = codecdata_length;
sh_a->wf = talloc_realloc_size(sh_a, sh_a->wf,
sizeof(*sh_a->wf) + sh_a->wf->cbSize);
memcpy(((char *) (sh_a->wf + 1)), src, codecdata_length);
switch (track->a_formattag) {
case MP_FOURCC('a', 't', 'r', 'c'):
sh_a->wf->nAvgBytesPerSec = atrc_fl2bps[flavor];
sh_a->wf->nBlockAlign = track->sub_packet_size;
goto audiobuf;
case MP_FOURCC('c', 'o', 'o', 'k'):
sh_a->wf->nAvgBytesPerSec = cook_fl2bps[flavor];
sh_a->wf->nBlockAlign = track->sub_packet_size;
goto audiobuf;
case MP_FOURCC('s', 'i', 'p', 'r'):
sh_a->wf->nAvgBytesPerSec = sipr_fl2bps[flavor];
sh_a->wf->nBlockAlign = track->coded_framesize;
goto audiobuf;
case MP_FOURCC('2', '8', '_', '8'):
sh_a->wf->nAvgBytesPerSec = 3600;
sh_a->wf->nBlockAlign = track->coded_framesize;
audiobuf:
track->audio_buf =
talloc_size(demuxer, track->sub_packet_h * track->audiopk_size);
track->audio_timestamp =
talloc_size(demuxer, track->sub_packet_h * sizeof(double));
break;
}
track->realmedia = 1;
} else if (!strcmp(track->codec_id, MKV_A_FLAC)
|| (track->a_formattag == 0xf1ac)) {
unsigned char *ptr;
int size;
talloc_free(sh_a->wf);
sh_a->wf = NULL;
if (!track->ms_compat) {
ptr = track->private_data;
size = track->private_size;
} else {
sh->format = MP_FOURCC('f', 'L', 'a', 'C');
cleanup: malloc+memset->calloc, sizeof(TYPE)->sizeof(*ptr) Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32181 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32182 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32183 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace some sizeof(type) by sizeof(*pointer) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32184 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32186 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32187 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32188 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizoef(type) by sizeof(*ptrvar). Besides being consistent with FFmpeg style, this reduces the size of a patch to rename these types to not conflict with the windows.h definitions. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32189 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32191 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32192 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(type) by sizeof(*ptrvar) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32193 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove a useless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32194 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(type) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32195 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove a useless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32196 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace several sizeof(WAVEFORMATEX) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32197 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace one more instance of sizeof(WAVEFORMATEX); fix compilation. patch by Clément Bœsch, ubitux gmail com git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32199 b3059339-0415-0410-9bf9-f77b7e298cf2 Avoid some pointless uses of sizeof() and one related cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32200 b3059339-0415-0410-9bf9-f77b7e298cf2 Merge one malloc() + memset() invocation into calloc(). git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32202 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32203 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(WAVEFORMATEX) occurrences. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32205 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace malloc+memset by calloc. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32206 b3059339-0415-0410-9bf9-f77b7e298cf2 Replace sizeof(BITMAPINFOHEADER) git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32207 b3059339-0415-0410-9bf9-f77b7e298cf2
2010-09-12 13:44:42 +02:00
ptr = track->private_data + sizeof(*sh_a->wf);
size = track->private_size - sizeof(*sh_a->wf);
}
if (size < 4 || ptr[0] != 'f' || ptr[1] != 'L' || ptr[2] != 'a'
|| ptr[3] != 'C') {
sh_a->codecdata = talloc_size(sh_a, 4);
sh_a->codecdata_len = 4;
memcpy(sh_a->codecdata, "fLaC", 4);
} else {
sh_a->codecdata = talloc_size(sh_a, size);
sh_a->codecdata_len = size;
memcpy(sh_a->codecdata, ptr, size);
}
} else if (!strcmp(track->codec_id, MKV_A_ALAC)) {
if (track->private_size && track->private_size < 10000000) {
sh_a->codecdata_len = track->private_size + 12;
sh_a->codecdata = talloc_size(sh_a, sh_a->codecdata_len);
char *data = sh_a->codecdata;
AV_WB32(data + 0, sh_a->codecdata_len);
memcpy(data + 4, "alac", 4);
AV_WB32(data + 8, 0);
memcpy(data + 12, track->private_data, track->private_size);
}
} else if (track->a_formattag == MP_FOURCC('W', 'V', 'P', 'K') ||
track->a_formattag == MP_FOURCC('T', 'R', 'H', 'D')) {
copy_audio_private_data(sh_a, track);
} else if (track->a_formattag == MP_FOURCC('T', 'T', 'A', '1')) {
sh_a->codecdata_len = 30;
sh_a->codecdata = talloc_zero_size(sh_a, sh_a->codecdata_len);
if (!sh_a->codecdata)
goto error;
char *data = sh_a->codecdata;
memcpy(data + 0, "TTA1", 4);
AV_WL16(data + 4, 1);
AV_WL16(data + 6, sh_a->channels.num);
AV_WL16(data + 8, sh_a->wf->wBitsPerSample);
AV_WL32(data + 10, track->a_osfreq);
// Bogus: last frame won't be played.
AV_WL32(data + 14, 0);
} else if (!track->ms_compat) {
goto error;
}
// Some files have broken default DefaultDuration set, which will lead to
// audio packets with incorrect timestamps. This follows FFmpeg commit
// 6158a3b, sample see FFmpeg ticket 2508.
if (sh_a->samplerate == 8000 && strcmp(track->codec_id, MKV_A_AC3) == 0)
track->default_duration = 0;
mp_set_codec_from_tag(sh);
core: redo how codecs are mapped, remove codecs.conf Use codec names instead of FourCCs to identify codecs. Rewrite how codecs are selected and initialized. Now each decoder exports a list of decoders (and the codec it supports) via add_decoders(). The order matters, and the first decoder for a given decoder is preferred over the other decoders. E.g. all ad_mpg123 decoders are preferred over ad_lavc, because it comes first in the mpcodecs_ad_drivers array. Likewise, decoders within ad_lavc that are enumerated first by libavcodec (using av_codec_next()) are preferred. (This is actually critical to select h264 software decoding by default instead of vdpau. libavcodec and ffmpeg/avconv use the same method to select decoders by default, so we hope this is sane.) The codec names follow libavcodec's codec names as defined by AVCodecDescriptor.name (see libavcodec/codec_desc.c). Some decoders have names different from the canonical codec name. The AVCodecDescriptor API is relatively new, so we need a compatibility layer for older libavcodec versions for codec names that are referenced internally, and which are different from the decoder name. (Add a configure check for that, because checking versions is getting way too messy.) demux/codec_tags.c is generated from the former codecs.conf (minus "special" decoders like vdpau, and excluding the mappings that are the same as the mappings libavformat's exported RIFF tables). It contains all the mappings from FourCCs to codec name. This is needed for demux_mkv, demux_mpg, demux_avi and demux_asf. demux_lavf will set the codec as determined by libavformat, while the other demuxers have to do this on their own, using the mp_set_audio/video_codec_from_tag() functions. Note that the sh_audio/video->format members don't uniquely identify the codec anymore, and sh->codec takes over this role. Replace the --ac/--vc/--afm/--vfm with new --vd/--ad options, which provide cover the functionality of the removed switched. Note: there's no CODECS_FLAG_FLIP flag anymore. This means some obscure container/video combinations (e.g. the sample Film_200_zygo_pro.mov) are played flipped. ffplay/avplay doesn't handle this properly either, so we don't care and blame ffmeg/libav instead.
2013-02-09 15:15:19 +01:00
return 0;
error:
MP_WARN(demuxer, "Unknown/unsupported audio "
"codec ID '%s' for track %u or missing/faulty\n"
"private codec data.\n", track->codec_id, track->tnum);
return 1;
}
static const char *mkv_sub_tag[][2] = {
{ MKV_S_VOBSUB, "dvd_subtitle" },
{ MKV_S_TEXTSSA, "ass"},
{ MKV_S_TEXTASS, "ass"},
{ MKV_S_SSA, "ass"},
{ MKV_S_ASS, "ass"},
{ MKV_S_TEXTASCII, "subrip"},
{ MKV_S_TEXTUTF8, "subrip"},
{ MKV_S_PGS, "hdmv_pgs_subtitle"},
{ MKV_S_WEBVTT_S, "webvtt-webm"},
{ MKV_S_WEBVTT_C, "webvtt-webm"},
{0}
};
static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track)
{
const char *subtitle_type = NULL;
for (int n = 0; mkv_sub_tag[n][0]; n++) {
if (strcmp(track->codec_id, mkv_sub_tag[n][0]) == 0) {
subtitle_type = mkv_sub_tag[n][1];
break;
}
}
bstr in = (bstr){track->private_data, track->private_size};
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_SUB);
if (!sh)
return 1;
track->stream = sh;
sh_sub_t *sh_s = sh->sub;
sh->demuxer_id = track->tnum;
sh->codec = subtitle_type;
bstr buffer = demux_mkv_decode(demuxer->log, track, in, 2);
if (buffer.start && buffer.start != track->private_data) {
talloc_free(track->private_data);
talloc_steal(track, buffer.start);
track->private_data = buffer.start;
track->private_size = buffer.len;
}
sh_s->extradata = talloc_size(sh, track->private_size);
memcpy(sh_s->extradata, track->private_data, track->private_size);
sh_s->extradata_len = track->private_size;
if (track->language && (strcmp(track->language, "und") != 0))
sh->lang = talloc_strdup(sh, track->language);
sh->title = talloc_strdup(sh, track->name);
sh->default_track = track->default_track;
if (!subtitle_type) {
MP_ERR(demuxer, "Subtitle type '%s' is not supported.\n",
track->codec_id);
}
return 0;
}
static int read_ebml_header(demuxer_t *demuxer)
{
stream_t *s = demuxer->stream;
if (ebml_read_id(s) != EBML_ID_EBML)
return 0;
struct ebml_ebml ebml_master = {{0}};
struct ebml_parse_ctx parse_ctx = { demuxer->log, .no_error_messages = true };
if (ebml_read_element(s, &parse_ctx, &ebml_master, &ebml_ebml_desc) < 0)
return 0;
if (ebml_master.doc_type.start == NULL) {
MP_VERBOSE(demuxer, "File has EBML header but no doctype."
" Assuming \"matroska\".\n");
} else if (bstrcmp(ebml_master.doc_type, bstr0("matroska")) != 0
&& bstrcmp(ebml_master.doc_type, bstr0("webm")) != 0) {
MP_DBG(demuxer, "no head found\n");
talloc_free(parse_ctx.talloc_ctx);
return 0;
}
if (ebml_master.doc_type_read_version > 2) {
MP_WARN(demuxer, "This looks like a Matroska file, "
"but we don't support format version %"PRIu64"\n",
ebml_master.doc_type_read_version);
talloc_free(parse_ctx.talloc_ctx);
return 0;
}
if ((ebml_master.n_ebml_read_version
&& ebml_master.ebml_read_version != EBML_VERSION)
|| (ebml_master.n_ebml_max_size_length
&& ebml_master.ebml_max_size_length > 8)
|| (ebml_master.n_ebml_max_id_length
&& ebml_master.ebml_max_id_length != 4))
{
MP_WARN(demuxer, "This looks like a Matroska file, "
"but the header has bad parameters\n");
talloc_free(parse_ctx.talloc_ctx);
return 0;
}
talloc_free(parse_ctx.talloc_ctx);
return 1;
}
static int read_mkv_segment_header(demuxer_t *demuxer, int64_t *segment_end)
{
stream_t *s = demuxer->stream;
int num_skip = 0;
if (demuxer->params)
num_skip = demuxer->params->matroska_wanted_segment;
while (!s->eof) {
if (ebml_read_id(s) != MATROSKA_ID_SEGMENT) {
MP_VERBOSE(demuxer, "segment not found\n");
return 0;
}
MP_VERBOSE(demuxer, "+ a segment...\n");
uint64_t len = ebml_read_length(s);
*segment_end = (len == EBML_UINT_INVALID) ? 0 : stream_tell(s) + len;
if (num_skip <= 0)
return 1;
num_skip--;
MP_VERBOSE(demuxer, " (skipping)\n");
if (*segment_end <= 0)
break;
if (*segment_end >= s->end_pos)
return 0;
if (!stream_seek(s, *segment_end)) {
MP_WARN(demuxer, "Failed to seek in file\n");
return 0;
}
// Segments are like concatenated Matroska files
if (!read_ebml_header(demuxer))
return 0;
}
MP_VERBOSE(demuxer, "End of file, no further segments.\n");
return 0;
}
static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
{
stream_t *s = demuxer->stream;
mkv_demuxer_t *mkv_d;
int64_t start_pos;
int64_t end_pos;
if (!read_ebml_header(demuxer))
return -1;
MP_VERBOSE(demuxer, "Found the head...\n");
if (!read_mkv_segment_header(demuxer, &end_pos))
return -1;
mkv_d = talloc_zero(demuxer, struct mkv_demuxer);
demuxer->priv = mkv_d;
mkv_d->tc_scale = 1000000;
mkv_d->segment_start = stream_tell(s);
mkv_d->segment_end = end_pos;
if (demuxer->params && demuxer->params->matroska_was_valid)
*demuxer->params->matroska_was_valid = true;
while (1) {
start_pos = stream_tell(s);
stream_peek(s, 4); // make sure we can always seek back
uint32_t id = ebml_read_id(s);
if (s->eof) {
MP_WARN(demuxer, "Unexpected end of file (no clusters found)\n");
break;
}
if (id == MATROSKA_ID_CLUSTER) {
MP_VERBOSE(demuxer, "|+ found cluster\n");
break;
}
int res = read_header_element(demuxer, id, start_pos);
if (res < 0)
return -1;
}
// Read headers that come after the first cluster (i.e. require seeking).
// Note: reading might increase ->num_headers.
// Likewise, ->headers might be reallocated.
for (int n = 0; n < mkv_d->num_headers; n++) {
struct header_elem *elem = &mkv_d->headers[n];
if (elem->parsed)
continue;
elem->parsed = true;
if (elem->id == MATROSKA_ID_CUES) {
// Read cues when they are needed, to avoid seeking on opening.
MP_VERBOSE(demuxer, "Deferring reading cues.\n");
mkv_d->deferred_cues = elem->pos;
continue;
}
MP_VERBOSE(demuxer, "Seeking to %"PRIu64" to read header element 0x%x.\n",
elem->pos, (unsigned)elem->id);
if (elem->pos >= s->end_pos) {
MP_WARN(demuxer, "SeekHead position beyond "
"end of file - incomplete file?\n");
continue;
}
if (!stream_seek(s, elem->pos)) {
MP_WARN(demuxer, "Failed to seek when reading header element.\n");
continue;
}
if (ebml_read_id(s) != elem->id) {
MP_ERR(demuxer, "Expected element 0x%x not found\n",
(unsigned int)elem->id);
continue;
}
elem->parsed = false; // don't make read_header_element skip it
int res = read_header_element(demuxer, elem->id, elem->pos);
if (res < 0)
return -1;
}
if (!stream_seek(s, start_pos)) {
MP_ERR(demuxer, "Couldn't seek back after reading headers?\n");
return -1;
}
MP_VERBOSE(demuxer, "All headers are parsed!\n");
process_tags(demuxer);
display_create_tracks(demuxer);
return 0;
}
static bool bstr_read_u8(bstr *buffer, uint8_t *out_u8)
{
if (buffer->len > 0) {
*out_u8 = buffer->start[0];
buffer->len -= 1;
buffer->start += 1;
return true;
} else {
return false;
}
}
static int demux_mkv_read_block_lacing(bstr *buffer, int *laces,
uint32_t lace_size[MAX_NUM_LACES])
{
uint32_t total = 0;
uint8_t flags, t;
int i;
/* lacing flags */
if (!bstr_read_u8(buffer, &flags))
goto error;
int type = (flags >> 1) & 0x03;
if (type == 0) { /* no lacing */
*laces = 1;
lace_size[0] = buffer->len;
} else {
if (!bstr_read_u8(buffer, &t))
goto error;
*laces = t + 1;
switch (type) {
case 1: /* xiph lacing */
for (i = 0; i < *laces - 1; i++) {
lace_size[i] = 0;
do {
if (!bstr_read_u8(buffer, &t))
goto error;
lace_size[i] += t;
} while (t == 0xFF);
total += lace_size[i];
}
lace_size[i] = buffer->len - total;
break;
case 2: /* fixed-size lacing */
for (i = 0; i < *laces; i++)
lace_size[i] = buffer->len / *laces;
break;
case 3:; /* EBML lacing */
uint64_t num = ebml_read_vlen_uint(buffer);
if (num == EBML_UINT_INVALID)
goto error;
if (num > buffer->len)
goto error;
total = lace_size[0] = num;
for (i = 1; i < *laces - 1; i++) {
int64_t snum = ebml_read_vlen_int(buffer);
if (snum == EBML_INT_INVALID)
goto error;
lace_size[i] = lace_size[i - 1] + snum;
total += lace_size[i];
}
lace_size[i] = buffer->len - total;
break;
default: abort();
}
}
total = buffer->len;
for (i = 0; i < *laces; i++) {
if (lace_size[i] > total)
goto error;
total -= lace_size[i];
}
if (total != 0)
goto error;
return 0;
error:
return 1;
}
static int64_t real_fix_timestamp(unsigned char *buf, int len, int64_t timestamp,
unsigned int format, int64_t *kf_base,
int *kf_pts)
{
if (format != MP_FOURCC('R', 'V', '3', '0') &&
format != MP_FOURCC('R', 'V', '4', '0'))
return timestamp;
if (len < 1) // invalid packet
return timestamp;
int offset = 1 + (buf[0] + 1) * 8;
if (offset + 4 > len) // invalid packet
return timestamp;
int hdr = AV_RB32(buf + offset);
int pict_type, pts;
if (format == MP_FOURCC('R', 'V', '3', '0')) {
pict_type = (hdr >> 27) & 3;
pts = (hdr >> 7) & 0x1FFF;
} else {
pict_type = (hdr >> 29) & 3;
pts = (hdr >> 6) & 0x1FFF;
}
if (pict_type != 3) {
*kf_base = timestamp;
*kf_pts = pts;
} else {
if (pict_type != 3) {
timestamp = *kf_base + ((pts - *kf_pts) & 0x1FFF);
} else {
timestamp = *kf_base - ((*kf_pts - pts) & 0x1FFF);
}
}
return timestamp;
}
static void handle_realvideo(demuxer_t *demuxer, mkv_track_t *track,
bstr data, bool keyframe)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
demux_packet_t *dp;
int64_t timestamp = mkv_d->last_pts * 1000;
dp = new_demux_packet_from(data.start, data.len);
if (mkv_d->v_skip_to_keyframe) {
dp->pts = mkv_d->last_pts;
track->rv_kf_base = 0;
track->rv_kf_pts = 0;
} else {
dp->pts =
real_fix_timestamp(dp->buffer, dp->len, timestamp,
track->stream->video->bih->biCompression,
&track->rv_kf_base, &track->rv_kf_pts) * 0.001;
}
dp->pos = mkv_d->last_filepos;
dp->keyframe = keyframe;
demuxer_add_packet(demuxer, track->stream, dp);
}
static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
bstr data, bool keyframe)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
int sps = track->sub_packet_size;
int sph = track->sub_packet_h;
int cfs = track->coded_framesize;
int w = track->audiopk_size;
int spc = track->sub_packet_cnt;
uint8_t *buffer = data.start;
uint32_t size = data.len;
demux_packet_t *dp;
if ((track->a_formattag == MP_FOURCC('2', '8', '_', '8'))
|| (track->a_formattag == MP_FOURCC('c', 'o', 'o', 'k'))
|| (track->a_formattag == MP_FOURCC('a', 't', 'r', 'c'))
|| (track->a_formattag == MP_FOURCC('s', 'i', 'p', 'r'))) {
// if(!block_bref)
// spc = track->sub_packet_cnt = 0;
switch (track->a_formattag) {
case MP_FOURCC('2', '8', '_', '8'):
for (int x = 0; x < sph / 2; x++)
memcpy(track->audio_buf + x * 2 * w + spc * cfs,
buffer + cfs * x, cfs);
break;
case MP_FOURCC('c', 'o', 'o', 'k'):
case MP_FOURCC('a', 't', 'r', 'c'):
for (int x = 0; x < w / sps; x++)
memcpy(track->audio_buf +
sps * (sph * x + ((sph + 1) / 2) * (spc & 1) +
(spc >> 1)), buffer + sps * x, sps);
break;
case MP_FOURCC('s', 'i', 'p', 'r'):
memcpy(track->audio_buf + spc * w, buffer, w);
if (spc == sph - 1) {
int n;
int bs = sph * w * 2 / 96; // nibbles per subpacket
// Perform reordering
for (n = 0; n < 38; n++) {
int j;
int i = bs * sipr_swaps[n][0];
int o = bs * sipr_swaps[n][1];
// swap nibbles of block 'i' with 'o' TODO: optimize
for (j = 0; j < bs; j++) {
int x = (i & 1) ?
(track->audio_buf[i >> 1] >> 4) :
(track->audio_buf[i >> 1] & 0x0F);
int y = (o & 1) ?
(track->audio_buf[o >> 1] >> 4) :
(track->audio_buf[o >> 1] & 0x0F);
if (o & 1)
track->audio_buf[o >> 1] =
(track->audio_buf[o >> 1] & 0x0F) | (x << 4);
else
track->audio_buf[o >> 1] =
(track->audio_buf[o >> 1] & 0xF0) | x;
if (i & 1)
track->audio_buf[i >> 1] =
(track->audio_buf[i >> 1] & 0x0F) | (y << 4);
else
track->audio_buf[i >> 1] =
(track->audio_buf[i >> 1] & 0xF0) | y;
++i;
++o;
}
}
}
break;
}
track->audio_timestamp[track->sub_packet_cnt] =
(track->ra_pts == mkv_d->last_pts) ? 0 : (mkv_d->last_pts);
track->ra_pts = mkv_d->last_pts;
if (track->sub_packet_cnt == 0)
track->audio_filepos = mkv_d->last_filepos;
if (++(track->sub_packet_cnt) == sph) {
int apk_usize = track->stream->audio->wf->nBlockAlign;
track->sub_packet_cnt = 0;
// Release all the audio packets
for (int x = 0; x < sph * w / apk_usize; x++) {
dp = new_demux_packet_from(track->audio_buf + x * apk_usize,
apk_usize);
/* Put timestamp only on packets that correspond to original
* audio packets in file */
dp->pts = (x * apk_usize % w) ? MP_NOPTS_VALUE :
track->audio_timestamp[x * apk_usize / w];
dp->pos = track->audio_filepos; // all equal
dp->keyframe = !x; // Mark first packet as keyframe
demuxer_add_packet(demuxer, track->stream, dp);
}
}
2013-04-24 21:33:33 +02:00
} else { // Not a codec that requires reordering
dp = new_demux_packet_from(buffer, size);
if (track->ra_pts == mkv_d->last_pts && !mkv_d->a_skip_to_keyframe)
dp->pts = MP_NOPTS_VALUE;
else
dp->pts = mkv_d->last_pts;
track->ra_pts = mkv_d->last_pts;
dp->pos = mkv_d->last_filepos;
dp->keyframe = keyframe;
demuxer_add_packet(demuxer, track->stream, dp);
}
}
static void mkv_seek_reset(demuxer_t *demuxer)
{
mkv_demuxer_t *mkv_d = demuxer->priv;
for (int i = 0; i < mkv_d->num_tracks; i++) {
mkv_track_t *track = mkv_d->tracks[i];
if (track->av_parser)
av_parser_close(track->av_parser);
track->av_parser = NULL;
if (track->av_parser_codec) {
avcodec_close(track->av_parser_codec);
av_free(track->av_parser_codec);
}
track->av_parser_codec = NULL;
}
}
#if NEED_WAVPACK_PARSE
// Copied from libavformat/matroskadec.c (FFmpeg 310f9dd / 2013-05-30)
// Originally added with Libav commit 9b6f47c
// License: LGPL v2.1 or later
// Author header: The FFmpeg Project (this function still came from Libav)
// Modified to use talloc, removed ffmpeg/libav specific error codes.
static int libav_parse_wavpack(mkv_track_t *track, uint8_t *src,
uint8_t **pdst, int *size)
{
uint8_t *dst = NULL;
int dstlen = 0;
int srclen = *size;
uint32_t samples;
uint16_t ver;
int offset = 0;
if (srclen < 12 || track->private_size < 2)
return -1;
ver = AV_RL16(track->private_data);
samples = AV_RL32(src);
src += 4;
srclen -= 4;
while (srclen >= 8) {
int multiblock;
uint32_t blocksize;
uint8_t *tmp;
uint32_t flags = AV_RL32(src);
uint32_t crc = AV_RL32(src + 4);
src += 8;
srclen -= 8;
multiblock = (flags & 0x1800) != 0x1800;
if (multiblock) {
if (srclen < 4)
goto fail;
blocksize = AV_RL32(src);
src += 4;
srclen -= 4;
} else {
blocksize = srclen;
}
if (blocksize > srclen)
goto fail;
tmp = talloc_realloc(track->parser_tmp, dst, uint8_t,
dstlen + blocksize + 32);
if (!tmp)
goto fail;
dst = tmp;
dstlen += blocksize + 32;
AV_WL32(dst + offset, MKTAG('w', 'v', 'p', 'k')); // tag
AV_WL32(dst + offset + 4, blocksize + 24); // blocksize - 8
AV_WL16(dst + offset + 8, ver); // version
AV_WL16(dst + offset + 10, 0); // track/index_no
AV_WL32(dst + offset + 12, 0); // total samples
AV_WL32(dst + offset + 16, 0); // block index
AV_WL32(dst + offset + 20, samples); // number of samples
AV_WL32(dst + offset + 24, flags); // flags
AV_WL32(dst + offset + 28, crc); // crc
memcpy (dst + offset + 32, src, blocksize); // block data
src += blocksize;
srclen -= blocksize;
offset += blocksize + 32;
}
*pdst = dst;
*size = dstlen;
return 0;
fail:
talloc_free(dst);
return -1;
}
#endif
static bool mkv_parse_packet(mkv_track_t *track, bstr *raw, bstr *out)
{
if (track->a_formattag == MP_FOURCC('W', 'V', 'P', 'K')) {
#if NEED_WAVPACK_PARSE
int size = raw->len;
uint8_t *parsed;
if (libav_parse_wavpack(track, raw->start, &parsed, &size) >= 0) {
out->start = parsed;
out->len = size;
*raw = (bstr){0};
return true;
}
#endif
} else if (track->codec_id && strcmp(track->codec_id, MKV_V_PRORES) == 0) {
size_t newlen = raw->len + 8;
char *data = talloc_size(track->parser_tmp, newlen);
AV_WB32(data + 0, newlen);
AV_WB32(data + 4, MKBETAG('i', 'c', 'p', 'f'));
memcpy(data + 8, raw->start, raw->len);
out->start = data;
out->len = newlen;
*raw = (bstr){0};
return true;
} else if (track->parse) {
if (!track->av_parser) {
int id = mp_codec_to_av_codec_id(track->stream->codec);
const AVCodec *codec = avcodec_find_decoder(id);
track->av_parser = av_parser_init(id);
if (codec)
track->av_parser_codec = avcodec_alloc_context3(codec);
}
if (track->av_parser && track->av_parser_codec) {
while (raw->len) {
uint8_t *data;
int size;
int len = av_parser_parse2(track->av_parser, track->av_parser_codec,
&data, &size, raw->start, raw->len,
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (len < 0)
return false;
*raw = bstr_cut(*raw, len);
if (size) {
out->start = data;
out->len = size;
return true;
}
}
return false;
}
}
// No parsing
*out = *raw;
*raw = (bstr){0};
return true;
}
struct block_info {
uint64_t duration;
bool simple, keyframe;
uint64_t timecode;
mkv_track_t *track;
bstr data;
void *alloc;
int64_t filepos;
};
static void free_block(struct block_info *block)
{
free(block->alloc);
block->alloc = NULL;
block->data = (bstr){0};
}
static void index_block(demuxer_t *demuxer, struct block_info *block)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
if (block->keyframe) {
add_block_position(demuxer, block->track, mkv_d->cluster_start,
block->timecode / mkv_d->tc_scale);
}
}
static int read_block(demuxer_t *demuxer, int64_t end, struct block_info *block)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
stream_t *s = demuxer->stream;
uint64_t num;
int16_t time;
uint64_t length;
int res = -1;
free_block(block);
length = ebml_read_length(s);
if (length > 500000000 || stream_tell(s) + length > (uint64_t)end)
goto exit;
block->alloc = malloc(length + AV_LZO_INPUT_PADDING);
2013-04-12 17:40:45 +02:00
if (!block->alloc)
goto exit;
block->data = (bstr){block->alloc, length};
block->filepos = stream_tell(s);
if (stream_read(s, block->data.start, block->data.len) != block->data.len)
goto exit;
// Parse header of the Block element
/* first byte(s): track num */
num = ebml_read_vlen_uint(&block->data);
if (num == EBML_UINT_INVALID)
goto exit;
/* time (relative to cluster time) */
if (block->data.len < 3)
goto exit;
time = block->data.start[0] << 8 | block->data.start[1];
block->data.start += 2;
block->data.len -= 2;
if (block->simple)
block->keyframe = block->data.start[0] & 0x80;
block->timecode = time * mkv_d->tc_scale + mkv_d->cluster_tc;
for (int i = 0; i < mkv_d->num_tracks; i++) {
if (mkv_d->tracks[i]->tnum == num) {
block->track = mkv_d->tracks[i];
break;
}
}
if (!block->track) {
res = 0;
goto exit;
}
res = 1;
exit:
if (res <= 0)
free_block(block);
return res;
}
static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
int laces;
double current_pts;
bstr data = block_info->data;
bool keyframe = block_info->keyframe;
uint64_t block_duration = block_info->duration;
uint64_t tc = block_info->timecode;
mkv_track_t *track = block_info->track;
struct sh_stream *stream = track->stream;
uint32_t lace_size[MAX_NUM_LACES];
bool use_this_block = tc >= mkv_d->skip_to_timecode;
if (!demuxer_stream_is_selected(demuxer, stream))
return 0;
if (demux_mkv_read_block_lacing(&data, &laces, lace_size)) {
MP_ERR(demuxer, "Bad input [lacing]\n");
return 0;
}
current_pts = tc / 1e9;
if (track->type == MATROSKA_TRACK_AUDIO) {
use_this_block = 1;
if (mkv_d->a_skip_to_keyframe)
use_this_block = keyframe;
if (mkv_d->v_skip_to_keyframe)
use_this_block = 0;
} else if (track->type == MATROSKA_TRACK_SUBTITLE) {
if (!use_this_block && mkv_d->subtitle_preroll)
use_this_block = 1;
if (use_this_block) {
if (mkv_d->subtitle_preroll) {
mkv_d->subtitle_preroll--;
} else {
// This could overflow the demuxer queue.
if (mkv_d->a_skip_to_keyframe || mkv_d->v_skip_to_keyframe)
use_this_block = 0;
}
}
if (use_this_block) {
if (laces > 1) {
MP_WARN(demuxer, "Subtitles use Matroska "
2013-04-24 21:33:33 +02:00
"lacing. This is abnormal and not supported.\n");
use_this_block = 0;
}
}
} else if (track->type == MATROSKA_TRACK_VIDEO) {
if (mkv_d->v_skip_to_keyframe)
use_this_block &= keyframe;
}
if (use_this_block) {
mkv_d->last_pts = current_pts;
mkv_d->last_filepos = block_info->filepos;
int p = 0;
for (int i = 0; i < laces; i++) {
bstr block = bstr_splice(data, 0, lace_size[i]);
if (stream->type == STREAM_VIDEO && track->realmedia)
handle_realvideo(demuxer, track, block, keyframe);
else if (stream->type == STREAM_AUDIO && track->realmedia)
handle_realaudio(demuxer, track, block, keyframe);
else {
bstr raw = demux_mkv_decode(demuxer->log, track, block, 1);
bstr buffer;
while (raw.start && mkv_parse_packet(track, &raw, &buffer)) {
demux_packet_t *dp =
new_demux_packet_from(buffer.start, buffer.len);
dp->keyframe = keyframe;
/* If default_duration is 0, assume no pts value is known
* for packets after the first one (rather than all pts
* values being the same) */
if (p == 0 || track->default_duration)
dp->pts = mkv_d->last_pts + p * track->default_duration;
p++;
if (track->ms_compat)
MPSWAP(double, dp->pts, dp->dts);
dp->duration = block_duration / 1e9;
demuxer_add_packet(demuxer, stream, dp);
}
talloc_free_children(track->parser_tmp);
}
data = bstr_cut(data, lace_size[i]);
}
if (stream->type == STREAM_VIDEO) {
mkv_d->v_skip_to_keyframe = 0;
mkv_d->skip_to_timecode = 0;
mkv_d->subtitle_preroll = 0;
} else if (stream->type == STREAM_AUDIO) {
mkv_d->a_skip_to_keyframe = 0;
}
return 1;
}
return 0;
}
static int read_block_group(demuxer_t *demuxer, int64_t end,
struct block_info *block)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
stream_t *s = demuxer->stream;
*block = (struct block_info){ .keyframe = true };
while (stream_tell(s) < end) {
switch (ebml_read_id(s)) {
case MATROSKA_ID_BLOCKDURATION:
block->duration = ebml_read_uint(s);
if (block->duration == EBML_UINT_INVALID)
goto error;
block->duration *= mkv_d->tc_scale;
break;
case MATROSKA_ID_BLOCK:
if (read_block(demuxer, end, block) < 0)
goto error;
break;
case MATROSKA_ID_REFERENCEBLOCK:;
int64_t num = ebml_read_int(s);
if (num == EBML_INT_INVALID)
goto error;
if (num)
block->keyframe = false;
break;
case MATROSKA_ID_CLUSTER:
case EBML_ID_INVALID:
goto error;
default:
if (ebml_read_skip(demuxer->log, end, s) != 0)
goto error;
break;
}
}
return block->data.start ? 1 : 0;
error:
free_block(block);
return -1;
}
static int read_next_block(demuxer_t *demuxer, struct block_info *block)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
stream_t *s = demuxer->stream;
while (1) {
while (stream_tell(s) < mkv_d->cluster_end) {
2013-04-12 14:57:06 +02:00
int64_t start_filepos = stream_tell(s);
switch (ebml_read_id(s)) {
2013-04-12 14:57:06 +02:00
case MATROSKA_ID_TIMECODE: {
uint64_t num = ebml_read_uint(s);
2013-04-12 14:57:06 +02:00
if (num == EBML_UINT_INVALID)
goto find_next_cluster;
mkv_d->cluster_tc = num * mkv_d->tc_scale;
break;
}
2013-04-12 14:57:06 +02:00
case MATROSKA_ID_BLOCKGROUP: {
int64_t end = ebml_read_length(s);
2013-04-12 14:57:06 +02:00
end += stream_tell(s);
if (end > mkv_d->cluster_end)
goto find_next_cluster;
2013-04-12 14:57:06 +02:00
int res = read_block_group(demuxer, end, block);
if (res < 0)
goto find_next_cluster;
if (res > 0)
return 1;
break;
}
2013-04-12 14:57:06 +02:00
case MATROSKA_ID_SIMPLEBLOCK: {
*block = (struct block_info){ .simple = true };
int res = read_block(demuxer, mkv_d->cluster_end, block);
2013-04-12 14:57:06 +02:00
if (res < 0)
goto find_next_cluster;
if (res > 0)
return 1;
break;
}
2013-04-12 14:57:06 +02:00
case MATROSKA_ID_CLUSTER:
mkv_d->cluster_start = start_filepos;
goto next_cluster;
2013-04-12 14:57:06 +02:00
case EBML_ID_INVALID:
goto find_next_cluster;
2013-04-12 14:57:06 +02:00
default: ;
if (ebml_read_skip(demuxer->log, mkv_d->cluster_end, s) != 0)
2013-04-12 14:57:06 +02:00
goto find_next_cluster;
break;
}
}
demux_mkv: improve robustness against broken files Fixes test7.mkv from the Matroska test file collection, as well as some real broken files I've found in the wild. (Unfortunately, true recovery requires resetting the decoders and playback state with a manual seek, but it's still better than just exiting.) If there are broken EBML elements, try harder to skip them correctly. Do this by searching for the next cluster element. The cluster element intentionally has a long ID, so it's a suitable element for resynchronizing (mkvmerge does something similar). We know that data is corrupt if the ID or length fields of an element are malformed. Additionally, if skipping an unknown element goes past the end of the file, we assume it's corrupt and undo the seek. Do this because it often happens that corrupt data is interpreted as correct EBML elements. Since these elements will have a ridiculous values in their length fields due to the large value range that is possible (0-2^56-2), they will go past the end of the file. So instead of skipping them (which would result in playback termination), try to find the next cluster instead. (We still skip unknown elements that are within the file, as this is needed for correct operation. Also, we first execute the seek, because we don't really know where the file ends. Doing it this way is better for unseekable streams too, because it will still work in the non-error case.) This is done as special case in the packet reading function only. On the other hand, that's the only part of the file that's read after initialization is done.
2013-03-28 00:01:17 +01:00
find_next_cluster:
mkv_d->cluster_end = 0;
demux_mkv: improve robustness against broken files Fixes test7.mkv from the Matroska test file collection, as well as some real broken files I've found in the wild. (Unfortunately, true recovery requires resetting the decoders and playback state with a manual seek, but it's still better than just exiting.) If there are broken EBML elements, try harder to skip them correctly. Do this by searching for the next cluster element. The cluster element intentionally has a long ID, so it's a suitable element for resynchronizing (mkvmerge does something similar). We know that data is corrupt if the ID or length fields of an element are malformed. Additionally, if skipping an unknown element goes past the end of the file, we assume it's corrupt and undo the seek. Do this because it often happens that corrupt data is interpreted as correct EBML elements. Since these elements will have a ridiculous values in their length fields due to the large value range that is possible (0-2^56-2), they will go past the end of the file. So instead of skipping them (which would result in playback termination), try to find the next cluster instead. (We still skip unknown elements that are within the file, as this is needed for correct operation. Also, we first execute the seek, because we don't really know where the file ends. Doing it this way is better for unseekable streams too, because it will still work in the non-error case.) This is done as special case in the packet reading function only. On the other hand, that's the only part of the file that's read after initialization is done.
2013-03-28 00:01:17 +01:00
for (;;) {
mkv_d->cluster_start = stream_tell(s);
uint32_t id = ebml_read_id(s);
demux_mkv: improve robustness against broken files Fixes test7.mkv from the Matroska test file collection, as well as some real broken files I've found in the wild. (Unfortunately, true recovery requires resetting the decoders and playback state with a manual seek, but it's still better than just exiting.) If there are broken EBML elements, try harder to skip them correctly. Do this by searching for the next cluster element. The cluster element intentionally has a long ID, so it's a suitable element for resynchronizing (mkvmerge does something similar). We know that data is corrupt if the ID or length fields of an element are malformed. Additionally, if skipping an unknown element goes past the end of the file, we assume it's corrupt and undo the seek. Do this because it often happens that corrupt data is interpreted as correct EBML elements. Since these elements will have a ridiculous values in their length fields due to the large value range that is possible (0-2^56-2), they will go past the end of the file. So instead of skipping them (which would result in playback termination), try to find the next cluster instead. (We still skip unknown elements that are within the file, as this is needed for correct operation. Also, we first execute the seek, because we don't really know where the file ends. Doing it this way is better for unseekable streams too, because it will still work in the non-error case.) This is done as special case in the packet reading function only. On the other hand, that's the only part of the file that's read after initialization is done.
2013-03-28 00:01:17 +01:00
if (id == MATROSKA_ID_CLUSTER)
break;
if (s->eof)
return -1;
if (id == EBML_ID_EBML && stream_tell(s) >= mkv_d->segment_end) {
// Appended segment - don't use its clusters, consider this EOF.
stream_seek(s, stream_tell(s) - 4);
return -1;
}
// For the sake of robustness, consider even unknown level 1
// elements the same as unknown/broken IDs.
if (!ebml_is_mkv_level1_id(id) ||
ebml_read_skip(demuxer->log, -1, s) != 0)
{
ebml_resync_cluster(demuxer->log, s);
}
}
next_cluster:
mkv_d->cluster_end = ebml_read_length(s);
// mkv files for "streaming" can have this legally
if (mkv_d->cluster_end != EBML_UINT_INVALID)
mkv_d->cluster_end += stream_tell(s);
}
}
static int demux_mkv_fill_buffer(demuxer_t *demuxer)
{
for (;;) {
int res;
struct block_info block;
res = read_next_block(demuxer, &block);
if (res < 0)
return 0;
if (res > 0) {
index_block(demuxer, &block);
res = handle_block(demuxer, &block);
free_block(&block);
if (res > 0)
return 1;
}
}
}
static mkv_index_t *get_highest_index_entry(struct demuxer *demuxer)
{
struct mkv_demuxer *mkv_d = demuxer->priv;
assert(!mkv_d->index_complete); // would require separate code
mkv_index_t *index = NULL;
for (int n = 0; n < mkv_d->num_tracks; n++) {
int n_index = mkv_d->tracks[n]->last_index_entry;
if (n_index >= 0) {
mkv_index_t *index2 = &mkv_d->indexes[n_index];
if (!index || index2->filepos > index->filepos)
index = index2;
}
}
return index;
}
static int create_index_until(struct demuxer *demuxer, uint64_t timecode)
{
struct mkv_demuxer *mkv_d = demuxer->priv;
struct stream *s = demuxer->stream;
read_deferred_cues(demuxer);
if (mkv_d->index_complete)
return 0;
mkv_index_t *index = get_highest_index_entry(demuxer);
if (!index || index->timecode * mkv_d->tc_scale < timecode) {
if (index)
stream_seek(s, index->filepos);
MP_VERBOSE(demuxer, "creating index until TC %" PRIu64 "\n", timecode);
for (;;) {
int res;
struct block_info block;
res = read_next_block(demuxer, &block);
if (res < 0)
break;
if (res > 0) {
index_block(demuxer, &block);
free_block(&block);
}
index = get_highest_index_entry(demuxer);
if (index && index->timecode * mkv_d->tc_scale >= timecode)
break;
}
}
if (!mkv_d->indexes) {
MP_WARN(demuxer, "no target for seek found\n");
return -1;
}
return 0;
}
static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
int64_t target_timecode, int flags)
{
struct mkv_demuxer *mkv_d = demuxer->priv;
struct mkv_index *index = NULL;
/* Find the entry in the index closest to the target timecode in the
* give direction. If there are no such entries - we're trying to seek
* backward from a target time before the first entry or forward from a
* target time after the last entry - then still seek to the first/last
* entry if that's further in the direction wanted than mkv_d->last_pts.
*/
int64_t min_diff = target_timecode - (int64_t)(mkv_d->last_pts * 1e9 + 0.5);
if (flags & SEEK_BACKWARD)
min_diff = -min_diff;
min_diff = FFMAX(min_diff, 1);
for (int i = 0; i < mkv_d->num_indexes; i++) {
if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) {
int64_t diff =
target_timecode -
(int64_t) (mkv_d->indexes[i].timecode * mkv_d->tc_scale);
if (flags & SEEK_BACKWARD)
diff = -diff;
if (diff <= 0) {
if (min_diff <= 0 && diff <= min_diff)
continue;
} else if (diff >= min_diff)
continue;
min_diff = diff;
index = mkv_d->indexes + i;
}
}
if (index) { /* We've found an entry. */
uint64_t seek_pos = index->filepos;
if (flags & SEEK_SUBPREROLL) {
uint64_t prev_target = 0;
for (int i = 0; i < mkv_d->num_indexes; i++) {
if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) {
uint64_t index_pos = mkv_d->indexes[i].filepos;
if (index_pos > prev_target && index_pos < seek_pos)
prev_target = index_pos;
}
}
if (prev_target)
seek_pos = prev_target;
}
mkv_d->cluster_end = 0;
stream_seek(demuxer->stream, seek_pos);
}
return index;
}
static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
{
mkv_demuxer_t *mkv_d = demuxer->priv;
int64_t old_pos = stream_tell(demuxer->stream);
uint64_t v_tnum = -1;
uint64_t a_tnum = -1;
bool st_active[STREAM_TYPE_COUNT] = {0};
mkv_seek_reset(demuxer);
for (int i = 0; i < mkv_d->num_tracks; i++) {
mkv_track_t *track = mkv_d->tracks[i];
if (demuxer_stream_is_selected(demuxer, track->stream)) {
st_active[track->stream->type] = true;
if (track->type == MATROSKA_TRACK_VIDEO)
v_tnum = track->tnum;
if (track->type == MATROSKA_TRACK_AUDIO)
a_tnum = track->tnum;
}
}
mkv_d->subtitle_preroll = NUM_SUB_PREROLL_PACKETS;
if (!st_active[STREAM_SUB] || !st_active[STREAM_VIDEO])
flags &= ~SEEK_SUBPREROLL;
Add improved relative seek mode When the new mode is active relative seeks are converted to absolute ones (current video pts + relative seek amount) and forward/backward flag before being sent to the demuxer. This mode is used if the demuxer has set the accurate_seek field in the demuxer struct and there is a video stream. At the moment the mkv and lavf demuxers enable the flag. This change is useful for later Matroska ordered chapter support (and for more general timelime editing), but also fixes problems in existing functionality. The main problem with the old mode, where relative seeks are passed directly to the demuxer, is that the user wants to seek relative to the currently displayed position but the demuxer does not know what that position is. There can be an arbitrary amount of buffering between the demuxer read position and what is displayed on the screen. In some situations this makes small seeks fail to move backward at all (especially visible at high playback speed, when audio needs to be demuxed and decoded further ahead to fill the output buffers after resampling). Some container formats that can be used with the lavf demuxer do not always have reliable timestamps that could be used for unambiguous absolute seeking. However I made the demuxer always enable the new mode because it already converted all seeks to absolute ones before sending them to libavformat, so cases without reliable absolute seeks were failing already and this should only improve the working cases.
2009-03-19 04:25:12 +01:00
if (!(flags & (SEEK_BACKWARD | SEEK_FORWARD))) {
if (flags & SEEK_ABSOLUTE || rel_seek_secs < 0) {
Add improved relative seek mode When the new mode is active relative seeks are converted to absolute ones (current video pts + relative seek amount) and forward/backward flag before being sent to the demuxer. This mode is used if the demuxer has set the accurate_seek field in the demuxer struct and there is a video stream. At the moment the mkv and lavf demuxers enable the flag. This change is useful for later Matroska ordered chapter support (and for more general timelime editing), but also fixes problems in existing functionality. The main problem with the old mode, where relative seeks are passed directly to the demuxer, is that the user wants to seek relative to the currently displayed position but the demuxer does not know what that position is. There can be an arbitrary amount of buffering between the demuxer read position and what is displayed on the screen. In some situations this makes small seeks fail to move backward at all (especially visible at high playback speed, when audio needs to be demuxed and decoded further ahead to fill the output buffers after resampling). Some container formats that can be used with the lavf demuxer do not always have reliable timestamps that could be used for unambiguous absolute seeking. However I made the demuxer always enable the new mode because it already converted all seeks to absolute ones before sending them to libavformat, so cases without reliable absolute seeks were failing already and this should only improve the working cases.
2009-03-19 04:25:12 +01:00
flags |= SEEK_BACKWARD;
} else {
Add improved relative seek mode When the new mode is active relative seeks are converted to absolute ones (current video pts + relative seek amount) and forward/backward flag before being sent to the demuxer. This mode is used if the demuxer has set the accurate_seek field in the demuxer struct and there is a video stream. At the moment the mkv and lavf demuxers enable the flag. This change is useful for later Matroska ordered chapter support (and for more general timelime editing), but also fixes problems in existing functionality. The main problem with the old mode, where relative seeks are passed directly to the demuxer, is that the user wants to seek relative to the currently displayed position but the demuxer does not know what that position is. There can be an arbitrary amount of buffering between the demuxer read position and what is displayed on the screen. In some situations this makes small seeks fail to move backward at all (especially visible at high playback speed, when audio needs to be demuxed and decoded further ahead to fill the output buffers after resampling). Some container formats that can be used with the lavf demuxer do not always have reliable timestamps that could be used for unambiguous absolute seeking. However I made the demuxer always enable the new mode because it already converted all seeks to absolute ones before sending them to libavformat, so cases without reliable absolute seeks were failing already and this should only improve the working cases.
2009-03-19 04:25:12 +01:00
flags |= SEEK_FORWARD;
}
Add improved relative seek mode When the new mode is active relative seeks are converted to absolute ones (current video pts + relative seek amount) and forward/backward flag before being sent to the demuxer. This mode is used if the demuxer has set the accurate_seek field in the demuxer struct and there is a video stream. At the moment the mkv and lavf demuxers enable the flag. This change is useful for later Matroska ordered chapter support (and for more general timelime editing), but also fixes problems in existing functionality. The main problem with the old mode, where relative seeks are passed directly to the demuxer, is that the user wants to seek relative to the currently displayed position but the demuxer does not know what that position is. There can be an arbitrary amount of buffering between the demuxer read position and what is displayed on the screen. In some situations this makes small seeks fail to move backward at all (especially visible at high playback speed, when audio needs to be demuxed and decoded further ahead to fill the output buffers after resampling). Some container formats that can be used with the lavf demuxer do not always have reliable timestamps that could be used for unambiguous absolute seeking. However I made the demuxer always enable the new mode because it already converted all seeks to absolute ones before sending them to libavformat, so cases without reliable absolute seeks were failing already and this should only improve the working cases.
2009-03-19 04:25:12 +01:00
}
// Adjust the target a little bit to catch cases where the target position
// specifies a keyframe with high, but not perfect, precision.
rel_seek_secs += flags & SEEK_FORWARD ? -0.005 : 0.005;
if (!(flags & SEEK_FACTOR)) { /* time in secs */
mkv_index_t *index = NULL;
if (!(flags & SEEK_ABSOLUTE)) /* relative seek */
rel_seek_secs += mkv_d->last_pts;
rel_seek_secs = FFMAX(rel_seek_secs, 0);
int64_t target_timecode = rel_seek_secs * 1e9 + 0.5;
if (create_index_until(demuxer, target_timecode) >= 0) {
int seek_id = st_active[STREAM_VIDEO] ? v_tnum : a_tnum;
index = seek_with_cues(demuxer, seek_id, target_timecode, flags);
if (!index)
index = seek_with_cues(demuxer, -1, target_timecode, flags);
}
if (!index)
stream_seek(demuxer->stream, old_pos);
mkv_d->v_skip_to_keyframe = st_active[STREAM_VIDEO];
mkv_d->a_skip_to_keyframe = st_active[STREAM_AUDIO];
if (flags & SEEK_FORWARD) {
mkv_d->skip_to_timecode = target_timecode;
} else {
mkv_d->skip_to_timecode = index ? index->timecode * mkv_d->tc_scale
: 0;
}
demux_mkv_fill_buffer(demuxer);
} else if (!(flags & SEEK_ABSOLUTE))
MP_VERBOSE(demuxer, "seek unsupported flags\n");
else {
stream_t *s = demuxer->stream;
uint64_t target_filepos;
mkv_index_t *index = NULL;
int i;
read_deferred_cues(demuxer);
if (!mkv_d->index_complete) { /* not implemented without index */
MP_VERBOSE(demuxer, "seek unsupported flags\n");
stream_seek(s, old_pos);
return;
}
target_filepos = (uint64_t) (s->end_pos * rel_seek_secs);
for (i = 0; i < mkv_d->num_indexes; i++)
if (mkv_d->indexes[i].tnum == v_tnum)
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) {
stream_seek(s, old_pos);
return;
}
mkv_d->cluster_end = 0;
stream_seek(s, index->filepos);
mkv_d->v_skip_to_keyframe = st_active[STREAM_VIDEO];
mkv_d->a_skip_to_keyframe = st_active[STREAM_AUDIO];
mkv_d->skip_to_timecode = index->timecode * mkv_d->tc_scale;
demux_mkv_fill_buffer(demuxer);
}
}
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;
default:
return DEMUXER_CTRL_NOTIMPL;
}
}
static void mkv_free(struct demuxer *demuxer)
{
struct mkv_demuxer *mkv_d = demuxer->priv;
if (!mkv_d)
return;
mkv_seek_reset(demuxer);
for (int i = 0; i < mkv_d->num_tracks; i++)
demux_mkv_free_trackentry(mkv_d->tracks[i]);
free(mkv_d->indexes);
}
const demuxer_desc_t demuxer_desc_matroska = {
.name = "mkv",
.desc = "Matroska",
.type = DEMUXER_TYPE_MATROSKA,
.open = demux_mkv_open,
.fill_buffer = demux_mkv_fill_buffer,
.close = mkv_free,
.seek = demux_mkv_seek,
.control = demux_mkv_control
};
bool demux_matroska_uid_cmp(struct matroska_segment_uid *a,
struct matroska_segment_uid *b)
{
return (!memcmp(a->segment, b->segment, 16) &&
a->edition == b->edition);
}