summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--demux/packet.c2
-rw-r--r--filters/f_decoder_wrapper.c638
-rw-r--r--filters/f_decoder_wrapper.h104
-rw-r--r--filters/f_demux_in.c83
-rw-r--r--filters/f_demux_in.h11
-rw-r--r--filters/f_output_chain.c14
-rw-r--r--filters/f_output_chain.h2
-rw-r--r--filters/f_utils.c2
-rw-r--r--filters/filter.h1
-rw-r--r--filters/frame.c13
-rw-r--r--filters/frame.h1
-rw-r--r--player/audio.c1
-rw-r--r--player/command.c41
-rw-r--r--player/core.h14
-rw-r--r--player/loadfile.c27
-rw-r--r--player/main.c2
-rw-r--r--player/osd.c7
-rw-r--r--player/playloop.c37
-rw-r--r--player/screenshot.c1
-rw-r--r--player/sub.c1
-rw-r--r--player/video.c228
-rw-r--r--video/decode/dec_video.c526
-rw-r--r--video/decode/dec_video.h101
-rw-r--r--video/decode/vd.h56
-rw-r--r--video/decode/vd_lavc.c218
-rw-r--r--wscript_build.py3
26 files changed, 1094 insertions, 1040 deletions
diff --git a/demux/packet.c b/demux/packet.c
index 84fda8c736..83aee6801a 100644
--- a/demux/packet.c
+++ b/demux/packet.c
@@ -78,6 +78,8 @@ struct demux_packet *new_demux_packet_from_avpacket(struct AVPacket *avpkt)
// (buf must include proper padding)
struct demux_packet *new_demux_packet_from_buf(struct AVBufferRef *buf)
{
+ if (!buf)
+ return NULL;
AVPacket pkt = {
.size = buf->size,
.data = buf->data,
diff --git a/filters/f_decoder_wrapper.c b/filters/f_decoder_wrapper.c
new file mode 100644
index 0000000000..e85621957f
--- /dev/null
+++ b/filters/f_decoder_wrapper.c
@@ -0,0 +1,638 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include <libavutil/buffer.h>
+#include <libavutil/rational.h>
+
+#include "config.h"
+#include "options/options.h"
+#include "common/msg.h"
+
+#include "osdep/timer.h"
+
+#include "demux/demux.h"
+#include "demux/packet.h"
+
+#include "common/codecs.h"
+#include "common/global.h"
+#include "common/recorder.h"
+
+#include "video/out/vo.h"
+#include "video/csputils.h"
+
+#include "demux/stheader.h"
+
+#include "f_decoder_wrapper.h"
+#include "f_demux_in.h"
+#include "filter_internal.h"
+
+struct priv {
+ struct mp_filter *f;
+ struct mp_log *log;
+ struct MPOpts *opts;
+
+ struct sh_stream *header;
+ struct mp_codec_params *codec;
+
+ struct mp_decoder *decoder;
+
+ // Demuxer output.
+ struct mp_pin *demux;
+
+ // Last PTS from decoder (set with each vd_driver->decode() call)
+ double codec_pts;
+ int num_codec_pts_problems;
+
+ // Last packet DTS from decoder (passed through from source packets)
+ double codec_dts;
+ int num_codec_dts_problems;
+
+ // PTS or DTS of packet first read
+ double first_packet_pdts;
+
+ // There was at least one packet with nonsense timestamps.
+ int has_broken_packet_pts; // <0: uninitialized, 0: no problems, 1: broken
+
+ int has_broken_decoded_pts;
+
+ int packets_without_output; // number packets sent without frame received
+
+ // Final PTS of previously decoded frame
+ double pts;
+
+ struct mp_image_params dec_format, last_format, fixed_format;
+
+ double start_pts;
+ double start, end;
+ struct demux_packet *new_segment;
+ struct mp_frame packet;
+
+ struct mp_frame decoded_coverart;
+ int coverart_returned; // 0: no, 1: coverart frame itself, 2: EOF returned
+
+ struct mp_decoder_wrapper public;
+};
+
+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->has_broken_decoded_pts = 0;
+ p->last_format = p->fixed_format = (struct mp_image_params){0};
+ p->public.dropped_frames = 0;
+ p->public.attempt_framedrops = 0;
+ p->packets_without_output = 0;
+ mp_frame_unref(&p->packet);
+ talloc_free(p->new_segment);
+ p->new_segment = NULL;
+ p->start = p->end = MP_NOPTS_VALUE;
+ p->coverart_returned = 0;
+
+ if (p->decoder)
+ mp_filter_reset(p->decoder->f);
+}
+
+static void reset(struct mp_filter *f)
+{
+ struct priv *p = f->priv;
+
+ reset_decoder(p);
+}
+
+int mp_decoder_wrapper_control(struct mp_decoder_wrapper *d,
+ enum dec_ctrl cmd, void *arg)
+{
+ struct priv *p = d->f->priv;
+ if (p->decoder && p->decoder->control)
+ return p->decoder->control(p->decoder->f, cmd, arg);
+ return CONTROL_UNKNOWN;
+}
+
+static void destroy(struct mp_filter *f)
+{
+ struct priv *p = f->priv;
+ if (p->decoder) {
+ MP_VERBOSE(f, "Uninit video.\n");
+ talloc_free(p->decoder->f);
+ p->decoder = NULL;
+ }
+ reset_decoder(p);
+ mp_frame_unref(&p->decoded_coverart);
+}
+
+struct mp_decoder_list *video_decoder_list(void)
+{
+ struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list);
+ vd_lavc.add_decoders(list);
+ return list;
+}
+
+bool mp_decoder_wrapper_reinit(struct mp_decoder_wrapper *d)
+{
+ struct priv *p = d->f->priv;
+ struct MPOpts *opts = p->opts;
+
+ if (p->decoder)
+ talloc_free(p->decoder->f);
+ p->decoder = NULL;
+
+ reset_decoder(p);
+ p->has_broken_packet_pts = -10; // needs 10 packets to reach decision
+
+ const struct mp_decoder_fns *driver = &vd_lavc;
+
+ struct mp_decoder_list *full = talloc_zero(NULL, struct mp_decoder_list);
+ driver->add_decoders(full);
+ struct mp_decoder_list *list =
+ mp_select_decoders(p->log, full, p->codec->codec, opts->video_decoders);
+ talloc_free(full);
+
+ mp_print_decoders(p->log, MSGL_V, "Codec list:", list);
+
+ for (int n = 0; n < list->num_entries; n++) {
+ struct mp_decoder_entry *sel = &list->entries[n];
+ MP_VERBOSE(p, "Opening decoder %s\n", sel->decoder);
+
+ p->decoder = driver->create(p->f, p->codec, sel->decoder);
+ if (p->decoder) {
+ p->public.decoder_desc =
+ talloc_asprintf(p, "%s (%s)", sel->decoder, sel->desc);
+ MP_VERBOSE(p, "Selected codec: %s\n", p->public.decoder_desc);
+ break;
+ }
+
+ MP_WARN(p, "Decoder init failed for %s\n", sel->decoder);
+ }
+
+ if (!p->decoder) {
+ MP_ERR(p, "Failed to initialize a decoder for codec '%s'.\n",
+ p->codec->codec ? p->codec->codec : "<?>");
+ }
+
+ talloc_free(list);
+ return !!p->decoder;
+}
+
+static bool is_valid_peak(float sig_peak)
+{
+ return !sig_peak || (sig_peak >= 1 && sig_peak <= 100);
+}
+
+static void fix_image_params(struct priv *p,
+ struct mp_image_params *params)
+{
+ struct MPOpts *opts = p->opts;
+ struct mp_image_params m = *params;
+ struct mp_codec_params *c = p->codec;
+
+ MP_VERBOSE(p, "Decoder format: %s\n", mp_image_params_to_str(params));
+ p->dec_format = *params;
+
+ // While mp_image_params normally always have to have d_w/d_h set, the
+ // decoder signals unknown bitstream aspect ratio with both set to 0.
+ bool use_container = true;
+ if (opts->aspect_method == 1 && m.p_w > 0 && m.p_h > 0) {
+ MP_VERBOSE(p, "Using bitstream aspect ratio.\n");
+ use_container = false;
+ }
+
+ if (use_container && c->par_w > 0 && c->par_h) {
+ MP_VERBOSE(p, "Using container aspect ratio.\n");
+ m.p_w = c->par_w;
+ m.p_h = c->par_h;
+ }
+
+ if (opts->movie_aspect >= 0) {
+ MP_VERBOSE(p, "Forcing user-set aspect ratio.\n");
+ if (opts->movie_aspect == 0) {
+ m.p_w = m.p_h = 1;
+ } else {
+ AVRational a = av_d2q(opts->movie_aspect, INT_MAX);
+ mp_image_params_set_dsize(&m, a.num, a.den);
+ }
+ }
+
+ // Assume square pixels if no aspect ratio is set at all.
+ if (m.p_w <= 0 || m.p_h <= 0)
+ m.p_w = m.p_h = 1;
+
+ m.rotate = p->codec->rotate;
+ m.stereo_in = p->codec->stereo_mode;
+
+ if (opts->video_rotate < 0) {
+ m.rotate = 0;
+ } else {
+ m.rotate = (m.rotate + opts->video_rotate) % 360;
+ }
+ m.stereo_out = opts->video_stereo_mode;
+
+ mp_colorspace_merge(&m.color, &c->color);
+
+ // Sanitize the HDR peak. Sadly necessary
+ if (!is_valid_peak(m.color.sig_peak)) {
+ MP_WARN(p, "Invalid HDR peak in stream: %f\n", m.color.sig_peak);
+ m.color.sig_peak = 0.0;
+ }
+
+ m.spherical = c->spherical;
+ if (m.spherical.type == MP_SPHERICAL_AUTO)
+ m.spherical.type = MP_SPHERICAL_NONE;
+
+ // Guess missing colorspace fields from metadata. This guarantees all
+ // fields are at least set to legal values afterwards.
+ mp_image_params_guess_csp(&m);
+
+ p->last_format = *params;
+ p->fixed_format = m;
+}
+
+static void process_video_frame(struct priv *p, struct mp_image *mpi)
+{
+ struct MPOpts *opts = p->opts;
+
+ // 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)
+ p->num_codec_pts_problems++;
+ p->codec_pts = mpi->pts;
+ }
+
+ if (dts != MP_NOPTS_VALUE) {
+ if (dts <= p->codec_dts)
+ p->num_codec_dts_problems++;
+ p->codec_dts = mpi->dts;
+ }
+
+ if (p->has_broken_packet_pts < 0)
+ p->has_broken_packet_pts++;
+ if (p->num_codec_pts_problems)
+ p->has_broken_packet_pts = 1;
+
+ // 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;
+
+ if (!opts->correct_pts || pts == MP_NOPTS_VALUE) {
+ double fps = p->public.fps > 0 ? p->public.fps : 25;
+
+ if (opts->correct_pts) {
+ if (p->has_broken_decoded_pts <= 1) {
+ MP_WARN(p, "No video PTS! Making something up. using "
+ "%f FPS.\n", fps);
+ if (p->has_broken_decoded_pts == 1)
+ MP_WARN(p, "Ignoring further missing PTS warnings.\n");
+ p->has_broken_decoded_pts++;
+ }
+ }
+
+ 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;
+ } else {
+ 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->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;
+}
+
+// Frames before the start timestamp can be dropped. (Used for hr-seek.)
+void mp_decoder_wrapper_set_start_pts(struct mp_decoder_wrapper *d, double pts)
+{
+ struct priv *p = d->f->priv;
+ p->start_pts = pts;
+}
+
+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);
+}
+
+static void feed_packet(struct priv *p)
+{
+ if (!p->decoder || !mp_pin_in_needs_data(p->decoder->f->pins[0]))
+ return;
+
+ if (!p->packet.type && !p->new_segment) {
+ p->packet = mp_pin_out_read(p->demux);
+ if (!p->packet.type)
+ return;
+ if (p->packet.type != MP_FRAME_EOF && p->packet.type != MP_FRAME_PACKET) {
+ MP_ERR(p, "invalid frame type from demuxer\n");
+ mp_frame_unref(&p->packet);
+ mp_filter_internal_mark_failed(p->f);
+ return;
+ }
+ }
+
+ // Flush current data if the packet is a new segment.
+ if (is_new_segment(p, p->packet)) {
+ assert(!p->new_segment);
+ p->new_segment = p->packet.data;
+ p->packet = MP_EOF_FRAME;
+ }
+
+ assert(p->packet.type == MP_FRAME_PACKET || p->packet.type == MP_FRAME_EOF);
+ struct demux_packet *packet = p->packet.data;
+
+ // For video framedropping, including parts of the hr-seek logic.
+ if (p->decoder->control) {
+ double start_pts = p->start_pts;
+ if (p->start != MP_NOPTS_VALUE && (start_pts == MP_NOPTS_VALUE ||
+ p->start > start_pts))
+ start_pts = p->start;
+
+ int framedrop_type = 0;
+
+ if (p->public.attempt_framedrops)
+ framedrop_type = 1;
+
+ if (start_pts != MP_NOPTS_VALUE && packet &&
+ packet->pts < start_pts - .005 && !p->has_broken_packet_pts)
+ framedrop_type = 2;
+
+ p->decoder->control(p->decoder->f, VDCTRL_SET_FRAMEDROP, &framedrop_type);
+ }
+
+ if (p->public.recorder_sink)
+ mp_recorder_feed_packet(p->public.recorder_sink, packet);
+
+ double pkt_pts = packet ? packet->pts : MP_NOPTS_VALUE;
+ double pkt_dts = packet ? packet->dts : MP_NOPTS_VALUE;
+
+ if (pkt_pts == MP_NOPTS_VALUE)
+ p->has_broken_packet_pts = 1;
+
+ if (packet && packet->dts == MP_NOPTS_VALUE && !p->codec->avi_dts)
+ packet->dts = packet->pts;
+
+ double pkt_pdts = pkt_pts == MP_NOPTS_VALUE ? pkt_dts : pkt_pts;
+ if (p->first_packet_pdts == MP_NOPTS_VALUE)
+ p->first_packet_pdts = pkt_pdts;
+
+ mp_pin_in_write(p->decoder->f->pins[0], p->packet);
+ p->packet = MP_NO_FRAME;
+
+ 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 {
+ MP_ERR(p, "unknown frame type from decoder\n");
+ }
+
+ return segment_ended;
+}
+
+static void read_frame(struct priv *p)
+{
+ struct mp_pin *pin = p->f->ppins[0];
+
+ if (!p->decoder || !mp_pin_in_needs_data(pin))
+ return;
+
+ if (p->decoded_coverart.type) {
+ if (p->coverart_returned == 0) {
+ mp_pin_in_write(pin, mp_frame_ref(p->decoded_coverart));
+ p->coverart_returned = 1;
+ } else if (p->coverart_returned == 1) {
+ mp_pin_in_write(pin, MP_EOF_FRAME);
+ p->coverart_returned = 2;
+ }
+ return;
+ }
+
+ struct mp_frame frame = mp_pin_out_read(p->decoder->f->pins[1]);
+ if (!frame.type)
+ return;
+
+ if (p->public.attempt_framedrops) {
+ int dropped = MPMAX(0, p->packets_without_output - 1);
+ p->public.attempt_framedrops =
+ MPMAX(0, p->public.attempt_framedrops - dropped);
+ p->public.dropped_frames += dropped;
+ }
+ p->packets_without_output = 0;
+
+ bool segment_ended = process_decoded_frame(p, &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;
+
+ 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);
+ }
+
+ p->start = new_segment->start;
+ p->end = new_segment->end;
+
+ p->packet = MAKE_FRAME(MP_FRAME_PACKET, new_segment);
+ mp_filter_internal_mark_progress(p->f);
+ }
+
+ if (!frame.type) {
+ mp_filter_internal_mark_progress(p->f); // make it retry
+ return;
+ }
+
+ if (p->header->attached_picture && frame.type == MP_FRAME_VIDEO) {
+ p->decoded_coverart = mp_frame_ref(frame);
+ p->coverart_returned = 1;
+ }
+
+ mp_pin_in_write(pin, frame);
+}
+
+static void process(struct mp_filter *f)
+{
+ struct priv *p = f->priv;
+
+ feed_packet(p);
+ read_frame(p);
+}
+
+static const struct mp_filter_info decode_wrapper_filter = {
+ .name = "decode",
+ .priv_size = sizeof(struct priv),
+ .process = process,
+ .reset = reset,
+ .destroy = destroy,
+};
+
+struct mp_decoder_wrapper *mp_decoder_wrapper_create(struct mp_filter *parent,
+ struct sh_stream *src)
+{
+ struct mp_filter *f = mp_filter_create(parent, &decode_wrapper_filter);
+ if (!f)
+ return NULL;
+
+ struct priv *p = f->priv;
+ struct mp_decoder_wrapper *w = &p->public;
+ p->opts = f->global->opts;
+ p->log = f->log;
+ p->f = f;
+ p->header = src;
+ p->codec = p->header->codec;
+ w->f = f;
+
+ mp_filter_add_pin(f, MP_PIN_OUT, "out");
+
+ if (p->header->type == STREAM_VIDEO) {
+ p->log = f->log = mp_log_new(f, parent->log, "!vd");
+
+ p->public.fps = src->codec->fps;
+
+ MP_VERBOSE(p, "Container reported FPS: %f\n", p->public.fps);
+
+ if (p->opts->force_fps) {
+ p->public.fps = p->opts->force_fps;
+ MP_INFO(p, "FPS forced to %5.3f.\n", p->public.fps);
+ MP_INFO(p, "Use --no-correct-pts to force FPS based timing.\n");
+ }
+ }
+
+ struct mp_filter *demux = mp_demux_in_create(f, p->header);
+ if (!demux)
+ goto error;
+ p->demux = demux->pins[0];
+
+ return w;
+error:
+ talloc_free(f);
+ return NULL;
+}
+
+void lavc_process(struct mp_filter *f, bool *eof_flag,
+ bool (*send)(struct mp_filter *f, struct demux_packet *pkt),
+ bool (*receive)(struct mp_filter *f, struct mp_frame *res))
+{
+ if (!mp_pin_in_needs_data(f->ppins[1]))
+ return;
+
+ struct mp_frame frame = {0};
+ if (!receive(f, &frame)) {
+ if (!*eof_flag)
+ mp_pin_in_write(f->ppins[1], MP_EOF_FRAME);
+ *eof_flag = true;
+ } else if (frame.type) {
+ *eof_flag = false;
+ mp_pin_in_write(f->ppins[1], frame);
+ } else {
+ // Need to feed a packet.
+ frame = mp_pin_out_read(f->ppins[0]);
+ struct demux_packet *pkt = NULL;
+ if (frame.type == MP_FRAME_PACKET) {
+ pkt = frame.data;
+ } else if (frame.type != MP_FRAME_EOF) {
+ if (frame.type) {
+ MP_ERR(f, "unexpected frame type\n");
+ mp_frame_unref(&frame);
+ mp_filter_internal_mark_failed(f);
+ }
+ return;
+ }
+ if (!send(f, pkt))
+ MP_WARN(f, "could not consume packet\n"); // should never happen
+ talloc_free(pkt);
+ mp_filter_internal_mark_progress(f);
+ }
+}
diff --git a/filters/f_decoder_wrapper.h b/filters/f_decoder_wrapper.h
new file mode 100644
index 0000000000..4d970bd79a
--- /dev/null
+++ b/filters/f_decoder_wrapper.h
@@ -0,0 +1,104 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include "filter.h"
+
+struct sh_stream;
+struct mp_codec_params;
+struct mp_image_params;
+struct mp_decoder_list;
+struct demux_packet;
+
+// (free with talloc_free(mp_decoder_wrapper.f)
+struct mp_decoder_wrapper {
+ // Filter with no input and 1 output, which returns the decoded data.
+ struct mp_filter *f;
+
+ // For informational purposes.
+ char *decoder_desc;
+
+ // Can be set by user.
+ struct mp_recorder_sink *recorder_sink;
+
+ // --- for STREAM_VIDEO
+
+ // FPS from demuxer or from user override
+ float fps;
+
+ // Framedrop control for playback (not used for hr seek etc.)
+ int attempt_framedrops; // try dropping this many frames
+ int dropped_frames; // total frames _probably_ dropped
+};
+
+// Create the decoder wrapper for the given stream, plus underlying decoder.
+// The src stream must be selected, and remain valid and selected until the
+// wrapper is destroyed.
+struct mp_decoder_wrapper *mp_decoder_wrapper_create(struct mp_filter *parent,
+ struct sh_stream *src);
+
+struct mp_decoder_list *video_decoder_list(void);
+
+// For precise seeking: if possible, try to drop frames up until the given PTS.
+// This is automatically unset if the target is reached, or on reset.
+void mp_decoder_wrapper_set_start_pts(struct mp_decoder_wrapper *d, double pts);
+
+enum dec_ctrl {
+ VDCTRL_FORCE_HWDEC_FALLBACK, // force software decoding fallback
+ VDCTRL_GET_HWDEC,
+ VDCTRL_REINIT,
+ VDCTRL_GET_BFRAMES,
+ // framedrop mode: 0=none, 1=standard, 2=hrseek
+ VDCTRL_SET_FRAMEDROP,
+};
+
+int mp_decoder_wrapper_control(struct mp_decoder_wrapper *d,
+ enum dec_ctrl cmd, void *arg);
+
+// Force it to reevaluate output parameters (for overrides like aspect).
+void mp_decoder_wrapper_reset_params(struct mp_decoder_wrapper *d);
+
+void mp_decoder_wrapper_get_video_dec_params(struct mp_decoder_wrapper *d,
+ struct mp_image_params *p);
+
+bool mp_decoder_wrapper_reinit(struct mp_decoder_wrapper *d);
+
+struct mp_decoder {
+ // Bidirectional filter; takes MP_FRAME_PACKET for input.
+ struct mp_filter *f;
+
+ // Can be set by decoder impl. on init for "special" functionality.
+ int (*control)(struct mp_filter *f, enum dec_ctrl cmd, void *arg);
+};
+
+struct mp_decoder_fns {
+ struct mp_decoder *(*create)(struct mp_filter *parent,
+ struct mp_codec_params *codec,
+ const char *decoder);
+ void (*add_decoders)(struct mp_decoder_list *list);
+};
+
+extern const struct mp_decoder_fns vd_lavc;
+
+// Convenience wrapper for lavc based decoders. eof_flag must be set to false
+// on init and resets.
+void lavc_process(struct mp_filter *f, bool *eof_flag,
+ bool (*send)(struct mp_filter *f, struct demux_packet *pkt),
+ bool (*receive)(struct mp_filter *f, struct mp_frame *res));
diff --git a/filters/f_demux_in.c b/filters/f_demux_in.c
new file mode 100644
index 0000000000..defe68426f
--- /dev/null
+++ b/filters/f_demux_in.c
@@ -0,0 +1,83 @@
+#include "common/common.h"
+#include "demux/demux.h"
+#include "demux/packet.h"
+
+#include "f_demux_in.h"
+#include "filter_internal.h"
+
+struct priv {
+ struct sh_stream *src;
+ bool eof_returned;
+};
+
+static void wakeup(void *ctx)
+{
+ struct mp_filter *f = ctx;
+
+ mp_filter_wakeup(f);
+}
+
+static void process(struct mp_filter *f)
+{
+ struct priv *p = f->priv;
+
+ if (!mp_pin_in_needs_data(f->ppins[0]))
+ return;
+
+ struct demux_packet *pkt = NULL;
+ if (demux_read_packet_async(p->src, &pkt) == 0)
+ return; // wait
+
+ struct mp_frame frame = {MP_FRAME_PACKET, pkt};
+ if (pkt) {
+ p->eof_returned = false;
+ } else {
+ frame.type = MP_FRAME_EOF;
+
+ // While the demuxer will repeat EOFs, filters never do that.
+ if (p->eof_returned)
+ return;
+ p->eof_returned = true;
+ }
+
+ mp_pin_in_write(f->ppins[0], frame);
+}
+
+static void reset(struct mp_filter *f)
+{
+ struct priv *p = f->priv;
+
+ p->eof_returned = false;
+}
+
+static void destroy(struct mp_filter *f)
+{
+ struct priv *p = f->priv;
+
+ demux_set_stream_wakeup_cb(p->src, NULL, NULL);
+}
+
+static const struct mp_filter_info demux_filter = {
+ .name = "demux",
+ .priv_size = sizeof(struct priv),
+ .process = process,
+ .reset = reset,
+ .destroy = destroy,
+};
+
+struct mp_filter *mp_demux_in_create(struct mp_filter *parent,
+ struct sh_stream *src)
+{
+ struct mp_filter *f = mp_filter_create(parent, &demux_filter);
+ if (!f)
+ return NULL;
+
+ struct priv *p = f->priv;
+ p->src = src;
+
+ mp_filter_add_pin(f, MP_PIN_OUT, "out");
+
+ demux_set_stream_wakeup_cb(p->src, wakeup, f);
+
+ return f;
+}
diff --git a/filters/f_demux_in.h b/filters/f_demux_in.h
new file mode 100644
index 0000000000..eebd428fee
--- /dev/null
+++ b/filters/f_demux_in.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "filter.h"
+
+struct sh_stream;
+
+// Create a filter with a single output for the given stream. The stream must
+// be selected, and remain so until the filter is destroyed. The filter will
+// set/unset the stream's wakeup callback.
+struct mp_filter *mp_demux_in_create(struct mp_filter *parent,
+ struct sh_stream *src);
diff --git a/filters/f_output_chain.c b/filters/f_output_chain.c
index 3cbbaa2ccb..e53f9eafaa 100644
--- a/filters/f_output_chain.c
+++ b/filters/f_output_chain.c
@@ -406,9 +406,13 @@ static void process(struct mp_filter *f)
if (mp_pin_can_transfer_data(p->filters_in, f->ppins[0])) {
struct mp_frame frame = mp_pin_out_read(f->ppins[0]);
- if (frame.type == MP_FRAME_EOF) {
+ p->public.got_input_eof = frame.type == MP_FRAME_EOF;
+ if (p->public.got_input_eof)
MP_VERBOSE(p, "filter input EOF\n");
- p->public.got_input_eof = true;
+
+ if (frame.type == MP_FRAME_VIDEO && p->public.update_subtitles) {
+ p->public.update_subtitles(p->public.update_subtitles_ctx,
+ mp_frame_get_pts(frame));
}
mp_pin_in_write(p->filters_in, frame);
@@ -417,10 +421,9 @@ static void process(struct mp_filter *f)
if (mp_pin_can_transfer_data(f->ppins[1], p->filters_out)) {
struct mp_frame frame = mp_pin_out_read(p->filters_out);
- if (frame.type == MP_FRAME_EOF) {
+ p->public.got_output_eof = frame.type == MP_FRAME_EOF;
+ if (p->public.got_output_eof)
MP_VERBOSE(p, "filter output EOF\n");
- p->public.got_output_eof = true;
- }
mp_pin_in_write(f->ppins[1], frame);
}
@@ -488,6 +491,7 @@ void mp_output_chain_set_vo(struct mp_output_chain *c, struct vo *vo)
p->stream_info.hwdec_devs = vo ? vo->hwdec_devs : NULL;
p->stream_info.osd = vo ? vo->osd : NULL;
p->stream_info.rotate90 = vo ? vo->driver->caps & VO_CAP_ROTATE90 : false;
+ p->stream_info.dr_vo = vo;
p->vo = vo;
update_output_caps(p);
}
diff --git a/filters/f_output_chain.h b/filters/f_output_chain.h
index 246f84181f..e5f9eff125 100644
--- a/filters/f_output_chain.h
+++ b/filters/f_output_chain.h
@@ -32,6 +32,8 @@ struct mp_output_chain {
struct mp_image_params input_params;
struct mp_image_params output_params;
double container_fps;
+ void (*update_subtitles)(void *ctx, double pts);
+ void *update_subtitles_ctx;
// --- for type==MP_OUTPUT_CHAIN_AUDIO
struct mp_aframe *input_aformat;
diff --git a/filters/f_utils.c b/filters/f_utils.c
index d15d063879..8dc185f90e 100644
--- a/filters/f_utils.c
+++ b/filters/f_utils.c
@@ -28,7 +28,9 @@ static void frame_duration_process(struct mp_filter *f)
if (p->buffered->pts != MP_NOPTS_VALUE &&
next->pts != MP_NOPTS_VALUE &&
next->pts >= p->buffered->pts)
+ {
p->buffered->pkt_duration = next->pts - p->buffered->pts;
+ }
mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_VIDEO, p->buffered));
} else {
mp_pin_out_request_data(f->ppins[0]);
diff --git a/filters/filter.h b/filters/filter.h
index eb554c29c8..9ba387691a 100644
--- a/filters/filter.h
+++ b/filters/filter.h
@@ -345,6 +345,7 @@ struct mp_stream_info {
struct mp_hwdec_devices *hwdec_devs;
struct osd_state *osd;
bool rotate90;
+ struct vo *dr_vo; // for calling vo_get_image()
};
// Search for a parent filter (including f) that has this set, and return it.
diff --git a/filters/frame.c b/filters/frame.c
index 6c5b28e77a..f1d4c98eab 100644
--- a/filters/frame.c
+++ b/filters/frame.c
@@ -2,6 +2,7 @@
#include "audio/aframe.h"
#include "common/av_common.h"
+#include "demux/packet.h"
#include "video/mp_image.h"
#include "frame.h"
@@ -11,7 +12,6 @@ struct frame_handler {
bool is_data;
bool is_signaling;
void *(*new_ref)(void *data);
- // The following must be non-NULL if new_ref is non-NULL.
double (*get_pts)(void *data);
void (*set_pts)(void *data, double pts);
AVFrame *(*new_av_ref)(void *data);
@@ -69,6 +69,11 @@ static void *audio_from_av_ref(AVFrame *data)
return mp_aframe_from_avframe(data);
}
+static void *packet_ref(void *data)
+{
+ return demux_copy_packet(data);
+}
+
static const struct frame_handler frame_handlers[] = {
[MP_FRAME_NONE] = {
.name = "none",
@@ -97,6 +102,12 @@ static const struct frame_handler frame_handlers[] = {
.from_av_ref = audio_from_av_ref,
.free = talloc_free,
},
+ [MP_FRAME_PACKET] = {
+ .name = "packet",
+ .is_data = true,
+ .new_ref = packet_ref,
+ .free = talloc_free,
+ },
};
const char *mp_frame_type_str(enum mp_frame_type t)
diff --git a/filters/frame.h b/filters/frame.h
index 135920dea7..606ca38846 100644
--- a/filters/frame.h
+++ b/filters/frame.h
@@ -6,6 +6,7 @@ enum mp_frame_type {
MP_FRAME_NONE = 0, // NULL, placeholder, no frame available (_not_ EOF)
MP_FRAME_VIDEO, // struct mp_image*
MP_FRAME_AUDIO, // struct mp_aframe*
+ MP_FRAME_PACKET, // struct demux_packet*
MP_FRAME_EOF, // NULL, signals end of stream (but frames after it can
// resume filtering!)
};
diff --git a/player/audio.c b/player/audio.c
index 014bde9ff1..ab53ab3b86 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -36,7 +36,6 @@
#include "audio/decode/dec_audio.h"
#include "audio/out/ao.h"
#include "demux/demux.h"
-#include "video/decode/dec_video.h"
#include "core.h"
#include "command.h"
diff --git a/player/command.c b/player/command.c
index 809f859280..d1de5a86ff 100644
--- a/player/command.c
+++ b/player/command.c
@@ -36,6 +36,7 @@
#include "common/codecs.h"
#include "common/msg.h"
#include "common/msg_control.h"
+#include "filters/f_decoder_wrapper.h"
#include "command.h"
#include "osdep/timer.h"
#include "common/common.h"
@@ -50,13 +51,12 @@
#include "options/m_option.h"
#include "options/m_property.h"
#include "options/m_config.h"
-#include "video/decode/vd.h"
#include "video/out/vo.h"
#include "video/csputils.h"
+#include "video/hwdec.h"
#include "audio/aframe.h"
#include "audio/format.h"
#include "audio/out/ao.h"
-#include "video/decode/dec_video.h"
#include "audio/decode/dec_audio.h"
#include "video/out/bitmap_packer.h"
#include "options/path.h"
@@ -675,10 +675,12 @@ static int mp_property_frame_drop_dec(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->vo_chain)
+ struct mp_decoder_wrapper *dec = mpctx->vo_chain && mpctx->vo_chain->track
+ ? mpctx->vo_chain->track->dec : NULL;
+ if (!dec)
return M_PROPERTY_UNAVAILABLE;
- return m_property_int_ro(action, arg, mpctx->vo_chain->video_src->dropped_frames);
+ return m_property_int_ro(action, arg, dec->dropped_frames);
}
static int mp_property_mistimed_frame_count(void *ctx, struct m_property *prop,
@@ -2182,8 +2184,8 @@ static int get_track_entry(int item, int action, void *arg, void *ctx)
track->stream ? *track->stream->codec : (struct mp_codec_params){0};
const char *decoder_desc = NULL;
- if (track->d_video)
- decoder_desc = track->d_video->decoder_desc;
+ if (track->dec)
+ decoder_desc = track->dec->decoder_desc;
if (track->d_audio)
decoder_desc = track->d_audio->decoder_desc;
@@ -2367,7 +2369,7 @@ static int mp_property_hwdec(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- struct dec_video *vd = track ? track->d_video : NULL;
+ struct mp_decoder_wrapper *dec = track ? track->dec : NULL;
struct MPOpts *opts = mpctx->opts;
if (action == M_PROPERTY_SET) {
@@ -2379,10 +2381,10 @@ static int mp_property_hwdec(void *ctx, struct m_property *prop,
talloc_free(opts->hwdec_api);
opts->hwdec_api = talloc_strdup(NULL, new);
- if (!vd)
+ if (!dec)
return M_PROPERTY_OK;
- video_vd_control(vd, VDCTRL_REINIT, NULL);
+ mp_decoder_wrapper_control(dec, VDCTRL_REINIT, NULL);
double last_pts = mpctx->last_vo_pts;
if (last_pts != MP_NOPTS_VALUE)
queue_seek(mpctx, MPSEEK_ABSOLUTE, last_pts, MPSEEK_EXACT, 0);
@@ -2397,13 +2399,13 @@ static int mp_property_hwdec_current(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- struct dec_video *vd = track ? track->d_video : NULL;
+ struct mp_decoder_wrapper *dec = track ? track->dec : NULL;
- if (!vd)
+ if (!dec)
return M_PROPERTY_UNAVAILABLE;
char *current = NULL;
- video_vd_control(vd, VDCTRL_GET_HWDEC, &current);
+ mp_decoder_wrapper_control(dec, VDCTRL_GET_HWDEC, &current);
if (!current)
current = "no";
return m_property_strdup_ro(action, arg, current);
@@ -2547,7 +2549,7 @@ static int mp_property_video_codec(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- const char *c = track && track->d_video ? track->d_video->decoder_desc : NULL;
+ const char *c = track && track->dec ? track->dec->decoder_desc : NULL;
return m_property_strdup_ro(action, arg, c);
}
@@ -2620,8 +2622,8 @@ static int mp_property_dec_imgparams(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
struct mp_image_params p = {0};
struct vo_chain *vo_c = mpctx->vo_chain;
- if (vo_c && vo_c->video_src)
- video_get_dec_params(vo_c->video_src, &p);
+ if (vo_c && vo_c->track)
+ mp_decoder_wrapper_get_video_dec_params(vo_c->track->dec, &p);
if (!p.imgfmt)
return M_PROPERTY_UNAVAILABLE;
return property_imgparams(p, action, arg);
@@ -2984,9 +2986,8 @@ static int mp_property_aspect(void *ctx, struct m_property *prop,
}
}
struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- if (track && track->d_video && aspect <= 0) {
- struct dec_video *d_video = track->d_video;
- struct mp_codec_params *c = d_video->header->codec;
+ if (track && track->stream && aspect <= 0) {
+ struct mp_codec_params *c = track->stream->codec;
if (c->disp_w && c->disp_h)
aspect = (float)c->disp_w / c->disp_h;
}
@@ -5807,8 +5808,8 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags)
if (flags & UPDATE_IMGPAR) {
struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- if (track && track->d_video) {
- video_reset_params(track->d_video);
+ if (track && track->dec) {
+ mp_decoder_wrapper_reset_params(track->dec);
mp_force_video_refresh(mpctx);
}
}
diff --git a/player/core.h b/player/core.h
index 0560393bd3..f27c30b145 100644
--- a/player/core.h
+++ b/player/core.h
@@ -153,7 +153,7 @@ struct track {
struct dec_sub *d_sub;
// Current decoding state (NULL if selected==false)
- struct dec_video *d_video;
+ struct mp_decoder_wrapper *dec;
struct dec_audio *d_audio;
// Where the decoded result goes to (one of them is not NULL if active)
@@ -170,7 +170,6 @@ struct track {
struct vo_chain {
struct mp_log *log;
- struct mp_hwdec_devices *hwdec_devs;
double container_fps;
struct mp_output_chain *filter;
@@ -178,19 +177,13 @@ struct vo_chain {
//struct vf_chain *vf;
struct vo *vo;
- // 1-element input frame queue.
- struct mp_image *input_mpi;
-
struct track *track;
struct mp_pin *filter_src;
- bool filter_src_got_eof; // whether this returned EOF last time
- struct dec_video *video_src;
+ struct mp_pin *dec_src;
// - video consists of a single picture, which should be shown only once
// - do not sync audio to video in any way
bool is_coverart;
- // Just to avoid decoding the coverart picture again after a seek.
- struct mp_image *cached_coverart;
};
// Like vo_chain, for audio.
@@ -351,7 +344,6 @@ typedef struct MPContext {
// Number of mistimed frames.
int mistimed_frames_total;
bool hrseek_active; // skip all data until hrseek_pts
- bool hrseek_framedrop; // allow decoder to drop frames before hrseek_pts
bool hrseek_lastframe; // drop everything until last frame reached
bool hrseek_backstep; // go to frame before seek target
double hrseek_pts;
@@ -366,8 +358,6 @@ typedef struct MPContext {
// How much video timing has been changed to make it match the audio
// timeline. Used for status line information only.
double total_avsync_change;
- // Used to compute the number of frames dropped in a row.
- int dropped_frames_start;
// A-V sync difference when last frame was displayed. Kept to display
// the same value if the status line is updated at a time where no new
// video frame is shown.
diff --git a/player/loadfile.c b/player/loadfile.c
index ad1b20ac8e..d35ae6ad6b 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -45,13 +45,13 @@
#include "audio/decode/dec_audio.h"
#include "audio/out/ao.h"
+#include "filters/f_decoder_wrapper.h"
#include "filters/f_lavfi.h"
#include "filters/filter_internal.h"
#include "demux/demux.h"
#include "stream/stream.h"
#include "sub/dec_sub.h"
#include "external_files.h"
-#include "video/decode/dec_video.h"
#include "video/out/vo.h"
#include "core.h"
@@ -984,9 +984,9 @@ static void cleanup_deassociated_complex_filters(struct MPContext *mpctx)
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (!(track->sink || track->vo_c || track->ao_c)) {
- if (track->d_video && !track->vo_c) {
- video_uninit(track->d_video);
- track->d_video = NULL;
+ if (track->dec && !track->vo_c) {
+ talloc_free(track->dec->f);
+ track->dec->f = NULL;
}
if (track->d_audio && !track->ao_c) {
audio_uninit(track->d_audio);
@@ -996,7 +996,7 @@ static void cleanup_deassociated_complex_filters(struct MPContext *mpctx)
}
}
- if (mpctx->vo_chain && !mpctx->vo_chain->video_src &&
+ if (mpctx->vo_chain && !mpctx->vo_chain->dec_src &&
!mpctx->vo_chain->filter_src)
{
uninit_video_chain(mpctx);
@@ -1079,18 +1079,16 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit)
struct mp_pin *pad = mp_filter_get_named_pin(mpctx->lavfi, "vo");
if (pad && mp_pin_get_dir(pad) == MP_PIN_OUT) {
if (mpctx->vo_chain) {
- if (mpctx->vo_chain->video_src) {
- MP_ERR(mpctx, "Pad vo tries to connect to already used VO.\n");
- goto done;
- }
+ MP_ERR(mpctx, "Pad vo tries to connect to already used VO.\n");
+ goto done;
} else {
reinit_video_chain_src(mpctx, NULL);
if (!mpctx->vo_chain)
goto done;
}
- mp_pin_set_manual_connection(pad, true);
struct vo_chain *vo_c = mpctx->vo_chain;
vo_c->filter_src = pad;
+ mp_pin_connect(vo_c->filter->f->pins[0], vo_c->filter_src);
}
pad = mp_filter_get_named_pin(mpctx->lavfi, "ao");
@@ -1112,8 +1110,9 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit)
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (track->sink && track->type == STREAM_VIDEO) {
- if (!track->d_video && !init_video_decoder(mpctx, track))
+ if (!track->dec && !init_video_decoder(mpctx, track))
goto done;
+ mp_pin_connect(track->sink, track->dec->f->pins[0]);
}
if (track->sink && track->type == STREAM_AUDIO) {
if (!track->d_audio && !init_audio_decoder(mpctx, track))
@@ -1587,8 +1586,8 @@ static void set_track_recorder_sink(struct track *track,
{
if (track->d_sub)
sub_set_recorder_sink(track->d_sub, sink);
- if (track->d_video)
- track->d_video->recorder_sink = sink;
+ if (track->dec)
+ track->dec->recorder_sink = sink;
if (track->d_audio)
track->d_audio->recorder_sink = sink;
track->remux_sink = sink;
@@ -1633,7 +1632,7 @@ void open_recorder(struct MPContext *mpctx, bool on_init)
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (track->stream && track->selected &&
- (track->d_sub || track->d_video || track->d_audio))
+ (track->d_sub || track->dec || track->d_audio))
{
MP_TARRAY_APPEND(NULL, streams, num_streams, track->stream);
}
diff --git a/player/main.c b/player/main.c
index 5f79e784ed..98abbc8e4f 100644
--- a/player/main.c
+++ b/player/main.c
@@ -42,6 +42,7 @@
#include "common/msg.h"
#include "common/msg_control.h"
#include "common/global.h"
+#include "filters/f_decoder_wrapper.h"
#include "options/parse_configfile.h"
#include "options/parse_commandline.h"
#include "common/playlist.h"
@@ -54,7 +55,6 @@
#include "demux/demux.h"
#include "stream/stream.h"
#include "sub/osd.h"
-#include "video/decode/dec_video.h"
#include "video/out/vo.h"
#include "core.h"
diff --git a/player/osd.c b/player/osd.c
index 875bc446db..fd6b421849 100644
--- a/player/osd.c
+++ b/player/osd.c
@@ -30,6 +30,7 @@
#include "options/options.h"
#include "common/common.h"
#include "options/m_property.h"
+#include "filters/f_decoder_wrapper.h"
#include "common/encode.h"
#include "osdep/terminal.h"
@@ -39,7 +40,6 @@
#include "stream/stream.h"
#include "sub/osd.h"
-#include "video/decode/dec_video.h"
#include "video/out/vo.h"
#include "core.h"
@@ -221,8 +221,9 @@ static char *get_term_status_msg(struct MPContext *mpctx)
talloc_free(r);
}
int64_t c = vo_get_drop_count(mpctx->video_out);
- struct dec_video *d_video = mpctx->vo_chain->video_src;
- int dropped_frames = d_video ? d_video->dropped_frames : 0;
+ struct mp_decoder_wrapper *dec = mpctx->vo_chain->track
+ ? mpctx->vo_chain->track->dec : NULL;
+ int dropped_frames = dec ? dec->dropped_frames : 0;
if (c > 0 || dropped_frames > 0) {
saddf(&line, " Dropped: %"PRId64, c);
if (dropped_frames)
diff --git a/player/playloop.c b/player/playloop.c
index a75263cc5f..748469354d 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -29,6 +29,7 @@
#include "common/common.h"
#include "common/encode.h"
#include "common/recorder.h"
+#include "filters/f_decoder_wrapper.h"
#include "options/m_config.h"
#include "options/m_property.h"
#include "common/playlist.h"
@@ -43,7 +44,6 @@
#include "demux/demux.h"
#include "stream/stream.h"
#include "sub/osd.h"
-#include "video/decode/dec_video.h"
#include "video/out/vo.h"
#include "core.h"
@@ -213,8 +213,6 @@ void add_step_frame(struct MPContext *mpctx, int dir)
void reset_playback_state(struct MPContext *mpctx)
{
for (int n = 0; n < mpctx->num_tracks; n++) {
- if (mpctx->tracks[n]->d_video)
- video_reset(mpctx->tracks[n]->d_video);
if (mpctx->tracks[n]->d_audio)
audio_reset_decoding(mpctx->tracks[n]->d_audio);
mpctx->tracks[n]->sink_eof = false;
@@ -227,7 +225,6 @@ void reset_playback_state(struct MPContext *mpctx)
reset_subtitle_state(mpctx);
mpctx->hrseek_active = false;
- mpctx->hrseek_framedrop = false;
mpctx->hrseek_lastframe = false;
mpctx->hrseek_backstep = false;
mpctx->current_seek = (struct seek_params){0};
@@ -376,13 +373,22 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
if (hr_seek) {
mpctx->hrseek_active = true;
- mpctx->hrseek_framedrop = !hr_seek_very_exact && opts->hr_seek_framedrop;
mpctx->hrseek_backstep = seek.type == MPSEEK_BACKSTEP;
mpctx->hrseek_pts = seek_pts;
+ // allow decoder to drop frames before hrseek_pts
+ bool hrseek_framedrop = !hr_seek_very_exact && opts->hr_seek_framedrop;
+
MP_VERBOSE(mpctx, "hr-seek, skipping to %f%s%s\n", mpctx->hrseek_pts,
- mpctx->hrseek_framedrop ? "" : " (no framedrop)",
+ hrseek_framedrop ? "" : " (no framedrop)",
mpctx->hrseek_backstep ? " (backstep)" : "");
+
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+ struct mp_decoder_wrapper *dec = track->dec;
+ if (dec && hrseek_framedrop)
+ mp_decoder_wrapper_set_start_pts(dec, mpctx->hrseek_pts);
+ }
}
if (mpctx->stop_play == AT_END_OF_FILE)
@@ -1079,9 +1085,9 @@ static void handle_complex_filter_decoders(struct MPContext *mpctx)
struct track *track = mpctx->tracks[n];
if (!track->selected)
continue;
- if (!track->sink || !mp_pin_in_needs_data(track->sink))
- continue;
if (track->d_audio) {
+ if (!track->sink || !mp_pin_in_needs_data(track->sink))
+ continue;
audio_work(track->d_audio);
struct mp_aframe *fr;
int res = audio_get_frame(track->d_audio, &fr);
@@ -1096,21 +1102,6 @@ static void handle_complex_filter_decoders(struct MPContext *mpctx)
mp_wakeup_core(mpctx);
}
}
- if (track->d_video) {
- video_work(track->d_video);
- struct mp_image *fr;
- int res = video_get_frame(track->d_video, &fr);
- if (res == DATA_OK) {
- mp_pin_in_write(track->sink, MAKE_FRAME(MP_FRAME_VIDEO, fr));
- track->sink_eof = false;
- } else if (res == DATA_EOF) {
- if (!track->sink_eof)
- mp_pin_in_write(track->sink, MP_EOF_FRAME);
- track->sink_eof = true;
- } else if (res == DATA_AGAIN) {
- mp_wakeup_core(mpctx);
- }
- }
}
}
diff --git a/player/screenshot.c b/player/screenshot.c
index 5234b39d8e..16ff357dc8 100644
--- a/player/screenshot.c
+++ b/player/screenshot.c
@@ -34,7 +34,6 @@
#include "options/path.h"
#include "video/mp_image.h"
#include "video/mp_image_pool.h"
-#include "video/decode/dec_video.h"
#include "video/out/vo.h"
#include "video/image_writer.h"
#include "sub/osd.h"
diff --git a/player/sub.c b/player/sub.c
index 2d644e3e00..65b8ecc78f 100644
--- a/player/sub.c
+++ b/player/sub.c
@@ -33,7 +33,6 @@
#include "sub/dec_sub.h"
#include "demux/demux.h"
#include "video/mp_image.h"
-#include "video/decode/dec_video.h"
#include "core.h"
diff --git a/player/video.c b/player/video.c
index 483043f358..48b02ecec7 100644
--- a/player/video.c
+++ b/player/video.c
@@ -39,8 +39,7 @@
#include "stream/stream.h"
#include "sub/osd.h"
#include "video/hwdec.h"
-#include "video/decode/dec_video.h"
-#include "video/decode/vd.h"
+#include "filters/f_decoder_wrapper.h"
#include "video/out/vo.h"
#include "audio/decode/dec_audio.h"
@@ -55,7 +54,6 @@ enum {
VD_PROGRESS = 1, // progress, but no output; repeat call with no waiting
VD_NEW_FRAME = 2, // the call produced a new frame
VD_WAIT = 3, // no EOF, but no output; wait until wakeup
- VD_RECONFIG = 4,
};
static const char av_desync_help_text[] =
@@ -93,17 +91,7 @@ int reinit_video_filters(struct MPContext *mpctx)
static void vo_chain_reset_state(struct vo_chain *vo_c)
{
- mp_image_unrefp(&vo_c->input_mpi);
vo_seek_reset(vo_c->vo);
-
- if (vo_c->video_src)
- video_reset(vo_c->video_src);
-
- // Prepare for continued playback after a seek.
- if (!vo_c->input_mpi && vo_c->cached_coverart)
- vo_c->input_mpi = mp_image_new_ref(vo_c->cached_coverart);
-
- vo_c->filter_src_got_eof = false;
}
void reset_video_state(struct MPContext *mpctx)
@@ -123,7 +111,6 @@ void reset_video_state(struct MPContext *mpctx)
mpctx->num_past_frames = 0;
mpctx->total_avsync_change = 0;
mpctx->last_av_difference = 0;
- mpctx->dropped_frames_start = 0;
mpctx->mistimed_frames_total = 0;
mpctx->drop_message_shown = 0;
mpctx->display_sync_drift_dir = 0;
@@ -148,16 +135,15 @@ static void vo_chain_uninit(struct vo_chain *vo_c)
if (track) {
assert(track->vo_c == vo_c);
track->vo_c = NULL;
- assert(track->d_video == vo_c->video_src);
- track->d_video = NULL;
- video_uninit(vo_c->video_src);
+ if (vo_c->dec_src)
+ assert(track->dec->f->pins[0] == vo_c->dec_src);
+ talloc_free(track->dec->f);
+ track->dec = NULL;
}
if (vo_c->filter_src)
mp_pin_disconnect(vo_c->filter_src);
- mp_image_unrefp(&vo_c->input_mpi);
- mp_image_unrefp(&vo_c->cached_coverart);
talloc_free(vo_c->filter->f);
talloc_free(vo_c);
// this does not free the VO
@@ -178,36 +164,25 @@ void uninit_video_chain(struct MPContext *mpctx)
int init_video_decoder(struct MPContext *mpctx, struct track *track)
{
- assert(!track->d_video);
+ assert(!track->dec);
if (!track->stream)
goto err_out;
- track->d_video = talloc_zero(NULL, struct dec_video);
- struct dec_video *d_video = track->d_video;
- d_video->global = mpctx->global;
- d_video->log = mp_log_new(d_video, mpctx->log, "!vd");
- d_video->opts = mpctx->opts;
- d_video->header = track->stream;
- d_video->codec = track->stream->codec;
- d_video->fps = d_video->header->codec->fps;
+ struct mp_filter *parent = mpctx->filter_root;
+ // If possible, set this as parent so the decoder gets the hwdec and DR
+ // interfaces.
// Note: at least mpv_opengl_cb_uninit_gl() relies on being able to get
// rid of all references to the VO by destroying the VO chain. Thus,
// decoders not linked to vo_chain must not use the hwdec context.
- if (mpctx->vo_chain) {
- d_video->hwdec_devs = mpctx->vo_chain->hwdec_devs;
- d_video->vo = mpctx->vo_chain->vo;
- }
-
- MP_VERBOSE(d_video, "Container reported FPS: %f\n", d_video->fps);
+ if (track->vo_c)
+ parent = track->vo_c->filter->f;
- if (d_video->opts->force_fps) {
- d_video->fps = d_video->opts->force_fps;
- MP_INFO(mpctx, "FPS forced to %5.3f.\n", d_video->fps);
- MP_INFO(mpctx, "Use --no-correct-pts to force FPS based timing.\n");
- }
+ track->dec = mp_decoder_wrapper_create(parent, track->stream);
+ if (!track->dec)
+ goto err_out;
- if (!video_init_best_codec(d_video))
+ if (!mp_decoder_wrapper_reinit(track->dec))
goto err_out;
return 1;
@@ -216,8 +191,6 @@ err_out:
if (track->sink)
mp_pin_disconnect(track->sink);
track->sink = NULL;
- video_uninit(track->d_video);
- track->d_video = NULL;
error_on_track(mpctx, track);
return 0;
}
@@ -232,6 +205,14 @@ void reinit_video_chain(struct MPContext *mpctx)
reinit_video_chain_src(mpctx, track);
}
+static void filter_update_subtitles(void *ctx, double pts)
+{
+ struct MPContext *mpctx = ctx;
+
+ if (osd_get_render_subs_in_filter(mpctx->osd))
+ update_subtitles(mpctx, pts);
+}
+
// (track=NULL creates a blank chain, used for lavfi-complex)
void reinit_video_chain_src(struct MPContext *mpctx, struct track *track)
{
@@ -266,8 +247,8 @@ void reinit_video_chain_src(struct MPContext *mpctx, struct track *track)
mp_output_chain_create(mpctx->filter_root, MP_OUTPUT_CHAIN_VIDEO);
vo_c->filter->container_fps = vo_c->container_fps;
mp_output_chain_set_vo(vo_c->filter, vo_c->vo);
-
- vo_c->hwdec_devs = vo_c->vo->hwdec_devs;
+ vo_c->filter->update_subtitles = filter_update_subtitles;
+ vo_c->filter->update_subtitles_ctx = mpctx;
if (track) {
vo_c->track = track;
@@ -275,12 +256,14 @@ void reinit_video_chain_src(struct MPContext *mpctx, struct track *track)
if (!init_video_decoder(mpctx, track))
goto err_out;
- vo_c->video_src = track->d_video;
- vo_c->container_fps = vo_c->video_src->fps;
+ vo_c->dec_src = track->dec->f->pins[0];
+ vo_c->container_fps = track->dec->fps;
vo_c->is_coverart = !!track->stream->attached_picture;
track->vo_c = vo_c;
vo_c->track = track;
+
+ mp_pin_connect(vo_c->filter->f->pins[0], vo_c->dec_src);
}
#if HAVE_ENCODING
@@ -328,129 +311,23 @@ void mp_force_video_refresh(struct MPContext *mpctx)
}
}
-static bool check_framedrop(struct MPContext *mpctx, struct vo_chain *vo_c)
+static void check_framedrop(struct MPContext *mpctx, struct vo_chain *vo_c)
{
struct MPOpts *opts = mpctx->opts;
// check for frame-drop:
if (mpctx->video_status == STATUS_PLAYING && !mpctx->paused &&
mpctx->audio_status == STATUS_PLAYING && !ao_untimed(mpctx->ao) &&
- vo_c->video_src)
+ vo_c->track && vo_c->track->dec && (opts->frame_dropping & 2))
{
float fps = vo_c->container_fps;
- double frame_time = fps > 0 ? 1.0 / fps : 0;
- // we should avoid dropping too many frames in sequence unless we
- // are too late. and we allow 100ms A-V delay here:
- int dropped_frames =
- vo_c->video_src->dropped_frames - mpctx->dropped_frames_start;
- if (mpctx->last_av_difference - 0.100 > dropped_frames * frame_time)
- return !!(opts->frame_dropping & 2);
- }
- return false;
-}
-
-// Read a packet, store decoded image into d_video->waiting_decoded_mpi
-// returns VD_* code
-static int decode_image(struct MPContext *mpctx)
-{
- struct vo_chain *vo_c = mpctx->vo_chain;
- if (vo_c->input_mpi)
- return VD_PROGRESS;
-
- int res = DATA_EOF;
- if (vo_c->filter_src) {
- struct mp_frame frame = mp_pin_out_read(vo_c->filter_src);
- if (frame.type == MP_FRAME_EOF) {
- res = DATA_EOF;
- vo_c->filter_src_got_eof = true;
- } else if (frame.type == MP_FRAME_VIDEO) {
- res = DATA_OK;
- vo_c->input_mpi = frame.data;
- vo_c->filter_src_got_eof = false;
- } else if (frame.type) {
- MP_ERR(vo_c, "unexpected frame type\n");
- mp_frame_unref(&frame);
- res = DATA_EOF;
- } else {
- res = vo_c->filter_src_got_eof ? DATA_EOF : DATA_WAIT;
- }
- } else if (vo_c->video_src) {
- struct dec_video *d_video = vo_c->video_src;
- bool hrseek = mpctx->hrseek_active && mpctx->hrseek_framedrop &&
- mpctx->video_status == STATUS_SYNCING;
- video_set_start(d_video, hrseek ? mpctx->hrseek_pts : MP_NOPTS_VALUE);
-
- video_set_framedrop(d_video, check_framedrop(mpctx, vo_c));
-
- video_work(d_video);
- res = video_get_frame(d_video, &vo_c->input_mpi);
- }
-
- switch (res) {
- case DATA_WAIT: return VD_WAIT;
- case DATA_OK:
- case DATA_STARVE:
- case DATA_AGAIN: return VD_PROGRESS;
- case DATA_EOF: return VD_EOF;
- default: abort();
- }
-}
-
-static int video_filter(struct MPContext *mpctx, bool eof)
-{
- struct vo_chain *vo_c = mpctx->vo_chain;
-
- if (vo_c->input_mpi || eof) {
- struct mp_frame frame = {MP_FRAME_VIDEO, vo_c->input_mpi};
- if (!vo_c->input_mpi) {
- frame = MP_EOF_FRAME;
- if (vo_c->filter->got_input_eof)
- return vo_c->filter->got_output_eof ? VD_EOF : VD_WAIT;
- }
- if (mp_pin_in_needs_data(vo_c->filter->f->pins[0])) {
- if (osd_get_render_subs_in_filter(mpctx->osd))
- update_subtitles(mpctx, vo_c->input_mpi->pts);
- mp_pin_in_write(vo_c->filter->f->pins[0], frame);
- vo_c->input_mpi = NULL;
- return VD_PROGRESS;
- }
- }
-
- return VD_WAIT;
-}
-
-// Make sure at least 1 filtered image is available, decode new video if needed.
-// returns VD_* code
-// A return value of VD_PROGRESS doesn't necessarily output a frame, but makes
-// the promise that calling this function again will eventually do something.
-static int video_decode_and_filter(struct MPContext *mpctx)
-{
- struct vo_chain *vo_c = mpctx->vo_chain;
-
- int r = video_filter(mpctx, false);
- if (r < 0)
- return r;
-
- if (!vo_c->input_mpi) {
- if (vo_c->cached_coverart) {
- // Don't ever decode it twice, not even after seek resets.
- // (On seek resets, input_mpi is set to the cached image.)
- r = VD_EOF;
- } else {
- // Decode a new image, or at least feed the decoder a packet.
- r = decode_image(mpctx);
- if (r == VD_WAIT)
- return r;
- }
- }
-
- if (vo_c->input_mpi) {
- if (vo_c->is_coverart && !vo_c->cached_coverart)
- vo_c->cached_coverart = mp_image_new_ref(vo_c->input_mpi);
- } else if (r == VD_EOF) {
- r = video_filter(mpctx, true);
+ // it's a crappy heuristic; avoid getting upset by incorrect fps
+ if (fps <= 20 || fps >= 500)
+ return;
+ double frame_time = 1.0 / fps;
+ // try to drop as many frames as we appear to be behind
+ vo_c->track->dec->attempt_framedrops =
+ MPCLAMP((mpctx->last_av_difference - 0.010) / frame_time, 0, 100);
}
-
- return r;
}
/* Modify video timing to match the audio timeline. There are two main
@@ -511,9 +388,6 @@ static void handle_new_frame(struct MPContext *mpctx)
mpctx->time_frame += frame_time / mpctx->video_speed;
adjust_sync(mpctx, pts, frame_time);
}
- struct dec_video *d_video = mpctx->vo_chain->video_src;
- if (d_video)
- mpctx->dropped_frames_start = d_video->dropped_frames;
MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time);
}
@@ -586,18 +460,11 @@ static int video_output_image(struct MPContext *mpctx)
int r = VD_PROGRESS;
if (needs_new_frame(mpctx)) {
// Filter a new frame.
- if (!mp_pin_out_request_data(vo_c->filter->f->pins[1])) {
- r = video_decode_and_filter(mpctx);
- if (r < 0)
- return r; // error
- }
struct mp_image *img = NULL;
struct mp_frame frame = mp_pin_out_read(vo_c->filter->f->pins[1]);
- if (frame.type == MP_FRAME_NONE && vo_c->filter->got_output_eof)
- frame = MP_EOF_FRAME;
- if (frame.type == MP_FRAME_NONE)
- return video_decode_and_filter(mpctx);
- if (frame.type == MP_FRAME_EOF) {
+ if (frame.type == MP_FRAME_NONE) {
+ r = vo_c->filter->got_output_eof ? VD_EOF : VD_WAIT;
+ } else if (frame.type == MP_FRAME_EOF) {
r = VD_EOF;
} else if (frame.type == MP_FRAME_VIDEO) {
img = frame.data;
@@ -657,11 +524,11 @@ static bool check_for_hwdec_fallback(struct MPContext *mpctx)
{
struct vo_chain *vo_c = mpctx->vo_chain;
- if (!vo_c->filter->failed_output_conversion || !vo_c->video_src)
+ if (!vo_c->filter->failed_output_conversion || !vo_c->track)
return false;
- if (video_vd_control(vo_c->video_src, VDCTRL_FORCE_HWDEC_FALLBACK, NULL)
- != CONTROL_OK)
+ if (mp_decoder_wrapper_control(vo_c->track->dec,
+ VDCTRL_FORCE_HWDEC_FALLBACK, NULL) != CONTROL_OK)
return false;
mp_output_chain_reset_harder(vo_c->filter);
@@ -1213,11 +1080,8 @@ void write_video(struct MPContext *mpctx)
// wait until VO wakes us up to get more frames
// (NB: in theory, the 1st frame after display sync mode change uses the
// wrong waiting mode)
- if (!vo_is_ready_for_frame(vo, mpctx->display_sync_active ? -1 : pts)) {
- if (video_decode_and_filter(mpctx) < 0)
- goto error;
+ if (!vo_is_ready_for_frame(vo, mpctx->display_sync_active ? -1 : pts))
return;
- }
assert(mpctx->num_next_frames >= 1);
@@ -1269,6 +1133,8 @@ void write_video(struct MPContext *mpctx)
vo_queue_frame(vo, frame);
+ check_framedrop(mpctx, vo_c);
+
// The frames were shifted down; "initialize" the new first entry.
if (mpctx->num_next_frames >= 1)
handle_new_frame(mpctx);
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c
deleted file mode 100644
index aefafbc00c..0000000000
--- a/video/decode/dec_video.c
+++ /dev/null
@@ -1,526 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <assert.h>
-
-#include <libavutil/rational.h>
-
-#include "config.h"
-#include "options/options.h"
-#include "common/msg.h"
-
-#include "osdep/timer.h"
-
-#include "stream/stream.h"
-#include "demux/demux.h"
-#include "demux/packet.h"
-
-#include "common/codecs.h"
-#include "common/recorder.h"
-
-#include "video/out/vo.h"
-#include "video/csputils.h"
-
-#include "demux/stheader.h"
-#include "video/decode/vd.h"
-
-#include "video/decode/dec_video.h"
-
-extern const vd_functions_t mpcodecs_vd_ffmpeg;
-
-/* Please do not add any new decoders here. If you want to implement a new
- * decoder, add it to libavcodec, except for wrappers around external
- * libraries and decoders requiring binary support. */
-
-const vd_functions_t * const mpcodecs_vd_drivers[] = {
- &mpcodecs_vd_ffmpeg,
- /* Please do not add any new decoders here. If you want to implement a new
- * decoder, add it to libavcodec, except for wrappers around external
- * libraries and decoders requiring binary support. */
- NULL
-};
-
-void video_reset(struct dec_video *d_video)
-{
- video_vd_control(d_video, VDCTRL_RESET, NULL);
- d_video->first_packet_pdts = MP_NOPTS_VALUE;
- d_video->start_pts = MP_NOPTS_VALUE;
- d_video->decoded_pts = MP_NOPTS_VALUE;
- d_video->codec_pts = MP_NOPTS_VALUE;
- d_video->codec_dts = MP_NOPTS_VALUE;
- d_video->has_broken_decoded_pts = 0;
- d_video->last_format = d_video->fixed_format = (struct mp_image_params){0};
- d_video->dropped_frames = 0;
- d_video->may_decoder_framedrop = false;
- d_video->current_state = DATA_AGAIN;
- mp_image_unrefp(&d_video->current_mpi);
- talloc_free(d_video->packet);
- d_video->packet = NULL;
- talloc_free(d_video->new_segment);
- d_video->new_segment = NULL;
- d_video->start = d_video->end = MP_NOPTS_VALUE;
-}
-
-int video_vd_control(struct dec_video *d_video, int cmd, void *arg)
-{
- const struct vd_functions *vd = d_video->vd_driver;
- if (vd)
- return vd->control(d_video, cmd, arg);
- return CONTROL_UNKNOWN;
-}
-
-void video_uninit(struct dec_video *d_video)
-{
- if (!d_video)
- return;
- mp_image_unrefp(&d_video->current_mpi);
- if (d_video->vd_driver) {
- MP_VERBOSE(d_video, "Uninit video.\n");
- d_video->vd_driver->uninit(d_video);
- }
- talloc_free(d_video->packet);
- talloc_free(d_video->new_segment);
- talloc_free(d_video);
-}
-
-static int init_video_codec(struct dec_video *d_video, const char *decoder)
-{
- if (!d_video->vd_driver->init(d_video, decoder)) {
- MP_VERBOSE(d_video, "Video decoder init failed.\n");
- return 0;
- }
- return 1;
-}
-
-struct mp_decoder_list *video_decoder_list(void)
-{
- struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list);
- for (int i = 0; mpcodecs_vd_drivers[i] != NULL; i++)
- mpcodecs_vd_drivers[i]->add_decoders(list);
- return list;
-}
-
-static struct mp_decoder_list *mp_select_video_decoders(struct mp_log *log,
- const char *codec,
- char *selection)
-{
- struct mp_decoder_list *list = video_decoder_list();
- struct mp_decoder_list *new = mp_select_decoders(log, list, codec, selection);
- talloc_free(list);
- return new;
-}
-
-static const struct vd_functions *find_driver(const char *name)
-{
- for (int i = 0; mpcodecs_vd_drivers[i] != NULL; i++) {
- if (strcmp(mpcodecs_vd_drivers[i]->name, name) == 0)
- return mpcodecs_vd_drivers[i];
- }
- return NULL;
-}
-
-bool video_init_best_codec(struct dec_video *d_video)
-{
- struct MPOpts *opts = d_video->opts;
-
- assert(!d_video->vd_driver);
- video_reset(d_video);
- d_video->has_broken_packet_pts = -10; // needs 10 packets to reach decision
-
- struct mp_decoder_entry *decoder = NULL;
- struct mp_decoder_list *list = mp_select_video_decoders(d_video->log,
- d_video->codec->codec,
- opts->video_decoders);
-
- mp_print_decoders(d_video->log, MSGL_V, "Codec list:", list);
-
- for (int n = 0; n < list->num_entries; n++) {
- struct mp_decoder_entry *sel = &list->entries[n];
- const struct vd_functions *driver = find_driver(sel->family);
- if (!driver)
- continue;
- MP_VERBOSE(d_video, "Opening video decoder %s\n", sel->decoder);
- d_video->vd_driver = driver;
- if (init_video_codec(d_video, sel->decoder)) {
- decoder = sel;
- break;
- }
- d_video->vd_driver = NULL;
- MP_WARN(d_video, "Video decoder init failed for %s\n", sel->decoder);
- }
-
- if (d_video->vd_driver) {
- d_video->decoder_desc =
- talloc_asprintf(d_video, "%s (%s)", decoder->decoder, decoder->desc);
- MP_VERBOSE(d_video, "Selected video codec: %s\n", d_video->decoder_desc);
- } else {
- MP_ERR(d_video, "Failed to initialize a video decoder for codec '%s'.\n",
- d_video->codec->codec);
- }
-
- talloc_free(list);
- return !!d_video->vd_driver;
-}
-
-static bool is_valid_peak(float sig_peak)
-{
- return !sig_peak || (sig_peak >= 1 && sig_peak <= 100);
-}
-
-static void fix_image_params(struct dec_video *d_video,
- struct mp_image_params *params)
-{
- struct MPOpts *opts = d_video->opts;
- struct mp_image_params p = *params;
- struct mp_codec_params *c = d_video->codec;
-
- MP_VERBOSE(d_video, "Decoder format: %s\n", mp_image_params_to_str(params));
- d_video->dec_format = *params;
-
- // While mp_image_params normally always have to have d_w/d_h set, the
- // decoder signals unknown bitstream aspect ratio with both set to 0.
- bool use_container = true;
- if (opts->aspect_method == 1 && p.p_w > 0 && p.p_h > 0) {
- MP_VERBOSE(d_video, "Using bitstream aspect ratio.\n");
- use_container = false;
- }
-
- if (use_container && c->par_w > 0 && c->par_h) {
- MP_VERBOSE(d_video, "Using container aspect ratio.\n");
- p.p_w = c->par_w;
- p.p_h = c->par_h;
- }
-
- if (opts->movie_aspect >= 0) {
- MP_VERBOSE(d_video, "Forcing user-set aspect ratio.\n");
- if (opts->movie_aspect == 0) {
- p.p_w = p.p_h = 1;
- } else {
- AVRational a = av_d2q(opts->movie_aspect, INT_MAX);
- mp_image_params_set_dsize(&p, a.num, a.den);
- }
- }
-
- // Assume square pixels if no aspect ratio is set at all.
- if (p.p_w <= 0 || p.p_h <= 0)
- p.p_w = p.p_h = 1;
-
- p.rotate = d_video->codec->rotate;
- p.stereo_in = d_video->codec->stereo_mode;
-
- if (opts->video_rotate < 0) {
- p.rotate = 0;
- } else {
- p.rotate = (p.rotate + opts->video_rotate) % 360;
- }
- p.stereo_out = opts->video_stereo_mode;
-
- mp_colorspace_merge(&p.color, &c->color);
-
- // Sanitize the HDR peak. Sadly necessary
- if (!is_valid_peak(p.color.sig_peak)) {
- MP_WARN(d_video, "Invalid HDR peak in stream: %f\n", p.color.sig_peak);
- p.color.sig_peak = 0.0;
- }
-
- p.spherical = c->spherical;
- if (p.spherical.type == MP_SPHERICAL_AUTO)
- p.spherical.type = MP_SPHERICAL_NONE;
-
- // Guess missing colorspace fields from metadata. This guarantees all
- // fields are at least set to legal values afterwards.
- mp_image_params_guess_csp(&p);
-
- d_video->last_format = *params;
- d_video->fixed_format = p;
-}
-
-static bool send_packet(struct dec_video *d_video, struct demux_packet *packet)
-{
- double pkt_pts = packet ? packet->pts : MP_NOPTS_VALUE;
- double pkt_dts = packet ? packet->dts : MP_NOPTS_VALUE;
-
- if (pkt_pts == MP_NOPTS_VALUE)
- d_video->has_broken_packet_pts = 1;
-
- bool dts_replaced = false;
- if (packet && packet->dts == MP_NOPTS_VALUE && !d_video->codec->avi_dts) {
- packet->dts = packet->pts;
- dts_replaced = true;
- }
-
- double pkt_pdts = pkt_pts == MP_NOPTS_VALUE ? pkt_dts : pkt_pts;
- if (d_video->first_packet_pdts == MP_NOPTS_VALUE)
- d_video->first_packet_pdts = pkt_pdts;
-
- MP_STATS(d_video, "start decode video");
-
- bool res = d_video->vd_driver->send_packet(d_video, packet);
-
- MP_STATS(d_video, "end decode video");
-
- // Stream recording can't deal with almost surely wrong fake DTS.
- if (dts_replaced)
- packet->dts = MP_NOPTS_VALUE;
-
- return res;
-}
-
-static bool receive_frame(struct dec_video *d_video, struct mp_image **out_image)
-{
- struct MPOpts *opts = d_video->opts;
- struct mp_image *mpi = NULL;
-
- assert(!*out_image);
-
- MP_STATS(d_video, "start decode video");
-
- bool progress = d_video->vd_driver->receive_frame(d_video, &mpi);
-
- MP_STATS(d_video, "end decode video");
-
- // Error, EOF, discarded frame, dropped frame, or initial codec delay.
- if (!mpi)
- return progress;
-
- // 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 < d_video->codec_pts)
- d_video->num_codec_pts_problems++;
- d_video->codec_pts = mpi->pts;
- }
-
- if (dts != MP_NOPTS_VALUE) {
- if (dts <= d_video->codec_dts)
- d_video->num_codec_dts_problems++;
- d_video->codec_dts = mpi->dts;
- }
-
- if (d_video->has_broken_packet_pts < 0)
- d_video->has_broken_packet_pts++;
- if (d_video->num_codec_pts_problems)
- d_video->has_broken_packet_pts = 1;
-
- // If PTS is unset, or non-monotonic, fall back to DTS.
- if ((d_video->num_codec_pts_problems > d_video->num_codec_dts_problems ||
- pts == MP_NOPTS_VALUE) && dts != MP_NOPTS_VALUE)
- pts = dts;
-
- if (!opts->correct_pts || pts == MP_NOPTS_VALUE) {
- double fps = d_video->fps > 0 ? d_video->fps : 25;
-
- if (opts->correct_pts) {
- if (d_video->has_broken_decoded_pts <= 1) {
- MP_WARN(d_video, "No video PTS! Making something up. using "
- "%f FPS.\n", fps);
- if (d_video->has_broken_decoded_pts == 1)
- MP_WARN(d_video, "Ignoring further missing PTS warnings.\n");
- d_video->has_broken_decoded_pts++;
- }
- }
-
- double frame_time = 1.0f / fps;
- double base = d_video->first_packet_pdts;
- pts = d_video->decoded_pts;
- if (pts == MP_NOPTS_VALUE) {
- pts = base == MP_NOPTS_VALUE ? 0 : base;
- } else {
- pts += frame_time;
- }
- }
-
- if (!mp_image_params_equal(&d_video->last_format, &mpi->params))
- fix_image_params(d_video, &mpi->params);
-
- mpi->params = d_video->fixed_format;
-
- mpi->pts = pts;
- d_video->decoded_pts = pts;
-
- // Compensate for incorrectly using mpeg-style DTS for avi timestamps.
- if (d_video->codec->avi_dts && opts->correct_pts &&
- mpi->pts != MP_NOPTS_VALUE && d_video->fps > 0)
- {
- int delay = -1;
- video_vd_control(d_video, VDCTRL_GET_BFRAMES, &delay);
- mpi->pts -= MPMAX(delay, 0) / d_video->fps;
- }
-
- *out_image = mpi;
- return true;
-}
-
-void video_reset_params(struct dec_video *d_video)
-{
- d_video->last_format = (struct mp_image_params){0};
-}
-
-void video_get_dec_params(struct dec_video *d_video, struct mp_image_params *p)
-{
- *p = d_video->dec_format;
-}
-
-void video_set_framedrop(struct dec_video *d_video, bool enabled)
-{
- d_video->framedrop_enabled = enabled;
-}
-
-// Frames before the start timestamp can be dropped. (Used for hr-seek.)
-void video_set_start(struct dec_video *d_video, double start_pts)
-{
- d_video->start_pts = start_pts;
-}
-
-static bool is_new_segment(struct dec_video *d_video, struct demux_packet *p)
-{
- return p->segmented &&
- (p->start != d_video->start || p->end != d_video->end ||
- p->codec != d_video->codec);
-}
-
-static void feed_packet(struct dec_video *d_video)
-{
- if (d_video->current_mpi || !d_video->vd_driver)
- return;
-
- if (!d_video->packet && !d_video->new_segment &&
- demux_read_packet_async(d_video->header, &d_video->packet) == 0)
- {
- d_video->current_state = DATA_WAIT;
- return;
- }
-
- if (d_video->packet && is_new_segment(d_video, d_video->packet)) {
- assert(!d_video->new_segment);
- d_video->new_segment = d_video->packet;
- d_video->packet = NULL;
- }
-
- double start_pts = d_video->start_pts;
- if (d_video->start != MP_NOPTS_VALUE && (start_pts == MP_NOPTS_VALUE ||
- d_video->start > start_pts))
- start_pts = d_video->start;
-
- int framedrop_type = d_video->framedrop_enabled ? 1 : 0;
- if (start_pts != MP_NOPTS_VALUE && d_video->packet &&
- d_video->packet->pts < start_pts - .005 &&
- !d_video->has_broken_packet_pts)
- {
- framedrop_type = 2;
- }
-
- d_video->vd_driver->control(d_video, VDCTRL_SET_FRAMEDROP, &framedrop_type);
-
- if (send_packet(d_video, d_video->packet)) {
- if (d_video->recorder_sink)
- mp_recorder_feed_packet(d_video->recorder_sink, d_video->packet);
-
- talloc_free(d_video->packet);
- d_video->packet = NULL;
-
- d_video->may_decoder_framedrop = framedrop_type == 1;
- }
-
- d_video->current_state = DATA_AGAIN;
-}
-
-static void read_frame(struct dec_video *d_video)
-{
- if (d_video->current_mpi || !d_video->vd_driver)
- return;
-
- bool progress = receive_frame(d_video, &d_video->current_mpi);
-
- d_video->current_state = DATA_OK;
- if (!progress) {
- d_video->current_state = DATA_EOF;
- } else if (!d_video->current_mpi) {
- if (d_video->may_decoder_framedrop)
- d_video->dropped_frames += 1;
- d_video->current_state = DATA_AGAIN;
- }
- d_video->may_decoder_framedrop = false;
-
- bool segment_ended = d_video->current_state == DATA_EOF;
-
- if (d_video->current_mpi && d_video->current_mpi->pts != MP_NOPTS_VALUE) {
- double vpts = d_video->current_mpi->pts;
- segment_ended = d_video->end != MP_NOPTS_VALUE && vpts >= d_video->end;
- if ((d_video->start != MP_NOPTS_VALUE && vpts < d_video->start)
- || segment_ended)
- {
- talloc_free(d_video->current_mpi);
- d_video->current_mpi = NULL;
- }
- }
-
- // If there's a new segment, start it as soon as we're drained/finished.
- if (segment_ended && d_video->new_segment) {
- struct demux_packet *new_segment = d_video->new_segment;
- d_video->new_segment = NULL;
-
- if (d_video->codec == new_segment->codec) {
- video_reset(d_video);
- } else {
- d_video->codec = new_segment->codec;
- d_video->vd_driver->uninit(d_video);
- d_video->vd_driver = NULL;
- video_init_best_codec(d_video);
- }
-
- d_video->start = new_segment->start;
- d_video->end = new_segment->end;
-
- d_video->packet = new_segment;
- d_video->current_state = DATA_AGAIN;
- }
-}
-
-void video_work(struct dec_video *d_video)
-{
- read_frame(d_video);
- if (!d_video->current_mpi) {
- feed_packet(d_video);
- if (d_video->current_state == DATA_WAIT)
- return;
- read_frame(d_video); // retry, to avoid redundant iterations
- }
-}
-
-// Fetch an image decoded with video_work(). Returns one of:
-// DATA_OK: *out_mpi is set to a new image
-// DATA_WAIT: waiting for demuxer; will receive a wakeup signal
-// DATA_EOF: end of file, no more frames to be expected
-// DATA_AGAIN: dropped frame or something similar
-int video_get_frame(struct dec_video *d_video, struct mp_image **out_mpi)
-{
- *out_mpi = NULL;
- if (d_video->current_mpi) {
- *out_mpi = d_video->current_mpi;
- d_video->current_mpi = NULL;
- return DATA_OK;
- }
- if (d_video->current_state == DATA_OK)
- return DATA_AGAIN;
- return d_video->current_state;
-}
diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h
deleted file mode 100644
index 426dd967db..0000000000
--- a/video/decode/dec_video.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef MPLAYER_DEC_VIDEO_H
-#define MPLAYER_DEC_VIDEO_H
-
-#include <stdbool.h>
-
-#include "demux/stheader.h"
-#include "video/hwdec.h"
-#include "video/mp_image.h"
-
-struct mp_decoder_list;
-struct vo;
-
-struct dec_video {
- struct mp_log *log;
- struct mpv_global *global;
- struct MPOpts *opts;
- const struct vd_functions *vd_driver;
- struct mp_hwdec_devices *hwdec_devs; // video output hwdec handles
- struct sh_stream *header;
- struct mp_codec_params *codec;
- struct vo *vo; // required for direct rendering into video memory
-
- char *decoder_desc;
-
- float fps; // FPS from demuxer or from user override
-
- int dropped_frames;
-
- struct mp_recorder_sink *recorder_sink;
-
- // Internal (shared with vd_lavc.c).
-
- void *priv; // for free use by vd_driver
-
- // Strictly internal (dec_video.c).
-
- // Last PTS from decoder (set with each vd_driver->decode() call)
- double codec_pts;
- int num_codec_pts_problems;
-
- // Last packet DTS from decoder (passed through from source packets)
- double codec_dts;
- int num_codec_dts_problems;
-
- // PTS or DTS of packet first read
- double first_packet_pdts;
-
- // There was at least one packet with non-sense timestamps.
- int has_broken_packet_pts; // <0: uninitialized, 0: no problems, 1: broken
-
- int has_broken_decoded_pts;
-
- // Final PTS of previously decoded image
- double decoded_pts;
-
- struct mp_image_params dec_format, last_format, fixed_format;
-
- double start_pts;
- double start, end;
- struct demux_packet *new_segment;
- struct demux_packet *packet;
- bool framedrop_enabled;
- bool may_decoder_framedrop;
- struct mp_image *current_mpi;
- int current_state;
-};
-
-struct mp_decoder_list *video_decoder_list(void);
-
-bool video_init_best_codec(struct dec_video *d_video);
-void video_uninit(struct dec_video *d_video);
-
-void video_work(struct dec_video *d_video);
-int video_get_frame(struct dec_video *d_video, struct mp_image **out_mpi);
-
-void video_set_framedrop(struct dec_video *d_video, bool enabled);
-void video_set_start(struct dec_video *d_video, double start_pts);
-
-int video_vd_control(struct dec_video *d_video, int cmd, void *arg);
-void video_reset(struct dec_video *d_video);
-void video_reset_params(struct dec_video *d_video);
-void video_get_dec_params(struct dec_video *d_video, struct mp_image_params *p);
-
-#endif /* MPLAYER_DEC_VIDEO_H */
diff --git a/video/decode/vd.h b/video/decode/vd.h
deleted file mode 100644
index 980de44bdc..0000000000
--- a/video/decode/vd.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef MPLAYER_VD_H
-#define MPLAYER_VD_H
-
-#include "video/mp_image.h"
-#include "demux/stheader.h"
-#include "dec_video.h"
-
-struct demux_packet;
-struct mp_decoder_list;
-
-/* interface of video decoder drivers */
-typedef struct vd_functions
-{
- const char *name;
- void (*add_decoders)(struct mp_decoder_list *list);
- int (*init)(struct dec_video *vd, const char *decoder);
- void (*uninit)(struct dec_video *vd);
- int (*control)(struct dec_video *vd, int cmd, void *arg);
- // Return whether or not the packet has been consumed.
- bool (*send_packet)(struct dec_video *vd, struct demux_packet *pkt);
- // Return whether decoding is still going on (false if EOF was reached).
- // Never returns false & *out_image set, but can return true with no image.
- bool (*receive_frame)(struct dec_video *vd, struct mp_image **out_image);
-} vd_functions_t;
-
-// NULL terminated array of all drivers
-extern const vd_functions_t *const mpcodecs_vd_drivers[];
-
-enum vd_ctrl {
- VDCTRL_RESET = 1, // reset decode state after seeking
- VDCTRL_FORCE_HWDEC_FALLBACK, // force software decoding fallback
- VDCTRL_GET_HWDEC,
- VDCTRL_REINIT,
- VDCTRL_GET_BFRAMES,
- // framedrop mode: 0=none, 1=standard, 2=hrseek
- VDCTRL_SET_FRAMEDROP,
-};
-
-#endif /* MPLAYER_VD_H */
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 4660d813b5..1aaca6b335 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -29,7 +29,7 @@
#include <libavutil/pixdesc.h>
#include "mpv_talloc.h"
-#include "config.h"
+#include "common/global.h"
#include "common/msg.h"
#include "options/options.h"
#include "misc/bstr.h"
@@ -38,12 +38,12 @@
#include "video/fmt-conversion.h"
-#include "vd.h"
+#include "filters/f_decoder_wrapper.h"
+#include "filters/filter_internal.h"
#include "video/hwdec.h"
#include "video/img_format.h"
#include "video/mp_image.h"
#include "video/mp_image_pool.h"
-#include "video/decode/dec_video.h"
#include "demux/demux.h"
#include "demux/stheader.h"
#include "demux/packet.h"
@@ -53,8 +53,8 @@
#include "options/m_option.h"
-static void init_avctx(struct dec_video *vd);
-static void uninit_avctx(struct dec_video *vd);
+static void init_avctx(struct mp_filter *vd);
+static void uninit_avctx(struct mp_filter *vd);
static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags);
static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
@@ -147,6 +147,7 @@ struct hwdec_info {
typedef struct lavc_ctx {
struct mp_log *log;
struct MPOpts *opts;
+ struct mp_codec_params *codec;
AVCodecContext *avctx;
AVFrame *pic;
bool use_hwdec;
@@ -154,6 +155,7 @@ typedef struct lavc_ctx {
AVRational codec_timebase;
enum AVDiscard skip_frame;
bool flushing;
+ bool eof_returned;
const char *decoder;
bool hwdec_requested;
bool hwdec_failed;
@@ -174,6 +176,7 @@ typedef struct lavc_ctx {
int max_delay_queue;
// From VO
+ struct vo *vo;
struct mp_hwdec_devices *hwdec_devs;
// Wrapped AVHWDeviceContext* used for decoding.
@@ -191,6 +194,8 @@ typedef struct lavc_ctx {
bool dr_failed;
struct mp_image_pool *dr_pool;
int dr_imgfmt, dr_w, dr_h, dr_stride_align;
+
+ struct mp_decoder public;
} vd_ffmpeg_ctx;
// Things not included in this list will be tried last, in random order.
@@ -355,9 +360,10 @@ static void add_all_hwdec_methods(struct hwdec_info **infos, int *num_infos)
qsort(*infos, *num_infos, sizeof(struct hwdec_info), hwdec_compare);
}
-static bool hwdec_codec_allowed(struct dec_video *vd, const char *codec)
+static bool hwdec_codec_allowed(struct mp_filter *vd, const char *codec)
{
- bstr s = bstr0(vd->opts->hwdec_codecs);
+ vd_ffmpeg_ctx *ctx = vd->priv;
+ bstr s = bstr0(ctx->opts->hwdec_codecs);
while (s.len) {
bstr item;
bstr_split_tok(s, ",", &item, &s);
@@ -367,10 +373,11 @@ static bool hwdec_codec_allowed(struct dec_video *vd, const char *codec)
return false;
}
-static AVBufferRef *hwdec_create_dev(struct dec_video *vd,
+static AVBufferRef *hwdec_create_dev(struct mp_filter *vd,
struct hwdec_info *hwdec,
bool autoprobe)
{
+ vd_ffmpeg_ctx *ctx = vd->priv;
assert(hwdec->lavc_device);
if (hwdec->copying) {
@@ -386,21 +393,21 @@ static AVBufferRef *hwdec_create_dev(struct dec_video *vd,
av_hwdevice_ctx_create(&ref, hwdec->lavc_device, NULL, NULL, 0);
return ref;
}
- } else if (vd->hwdec_devs) {
- hwdec_devices_request_all(vd->hwdec_devs);
- return hwdec_devices_get_lavc(vd->hwdec_devs, hwdec->lavc_device);
+ } else if (ctx->hwdec_devs) {
+ hwdec_devices_request_all(ctx->hwdec_devs);
+ return hwdec_devices_get_lavc(ctx->hwdec_devs, hwdec->lavc_device);
}
return NULL;
}
// Select if and which hwdec to use. Also makes sure to get the decode device.
-static void select_and_set_hwdec(struct dec_video *vd)
+static void select_and_set_hwdec(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- const char *codec = vd->codec->codec;
+ const char *codec = ctx->codec->codec;
- bstr opt = bstr0(vd->opts->hwdec_api);
+ bstr opt = bstr0(ctx->opts->hwdec_api);
bool hwdec_requested = !bstr_equals0(opt, "no");
bool hwdec_auto_all = bstr_equals0(opt, "auto") ||
@@ -454,8 +461,8 @@ static void select_and_set_hwdec(struct dec_video *vd)
} else if (!hwdec->copying) {
// Most likely METHOD_INTERNAL, which often use delay-loaded
// VO support as well.
- if (vd->hwdec_devs)
- hwdec_devices_request_all(vd->hwdec_devs);
+ if (ctx->hwdec_devs)
+ hwdec_devices_request_all(ctx->hwdec_devs);
}
ctx->use_hwdec = true;
@@ -503,17 +510,7 @@ int hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
return 0;
}
-static void uninit(struct dec_video *vd)
-{
- vd_ffmpeg_ctx *ctx = vd->priv;
-
- uninit_avctx(vd);
-
- pthread_mutex_destroy(&ctx->dr_lock);
- talloc_free(vd->priv);
-}
-
-static void force_fallback(struct dec_video *vd)
+static void force_fallback(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
@@ -523,7 +520,7 @@ static void force_fallback(struct dec_video *vd)
init_avctx(vd);
}
-static void reinit(struct dec_video *vd)
+static void reinit(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
@@ -537,33 +534,11 @@ static void reinit(struct dec_video *vd)
force_fallback(vd);
}
-static int init(struct dec_video *vd, const char *decoder)
-{
- vd_ffmpeg_ctx *ctx;
- ctx = vd->priv = talloc_zero(NULL, vd_ffmpeg_ctx);
- ctx->log = vd->log;
- ctx->opts = vd->opts;
- ctx->decoder = talloc_strdup(ctx, decoder);
- ctx->hwdec_devs = vd->hwdec_devs;
- ctx->hwdec_swpool = mp_image_pool_new(ctx);
- ctx->dr_pool = mp_image_pool_new(ctx);
-
- pthread_mutex_init(&ctx->dr_lock, NULL);
-
- reinit(vd);
-
- if (!ctx->avctx) {
- uninit(vd);
- return 0;
- }
- return 1;
-}
-
-static void init_avctx(struct dec_video *vd)
+static void init_avctx(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- struct vd_lavc_params *lavc_param = vd->opts->vd_lavc_params;
- struct mp_codec_params *c = vd->codec;
+ struct vd_lavc_params *lavc_param = ctx->opts->vd_lavc_params;
+ struct mp_codec_params *c = ctx->codec;
assert(!ctx->avctx);
@@ -580,7 +555,7 @@ static void init_avctx(struct dec_video *vd)
const AVCodecDescriptor *desc = avcodec_descriptor_get(lavc_codec->id);
ctx->intra_only = desc && (desc->props & AV_CODEC_PROP_INTRA_ONLY);
- ctx->codec_timebase = mp_get_codec_timebase(vd->codec);
+ ctx->codec_timebase = mp_get_codec_timebase(ctx->codec);
// This decoder does not read pkt_timebase correctly yet.
if (strstr(lavc_codec->name, "_mmal"))
@@ -632,7 +607,7 @@ static void init_avctx(struct dec_video *vd)
mp_set_avcodec_threads(vd->log, avctx, lavc_param->threads);
}
- if (!ctx->use_hwdec && vd->vo && lavc_param->dr) {
+ if (!ctx->use_hwdec && ctx->vo && lavc_param->dr) {
avctx->opaque = vd;
avctx->get_buffer2 = get_buffer2_direct;
avctx->thread_safe_callbacks = 1;
@@ -685,7 +660,7 @@ error:
uninit_avctx(vd);
}
-static void reset_avctx(struct dec_video *vd)
+static void reset_avctx(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
@@ -695,7 +670,7 @@ static void reset_avctx(struct dec_video *vd)
ctx->hwdec_request_reinit = false;
}
-static void flush_all(struct dec_video *vd)
+static void flush_all(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
@@ -714,7 +689,7 @@ static void flush_all(struct dec_video *vd)
reset_avctx(vd);
}
-static void uninit_avctx(struct dec_video *vd)
+static void uninit_avctx(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
@@ -734,7 +709,7 @@ static void uninit_avctx(struct dec_video *vd)
ctx->use_hwdec = false;
}
-static int init_generic_hwaccel(struct dec_video *vd, enum AVPixelFormat hw_fmt)
+static int init_generic_hwaccel(struct mp_filter *vd, enum AVPixelFormat hw_fmt)
{
struct lavc_ctx *ctx = vd->priv;
AVBufferRef *new_frames_ctx = NULL;
@@ -756,8 +731,8 @@ static int init_generic_hwaccel(struct dec_video *vd, enum AVPixelFormat hw_fmt)
AVHWFramesContext *new_fctx = (void *)new_frames_ctx->data;
- if (vd->opts->hwdec_image_format)
- new_fctx->sw_format = imgfmt2pixfmt(vd->opts->hwdec_image_format);
+ if (ctx->opts->hwdec_image_format)
+ new_fctx->sw_format = imgfmt2pixfmt(ctx->opts->hwdec_image_format);
// 1 surface is already included by libavcodec. The field is 0 if the
// hwaccel supports dynamic surface allocation.
@@ -808,7 +783,7 @@ error:
static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
const enum AVPixelFormat *fmt)
{
- struct dec_video *vd = avctx->opaque;
+ struct mp_filter *vd = avctx->opaque;
vd_ffmpeg_ctx *ctx = vd->priv;
MP_VERBOSE(vd, "Pixel formats supported by decoder:");
@@ -853,7 +828,7 @@ static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags)
{
- struct dec_video *vd = avctx->opaque;
+ struct mp_filter *vd = avctx->opaque;
vd_ffmpeg_ctx *p = vd->priv;
pthread_mutex_lock(&p->dr_lock);
@@ -893,7 +868,7 @@ static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags)
struct mp_image *img = mp_image_pool_get_no_alloc(p->dr_pool, imgfmt, w, h);
if (!img) {
MP_DBG(p, "Allocating new DR image...\n");
- img = vo_get_image(vd->vo, imgfmt, w, h, stride_align);
+ img = vo_get_image(p->vo, imgfmt, w, h, stride_align);
if (!img) {
MP_DBG(p, "...failed..\n");
goto fallback;
@@ -932,7 +907,7 @@ fallback:
return avcodec_default_get_buffer2(avctx, pic, flags);
}
-static bool prepare_decoding(struct dec_video *vd)
+static bool prepare_decoding(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
@@ -959,7 +934,7 @@ static bool prepare_decoding(struct dec_video *vd)
return true;
}
-static void handle_err(struct dec_video *vd)
+static void handle_err(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
struct vd_lavc_params *opts = ctx->opts->vd_lavc_params;
@@ -973,7 +948,7 @@ static void handle_err(struct dec_video *vd)
}
}
-static bool do_send_packet(struct dec_video *vd, struct demux_packet *pkt)
+static bool do_send_packet(struct mp_filter *vd, struct demux_packet *pkt)
{
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
@@ -1001,7 +976,7 @@ static bool do_send_packet(struct dec_video *vd, struct demux_packet *pkt)
return true;
}
-static bool send_packet(struct dec_video *vd, struct demux_packet *pkt)
+static bool send_packet(struct mp_filter *vd, struct demux_packet *pkt)
{
vd_ffmpeg_ctx *ctx = vd->priv;
@@ -1017,7 +992,7 @@ static bool send_packet(struct dec_video *vd, struct demux_packet *pkt)
}
// Returns whether decoder is still active (!EOF state).
-static bool decode_frame(struct dec_video *vd)
+static bool decode_frame(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
@@ -1040,15 +1015,6 @@ static bool decode_frame(struct dec_video *vd)
ctx->hwdec_fail_count = 0;
- AVFrameSideData *sd = NULL;
- sd = av_frame_get_side_data(ctx->pic, AV_FRAME_DATA_A53_CC);
- if (sd) {
- struct demux_packet *cc = new_demux_packet_from(sd->data, sd->size);
- cc->pts = vd->codec_pts;
- cc->dts = vd->codec_dts;
- demuxer_feed_caption(vd->header, cc);
- }
-
struct mp_image *mpi = mp_image_from_av_frame(ctx->pic);
if (!mpi) {
av_frame_unref(ctx->pic);
@@ -1069,12 +1035,10 @@ static bool decode_frame(struct dec_video *vd)
return true;
}
-static bool receive_frame(struct dec_video *vd, struct mp_image **out_image)
+static bool receive_frame(struct mp_filter *vd, struct mp_frame *out_frame)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- assert(!*out_image);
-
bool progress = decode_frame(vd);
if (ctx->hwdec_failed) {
@@ -1132,17 +1096,14 @@ static bool receive_frame(struct dec_video *vd, struct mp_image **out_image)
ctx->hw_probing = false;
}
- *out_image = res;
+ *out_frame = MAKE_FRAME(MP_FRAME_VIDEO, res);
return true;
}
-static int control(struct dec_video *vd, int cmd, void *arg)
+static int control(struct mp_filter *vd, enum dec_ctrl cmd, void *arg)
{
vd_ffmpeg_ctx *ctx = vd->priv;
switch (cmd) {
- case VDCTRL_RESET:
- flush_all(vd);
- return CONTROL_TRUE;
case VDCTRL_SET_FRAMEDROP:
ctx->framedrop_flags = *(int *)arg;
return CONTROL_TRUE;
@@ -1172,17 +1133,88 @@ static int control(struct dec_video *vd, int cmd, void *arg)
return CONTROL_UNKNOWN;
}
+static void process(struct mp_filter *vd)
+{
+ vd_ffmpeg_ctx *ctx = vd->priv;
+
+ lavc_process(vd, &ctx->eof_returned, send_packet, receive_frame);
+}
+
+static void reset(struct mp_filter *vd)
+{
+ vd_ffmpeg_ctx *ctx = vd->priv;
+
+ flush_all(vd);
+
+ ctx->eof_returned = false;
+ ctx->framedrop_flags = 0;
+}
+
+static void destroy(struct mp_filter *vd)
+{
+ vd_ffmpeg_ctx *ctx = vd->priv;
+
+ uninit_avctx(vd);
+
+ pthread_mutex_destroy(&ctx->dr_lock);
+}
+
+static const struct mp_filter_info vd_lavc_filter = {
+ .name = "vd_lavc",
+ .priv_size = sizeof(vd_ffmpeg_ctx),
+ .process = process,
+ .reset = reset,
+ .destroy = destroy,
+};
+
+static struct mp_decoder *create(struct mp_filter *parent,
+ struct mp_codec_params *codec,
+ const char *decoder)
+{
+ struct mp_filter *vd = mp_filter_create(parent, &vd_lavc_filter);
+ if (!vd)
+ return NULL;
+
+ mp_filter_add_pin(vd, MP_PIN_IN, "in");
+ mp_filter_add_pin(vd, MP_PIN_OUT, "out");
+
+ vd->log = mp_log_new(vd, parent->log, NULL);
+
+ vd_ffmpeg_ctx *ctx = vd->priv;
+ ctx->log = vd->log;
+ ctx->opts = vd->global->opts;
+ ctx->codec = codec;
+ ctx->decoder = talloc_strdup(ctx, decoder);
+ ctx->hwdec_swpool = mp_image_pool_new(ctx);
+ ctx->dr_pool = mp_image_pool_new(ctx);
+
+ ctx->public.f = vd;
+ ctx->public.control = control;
+
+ pthread_mutex_init(&ctx->dr_lock, NULL);
+
+ // hwdec/DR
+ struct mp_stream_info *info = mp_filter_find_stream_info(vd);
+ if (info) {
+ ctx->hwdec_devs = info->hwdec_devs;
+ ctx->vo = info->dr_vo;
+ }
+
+ reinit(vd);
+
+ if (!ctx->avctx) {
+ talloc_free(vd);
+ return NULL;
+ }
+ return &ctx->public;
+}
+
static void add_decoders(struct mp_decoder_list *list)
{
mp_add_lavc_decoders(list, AVMEDIA_TYPE_VIDEO);
}
-const struct vd_functions mpcodecs_vd_ffmpeg = {
- .name = "lavc",
+const struct mp_decoder_fns vd_lavc = {
+ .create = create,
.add_decoders = add_decoders,
- .init = init,
- .uninit = uninit,
- .control = control,
- .send_packet = send_packet,
- .receive_frame = receive_frame,
};
diff --git a/wscript_build.py b/wscript_build.py
index 24bdcea12e..1e2b2f7b45 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -250,6 +250,8 @@ def build(ctx):
( "filters/f_autoconvert.c" ),
( "filters/f_auto_filters.c" ),
+ ( "filters/f_decoder_wrapper.c" ),
+ ( "filters/f_demux_in.c" ),
( "filters/f_hwtransfer.c" ),
( "filters/f_lavfi.c" ),
( "filters/f_output_chain.c" ),
@@ -368,7 +370,6 @@ def build(ctx):
( "video/vaapi.c", "vaapi" ),
( "video/vdpau.c", "vdpau" ),
( "video/vdpau_mixer.c", "vdpau" ),
- ( "video/decode/dec_video.c"),
( "video/decode/vd_lavc.c" ),
( "video/filter/refqueue.c" ),
( "video/filter/vf_d3d11vpp.c", "d3d-hwaccel" ),