diff options
Diffstat (limited to 'filters')
-rw-r--r-- | filters/f_decoder_wrapper.c | 128 | ||||
-rw-r--r-- | filters/f_decoder_wrapper.h | 1 | ||||
-rw-r--r-- | filters/frame.c | 20 | ||||
-rw-r--r-- | filters/frame.h | 3 |
4 files changed, 139 insertions, 13 deletions
diff --git a/filters/f_decoder_wrapper.c b/filters/f_decoder_wrapper.c index 0edca5c83d..0b2a4f52ec 100644 --- a/filters/f_decoder_wrapper.c +++ b/filters/f_decoder_wrapper.c @@ -87,6 +87,13 @@ struct priv { double start, end; struct demux_packet *new_segment; struct mp_frame packet; + bool packet_fed; + int preroll_discard; + + size_t reverse_queue_byte_size; + struct mp_frame *reverse_queue; + int num_reverse_queue; + bool reverse_queue_complete; struct mp_frame decoded_coverart; int coverart_returned; // 0: no, 1: coverart frame itself, 2: EOF returned @@ -108,11 +115,19 @@ static void reset_decoder(struct priv *p) p->public.pts_reset = false; p->packets_without_output = 0; mp_frame_unref(&p->packet); + p->packet_fed = false; + p->preroll_discard = 0; talloc_free(p->new_segment); p->new_segment = NULL; p->start = p->end = MP_NOPTS_VALUE; p->coverart_returned = 0; + for (int n = 0; n < p->num_reverse_queue; n++) + mp_frame_unref(&p->reverse_queue[n]); + p->num_reverse_queue = 0; + p->reverse_queue_byte_size = 0; + p->reverse_queue_complete = false; + if (p->decoder) mp_filter_reset(p->decoder->f); } @@ -307,18 +322,22 @@ static void process_video_frame(struct priv *p, struct mp_image *mpi) struct MPOpts *opts = p->opt_cache->opts; m_config_cache_update(p->opt_cache); + int dir = p->public.play_dir; + // Note: the PTS is reordered, but the DTS is not. Both should be monotonic. double pts = mpi->pts; double dts = mpi->dts; if (pts != MP_NOPTS_VALUE) { - if (pts < p->codec_pts) + pts *= dir; + if (pts < p->codec_pts && dir > 0) p->num_codec_pts_problems++; p->codec_pts = mpi->pts; } if (dts != MP_NOPTS_VALUE) { - if (dts <= p->codec_dts) + dts *= dir; + if (dts <= p->codec_dts && dir > 0) p->num_codec_dts_problems++; p->codec_dts = mpi->dts; } @@ -401,8 +420,12 @@ void mp_decoder_wrapper_get_video_dec_params(struct mp_decoder_wrapper *d, static void process_audio_frame(struct priv *p, struct mp_aframe *aframe) { + double dir = p->public.play_dir; + double frame_pts = mp_aframe_get_pts(aframe); if (frame_pts != MP_NOPTS_VALUE) { + frame_pts *= dir; + if (p->pts != MP_NOPTS_VALUE) MP_STATS(p, "value %f audio-pts-err", p->pts - frame_pts); @@ -429,7 +452,10 @@ 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); + p->pts += mp_aframe_duration(aframe) * dir; + + if (dir < 0) + mp_aframe_set_pts(aframe, p->pts); } @@ -445,8 +471,9 @@ static bool is_new_segment(struct priv *p, struct mp_frame frame) if (frame.type != MP_FRAME_PACKET) return false; struct demux_packet *pkt = frame.data; - return pkt->segmented && (pkt->start != p->start || pkt->end != p->end || - pkt->codec != p->codec); + return (pkt->segmented && (pkt->start != p->start || pkt->end != p->end || + pkt->codec != p->codec)) || + (p->public.play_dir < 0 && pkt->back_restart && p->packet_fed); } static void feed_packet(struct priv *p) @@ -466,6 +493,9 @@ static void feed_packet(struct priv *p) } } + if (!p->packet.type) + return; + // Flush current data if the packet is a new segment. if (is_new_segment(p, p->packet)) { assert(!p->new_segment); @@ -474,7 +504,8 @@ static void feed_packet(struct priv *p) } assert(p->packet.type == MP_FRAME_PACKET || p->packet.type == MP_FRAME_EOF); - struct demux_packet *packet = p->packet.data; + struct demux_packet *packet = + p->packet.type == MP_FRAME_PACKET ? p->packet.data : NULL; // For video framedropping, including parts of the hr-seek logic. if (p->decoder->control) { @@ -488,7 +519,7 @@ static void feed_packet(struct priv *p) if (p->public.attempt_framedrops) framedrop_type = 1; - if (start_pts != MP_NOPTS_VALUE && packet && + if (start_pts != MP_NOPTS_VALUE && packet && p->public.play_dir > 0 && packet->pts < start_pts - .005 && !p->has_broken_packet_pts) framedrop_type = 2; @@ -511,8 +542,12 @@ static void feed_packet(struct priv *p) if (p->first_packet_pdts == MP_NOPTS_VALUE) p->first_packet_pdts = pkt_pdts; + if (packet && packet->back_preroll) + p->preroll_discard += 1; + mp_pin_in_write(p->decoder->f->pins[0], p->packet); p->packet = MP_NO_FRAME; + p->packet_fed = true; p->packets_without_output += 1; } @@ -549,6 +584,10 @@ static bool process_decoded_frame(struct priv *p, struct mp_frame *frame) 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 { @@ -558,6 +597,35 @@ static bool process_decoded_frame(struct priv *p, struct mp_frame *frame) return segment_ended; } +static void enqueue_backward_frame(struct priv *p, struct mp_frame frame) +{ + bool eof = frame.type == MP_FRAME_EOF; + + if (!eof) { + struct MPOpts *opts = p->opt_cache->opts; + + uint64_t queue_size = 0; + switch (p->header->type) { + case STREAM_VIDEO: queue_size = opts->video_reverse_size; break; + case STREAM_AUDIO: queue_size = opts->audio_reverse_size; break; + } + + if (p->reverse_queue_byte_size >= queue_size) { + MP_ERR(p, "Reversal queue overflow, discarding frame.\n"); + mp_frame_unref(&frame); + return; + } + + p->reverse_queue_byte_size += mp_frame_approx_size(frame); + } + + // Note: EOF (really BOF) is propagated, but not reversed. + MP_TARRAY_INSERT_AT(p, p->reverse_queue, p->num_reverse_queue, + eof ? 0 : p->num_reverse_queue, frame); + + p->reverse_queue_complete = eof; +} + static void read_frame(struct priv *p) { struct mp_pin *pin = p->f->ppins[0]; @@ -576,6 +644,14 @@ static void read_frame(struct priv *p) return; } + if (p->reverse_queue_complete && p->num_reverse_queue) { + struct mp_frame frame = p->reverse_queue[p->num_reverse_queue - 1]; + p->num_reverse_queue -= 1; + mp_pin_in_write(pin, frame); + return; + } + p->reverse_queue_complete = false; + struct mp_frame frame = mp_pin_out_read(p->decoder->f->pins[1]); if (!frame.type) return; @@ -593,23 +669,47 @@ static void read_frame(struct priv *p) } p->packets_without_output = 0; + if (p->preroll_discard > 0 && frame.type != MP_FRAME_EOF) { + p->preroll_discard -= 1; + mp_frame_unref(&frame); + mp_filter_internal_mark_progress(p->f); + return; + } + bool segment_ended = process_decoded_frame(p, &frame); + if (p->public.play_dir < 0 && frame.type) { + enqueue_backward_frame(p, frame); + frame = MP_NO_FRAME; + } + // If there's a new segment, start it as soon as we're drained/finished. if (segment_ended && p->new_segment) { struct demux_packet *new_segment = p->new_segment; p->new_segment = NULL; + struct mp_frame *reverse_queue = p->reverse_queue; + int num_reverse_queue = p->num_reverse_queue; + p->reverse_queue = NULL; + p->num_reverse_queue = 0; + reset_decoder(p); - if (p->codec != new_segment->codec) { - p->codec = new_segment->codec; - if (!mp_decoder_wrapper_reinit(&p->public)) - mp_filter_internal_mark_failed(p->f); + if (new_segment->segmented) { + if (p->codec != new_segment->codec) { + p->codec = new_segment->codec; + if (!mp_decoder_wrapper_reinit(&p->public)) + mp_filter_internal_mark_failed(p->f); + } + + p->start = new_segment->start; + p->end = new_segment->end; } - p->start = new_segment->start; - p->end = new_segment->end; + assert(!p->reverse_queue); + p->reverse_queue = reverse_queue; + p->num_reverse_queue = num_reverse_queue; + p->reverse_queue_complete = p->num_reverse_queue > 0; p->packet = MAKE_FRAME(MP_FRAME_PACKET, new_segment); mp_filter_internal_mark_progress(p->f); @@ -655,6 +755,8 @@ struct mp_decoder_wrapper *mp_decoder_wrapper_create(struct mp_filter *parent, p->codec = p->header->codec; w->f = f; + w->play_dir = 1; + struct MPOpts *opts = p->opt_cache->opts; mp_filter_add_pin(f, MP_PIN_OUT, "out"); diff --git a/filters/f_decoder_wrapper.h b/filters/f_decoder_wrapper.h index 119e0f9eb6..b69c0c7680 100644 --- a/filters/f_decoder_wrapper.h +++ b/filters/f_decoder_wrapper.h @@ -37,6 +37,7 @@ struct mp_decoder_wrapper { // Can be set by user. struct mp_recorder_sink *recorder_sink; + int play_dir; // --- for STREAM_VIDEO diff --git a/filters/frame.c b/filters/frame.c index f1d4c98eab..200e900fa2 100644 --- a/filters/frame.c +++ b/filters/frame.c @@ -14,6 +14,7 @@ struct frame_handler { void *(*new_ref)(void *data); double (*get_pts)(void *data); void (*set_pts)(void *data, double pts); + int (*approx_size)(void *data); AVFrame *(*new_av_ref)(void *data); void *(*from_av_ref)(AVFrame *data); void (*free)(void *data); @@ -34,6 +35,11 @@ static void video_set_pts(void *data, double pts) ((struct mp_image *)data)->pts = pts; } +static int video_approx_size(void *data) +{ + return mp_image_approx_byte_size(data); +} + static AVFrame *video_new_av_ref(void *data) { return mp_image_to_av_frame(data); @@ -59,6 +65,11 @@ static void audio_set_pts(void *data, double pts) mp_aframe_set_pts(data, pts); } +static int audio_approx_size(void *data) +{ + return mp_aframe_approx_byte_size(data); +} + static AVFrame *audio_new_av_ref(void *data) { return mp_aframe_to_avframe(data); @@ -88,6 +99,7 @@ static const struct frame_handler frame_handlers[] = { .new_ref = video_ref, .get_pts = video_get_pts, .set_pts = video_set_pts, + .approx_size = video_approx_size, .new_av_ref = video_new_av_ref, .from_av_ref = video_from_av_ref, .free = talloc_free, @@ -98,6 +110,7 @@ static const struct frame_handler frame_handlers[] = { .new_ref = audio_ref, .get_pts = audio_get_pts, .set_pts = audio_set_pts, + .approx_size = audio_approx_size, .new_av_ref = audio_new_av_ref, .from_av_ref = audio_from_av_ref, .free = talloc_free, @@ -160,6 +173,13 @@ void mp_frame_set_pts(struct mp_frame frame, double pts) frame_handlers[frame.type].set_pts(frame.data, pts); } +int mp_frame_approx_size(struct mp_frame frame) +{ + if (frame_handlers[frame.type].approx_size) + return frame_handlers[frame.type].approx_size(frame.data); + return 0; +} + AVFrame *mp_frame_to_av(struct mp_frame frame, struct AVRational *tb) { if (!frame_handlers[frame.type].new_av_ref) diff --git a/filters/frame.h b/filters/frame.h index 606ca38846..4c6c4ef127 100644 --- a/filters/frame.h +++ b/filters/frame.h @@ -45,6 +45,9 @@ struct mp_frame mp_frame_ref(struct mp_frame frame); double mp_frame_get_pts(struct mp_frame frame); void mp_frame_set_pts(struct mp_frame frame, double pts); +// Estimation of total size in bytes. This is for buffering purposes. +int mp_frame_approx_size(struct mp_frame frame); + struct AVFrame; struct AVRational; struct AVFrame *mp_frame_to_av(struct mp_frame frame, struct AVRational *tb); |