diff options
-rw-r--r-- | core/timeline/tl_matroska.c | 123 | ||||
-rw-r--r-- | demux/demux.h | 2 | ||||
-rw-r--r-- | demux/demux_mkv.c | 61 |
3 files changed, 133 insertions, 53 deletions
diff --git a/core/timeline/tl_matroska.c b/core/timeline/tl_matroska.c index 75530116aa..98ba635bec 100644 --- a/core/timeline/tl_matroska.c +++ b/core/timeline/tl_matroska.c @@ -114,16 +114,15 @@ static char **find_files(const char *original_file, const char *suffix) } static struct demuxer *open_demuxer(struct stream *stream, - struct MPContext *mpctx, char *filename, unsigned char uid_map[][16]) + struct MPContext *mpctx, char *filename, struct demuxer_params *params) { return demux_open_withparams(&mpctx->opts, stream, DEMUXER_TYPE_MATROSKA, NULL, mpctx->opts.audio_id, - mpctx->opts.video_id, mpctx->opts.sub_id, filename, - &(struct demuxer_params){.matroska_wanted_uids = uid_map}); + mpctx->opts.video_id, mpctx->opts.sub_id, filename, params); } static int enable_cache(struct MPContext *mpctx, struct stream **stream, - struct demuxer **demuxer, unsigned char uid_map[][16]) + struct demuxer **demuxer, struct demuxer_params *params) { struct MPOpts *opts = &mpctx->opts; @@ -147,7 +146,7 @@ static int enable_cache(struct MPContext *mpctx, struct stream **stream, opts->stream_cache_min_percent, opts->stream_cache_seek_min_percent); - *demuxer = open_demuxer(*stream, mpctx, filename, uid_map); + *demuxer = open_demuxer(*stream, mpctx, filename, params); if (!*demuxer) { talloc_free(filename); free_stream(*stream); @@ -158,6 +157,68 @@ static int enable_cache(struct MPContext *mpctx, struct stream **stream, return 1; } +// segment = get Nth segment of a multi-segment file +static bool check_file_seg(struct MPContext *mpctx, struct demuxer **sources, + int num_sources, unsigned char uid_map[][16], + char *filename, int segment) +{ + bool was_valid = false; + struct demuxer_params params = { + .matroska_wanted_uids = uid_map, + .matroska_wanted_segment = segment, + .matroska_was_valid = &was_valid, + }; + int format = 0; + struct stream *s = open_stream(filename, &mpctx->opts, &format); + if (!s) + return false; + struct demuxer *d = open_demuxer(s, mpctx, filename, ¶ms); + + if (!d) { + free_stream(s); + return was_valid; + } + if (d->file_format == DEMUXER_TYPE_MATROSKA) { + for (int i = 1; i < num_sources; i++) { + if (sources[i]) + continue; + if (!memcmp(uid_map[i], d->matroska_data.segment_uid, 16)) { + mp_msg(MSGT_CPLAYER, MSGL_INFO, "Match for source %d: %s\n", + i, d->filename); + + if (enable_cache(mpctx, &s, &d, ¶ms) < 0) + continue; + + sources[i] = d; + return true; + } + } + } + free_demuxer(d); + free_stream(s); + return was_valid; +} + +static void check_file(struct MPContext *mpctx, struct demuxer **sources, + int num_sources, unsigned char uid_map[][16], + char *filename, int first) +{ + for (int segment = first; ; segment++) { + if (!check_file_seg(mpctx, sources, num_sources, uid_map, + filename, segment)) + break; + } +} + +static bool missing(struct demuxer **sources, int num_sources) +{ + for (int i = 0; i < num_sources; i++) { + if (!sources[i]) + return true; + } + return false; +} + static int find_ordered_chapter_sources(struct MPContext *mpctx, struct demuxer **sources, int num_sources, @@ -166,6 +227,7 @@ static int find_ordered_chapter_sources(struct MPContext *mpctx, int num_filenames = 0; char **filenames = NULL; if (num_sources > 1) { + char *main_filename = mpctx->demuxer->filename; mp_msg(MSGT_CPLAYER, MSGL_INFO, "This file references data from " "other sources.\n"); if (mpctx->demuxer->stream->type != STREAMTYPE_FILE) { @@ -174,59 +236,34 @@ static int find_ordered_chapter_sources(struct MPContext *mpctx, } else { mp_msg(MSGT_CPLAYER, MSGL_INFO, "Will scan other files in the " "same directory to find referenced sources.\n"); - filenames = find_files(mpctx->demuxer->filename, ".mkv"); + filenames = find_files(main_filename, ".mkv"); num_filenames = MP_TALLOC_ELEMS(filenames); } + // Possibly get further segments appended to the first segment + check_file(mpctx, sources, num_sources, uid_map, main_filename, 1); } - int num_left = num_sources - 1; - for (int i = 0; i < num_filenames && num_left > 0; i++) { + for (int i = 0; i < num_filenames; i++) { + if (!missing(sources, num_sources)) + break; mp_msg(MSGT_CPLAYER, MSGL_INFO, "Checking file %s\n", filenames[i]); - int format = 0; - struct stream *s = open_stream(filenames[i], &mpctx->opts, &format); - if (!s) - continue; - struct demuxer *d = open_demuxer(s, mpctx, filenames[i], uid_map); - - if (!d) { - free_stream(s); - continue; - } - if (d->file_format == DEMUXER_TYPE_MATROSKA) { - for (int i = 1; i < num_sources; i++) { - if (sources[i]) - continue; - if (!memcmp(uid_map[i], d->matroska_data.segment_uid, 16)) { - mp_msg(MSGT_CPLAYER, MSGL_INFO,"Match for source %d: %s\n", - i, d->filename); - - if (enable_cache(mpctx, &s, &d, uid_map) < 0) - continue; - - sources[i] = d; - num_left--; - goto match; - } - } - } - free_demuxer(d); - free_stream(s); - continue; - match: - ; + check_file(mpctx, sources, num_sources, uid_map, filenames[i], 0); } + talloc_free(filenames); - if (num_left) { + if (missing(sources, num_sources)) { mp_msg(MSGT_CPLAYER, MSGL_ERR, "Failed to find ordered chapter part!\n" "There will be parts MISSING from the video!\n"); - for (int i = 1, j = 1; i < num_sources; i++) + int j = 1; + for (int i = 1; i < num_sources; i++) if (sources[i]) { sources[j] = sources[i]; memcpy(uid_map[j], uid_map[i], 16); j++; } + num_sources = j; } - return num_sources - num_left; + return num_sources; } void build_ordered_chapter_timeline(struct MPContext *mpctx) diff --git a/demux/demux.h b/demux/demux.h index c922dc8bb5..fb695e6a45 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -211,6 +211,8 @@ typedef struct demux_attachment struct demuxer_params { unsigned char (*matroska_wanted_uids)[16]; + int matroska_wanted_segment; + bool *matroska_was_valid; }; typedef struct demuxer { diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c index 00f5a1a722..bd6e725251 100644 --- a/demux/demux_mkv.c +++ b/demux/demux_mkv.c @@ -1656,13 +1656,10 @@ static void mkv_free(struct demuxer *demuxer) free(mkv_d->indexes); } -static int demux_mkv_open(demuxer_t *demuxer) +static int read_ebml_header(demuxer_t *demuxer) { stream_t *s = demuxer->stream; - mkv_demuxer_t *mkv_d; - mkv_track_t *track; - stream_seek(s, s->start_pos); if (ebml_read_id(s, NULL) != EBML_ID_EBML) return 0; struct ebml_ebml ebml_master = {}; @@ -1698,21 +1695,65 @@ static int demux_mkv_open(demuxer_t *demuxer) } talloc_free(parse_ctx.talloc_ctx); - mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Found the head...\n"); + return 1; +} - if (ebml_read_id(s, NULL) != MATROSKA_ID_SEGMENT) { - mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] but no segment :(\n"); - return 0; +static int read_mkv_segment_header(demuxer_t *demuxer) +{ + 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, NULL) != MATROSKA_ID_SEGMENT) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] segment not found\n"); + return 0; + } + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] + a segment...\n"); + uint64_t len = ebml_read_length(s, NULL); + if (num_skip <= 0) + return 1; + num_skip--; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] (skipping)\n"); + if (len == EBML_UINT_INVALID) + break; + if (!stream_seek(s, stream_tell(s) + len)) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Failed to seek in file\n"); + return 0; + } + // Segments are like concatenated Matroska files + if (!read_ebml_header(demuxer)) + return 0; } - ebml_read_length(s, NULL); /* return bytes number until EOF */ - mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] + a segment...\n"); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] End of file, no further segments.\n"); + return 0; +} + +static int demux_mkv_open(demuxer_t *demuxer) +{ + stream_t *s = demuxer->stream; + mkv_demuxer_t *mkv_d; + mkv_track_t *track; + + stream_seek(s, s->start_pos); + + if (!read_ebml_header(demuxer)) + return 0; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Found the head...\n"); + + if (!read_mkv_segment_header(demuxer)) + return 0; mkv_d = talloc_zero(demuxer, struct mkv_demuxer); demuxer->priv = mkv_d; mkv_d->tc_scale = 1000000; mkv_d->segment_start = stream_tell(s); + if (demuxer->params && demuxer->params->matroska_was_valid) + *demuxer->params->matroska_was_valid = true; + while (1) { uint32_t id = ebml_read_id(s, NULL); if (s->eof) { |