From abe8f3f9dfb18f21f3cb1bd2ae9f6d9a7f11a5ed Mon Sep 17 00:00:00 2001 From: Aman Karmani Date: Tue, 19 Oct 2021 18:16:22 -0700 Subject: [PATCH] demux: keep track of ts information per stream type Signed-off-by: Aman Karmani --- DOCS/man/input.rst | 11 ++++++++++ demux/demux.c | 53 +++++++++++++++++++++++++++++++++++++--------- demux/demux.h | 11 +++++++--- player/command.c | 39 +++++++++++++++++++++++++--------- player/osd.c | 8 +++---- player/playloop.c | 10 ++++----- 6 files changed, 100 insertions(+), 32 deletions(-) diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 3e86004c28..bc9a4455f0 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -2318,6 +2318,11 @@ Property list other byte-oriented input layer) in bytes per second. May be inaccurate or missing. + ``ts-per-stream`` is an array containing an entry for each stream type: video, + audio, and subtitle. For each stream type, the details for the demuxer cache + for that stream type are available as ``cache-duration``, ``reader-pts`` and + ``cache-end``. + When querying the property with the client API using ``MPV_FORMAT_NODE``, or with Lua ``mp.get_property_native``, this will return a mpv_node with the following contents: @@ -2337,6 +2342,12 @@ Property list "reader-pts" MPV_FORMAT_DOUBLE "cache-duration" MPV_FORMAT_DOUBLE "raw-input-rate" MPV_FORMAT_INT64 + "ts-per-stream" MPV_FORMAT_NODE_ARRAY + MPV_FORMAT_NODE_MAP + "type" MPV_FORMAT_STRING + "cache-duration" MPV_FORMAT_DOUBLE + "reader-pts" MPV_FORMAT_DOUBLE + "cache-end" MPV_FORMAT_DOUBLE Other fields (might be changed or removed in the future): diff --git a/demux/demux.c b/demux/demux.c index 5997a96ed6..37d8341852 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -4487,9 +4487,11 @@ void demux_get_reader_state(struct demuxer *demuxer, struct demux_reader_state * *r = (struct demux_reader_state){ .eof = in->eof, - .ts_reader = MP_NOPTS_VALUE, - .ts_end = MP_NOPTS_VALUE, - .ts_duration = -1, + .ts_info = { + .reader = MP_NOPTS_VALUE, + .end = MP_NOPTS_VALUE, + .duration = -1, + }, .total_bytes = in->total_bytes, .seeking = in->seeking_in_progress, .low_level_seeks = in->low_level_seeks, @@ -4499,24 +4501,55 @@ void demux_get_reader_state(struct demuxer *demuxer, struct demux_reader_state * .file_cache_bytes = in->cache ? demux_cache_get_size(in->cache) : -1, }; bool any_packets = false; + for (int n = 0; n < STREAM_TYPE_COUNT; n++) { + r->ts_per_stream[n] = r->ts_info; + } for (int n = 0; n < in->num_streams; n++) { struct demux_stream *ds = in->streams[n]->ds; if (ds->eager && !(!ds->queue->head && ds->eof) && !ds->ignore_eof) { r->underrun |= !ds->reader_head && !ds->eof && !ds->still_image; - r->ts_reader = MP_PTS_MAX(r->ts_reader, ds->base_ts); - r->ts_end = MP_PTS_MAX(r->ts_end, ds->queue->last_ts); any_packets |= !!ds->reader_head; + + double ts_reader = ds->base_ts; + double ts_end = ds->queue->last_ts; + double ts_duration = -1; + if (ts_reader != MP_NOPTS_VALUE && ts_reader <= ts_end) + ts_duration = ts_end - ts_reader; + + struct demux_ctrl_ts_info *i = &r->ts_per_stream[ds->type]; + i->reader = MP_PTS_MIN(i->reader, ts_reader); + i->end = MP_PTS_MIN(i->end, ts_end); + i->duration = ts_duration; } r->fw_bytes += get_forward_buffered_bytes(ds); } + struct demux_ctrl_ts_info *ots = &r->ts_info; + // find stream type with lowest duration and use its state + for (int n = 0; n < STREAM_TYPE_COUNT; n++) { + struct demux_ctrl_ts_info *ts = &r->ts_per_stream[n]; + if (r->ts_info.duration != -1) { + // skip if timestamps unknown + if (ts->duration == -1) + continue; + // skip if we already know of a smaller cached stream + if (ts->duration > ots->duration) + continue; + // skip empty subtitle streams when other streams exist + if (n == STREAM_SUB && ts->duration == 0.0) + continue; + } + ots->duration = ts->duration; + ots->reader = ts->reader; + ots->end = ts->end; + } r->idle = (!in->reading && !r->underrun) || r->eof; r->underrun &= !r->idle && in->threading; - r->ts_reader = MP_ADD_PTS(r->ts_reader, in->ts_offset); - r->ts_end = MP_ADD_PTS(r->ts_end, in->ts_offset); - if (r->ts_reader != MP_NOPTS_VALUE && r->ts_reader <= r->ts_end) - r->ts_duration = r->ts_end - r->ts_reader; + ots->reader = MP_ADD_PTS(ots->reader, in->ts_offset); + ots->end = MP_ADD_PTS(ots->end, in->ts_offset); + if (ots->reader != MP_NOPTS_VALUE && ots->reader <= ots->end) + ots->duration = ots->end - ots->reader; if (in->seeking || !any_packets) - r->ts_duration = 0; + ots->duration = 0; for (int n = 0; n < MPMIN(in->num_ranges, MAX_SEEK_RANGES); n++) { struct demux_cached_range *range = in->ranges[n]; if (range->seek_start != MP_NOPTS_VALUE) { diff --git a/demux/demux.h b/demux/demux.h index 08904f26cc..401f1d146c 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -36,12 +36,17 @@ struct demux_seek_range { double start, end; }; +struct demux_ctrl_ts_info { + double duration; + double reader; // approx. timestamp of decoder position + double end; // approx. timestamp of end of buffered range +}; + struct demux_reader_state { bool eof, underrun, idle; bool bof_cached, eof_cached; - double ts_duration; - double ts_reader; // approx. timerstamp of decoder position - double ts_end; // approx. timestamp of end of buffered range + struct demux_ctrl_ts_info ts_info; + struct demux_ctrl_ts_info ts_per_stream[STREAM_TYPE_COUNT]; int64_t total_bytes; int64_t fw_bytes; int64_t file_cache_bytes; diff --git a/player/command.c b/player/command.c index 9e239520d1..09a5df7744 100644 --- a/player/command.c +++ b/player/command.c @@ -1463,10 +1463,10 @@ static int mp_property_demuxer_cache_duration(void *ctx, struct m_property *prop struct demux_reader_state s; demux_get_reader_state(mpctx->demuxer, &s); - if (s.ts_duration < 0) + if (s.ts_info.duration < 0) return M_PROPERTY_UNAVAILABLE; - return m_property_double_ro(action, arg, s.ts_duration); + return m_property_double_ro(action, arg, s.ts_info.duration); } static int mp_property_demuxer_cache_time(void *ctx, struct m_property *prop, @@ -1479,10 +1479,10 @@ static int mp_property_demuxer_cache_time(void *ctx, struct m_property *prop, struct demux_reader_state s; demux_get_reader_state(mpctx->demuxer, &s); - if (s.ts_end == MP_NOPTS_VALUE) + if (s.ts_info.end == MP_NOPTS_VALUE) return M_PROPERTY_UNAVAILABLE; - return m_property_double_ro(action, arg, s.ts_end); + return m_property_double_ro(action, arg, s.ts_info.end); } static int mp_property_demuxer_cache_idle(void *ctx, struct m_property *prop, @@ -1518,14 +1518,14 @@ static int mp_property_demuxer_cache_state(void *ctx, struct m_property *prop, struct mpv_node *r = (struct mpv_node *)arg; node_init(r, MPV_FORMAT_NODE_MAP, NULL); - if (s.ts_end != MP_NOPTS_VALUE) - node_map_add_double(r, "cache-end", s.ts_end); + if (s.ts_info.end != MP_NOPTS_VALUE) + node_map_add_double(r, "cache-end", s.ts_info.end); - if (s.ts_reader != MP_NOPTS_VALUE) - node_map_add_double(r, "reader-pts", s.ts_reader); + if (s.ts_info.reader != MP_NOPTS_VALUE) + node_map_add_double(r, "reader-pts", s.ts_info.reader); - if (s.ts_duration >= 0) - node_map_add_double(r, "cache-duration", s.ts_duration); + if (s.ts_info.duration >= 0) + node_map_add_double(r, "cache-duration", s.ts_info.duration); node_map_add_flag(r, "eof", s.eof); node_map_add_flag(r, "underrun", s.underrun); @@ -1543,6 +1543,25 @@ static int mp_property_demuxer_cache_state(void *ctx, struct m_property *prop, if (s.ts_last != MP_NOPTS_VALUE) node_map_add_double(r, "debug-ts-last", s.ts_last); + struct mpv_node *stream_types = + node_map_add(r, "ts-per-stream", MPV_FORMAT_NODE_ARRAY); + for (int n = 0; n < STREAM_TYPE_COUNT; n++) { + struct demux_ctrl_ts_info ts = s.ts_per_stream[n]; + if (ts.duration == -1) + continue; + + struct mpv_node *st = node_array_add(stream_types, MPV_FORMAT_NODE_MAP); + node_map_add_string(st, "type", + n == STREAM_VIDEO ? "video" : + n == STREAM_AUDIO ? "audio" : + n == STREAM_SUB ? "subtitle" : "unknown"); + node_map_add_double(st, "cache-duration", ts.duration); + if (ts.reader != MP_NOPTS_VALUE) + node_map_add_double(st, "reader-pts", ts.reader); + if (ts.end != MP_NOPTS_VALUE) + node_map_add_double(st, "cache-end", ts.end); + } + node_map_add_flag(r, "bof-cached", s.bof_cached); node_map_add_flag(r, "eof-cached", s.eof_cached); diff --git a/player/osd.c b/player/osd.c index 6c05933276..d3ab644c77 100644 --- a/player/osd.c +++ b/player/osd.c @@ -245,12 +245,12 @@ static char *get_term_status_msg(struct MPContext *mpctx) struct demux_reader_state s; demux_get_reader_state(mpctx->demuxer, &s); - if (s.ts_duration < 0) { + if (s.ts_info.duration < 0) { saddf(&line, "???"); - } else if (s.ts_duration < 10) { - saddf(&line, "%2.1fs", s.ts_duration); + } else if (s.ts_info.duration < 10) { + saddf(&line, "%2.1fs", s.ts_info.duration); } else { - saddf(&line, "%2ds", (int)s.ts_duration); + saddf(&line, "%2ds", (int)s.ts_info.duration); } int64_t cache_size = s.fw_bytes; if (cache_size > 0) { diff --git a/player/playloop.c b/player/playloop.c index 12239d69ab..e101f5fe3d 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -714,7 +714,7 @@ static void handle_update_cache(struct MPContext *mpctx) } bool is_low = use_pause_on_low_cache && !s.idle && - s.ts_duration < opts->cache_pause_wait; + s.ts_info.duration < opts->cache_pause_wait; // Enter buffering state only if there actually was an underrun (or if // initial caching before playback restart is used). @@ -754,7 +754,7 @@ static void handle_update_cache(struct MPContext *mpctx) if (mpctx->paused_for_cache) { cache_buffer = - 100 * MPCLAMP(s.ts_duration / opts->cache_pause_wait, 0, 0.99); + 100 * MPCLAMP(s.ts_info.duration / opts->cache_pause_wait, 0, 0.99); mp_set_timeout(mpctx, 0.2); } @@ -775,15 +775,15 @@ static void handle_update_cache(struct MPContext *mpctx) if ((mpctx->cache_buffer == 100) != (cache_buffer == 100)) { if (cache_buffer < 100) { MP_VERBOSE(mpctx, "Enter buffering (buffer went from %d%% -> %d%%) [%fs].\n", - mpctx->cache_buffer, cache_buffer, s.ts_duration); + mpctx->cache_buffer, cache_buffer, s.ts_info.duration); } else { double t = now - mpctx->cache_stop_time; MP_VERBOSE(mpctx, "End buffering (waited %f secs) [%fs].\n", - t, s.ts_duration); + t, s.ts_info.duration); } } else { MP_VERBOSE(mpctx, "Still buffering (buffer went from %d%% -> %d%%) [%fs].\n", - mpctx->cache_buffer, cache_buffer, s.ts_duration); + mpctx->cache_buffer, cache_buffer, s.ts_info.duration); } mpctx->cache_buffer = cache_buffer; force_update = true;