summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-06-02 02:14:54 +0200
committerwm4 <wm4@nowhere>2019-09-19 20:37:05 +0200
commitf439064e7f142c9503b2a7f99c7da0e49eaab660 (patch)
tree66db1311f1039f0875b37199eba3cb370eaae4f5
parente62afe405510028d71e16c2db92c94063d23a3ba (diff)
downloadmpv-f439064e7f142c9503b2a7f99c7da0e49eaab660.tar.bz2
mpv-f439064e7f142c9503b2a7f99c7da0e49eaab660.tar.xz
demux: demux multiple audio frames in backward playback
Until now, this usually passed a single audio frame to the decoder, and then did a backstep operation (cache seek + frame search) again. This is probably not very efficient, especially considering it has to search the packet queue from the "start" every time again. Also, with most audio codecs, an additional "preroll" frame was passed first. In these cases, the preroll frame would make up 50% of audio decoding time. Also not very efficient. Attempt to fix this by returning multiple frames at once. This reduces the number of backstep operations and the ratio the preoll frames. In theory, this should help efficiency. I didn't test it though, why would I do this? It's just a pain. Set it to unscientific 10 frames. (Actually, these are 10 keyframes, so it's much more for codecs like TrueHD. But I don't care about TrueHD.) This commit changes some other implementation details. Since we can return more than 1 non-preroll keyframe to the decoder, some new state is needed to remember how much. The resume packet search is adjusted to find N ("total") keyframe packets in general, not just preroll frames. I'm removing the special case for 1 preroll packet; audio used this, but doesn't anymore, and it's premature optimization anyway. Expose the new mechanism with 2 new options. They're almost completely pointless, since nobody will try them, and if they do, they won't understand what these options truly do. And if they actually do, they most likely would be capable of editing the source code, and we could just hardcode the parameters. Just so you know that I know that the added options are pointless. The following two things are truly unrelated to this commit, and more like general refactoring, but fortunately nobody can stop me. Don't set back_seek_pos in dequeue_packet() anymore. This was sort of pointless, since it was set in find_backward_restart_pos() anyway (using some of the same packets). The latter function tries to restrict this to the first keyframe range though, which is an optimization that in theory might break with broken files (duh), but in these cases a lot of other things would be broken anyway. Don't set back_restart_* in dequeue_packet(). I think this is an artifact of the old restart code (cf. ad9e473c555). It can be done directly in find_backward_restart_pos() now. Although this adds another shitty packet search loop, I prefer this, because clearer what's actually happening.
-rw-r--r--DOCS/man/options.rst15
-rw-r--r--demux/demux.c103
2 files changed, 80 insertions, 38 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index f55b38eb7a..754c2476f8 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -568,6 +568,21 @@ Playback Control
depending on the exact conditions. You may have to use the
``--vd-lavc-show-all`` option as well.
+``--video-backward-batch=<number>``, ``--audio-backward-batch=<number>``
+ Number of keyframe ranges to decode at once when backward decoding (default:
+ 1 for video, 10 for audio). Another pointless tuning parameter nobody should
+ use. This should affect performance only. In theory, setting a number higher
+ than 1 for audio will reduce overhead due to less frequent backstep
+ operations and less redundant decoding work due to fewer decoded overlap
+ frames (see ``--audio-backward-overlap``). On the other hand, it requires
+ a larger reversal buffer, and could make playback less smooth due to
+ breaking pipelining (e.g. by decoding a lot, and then doing nothing for a
+ while).
+
+ It probably never makes sense to set ``--video-backward-batch``. But in
+ theory, it could help with intra-only video codecs by reducing backstep
+ operations.
+
``--demuxer-backward-playback-step=<seconds>``
Number of seconds the demuxer should seek back to get new packets during
backward playback (default: 60). This is useful for tuning backward
diff --git a/demux/demux.c b/demux/demux.c
index 5b830284ec..f732cf9e6c 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -91,6 +91,7 @@ struct demux_opts {
char *record_file;
int video_back_preroll;
int audio_back_preroll;
+ int back_batch[STREAM_TYPE_COUNT];
double back_seek_size;
};
@@ -118,6 +119,8 @@ const struct m_sub_options demux_conf = {
1024, ({"auto", -1})),
OPT_CHOICE_OR_INT("audio-backward-overlap", audio_back_preroll, 0, 0,
1024, ({"auto", -1})),
+ OPT_INTRANGE("video-backward-batch", back_batch[STREAM_VIDEO], 0, 0, 1024),
+ OPT_INTRANGE("audio-backward-batch", back_batch[STREAM_AUDIO], 0, 0, 1024),
OPT_DOUBLE("demuxer-backward-playback-step", back_seek_size, M_OPT_MIN,
.min = 0),
{0}
@@ -134,6 +137,10 @@ const struct m_sub_options demux_conf = {
.video_back_preroll = -1,
.audio_back_preroll = -1,
.back_seek_size = 60,
+ .back_batch = {
+ [STREAM_VIDEO] = 1,
+ [STREAM_AUDIO] = 10,
+ },
},
};
@@ -343,8 +350,8 @@ struct demux_stream {
double last_ret_dts;
// Backwards demuxing.
- // pos/dts of the previous keyframe packet returned; valid if
- // back_range_started or back_restarting are set.
+ // pos/dts of the previous keyframe packet returned; always valid if back-
+ // demuxing is enabled, and back_restart_eof/back_restart_next are false.
int64_t back_restart_pos;
double back_restart_dts;
bool back_restart_eof; // restart position is at EOF; overrides pos/dts
@@ -360,8 +367,10 @@ struct demux_stream {
bool back_resuming; // resuming mode (above fields are valid/used)
// Set to true if the first packet (keyframe) of a range was returned.
bool back_range_started;
- // Number of packets at start of range yet to return. -1 is used for BOF.
- int back_range_min;
+ // Number of KF packets at start of range yet to return. -1 is used for BOF.
+ int back_range_count;
+ // Number of KF packets yet to return that are marked as preroll.
+ int back_range_preroll;
// Static packet preroll count.
int back_preroll;
@@ -752,7 +761,8 @@ static void ds_clear_reader_state(struct demux_stream *ds,
ds->back_resume_dts = MP_NOPTS_VALUE;
ds->back_resuming = false;
ds->back_range_started = false;
- ds->back_range_min = 0;
+ ds->back_range_count = 0;
+ ds->back_range_preroll = 0;
}
}
@@ -1350,43 +1360,43 @@ static void find_backward_restart_pos(struct demux_stream *ds)
}
// Find where to restart demuxing. It's usually the last keyframe packet
- // before restart_pos, but might be up to back_preroll packets earlier.
-
- struct demux_packet *last_keyframe = NULL; // keyframe before back_restart
+ // before restart_pos, but might be up to back_preroll + batch keyframe
+ // packets earlier.
// (Normally, we'd just iterate backwards, but no back links.)
int num_kf = 0;
- struct demux_packet *pre_1 = NULL; // idiotic "optimization" for preroll=1
+ struct demux_packet *pre_1 = NULL; // idiotic "optimization" for total=1
for (struct demux_packet *dp = first; dp != back_restart; dp = dp->next) {
if (dp->keyframe) {
num_kf++;
- pre_1 = last_keyframe; // 1 keyframe before final last_keyframe
- last_keyframe = dp;
+ pre_1 = dp;
}
}
+ // Number of renderable keyframes to return to user.
+ // (Excludes preroll, which is decoded by user, but then discarded.)
+ int batch = MPMAX(in->opts->back_batch[ds->type], 1);
+ // Number of keyframes to return to the user in total.
+ int total = batch + ds->back_preroll;
+
+ assert(total >= 1);
+
struct demux_packet *target = NULL; // resume pos
- int got_preroll = 0; // nr. of keyframes, incl. target, excl. last_keyframe
+ // nr. of keyframes, incl. target, excl. restart_pos
+ int got_total = num_kf < total && ds->queue->is_bof ? num_kf : total;
+ int got_preroll = MPMAX(got_total - batch, 0);
- if (ds->back_preroll == 0) {
- target = last_keyframe;
- } else if (ds->back_preroll == 1) {
+ if (got_total == 1) {
target = pre_1;
- if (!target && ds->queue->is_bof)
- target = last_keyframe;
- got_preroll = target == pre_1 ? 1 : 0;
- } else if (num_kf > ds->back_preroll || ds->queue->is_bof) {
- got_preroll = ds->back_preroll;
- if (num_kf <= ds->back_preroll && ds->queue->is_bof)
- got_preroll = MPMAX(0, num_kf - 1);
+ } else if (got_total <= num_kf) {
int cur_kf = 0;
for (struct demux_packet *dp = first; dp != back_restart; dp = dp->next) {
if (dp->keyframe) {
- cur_kf++;
- if (num_kf - cur_kf == got_preroll) {
+ if (num_kf - cur_kf == got_total) {
target = dp;
break;
}
+ cur_kf++;
}
}
}
@@ -1399,7 +1409,8 @@ static void find_backward_restart_pos(struct demux_stream *ds)
MP_VERBOSE(in, "BOF for stream %d\n", ds->index);
ds->back_restarting = false;
ds->back_range_started = false;
- ds->back_range_min = -1;
+ ds->back_range_count = -1;
+ ds->back_range_preroll = 0;
ds->need_wakeup = true;
wakeup_ds(ds);
return;
@@ -1423,9 +1434,26 @@ static void find_backward_restart_pos(struct demux_stream *ds)
if (seek_pts != MP_NOPTS_VALUE)
ds->back_seek_pos = seek_pts;
+ // For next backward adjust action.
+ struct demux_packet *restart_pkt = NULL;
+ int kf_pos = 0;
+ for (struct demux_packet *dp = target; dp; dp = dp->next) {
+ if (dp->keyframe) {
+ if (kf_pos == got_preroll) {
+ restart_pkt = dp;
+ break;
+ }
+ kf_pos++;
+ }
+ }
+ assert(restart_pkt);
+ ds->back_restart_dts = restart_pkt->dts;
+ ds->back_restart_pos = restart_pkt->pos;
+
ds->back_restarting = false;
ds->back_range_started = false;
- ds->back_range_min = got_preroll + 1;
+ ds->back_range_count = got_total;
+ ds->back_range_preroll = got_preroll;
ds->need_wakeup = true;
wakeup_ds(ds);
return;
@@ -2263,7 +2291,7 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
return -1;
// Next keyframe (or EOF) was reached => step back.
- if (ds->back_range_started && !ds->back_range_min &&
+ if (ds->back_range_started && !ds->back_range_count &&
((ds->reader_head && ds->reader_head->keyframe) || eof))
{
ds->back_restarting = true;
@@ -2276,7 +2304,7 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
return 0;
}
- eof = ds->back_range_min < 0;
+ eof = ds->back_range_count < 0;
}
ds->need_wakeup = !ds->reader_head;
@@ -2301,21 +2329,20 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
pkt->next = NULL;
if (in->back_demuxing) {
- if (ds->back_range_min && pkt->keyframe)
- ds->back_range_min -= 1;
- if (ds->back_range_min) {
- pkt->back_preroll = true;
- } else if (pkt->keyframe) {
- // For next backward adjust action.
- ds->back_restart_dts = pkt->dts;
- ds->back_restart_pos = pkt->pos;
- ds->back_restart_eof = false;
+ if (pkt->keyframe) {
+ assert(ds->back_range_count > 0);
+ ds->back_range_count -= 1;
+ if (ds->back_range_preroll >= 0)
+ ds->back_range_preroll -= 1;
}
+
+ if (ds->back_range_preroll >= 0)
+ pkt->back_preroll = true;
+
if (!ds->back_range_started) {
pkt->back_restart = true;
ds->back_range_started = true;
}
- ds->back_seek_pos = MP_PTS_MIN(ds->back_seek_pos, pkt->pts);
}
double ts = MP_PTS_OR_DEF(pkt->dts, pkt->pts);