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
|
other byte-oriented input layer) in bytes per second. May be inaccurate or
|
||||||
missing.
|
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``,
|
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
|
or with Lua ``mp.get_property_native``, this will return a mpv_node with
|
||||||
the following contents:
|
the following contents:
|
||||||
|
@ -2337,6 +2342,12 @@ Property list
|
||||||
"reader-pts" MPV_FORMAT_DOUBLE
|
"reader-pts" MPV_FORMAT_DOUBLE
|
||||||
"cache-duration" MPV_FORMAT_DOUBLE
|
"cache-duration" MPV_FORMAT_DOUBLE
|
||||||
"raw-input-rate" MPV_FORMAT_INT64
|
"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):
|
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){
|
*r = (struct demux_reader_state){
|
||||||
.eof = in->eof,
|
.eof = in->eof,
|
||||||
.ts_reader = MP_NOPTS_VALUE,
|
.ts_info = {
|
||||||
.ts_end = MP_NOPTS_VALUE,
|
.reader = MP_NOPTS_VALUE,
|
||||||
.ts_duration = -1,
|
.end = MP_NOPTS_VALUE,
|
||||||
|
.duration = -1,
|
||||||
|
},
|
||||||
.total_bytes = in->total_bytes,
|
.total_bytes = in->total_bytes,
|
||||||
.seeking = in->seeking_in_progress,
|
.seeking = in->seeking_in_progress,
|
||||||
.low_level_seeks = in->low_level_seeks,
|
.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,
|
.file_cache_bytes = in->cache ? demux_cache_get_size(in->cache) : -1,
|
||||||
};
|
};
|
||||||
bool any_packets = false;
|
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++) {
|
for (int n = 0; n < in->num_streams; n++) {
|
||||||
struct demux_stream *ds = in->streams[n]->ds;
|
struct demux_stream *ds = in->streams[n]->ds;
|
||||||
if (ds->eager && !(!ds->queue->head && ds->eof) && !ds->ignore_eof) {
|
if (ds->eager && !(!ds->queue->head && ds->eof) && !ds->ignore_eof) {
|
||||||
r->underrun |= !ds->reader_head && !ds->eof && !ds->still_image;
|
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;
|
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);
|
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->idle = (!in->reading && !r->underrun) || r->eof;
|
||||||
r->underrun &= !r->idle && in->threading;
|
r->underrun &= !r->idle && in->threading;
|
||||||
r->ts_reader = MP_ADD_PTS(r->ts_reader, in->ts_offset);
|
ots->reader = MP_ADD_PTS(ots->reader, in->ts_offset);
|
||||||
r->ts_end = MP_ADD_PTS(r->ts_end, in->ts_offset);
|
ots->end = MP_ADD_PTS(ots->end, in->ts_offset);
|
||||||
if (r->ts_reader != MP_NOPTS_VALUE && r->ts_reader <= r->ts_end)
|
if (ots->reader != MP_NOPTS_VALUE && ots->reader <= ots->end)
|
||||||
r->ts_duration = r->ts_end - r->ts_reader;
|
ots->duration = ots->end - ots->reader;
|
||||||
if (in->seeking || !any_packets)
|
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++) {
|
for (int n = 0; n < MPMIN(in->num_ranges, MAX_SEEK_RANGES); n++) {
|
||||||
struct demux_cached_range *range = in->ranges[n];
|
struct demux_cached_range *range = in->ranges[n];
|
||||||
if (range->seek_start != MP_NOPTS_VALUE) {
|
if (range->seek_start != MP_NOPTS_VALUE) {
|
||||||
|
|
|
@ -36,12 +36,17 @@ struct demux_seek_range {
|
||||||
double start, end;
|
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 {
|
struct demux_reader_state {
|
||||||
bool eof, underrun, idle;
|
bool eof, underrun, idle;
|
||||||
bool bof_cached, eof_cached;
|
bool bof_cached, eof_cached;
|
||||||
double ts_duration;
|
struct demux_ctrl_ts_info ts_info;
|
||||||
double ts_reader; // approx. timerstamp of decoder position
|
struct demux_ctrl_ts_info ts_per_stream[STREAM_TYPE_COUNT];
|
||||||
double ts_end; // approx. timestamp of end of buffered range
|
|
||||||
int64_t total_bytes;
|
int64_t total_bytes;
|
||||||
int64_t fw_bytes;
|
int64_t fw_bytes;
|
||||||
int64_t file_cache_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;
|
struct demux_reader_state s;
|
||||||
demux_get_reader_state(mpctx->demuxer, &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_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,
|
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;
|
struct demux_reader_state s;
|
||||||
demux_get_reader_state(mpctx->demuxer, &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_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,
|
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;
|
struct mpv_node *r = (struct mpv_node *)arg;
|
||||||
node_init(r, MPV_FORMAT_NODE_MAP, NULL);
|
node_init(r, MPV_FORMAT_NODE_MAP, NULL);
|
||||||
|
|
||||||
if (s.ts_end != MP_NOPTS_VALUE)
|
if (s.ts_info.end != MP_NOPTS_VALUE)
|
||||||
node_map_add_double(r, "cache-end", s.ts_end);
|
node_map_add_double(r, "cache-end", s.ts_info.end);
|
||||||
|
|
||||||
if (s.ts_reader != MP_NOPTS_VALUE)
|
if (s.ts_info.reader != MP_NOPTS_VALUE)
|
||||||
node_map_add_double(r, "reader-pts", s.ts_reader);
|
node_map_add_double(r, "reader-pts", s.ts_info.reader);
|
||||||
|
|
||||||
if (s.ts_duration >= 0)
|
if (s.ts_info.duration >= 0)
|
||||||
node_map_add_double(r, "cache-duration", s.ts_duration);
|
node_map_add_double(r, "cache-duration", s.ts_info.duration);
|
||||||
|
|
||||||
node_map_add_flag(r, "eof", s.eof);
|
node_map_add_flag(r, "eof", s.eof);
|
||||||
node_map_add_flag(r, "underrun", s.underrun);
|
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)
|
if (s.ts_last != MP_NOPTS_VALUE)
|
||||||
node_map_add_double(r, "debug-ts-last", s.ts_last);
|
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, "bof-cached", s.bof_cached);
|
||||||
node_map_add_flag(r, "eof-cached", s.eof_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;
|
struct demux_reader_state s;
|
||||||
demux_get_reader_state(mpctx->demuxer, &s);
|
demux_get_reader_state(mpctx->demuxer, &s);
|
||||||
|
|
||||||
if (s.ts_duration < 0) {
|
if (s.ts_info.duration < 0) {
|
||||||
saddf(&line, "???");
|
saddf(&line, "???");
|
||||||
} else if (s.ts_duration < 10) {
|
} else if (s.ts_info.duration < 10) {
|
||||||
saddf(&line, "%2.1fs", s.ts_duration);
|
saddf(&line, "%2.1fs", s.ts_info.duration);
|
||||||
} else {
|
} else {
|
||||||
saddf(&line, "%2ds", (int)s.ts_duration);
|
saddf(&line, "%2ds", (int)s.ts_info.duration);
|
||||||
}
|
}
|
||||||
int64_t cache_size = s.fw_bytes;
|
int64_t cache_size = s.fw_bytes;
|
||||||
if (cache_size > 0) {
|
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 &&
|
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
|
// Enter buffering state only if there actually was an underrun (or if
|
||||||
// initial caching before playback restart is used).
|
// initial caching before playback restart is used).
|
||||||
|
@ -754,7 +754,7 @@ static void handle_update_cache(struct MPContext *mpctx)
|
||||||
|
|
||||||
if (mpctx->paused_for_cache) {
|
if (mpctx->paused_for_cache) {
|
||||||
cache_buffer =
|
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);
|
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 ((mpctx->cache_buffer == 100) != (cache_buffer == 100)) {
|
||||||
if (cache_buffer < 100) {
|
if (cache_buffer < 100) {
|
||||||
MP_VERBOSE(mpctx, "Enter buffering (buffer went from %d%% -> %d%%) [%fs].\n",
|
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 {
|
} else {
|
||||||
double t = now - mpctx->cache_stop_time;
|
double t = now - mpctx->cache_stop_time;
|
||||||
MP_VERBOSE(mpctx, "End buffering (waited %f secs) [%fs].\n",
|
MP_VERBOSE(mpctx, "End buffering (waited %f secs) [%fs].\n",
|
||||||
t, s.ts_duration);
|
t, s.ts_info.duration);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MP_VERBOSE(mpctx, "Still buffering (buffer went from %d%% -> %d%%) [%fs].\n",
|
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;
|
mpctx->cache_buffer = cache_buffer;
|
||||||
force_update = true;
|
force_update = true;
|
||||||
|
|
Loading…
Reference in New Issue