summaryrefslogtreecommitdiffstats
path: root/filters/f_decoder_wrapper.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-05-31 19:52:26 +0200
committerwm4 <wm4@nowhere>2019-09-19 20:37:05 +0200
commite8a051b3cb3da739ff79cfa8450780a157374558 (patch)
treea9139cbcd39fc11ee7829fe59ec2d6c574f16cc1 /filters/f_decoder_wrapper.c
parentd55c2a1243af1446ec40b5d02a5ebfe408b2f81b (diff)
downloadmpv-e8a051b3cb3da739ff79cfa8450780a157374558.tar.bz2
mpv-e8a051b3cb3da739ff79cfa8450780a157374558.tar.xz
f_decoder_wrapper: reorganize, fix EDL/ordered chapters backward playback
Before this commit, there was a single process_decoded_frame() function. It handled various aspects of dealing with a newly decoded frame. Move some of these to a separate process_output_frame() function. This new function is called in the order the frames are returned to the playback core. Some correct_audio_pts() (was process_audio_frame()) becomes slightly less awkward due to this, and the timestamp smoothing can actually work in backward playback mode now (thus moving p->pts out of reset_decoder()). Behavior for normal playback also changes subtly. This shouldn't matter in sane cases, but if you mix broken files, --no-correct-pts, and timeline stuff, differences in behavior might be visible. Timeline clipping (EDL/ordered chapters) works now, because it's done before "transforming" the timestamps. Audio timestamp smoothing happens after it, which is a behavior change, but should be more correct. This still runs crazy_video_pts_stuff() before everything else. On the pther hand, --no-correct-pts or missing timestamp processing is done last. But these things didn't really work with timeline before.
Diffstat (limited to 'filters/f_decoder_wrapper.c')
-rw-r--r--filters/f_decoder_wrapper.c251
1 files changed, 136 insertions, 115 deletions
diff --git a/filters/f_decoder_wrapper.c b/filters/f_decoder_wrapper.c
index de871f5d57..f7a1def177 100644
--- a/filters/f_decoder_wrapper.c
+++ b/filters/f_decoder_wrapper.c
@@ -110,7 +110,6 @@ static void reset_decoder(struct priv *p)
{
p->first_packet_pdts = MP_NOPTS_VALUE;
p->start_pts = MP_NOPTS_VALUE;
- p->pts = MP_NOPTS_VALUE;
p->codec_pts = MP_NOPTS_VALUE;
p->codec_dts = MP_NOPTS_VALUE;
p->num_codec_pts_problems = 0;
@@ -132,6 +131,7 @@ static void reset(struct mp_filter *f)
{
struct priv *p = f->priv;
+ p->pts = MP_NOPTS_VALUE;
p->last_format = p->fixed_format = (struct mp_image_params){0};
p->public.dropped_frames = 0;
p->public.attempt_framedrops = 0;
@@ -324,26 +324,34 @@ static void fix_image_params(struct priv *p,
p->fixed_format = m;
}
-static void process_video_frame(struct priv *p, struct mp_image *mpi)
+void mp_decoder_wrapper_reset_params(struct mp_decoder_wrapper *d)
{
- struct MPOpts *opts = p->opt_cache->opts;
+ struct priv *p = d->f->priv;
+ p->last_format = (struct mp_image_params){0};
+}
- int dir = p->public.play_dir;
+void mp_decoder_wrapper_get_video_dec_params(struct mp_decoder_wrapper *d,
+ struct mp_image_params *m)
+{
+ struct priv *p = d->f->priv;
+ *m = p->dec_format;
+}
- // Note: the PTS is reordered, but the DTS is not. Both should be monotonic.
- double pts = mpi->pts;
- double dts = mpi->dts;
+// This code exists only because multimedia is so god damn crazy. In a sane
+// world, the video decoder would always output a video frame with a valid PTS;
+// this deals with cases where it doesn't.
+static void crazy_video_pts_stuff(struct priv *p, struct mp_image *mpi)
+{
+ // Note: the PTS is reordered, but the DTS is not. Both must be monotonic.
- if (pts != MP_NOPTS_VALUE) {
- pts *= dir;
- if (pts < p->codec_pts && dir > 0)
+ if (mpi->pts != MP_NOPTS_VALUE) {
+ if (mpi->pts < p->codec_pts)
p->num_codec_pts_problems++;
p->codec_pts = mpi->pts;
}
- if (dts != MP_NOPTS_VALUE) {
- dts *= dir;
- if (dts <= p->codec_dts && dir > 0)
+ if (mpi->dts != MP_NOPTS_VALUE) {
+ if (mpi->dts <= p->codec_dts)
p->num_codec_dts_problems++;
p->codec_dts = mpi->dts;
}
@@ -355,10 +363,84 @@ static void process_video_frame(struct priv *p, struct mp_image *mpi)
// If PTS is unset, or non-monotonic, fall back to DTS.
if ((p->num_codec_pts_problems > p->num_codec_dts_problems ||
- pts == MP_NOPTS_VALUE) && dts != MP_NOPTS_VALUE)
- pts = dts;
+ mpi->pts == MP_NOPTS_VALUE) && mpi->dts != MP_NOPTS_VALUE)
+ mpi->pts = mpi->dts;
- if (!opts->correct_pts || pts == MP_NOPTS_VALUE) {
+ // Compensate for incorrectly using mpeg-style DTS for avi timestamps.
+ if (p->decoder && p->decoder->control && p->codec->avi_dts &&
+ mpi->pts != MP_NOPTS_VALUE && p->public.fps > 0)
+ {
+ int delay = -1;
+ p->decoder->control(p->decoder->f, VDCTRL_GET_BFRAMES, &delay);
+ mpi->pts -= MPMAX(delay, 0) / p->public.fps;
+ }
+}
+
+// Return true if the current frame is outside segment range.
+static bool process_decoded_frame(struct priv *p, struct mp_frame *frame)
+{
+ if (frame->type == MP_FRAME_EOF) {
+ // if we were just draining current segment, don't propagate EOF
+ if (p->new_segment)
+ mp_frame_unref(frame);
+ return true;
+ }
+
+ bool segment_ended = false;
+
+ if (frame->type == MP_FRAME_VIDEO) {
+ struct mp_image *mpi = frame->data;
+
+ crazy_video_pts_stuff(p, mpi);
+
+ struct demux_packet *ccpkt = new_demux_packet_from_buf(mpi->a53_cc);
+ if (ccpkt) {
+ av_buffer_unref(&mpi->a53_cc);
+ ccpkt->pts = mpi->pts;
+ ccpkt->dts = mpi->dts;
+ demuxer_feed_caption(p->header, ccpkt);
+ }
+
+ // Stop hr-seek logic.
+ if (mpi->pts == MP_NOPTS_VALUE || mpi->pts >= p->start_pts)
+ p->start_pts = MP_NOPTS_VALUE;
+
+ if (mpi->pts != MP_NOPTS_VALUE) {
+ segment_ended = p->end != MP_NOPTS_VALUE && mpi->pts >= p->end;
+ if ((p->start != MP_NOPTS_VALUE && mpi->pts < p->start) ||
+ segment_ended)
+ {
+ mp_frame_unref(frame);
+ goto done;
+ }
+ }
+ } else if (frame->type == MP_FRAME_AUDIO) {
+ struct mp_aframe *aframe = frame->data;
+
+ mp_aframe_clip_timestamps(aframe, p->start, p->end);
+ double pts = mp_aframe_get_pts(aframe);
+ if (pts != MP_NOPTS_VALUE && p->start != MP_NOPTS_VALUE)
+ segment_ended = pts >= p->end;
+
+ if (mp_aframe_get_size(aframe) == 0) {
+ mp_frame_unref(frame);
+ goto done;
+ }
+ } else {
+ MP_ERR(p, "unknown frame type from decoder\n");
+ }
+
+done:
+ return segment_ended;
+}
+
+static void correct_video_pts(struct priv *p, struct mp_image *mpi)
+{
+ struct MPOpts *opts = p->opt_cache->opts;
+
+ mpi->pts *= p->public.play_dir;
+
+ if (!opts->correct_pts || mpi->pts == MP_NOPTS_VALUE) {
double fps = p->public.fps > 0 ? p->public.fps : 25;
if (opts->correct_pts) {
@@ -373,64 +455,27 @@ static void process_video_frame(struct priv *p, struct mp_image *mpi)
double frame_time = 1.0f / fps;
double base = p->first_packet_pdts;
- pts = p->pts;
- if (pts == MP_NOPTS_VALUE) {
- pts = base == MP_NOPTS_VALUE ? 0 : base;
+ mpi->pts = p->pts;
+ if (mpi->pts == MP_NOPTS_VALUE) {
+ mpi->pts = base == MP_NOPTS_VALUE ? 0 : base;
} else {
- pts += frame_time;
+ mpi->pts += frame_time;
}
}
- if (!mp_image_params_equal(&p->last_format, &mpi->params))
- fix_image_params(p, &mpi->params);
-
- mpi->params = p->fixed_format;
- mpi->nominal_fps = p->public.fps;
-
- mpi->pts = pts;
- p->pts = pts;
-
- // Compensate for incorrectly using mpeg-style DTS for avi timestamps.
- if (p->decoder && p->decoder->control && p->codec->avi_dts &&
- opts->correct_pts && mpi->pts != MP_NOPTS_VALUE && p->public.fps > 0)
- {
- int delay = -1;
- p->decoder->control(p->decoder->f, VDCTRL_GET_BFRAMES, &delay);
- mpi->pts -= MPMAX(delay, 0) / p->public.fps;
- }
-
- struct demux_packet *ccpkt = new_demux_packet_from_buf(mpi->a53_cc);
- if (ccpkt) {
- av_buffer_unref(&mpi->a53_cc);
- ccpkt->pts = mpi->pts;
- ccpkt->dts = mpi->dts;
- demuxer_feed_caption(p->header, ccpkt);
- }
-
- if (mpi->pts == MP_NOPTS_VALUE || mpi->pts >= p->start_pts)
- p->start_pts = MP_NOPTS_VALUE;
-}
-
-void mp_decoder_wrapper_reset_params(struct mp_decoder_wrapper *d)
-{
- struct priv *p = d->f->priv;
- p->last_format = (struct mp_image_params){0};
-}
-
-void mp_decoder_wrapper_get_video_dec_params(struct mp_decoder_wrapper *d,
- struct mp_image_params *m)
-{
- struct priv *p = d->f->priv;
- *m = p->dec_format;
+ p->pts = mpi->pts;
}
-static void process_audio_frame(struct priv *p, struct mp_aframe *aframe)
+static void correct_audio_pts(struct priv *p, struct mp_aframe *aframe)
{
double dir = p->public.play_dir;
double frame_pts = mp_aframe_get_pts(aframe);
+ double frame_len = mp_aframe_duration(aframe);
+
if (frame_pts != MP_NOPTS_VALUE) {
- frame_pts *= dir;
+ if (dir < 0)
+ frame_pts = -(frame_pts + frame_len);
if (p->pts != MP_NOPTS_VALUE)
MP_STATS(p, "value %f audio-pts-err", p->pts - frame_pts);
@@ -458,10 +503,29 @@ static void process_audio_frame(struct priv *p, struct mp_aframe *aframe)
mp_aframe_set_pts(aframe, p->pts);
if (p->pts != MP_NOPTS_VALUE)
- p->pts += mp_aframe_duration(aframe) * dir;
+ p->pts += frame_len;
+}
+
+static void process_output_frame(struct priv *p, struct mp_frame frame)
+{
+ if (frame.type == MP_FRAME_VIDEO) {
+ struct mp_image *mpi = frame.data;
+
+ correct_video_pts(p, mpi);
+
+ if (!mp_image_params_equal(&p->last_format, &mpi->params))
+ fix_image_params(p, &mpi->params);
+
+ mpi->params = p->fixed_format;
+ mpi->nominal_fps = p->public.fps;
+ } else if (frame.type == MP_FRAME_AUDIO) {
+ struct mp_aframe *aframe = frame.data;
- if (dir < 0)
- mp_aframe_set_pts(aframe, p->pts);
+ if (p->public.play_dir < 0 && !mp_aframe_reverse(aframe))
+ MP_ERR(p, "Couldn't reverse audio frame.\n");
+
+ correct_audio_pts(p, aframe);
+ }
}
void mp_decoder_wrapper_set_start_pts(struct mp_decoder_wrapper *d, double pts)
@@ -561,51 +625,6 @@ static void feed_packet(struct priv *p)
p->packets_without_output += 1;
}
-// Return true if the current frame is outside segment range.
-static bool process_decoded_frame(struct priv *p, struct mp_frame *frame)
-{
- if (frame->type == MP_FRAME_EOF) {
- // if we were just draining current segment, don't propagate EOF
- if (p->new_segment)
- mp_frame_unref(frame);
- return true;
- }
-
- bool segment_ended = false;
-
- if (frame->type == MP_FRAME_VIDEO) {
- struct mp_image *mpi = frame->data;
-
- process_video_frame(p, mpi);
-
- if (mpi->pts != MP_NOPTS_VALUE) {
- double vpts = mpi->pts;
- segment_ended = p->end != MP_NOPTS_VALUE && vpts >= p->end;
- if ((p->start != MP_NOPTS_VALUE && vpts < p->start) || segment_ended)
- mp_frame_unref(frame);
- }
- } else if (frame->type == MP_FRAME_AUDIO) {
- struct mp_aframe *aframe = frame->data;
-
- process_audio_frame(p, aframe);
-
- mp_aframe_clip_timestamps(aframe, p->start, p->end);
- double pts = mp_aframe_get_pts(aframe);
- if (pts != MP_NOPTS_VALUE && p->start != MP_NOPTS_VALUE)
- segment_ended = pts >= p->end;
-
- if (p->public.play_dir < 0 && !mp_aframe_reverse(aframe))
- MP_ERR(p, "Couldn't reverse audio frame.\n");
-
- if (mp_aframe_get_size(aframe) == 0)
- mp_frame_unref(frame);
- } else {
- MP_ERR(p, "unknown frame type from decoder\n");
- }
-
- return segment_ended;
-}
-
static void enqueue_backward_frame(struct priv *p, struct mp_frame frame)
{
bool eof = frame.type == MP_FRAME_EOF;
@@ -638,6 +657,7 @@ static void enqueue_backward_frame(struct priv *p, struct mp_frame frame)
static void read_frame(struct priv *p)
{
struct mp_pin *pin = p->f->ppins[0];
+ struct mp_frame frame = {0};
if (!p->decoder || !mp_pin_in_needs_data(pin))
return;
@@ -654,14 +674,13 @@ static void read_frame(struct priv *p)
}
if (p->reverse_queue_complete && p->num_reverse_queue) {
- struct mp_frame frame = p->reverse_queue[p->num_reverse_queue - 1];
+ frame = p->reverse_queue[p->num_reverse_queue - 1];
p->num_reverse_queue -= 1;
- mp_pin_in_write(pin, frame);
- return;
+ goto output_frame;
}
p->reverse_queue_complete = false;
- struct mp_frame frame = mp_pin_out_read(p->decoder->f->pins[1]);
+ frame = mp_pin_out_read(p->decoder->f->pins[1]);
if (!frame.type)
return;
@@ -726,6 +745,8 @@ static void read_frame(struct priv *p)
return;
}
+output_frame:
+ process_output_frame(p, frame);
mp_pin_in_write(pin, frame);
}