demux: keep track of ts information per stream type

Signed-off-by: Aman Karmani <aman@tmm1.net>
This commit is contained in:
Aman Karmani 2021-10-19 18:16:22 -07:00 committed by Kacper Michajłow
parent 4cc9e6f999
commit abe8f3f9df
6 changed files with 100 additions and 32 deletions

View File

@ -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):

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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;