From 5a186d5942bda0c7cae56bc524a6dceccb5c7d3f Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Feb 2015 23:46:50 +0100 Subject: matroska: make timeline code independent of MPContext --- player/core.h | 2 - player/loadfile.c | 3 - player/timeline/tl_matroska.c | 226 ++++++++++++++++++++++-------------------- 3 files changed, 117 insertions(+), 114 deletions(-) (limited to 'player') diff --git a/player/core.h b/player/core.h index 1ac5ac7817..b692c29274 100644 --- a/player/core.h +++ b/player/core.h @@ -477,8 +477,6 @@ void uninit_sub_renderer(struct MPContext *mpctx); void update_osd_sub_state(struct MPContext *mpctx, int order, struct osd_sub_state *out_state); -// timeline/tl_matroska.c -void build_ordered_chapter_timeline(struct MPContext *mpctx); // timeline/tl_mpv_edl.c void build_mpv_edl_timeline(struct MPContext *mpctx); // timeline/tl_cue.c diff --git a/player/loadfile.c b/player/loadfile.c index c6b2133e51..26e3be43ea 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -988,9 +988,6 @@ static void load_timeline(struct MPContext *mpctx) MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, mpctx->demuxer); - if (mpctx->demuxer->matroska_data.ordered_chapters) - build_ordered_chapter_timeline(mpctx); - if (mpctx->demuxer->type == DEMUXER_TYPE_EDL) build_mpv_edl_timeline(mpctx); diff --git a/player/timeline/tl_matroska.c b/player/timeline/tl_matroska.c index 17734d0363..57bb93e1cd 100644 --- a/player/timeline/tl_matroska.c +++ b/player/timeline/tl_matroska.c @@ -32,15 +32,36 @@ #include "talloc.h" -#include "player/core.h" #include "common/msg.h" +#include "common/global.h" #include "demux/demux.h" +#include "demux/timeline.h" +#include "demux/matroska.h" +#include "options/options.h" #include "options/path.h" #include "misc/bstr.h" #include "common/common.h" #include "common/playlist.h" #include "stream/stream.h" +struct tl_ctx { + struct mp_log *log; + struct mpv_global *global; + + struct demuxer *demuxer; + + struct demuxer **sources; + int num_sources; + + struct timeline_part *timeline; + int num_parts; + + uint64_t start_time; // When the next part should start on the complete timeline. + uint64_t missing_time; // Total missing time so far. + uint64_t last_end_time; // When the last part ended on the complete timeline. + int num_chapters; // Total number of expected chapters. +}; + struct find_entry { char *name; int matchlen; @@ -127,10 +148,10 @@ static char **find_files(const char *original_file) return results; } -static int enable_cache(struct MPContext *mpctx, struct stream **stream, +static int enable_cache(struct mpv_global *global, struct stream **stream, struct demuxer **demuxer, struct demuxer_params *params) { - struct MPOpts *opts = mpctx->opts; + struct MPOpts *opts = global->opts; if (!stream_wants_cache(*stream, &opts->stream_cache)) return 0; @@ -139,7 +160,7 @@ static int enable_cache(struct MPContext *mpctx, struct stream **stream, free_demuxer(*demuxer); free_stream(*stream); - *stream = stream_open(filename, mpctx->global); + *stream = stream_open(filename, global); if (!*stream) { talloc_free(filename); return -1; @@ -147,7 +168,7 @@ static int enable_cache(struct MPContext *mpctx, struct stream **stream, stream_enable_cache(stream, &opts->stream_cache); - *demuxer = demux_open(*stream, "mkv", params, mpctx->global); + *demuxer = demux_open(*stream, "mkv", params, global); if (!*demuxer) { talloc_free(filename); free_stream(*stream); @@ -171,7 +192,7 @@ static bool has_source_request(struct matroska_segment_uid *uids, } // segment = get Nth segment of a multi-segment file -static bool check_file_seg(struct MPContext *mpctx, struct demuxer ***sources, +static bool check_file_seg(struct tl_ctx *ctx, struct demuxer ***sources, int *num_sources, struct matroska_segment_uid **uids, char *filename, int segment) { @@ -182,10 +203,10 @@ static bool check_file_seg(struct MPContext *mpctx, struct demuxer ***sources, .matroska_wanted_segment = segment, .matroska_was_valid = &was_valid, }; - struct stream *s = stream_open(filename, mpctx->global); + struct stream *s = stream_open(filename, ctx->global); if (!s) return false; - struct demuxer *d = demux_open(s, "mkv", ¶ms, mpctx->global); + struct demuxer *d = demux_open(s, "mkv", ¶ms, ctx->global); if (!d) { free_stream(s); @@ -203,7 +224,7 @@ static bool check_file_seg(struct MPContext *mpctx, struct demuxer ***sources, if (!memcmp(uid->segment, m->uid.segment, 16) && (!uid->edition || uid->edition == m->uid.edition)) { - MP_INFO(mpctx, "Match for source %d: %s\n", i, d->filename); + MP_INFO(ctx, "Match for source %d: %s\n", i, d->filename); for (int j = 0; j < m->num_ordered_chapters; j++) { struct matroska_chapter *c = m->ordered_chapters + j; @@ -222,7 +243,7 @@ static bool check_file_seg(struct MPContext *mpctx, struct demuxer ***sources, MP_TARRAY_APPEND(NULL, *sources, *num_sources, NULL); } - if (enable_cache(mpctx, &s, &d, ¶ms) < 0) + if (enable_cache(ctx->global, &s, &d, ¶ms) < 0) continue; (*sources)[i] = d; @@ -235,12 +256,12 @@ static bool check_file_seg(struct MPContext *mpctx, struct demuxer ***sources, return was_valid; } -static void check_file(struct MPContext *mpctx, struct demuxer ***sources, +static void check_file(struct tl_ctx *ctx, struct demuxer ***sources, int *num_sources, struct matroska_segment_uid **uids, char *filename, int first) { for (int segment = first; ; segment++) { - if (!check_file_seg(mpctx, sources, num_sources, uids, filename, segment)) + if (!check_file_seg(ctx, sources, num_sources, uids, filename, segment)) break; } } @@ -254,38 +275,38 @@ static bool missing(struct demuxer **sources, int num_sources) return false; } -static int find_ordered_chapter_sources(struct MPContext *mpctx, +static int find_ordered_chapter_sources(struct tl_ctx *ctx, struct demuxer ***sources, int *num_sources, struct matroska_segment_uid **uids) { - struct MPOpts *opts = mpctx->opts; + struct MPOpts *opts = ctx->global->opts; void *tmp = talloc_new(NULL); int num_filenames = 0; char **filenames = NULL; if (*num_sources > 1) { - char *main_filename = mpctx->demuxer->filename; - MP_INFO(mpctx, "This file references data from other sources.\n"); + char *main_filename = ctx->demuxer->filename; + MP_INFO(ctx, "This file references data from other sources.\n"); if (opts->ordered_chapters_files && opts->ordered_chapters_files[0]) { - MP_INFO(mpctx, "Loading references from '%s'.\n", + MP_INFO(ctx, "Loading references from '%s'.\n", opts->ordered_chapters_files); struct playlist *pl = - playlist_parse_file(opts->ordered_chapters_files, mpctx->global); + playlist_parse_file(opts->ordered_chapters_files, ctx->global); talloc_steal(tmp, pl); for (struct playlist_entry *e = pl->first; e; e = e->next) MP_TARRAY_APPEND(tmp, filenames, num_filenames, e->filename); - } else if (mpctx->demuxer->stream->uncached_type != STREAMTYPE_FILE) { - MP_WARN(mpctx, "Playback source is not a " + } else if (ctx->demuxer->stream->uncached_type != STREAMTYPE_FILE) { + MP_WARN(ctx, "Playback source is not a " "normal disk file. Will not search for related files.\n"); } else { - MP_INFO(mpctx, "Will scan other files in the " + MP_INFO(ctx, "Will scan other files in the " "same directory to find referenced sources.\n"); filenames = find_files(main_filename); num_filenames = MP_TALLOC_AVAIL(filenames); talloc_steal(tmp, filenames); } // Possibly get further segments appended to the first segment - check_file(mpctx, sources, num_sources, uids, main_filename, 1); + check_file(ctx, sources, num_sources, uids, main_filename, 1); } int old_source_count; @@ -294,13 +315,13 @@ static int find_ordered_chapter_sources(struct MPContext *mpctx, for (int i = 0; i < num_filenames; i++) { if (!missing(*sources, *num_sources)) break; - MP_VERBOSE(mpctx, "Checking file %s\n", filenames[i]); - check_file(mpctx, sources, num_sources, uids, filenames[i], 0); + MP_VERBOSE(ctx, "Checking file %s\n", filenames[i]); + check_file(ctx, sources, num_sources, uids, filenames[i], 0); } } while (old_source_count != *num_sources); if (missing(*sources, *num_sources)) { - MP_ERR(mpctx, "Failed to find ordered chapter part!\n"); + MP_ERR(ctx, "Failed to find ordered chapter part!\n"); int j = 1; for (int i = 1; i < *num_sources; i++) { if ((*sources)[i]) { @@ -318,25 +339,12 @@ static int find_ordered_chapter_sources(struct MPContext *mpctx, return *num_sources; } -struct timeline_info { - struct demuxer **sources; - int num_sources; - struct timeline_part **timeline; - int num_parts; - - uint64_t start_time; // When the next part should start on the complete timeline. - uint64_t missing_time; // Total missing time so far. - uint64_t last_end_time; // When the last part ended on the complete timeline. - int num_chapters; // Total number of expected chapters. -}; - struct inner_timeline_info { uint64_t skip; // Amount of time to skip. uint64_t limit; // How much time is expected for the parent chapter. }; -static int64_t add_timeline_part(struct MPContext *mpctx, - struct timeline_info *ctx, +static int64_t add_timeline_part(struct tl_ctx *ctx, struct demuxer *source, uint64_t start) { @@ -345,18 +353,18 @@ static int64_t add_timeline_part(struct MPContext *mpctx, * early; we don't want to try seeking over a one frame gap. */ int64_t join_diff = start - ctx->last_end_time; if (ctx->num_parts == 0 - || FFABS(join_diff) > mpctx->opts->chapter_merge_threshold * 1e6 - || source != (*ctx->timeline)[ctx->num_parts - 1].source) + || FFABS(join_diff) > ctx->global->opts->chapter_merge_threshold * 1e6 + || source != ctx->timeline[ctx->num_parts - 1].source) { struct timeline_part new = { .start = ctx->start_time / 1e9, .source_start = start / 1e9, .source = source, }; - MP_TARRAY_APPEND(NULL, *ctx->timeline, ctx->num_parts, new); + MP_TARRAY_APPEND(NULL, ctx->timeline, ctx->num_parts, new); } else if (ctx->num_parts > 0 && join_diff) { // Chapter was merged at an inexact boundary; adjust timestamps to match. - MP_VERBOSE(mpctx, "Merging timeline part %d with offset %g ms.\n", + MP_VERBOSE(ctx, "Merging timeline part %d with offset %g ms.\n", ctx->num_parts, join_diff / 1e6); ctx->start_time += join_diff; return join_diff; @@ -365,9 +373,8 @@ static int64_t add_timeline_part(struct MPContext *mpctx, return 0; } -static void build_timeline_loop(struct MPContext *mpctx, +static void build_timeline_loop(struct tl_ctx *ctx, struct demux_chapter *chapters, - struct timeline_info *ctx, struct inner_timeline_info *info, int current_source) { @@ -431,8 +438,7 @@ static void build_timeline_loop(struct MPContext *mpctx, chapter_length = source_length; } - join_diff = add_timeline_part(mpctx, ctx, - linked_source, c->start); + join_diff = add_timeline_part(ctx, linked_source, c->start); /* If we merged two chapters into a single part due to them * being off by a few frames, we need to change the limit to @@ -453,7 +459,7 @@ static void build_timeline_loop(struct MPContext *mpctx, .skip = c->start, .limit = c->end }; - build_timeline_loop(mpctx, chapters, ctx, &new_info, j); + build_timeline_loop(ctx, chapters, &new_info, j); // Already handled by the loop call. chapter_length = 0; } @@ -478,12 +484,12 @@ static void build_timeline_loop(struct MPContext *mpctx, ctx->missing_time += info->limit - local_starttime; } -static void check_track_compatibility(struct MPContext *mpctx) +static void check_track_compatibility(struct timeline *tl) { - struct demuxer *mainsrc = mpctx->track_layout; + struct demuxer *mainsrc = tl->track_layout; - for (int n = 0; n < mpctx->num_timeline_parts; n++) { - struct timeline_part *p = &mpctx->timeline[n]; + for (int n = 0; n < tl->num_parts; n++) { + struct timeline_part *p = &tl->parts[n]; if (p->source == mainsrc) continue; @@ -493,12 +499,12 @@ static void check_track_compatibility(struct MPContext *mpctx) continue; if (!demuxer_stream_by_demuxer_id(mainsrc, s->type, s->demuxer_id)) { - MP_WARN(mpctx, "Source %s has %s stream with TID=%d, which " - "is not present in the ordered chapters main " - "file. This is a broken file. " - "The additional stream is ignored.\n", - p->source->filename, stream_type_name(s->type), - s->demuxer_id); + MP_WARN(tl, "Source %s has %s stream with TID=%d, which " + "is not present in the ordered chapters main " + "file. This is a broken file. " + "The additional stream is ignored.\n", + p->source->filename, stream_type_name(s->type), + s->demuxer_id); } } @@ -513,39 +519,47 @@ static void check_track_compatibility(struct MPContext *mpctx) // There are actually many more things that in theory have to // match (though mpv's implementation doesn't care). if (s->codec && m->codec && strcmp(s->codec, m->codec) != 0) - MP_WARN(mpctx, "Timeline segments have mismatching codec.\n"); + MP_WARN(tl, "Timeline segments have mismatching codec.\n"); } else { - MP_WARN(mpctx, "Source %s lacks %s stream with TID=%d, which " - "is present in the ordered chapters main " - "file. This is a broken file.\n", - p->source->filename, stream_type_name(m->type), - m->demuxer_id); + MP_WARN(tl, "Source %s lacks %s stream with TID=%d, which " + "is present in the ordered chapters main " + "file. This is a broken file.\n", + p->source->filename, stream_type_name(m->type), + m->demuxer_id); } } } } -void build_ordered_chapter_timeline(struct MPContext *mpctx) +void build_ordered_chapter_timeline(struct timeline *tl) { - struct MPOpts *opts = mpctx->opts; + struct demuxer *demuxer = tl->demuxer; - if (!opts->ordered_chapters) { - MP_INFO(mpctx, "File uses ordered chapters, but " - "you have disabled support for them. Ignoring.\n"); + if (!demuxer->matroska_data.ordered_chapters) + return; + + if (!demuxer->global->opts->ordered_chapters) { + MP_INFO(demuxer, "File uses ordered chapters, but " + "you have disabled support for them. Ignoring.\n"); return; } - MP_INFO(mpctx, "File uses ordered chapters, will build " - "edit timeline.\n"); + struct tl_ctx *ctx = talloc_ptrtype(tl, ctx); + *ctx = (struct tl_ctx){ + .log = tl->log, + .global = tl->global, + .demuxer = demuxer, + }; + + MP_INFO(ctx, "File uses ordered chapters, will build edit timeline.\n"); - struct demuxer *demuxer = mpctx->demuxer; struct matroska_data *m = &demuxer->matroska_data; // +1 because sources/uid_map[0] is original file even if all chapters // actually use other sources and need separate entries - struct demuxer **sources = talloc_zero_array(NULL, struct demuxer *, + struct demuxer **sources = talloc_zero_array(tl, struct demuxer *, m->num_ordered_chapters+1); - sources[0] = mpctx->demuxer; + sources[0] = demuxer; struct matroska_segment_uid *uids = talloc_zero_array(NULL, struct matroska_segment_uid, m->num_ordered_chapters + 1); @@ -570,31 +584,25 @@ void build_ordered_chapter_timeline(struct MPContext *mpctx) num_sources++; } - num_sources = find_ordered_chapter_sources(mpctx, &sources, &num_sources, - &uids); + num_sources = find_ordered_chapter_sources(ctx, &sources, &num_sources, &uids); talloc_free(uids); - struct timeline_part *timeline = talloc_array_ptrtype(NULL, timeline, 0); struct demux_chapter *chapters = - talloc_zero_array(NULL, struct demux_chapter, m->num_ordered_chapters); + talloc_zero_array(tl, struct demux_chapter, m->num_ordered_chapters); // Stupid hack, because fuck everything. for (int n = 0; n < m->num_ordered_chapters; n++) chapters[n].pts = -1; - struct timeline_info ctx = { - .sources = sources, - .num_sources = num_sources, - .timeline = &timeline, - .num_parts = 0, - .start_time = 0, - .missing_time = 0, - .last_end_time = 0, - .num_chapters = m->num_ordered_chapters - }; + + ctx->timeline = talloc_array_ptrtype(tl, ctx->timeline, 0); + ctx->sources = sources; + ctx->num_sources = num_sources; + ctx->num_chapters = m->num_ordered_chapters; + struct inner_timeline_info info = { .skip = 0, .limit = 0 }; - build_timeline_loop(mpctx, chapters, &ctx, &info, 0); + build_timeline_loop(ctx, chapters, &info, 0); // Fuck everything (2): filter out all "unset" chapters. for (int n = m->num_ordered_chapters - 1; n >= 0; n--) { @@ -602,45 +610,45 @@ void build_ordered_chapter_timeline(struct MPContext *mpctx) MP_TARRAY_REMOVE_AT(chapters, m->num_ordered_chapters, n); } - if (!ctx.num_parts) { + if (!ctx->num_parts) { // None of the parts come from the file itself??? // Broken file, but we need at least 1 valid timeline part - add a dummy. - MP_WARN(mpctx, "Ordered chapters file with no parts?\n"); + MP_WARN(ctx, "Ordered chapters file with no parts?\n"); struct timeline_part new = { .source = demuxer, }; - MP_TARRAY_APPEND(NULL, timeline, ctx.num_parts, new); + MP_TARRAY_APPEND(NULL, ctx->timeline, ctx->num_parts, new); } struct timeline_part new = { - .start = ctx.start_time / 1e9, + .start = ctx->start_time / 1e9, }; - MP_TARRAY_APPEND(NULL, timeline, ctx.num_parts, new); + MP_TARRAY_APPEND(NULL, ctx->timeline, ctx->num_parts, new); /* Ignore anything less than a millisecond when reporting missing time. If * users really notice less than a millisecond missing, maybe this can be * revisited. */ - if (ctx.missing_time >= 1e6) { - MP_ERR(mpctx, "There are %.3f seconds missing from the timeline!\n", - ctx.missing_time / 1e9); + if (ctx->missing_time >= 1e6) { + MP_ERR(ctx, "There are %.3f seconds missing from the timeline!\n", + ctx->missing_time / 1e9); } - talloc_free(mpctx->sources); - mpctx->sources = sources; - mpctx->num_sources = num_sources; - mpctx->timeline = timeline; - mpctx->num_timeline_parts = ctx.num_parts - 1; - mpctx->num_chapters = m->num_ordered_chapters; - mpctx->chapters = chapters; + + tl->sources = sources; + tl->num_sources = num_sources; + tl->parts = ctx->timeline; + tl->num_parts = ctx->num_parts - 1; // minus termination + tl->chapters = chapters; + tl->num_chapters = m->num_ordered_chapters; // With Matroska, the "master" file usually dictates track layout etc., // except maybe with playlist-like files. - mpctx->track_layout = mpctx->timeline[0].source; - for (int n = 0; n < mpctx->num_timeline_parts; n++) { - if (mpctx->timeline[n].source == mpctx->demuxer) { - mpctx->track_layout = mpctx->demuxer; + tl->track_layout = tl->parts[0].source; + for (int n = 0; n < tl->num_parts; n++) { + if (tl->parts[n].source == ctx->demuxer) { + tl->track_layout = ctx->demuxer; break; } } - check_track_compatibility(mpctx); + check_track_compatibility(tl); } -- cgit v1.2.3