diff options
-rw-r--r-- | demux/packet.c | 2 | ||||
-rw-r--r-- | filters/f_decoder_wrapper.c | 638 | ||||
-rw-r--r-- | filters/f_decoder_wrapper.h | 104 | ||||
-rw-r--r-- | filters/f_demux_in.c | 83 | ||||
-rw-r--r-- | filters/f_demux_in.h | 11 | ||||
-rw-r--r-- | filters/f_output_chain.c | 14 | ||||
-rw-r--r-- | filters/f_output_chain.h | 2 | ||||
-rw-r--r-- | filters/f_utils.c | 2 | ||||
-rw-r--r-- | filters/filter.h | 1 | ||||
-rw-r--r-- | filters/frame.c | 13 | ||||
-rw-r--r-- | filters/frame.h | 1 | ||||
-rw-r--r-- | player/audio.c | 1 | ||||
-rw-r--r-- | player/command.c | 41 | ||||
-rw-r--r-- | player/core.h | 14 | ||||
-rw-r--r-- | player/loadfile.c | 27 | ||||
-rw-r--r-- | player/main.c | 2 | ||||
-rw-r--r-- | player/osd.c | 7 | ||||
-rw-r--r-- | player/playloop.c | 37 | ||||
-rw-r--r-- | player/screenshot.c | 1 | ||||
-rw-r--r-- | player/sub.c | 1 | ||||
-rw-r--r-- | player/video.c | 228 | ||||
-rw-r--r-- | video/decode/dec_video.c | 526 | ||||
-rw-r--r-- | video/decode/dec_video.h | 101 | ||||
-rw-r--r-- | video/decode/vd.h | 56 | ||||
-rw-r--r-- | video/decode/vd_lavc.c | 218 | ||||
-rw-r--r-- | wscript_build.py | 3 |
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); |