demux_mkv: improve video duration detection heuristic

Extend the --demuxer-mkv-probe-video-duration behavior to work with
files that are partial and are missing an index. Do this by finding a
cluster 10MB before the end of the file, and if that fails, just read
the entire file. This is actually pretty trivial to do and requires only
5 lines of code.

Also add a mode that always reads the entire file to estimate the video
duration.
This commit is contained in:
wm4 2015-07-09 22:47:41 +02:00
parent d23d9dc394
commit f3d06e3e91
2 changed files with 35 additions and 17 deletions

View File

@ -2137,13 +2137,18 @@ Demuxer
``--demuxer-mkv-subtitle-preroll-secs=<value>``
See ``--demuxer-mkv-subtitle-preroll``.
``--demuxer-mkv-probe-video-duration``
``--demuxer-mkv-probe-video-duration=<yes|no|full>``
When opening the file, seek to the end of it, and check what timestamp the
last video packet has, and report that as file duration. This is strictly
for compatibility with Haali only. In this mode, it's possible that opening
will be slower (especially when playing over http), or that behavior with
broken files is much worse. So don't use this option.
The ``yes`` mode merely uses the index and reads a small number of blocks
from the end of the file. The ``full`` mode actually traverses the entire
file and can make a reliable estimate even without an index present (such
as partial files).
``--demuxer-mkv-fix-timestamps=<yes|no>``
Fix rounded Matroska timestamps (enabled by default). Matroska usually
stores timestamps rounded to milliseconds. This means timestamps jitter

View File

@ -198,7 +198,8 @@ const struct m_sub_options demux_mkv_conf = {
OPT_FLAG("subtitle-preroll", subtitle_preroll, 0),
OPT_DOUBLE("subtitle-preroll-secs", subtitle_preroll_secs,
M_OPT_MIN, .min = 0),
OPT_FLAG("probe-video-duration", probe_duration, 0),
OPT_CHOICE("probe-video-duration", probe_duration, 0,
({"no", 0}, {"yes", 1}, {"full", 2})),
OPT_FLAG("fix-timestamps", fix_timestamps, 0),
{0}
},
@ -2794,23 +2795,32 @@ static void probe_last_timestamp(struct demuxer *demuxer)
if (v_tnum < 0)
return;
read_deferred_cues(demuxer);
// In full mode, we start reading data from the current file position,
// which works because this function is called after headers are parsed.
if (demuxer->opts->demux_mkv->probe_duration != 2) {
read_deferred_cues(demuxer);
if (mkv_d->index_complete) {
// Find last cluster that still has video packets
int64_t target = 0;
for (size_t i = 0; i < mkv_d->num_indexes; i++) {
struct mkv_index *cur = &mkv_d->indexes[i];
if (cur->tnum == v_tnum)
target = MPMAX(target, cur->filepos);
}
if (!target)
return;
if (!mkv_d->index_complete)
return;
// Find last cluster that still has video packets
int64_t target = 0;
for (size_t i = 0; i < mkv_d->num_indexes; i++) {
struct mkv_index *cur = &mkv_d->indexes[i];
if (cur->tnum == v_tnum)
target = MPMAX(target, cur->filepos);
if (!stream_seek(demuxer->stream, target))
return;
} else {
// No index -> just try to find a random cluster towards file end.
int64_t size = 0;
stream_control(demuxer->stream, STREAM_CTRL_GET_SIZE, &size);
stream_seek(demuxer->stream, MPMAX(size - 10 * 1024 * 1024, 0));
if (ebml_resync_cluster(mp_null_log, demuxer->stream) < 0)
stream_seek(demuxer->stream, old_pos); // full scan otherwise
}
}
if (!target)
return;
if (!stream_seek(demuxer->stream, target))
return;
int64_t last_ts[STREAM_TYPE_COUNT] = {0};
while (1) {
@ -2829,6 +2839,9 @@ static void probe_last_timestamp(struct demuxer *demuxer)
}
}
if (!last_ts[STREAM_VIDEO])
last_ts[STREAM_VIDEO] = mkv_d->cluster_tc;
if (last_ts[STREAM_VIDEO])
mkv_d->duration = last_ts[STREAM_VIDEO] / 1e9;