mirror of https://github.com/mpv-player/mpv
demux: keep track of ts information per stream type
Signed-off-by: Aman Karmani <aman@tmm1.net>
This commit is contained in:
parent
4cc9e6f999
commit
abe8f3f9df
|
@ -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):
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue