diff options
author | wm4 <wm4@nowhere> | 2018-01-26 04:36:47 +0100 |
---|---|---|
committer | Kevin Mitchell <kevmitch@gmail.com> | 2018-01-30 03:10:27 -0800 |
commit | 0366ba253159a551127a7d1817a4a000e078d322 (patch) | |
tree | 6264357813da67a293b20f8685031f21371debdb | |
parent | b9f804b566c4c528714e4ec5e63675ad7ba5fefd (diff) |
player: replace old lavfi wrapper with new filter code
lavfi.c is not necessary anymore, because f_lavfi.c (which was actually
converted from it) can be used now.
-rw-r--r-- | DOCS/interface-changes.rst | 5 | ||||
-rw-r--r-- | player/audio.c | 22 | ||||
-rw-r--r-- | player/core.h | 16 | ||||
-rw-r--r-- | player/lavfi.c | 819 | ||||
-rw-r--r-- | player/lavfi.h | 35 | ||||
-rw-r--r-- | player/loadfile.c | 61 | ||||
-rw-r--r-- | player/playloop.c | 36 | ||||
-rw-r--r-- | player/video.c | 22 | ||||
-rw-r--r-- | wscript_build.py | 1 |
9 files changed, 108 insertions, 909 deletions
diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst index 64dd36670a..da47ef6ba8 100644 --- a/DOCS/interface-changes.rst +++ b/DOCS/interface-changes.rst @@ -65,6 +65,11 @@ Interface changes audio formats are not convertible (such as switching between PCM and AC3 passthrough) - remove out-format sub-parameter from "format" audio filter (no replacement) + - --lavfi-complex now requires uniquely named filter pads. In addition, + unconnected filter pads are not allowed anymore (that means every filter + pad must be connected either to another filter, or to a video/audio track + or video/audio output). If they are disconnected at runtime, the stream + will probably stall. --- mpv 0.28.0 --- - rename --hwdec=mediacodec option to mediacodec-copy, to reflect conventions followed by other hardware video decoding APIs diff --git a/player/audio.c b/player/audio.c index d8f43278b0..014bde9ff1 100644 --- a/player/audio.c +++ b/player/audio.c @@ -193,6 +193,8 @@ static void ao_chain_reset_state(struct ao_chain *ao_c) if (ao_c->audio_src) audio_reset_decoding(ao_c->audio_src); + + ao_c->filter_src_got_eof = false; } void reset_audio_state(struct MPContext *mpctx) @@ -231,7 +233,7 @@ static void ao_chain_uninit(struct ao_chain *ao_c) } if (ao_c->filter_src) - lavfi_set_connected(ao_c->filter_src, false); + mp_pin_disconnect(ao_c->filter_src); talloc_free(ao_c->filter->f); talloc_free(ao_c->input_frame); @@ -428,7 +430,7 @@ int init_audio_decoder(struct MPContext *mpctx, struct track *track) init_error: if (track->sink) - lavfi_set_connected(track->sink, false); + mp_pin_disconnect(track->sink); track->sink = NULL; audio_uninit(track->d_audio); track->d_audio = NULL; @@ -708,7 +710,21 @@ static int decode_new_frame(struct ao_chain *ao_c) int res = DATA_EOF; if (ao_c->filter_src) { - res = lavfi_request_frame_a(ao_c->filter_src, &ao_c->input_frame); + struct mp_frame frame = mp_pin_out_read(ao_c->filter_src); + if (frame.type == MP_FRAME_EOF) { + res = DATA_EOF; + ao_c->filter_src_got_eof = true; + } else if (frame.type == MP_FRAME_AUDIO) { + res = DATA_OK; + ao_c->input_frame = frame.data; + ao_c->filter_src_got_eof = false; + } else if (frame.type) { + MP_ERR(ao_c, "unexpected frame type\n"); + mp_frame_unref(&frame); + res = DATA_EOF; + } else { + res = ao_c->filter_src_got_eof ? DATA_EOF : DATA_WAIT; + } } else if (ao_c->audio_src) { audio_work(ao_c->audio_src); res = audio_get_frame(ao_c->audio_src, &ao_c->input_frame); diff --git a/player/core.h b/player/core.h index a4cffa5d09..0560393bd3 100644 --- a/player/core.h +++ b/player/core.h @@ -34,8 +34,6 @@ #include "video/mp_image.h" #include "video/out/vo.h" -#include "lavfi.h" - // definitions used internally by the core player code enum stop_play_reason { @@ -161,7 +159,8 @@ struct track { // Where the decoded result goes to (one of them is not NULL if active) struct vo_chain *vo_c; struct ao_chain *ao_c; - struct lavfi_pad *sink; + struct mp_pin *sink; + bool sink_eof; // whether it got passed EOF // For stream recording (remuxing mode). struct mp_recorder_sink *remux_sink; @@ -183,7 +182,8 @@ struct vo_chain { struct mp_image *input_mpi; struct track *track; - struct lavfi_pad *filter_src; + struct mp_pin *filter_src; + bool filter_src_got_eof; // whether this returned EOF last time struct dec_video *video_src; // - video consists of a single picture, which should be shown only once @@ -216,7 +216,8 @@ struct ao_chain { double last_out_pts; struct track *track; - struct lavfi_pad *filter_src; + struct mp_pin *filter_src; + bool filter_src_got_eof; // whether this returned EOF last time struct dec_audio *audio_src; }; @@ -315,10 +316,11 @@ typedef struct MPContext { // Currently, this is used for the secondary subtitle track only. struct track *current_track[NUM_PTRACKS][STREAM_TYPE_COUNT]; - struct lavfi *lavfi; - struct mp_filter *filter_root; + struct mp_filter *lavfi; + char *lavfi_graph; + struct ao *ao; struct mp_aframe *ao_filter_fmt; // for weak gapless audio check struct ao_chain *ao_chain; diff --git a/player/lavfi.c b/player/lavfi.c deleted file mode 100644 index 2ceb5502db..0000000000 --- a/player/lavfi.c +++ /dev/null @@ -1,819 +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 <string.h> -#include <math.h> -#include <inttypes.h> -#include <stdarg.h> -#include <assert.h> - -#include <libavutil/avstring.h> -#include <libavutil/mem.h> -#include <libavutil/mathematics.h> -#include <libavutil/rational.h> -#include <libavutil/error.h> -#include <libavutil/opt.h> -#include <libavfilter/avfilter.h> -#include <libavfilter/buffersink.h> -#include <libavfilter/buffersrc.h> - -#include "common/common.h" -#include "common/av_common.h" -#include "common/msg.h" - -#include "audio/format.h" -#include "audio/aframe.h" -#include "video/mp_image.h" -#include "audio/fmt-conversion.h" -#include "video/fmt-conversion.h" -#include "video/hwdec.h" - -#include "lavfi.h" - -#if LIBAVFILTER_VERSION_MICRO < 100 -#define av_buffersink_get_frame_flags(a, b, c) av_buffersink_get_frame(a, b) -#define AV_BUFFERSINK_FLAG_NO_REQUEST 0 -#endif - -struct lavfi { - struct mp_log *log; - char *graph_string; - - struct mp_hwdec_devices *hwdec_devs; - - AVFilterGraph *graph; - // Set to true once all inputs have been initialized, and the graph is - // linked. - bool initialized; - - // Set if all inputs have been marked as LAVFI_WAIT (except LAVFI_EOF pads). - bool all_waiting; - - // Graph is draining to undo previously sent EOF. (If a stream leaves EOF - // state, the graph needs to be recreated to "unstuck" it.) - bool draining_recover_eof; - // Graph is draining for format changes. - bool draining_new_format; - - // Filter can't be put into a working state. - bool failed; - - struct lavfi_pad **pads; - int num_pads; - - AVFrame *tmp_frame; -}; - -struct lavfi_pad { - struct lavfi *main; - enum stream_type type; - enum lavfi_direction dir; - char *name; // user-given pad name - - bool connected; // if false, inputs/otuputs are considered always EOF - - AVFilterContext *filter; - int filter_pad; - // buffersrc or buffersink connected to filter/filter_pad - AVFilterContext *buffer; - AVRational timebase; - bool buffer_is_eof; // received/sent EOF to the buffer - - // 1-frame queue (used for both input and output) - struct mp_image *pending_v; - struct mp_aframe *pending_a; - - // -- dir==LAVFI_IN - - bool input_needed; // filter has signaled it needs new input - bool input_waiting; // caller notified us that it will feed after a wakeup - bool input_again; // caller wants us to feed data in the next iteration - bool input_eof; // caller notified us that no input will come anymore - - // used to check for format changes manually - struct mp_image *in_fmt_v; - struct mp_aframe *in_fmt_a; - - // -- dir==LAVFI_OUT - - bool output_needed; // caller has signaled it needs new output - bool output_eof; // last filter output was EOF -}; - -static void add_pad(struct lavfi *c, enum lavfi_direction dir, AVFilterInOut *item) -{ - int type = -1; - enum AVMediaType avmt; - if (dir == LAVFI_IN) { - avmt = avfilter_pad_get_type(item->filter_ctx->input_pads, item->pad_idx); - } else { - avmt = avfilter_pad_get_type(item->filter_ctx->output_pads, item->pad_idx); - } - switch (avmt) { - case AVMEDIA_TYPE_VIDEO: type = STREAM_VIDEO; break; - case AVMEDIA_TYPE_AUDIO: type = STREAM_AUDIO; break; - default: abort(); - } - - if (!item->name) { - MP_FATAL(c, "filter pad without name label\n"); - c->failed = true; - return; - } - - struct lavfi_pad *p = lavfi_find_pad(c, item->name); - if (p) { - // Graph recreation case: reassociate an existing pad. - if (p->dir != dir || p->type != type) { - MP_FATAL(c, "pad '%s' changed type or direction\n", item->name); - c->failed = true; - return; - } - } else { - p = talloc_zero(c, struct lavfi_pad); - p->main = c; - p->dir = dir; - p->name = talloc_strdup(p, item->name); - p->type = type; - MP_TARRAY_APPEND(c, c->pads, c->num_pads, p); - } - p->filter = item->filter_ctx; - p->filter_pad = item->pad_idx; -} - -static void add_pads(struct lavfi *c, enum lavfi_direction dir, AVFilterInOut *list) -{ - for (; list; list = list->next) - add_pad(c, dir, list); -} - -// Parse the user-provided filter graph, and populate the unlinked filter pads. -static void precreate_graph(struct lavfi *c) -{ - assert(!c->graph); - c->graph = avfilter_graph_alloc(); - if (!c->graph) - abort(); - AVFilterInOut *in = NULL, *out = NULL; - if (avfilter_graph_parse2(c->graph, c->graph_string, &in, &out) < 0) { - c->graph = NULL; - MP_FATAL(c, "parsing the filter graph failed\n"); - c->failed = true; - return; - } - add_pads(c, LAVFI_IN, in); - add_pads(c, LAVFI_OUT, out); - avfilter_inout_free(&in); - avfilter_inout_free(&out); - - // Now check for pads which could not be reassociated. - for (int n = 0; n < c->num_pads; n++) { - struct lavfi_pad *pad = c->pads[n]; - // ok, not much we can do - if (!pad->filter) - MP_FATAL(c, "filter pad '%s' can not be reconnected\n", pad->name); - } -} - -static void free_graph(struct lavfi *c) -{ - avfilter_graph_free(&c->graph); - for (int n = 0; n < c->num_pads; n++) { - struct lavfi_pad *pad = c->pads[n]; - pad->filter = NULL; - pad->filter_pad = -1; - pad->buffer = NULL; - TA_FREEP(&pad->in_fmt_v); - TA_FREEP(&pad->in_fmt_a); - pad->buffer_is_eof = false; - pad->input_needed = false; - pad->input_waiting = false; - pad->input_again = false; - pad->input_eof = false; - pad->output_needed = false; - pad->output_eof = false; - } - c->initialized = false; - c->all_waiting = false; - c->draining_recover_eof = false; - c->draining_new_format = false; -} - -static void drop_pad_data(struct lavfi_pad *pad) -{ - talloc_free(pad->pending_a); - pad->pending_a = NULL; - talloc_free(pad->pending_v); - pad->pending_v = NULL; -} - -static void clear_data(struct lavfi *c) -{ - for (int n = 0; n < c->num_pads; n++) - drop_pad_data(c->pads[n]); -} - -void lavfi_seek_reset(struct lavfi *c) -{ - free_graph(c); - clear_data(c); - precreate_graph(c); -} - -struct lavfi *lavfi_create(struct mp_log *log, char *graph_string) -{ - struct lavfi *c = talloc_zero(NULL, struct lavfi); - c->log = log; - c->graph_string = graph_string; - c->tmp_frame = av_frame_alloc(); - if (!c->tmp_frame) - abort(); - precreate_graph(c); - return c; -} - -void lavfi_destroy(struct lavfi *c) -{ - if (!c) - return; - free_graph(c); - clear_data(c); - av_frame_free(&c->tmp_frame); - talloc_free(c); -} - -const char *lavfi_get_graph(struct lavfi *c) -{ - return c->graph_string; -} - -struct lavfi_pad *lavfi_find_pad(struct lavfi *c, char *name) -{ - for (int n = 0; n < c->num_pads; n++) { - if (strcmp(c->pads[n]->name, name) == 0) - return c->pads[n]; - } - return NULL; -} - -enum lavfi_direction lavfi_pad_direction(struct lavfi_pad *pad) -{ - return pad->dir; -} - -enum stream_type lavfi_pad_type(struct lavfi_pad *pad) -{ - return pad->type; -} - -void lavfi_set_connected(struct lavfi_pad *pad, bool connected) -{ - pad->connected = connected; - if (!pad->connected) { - pad->output_needed = false; - drop_pad_data(pad); - } -} - -bool lavfi_get_connected(struct lavfi_pad *pad) -{ - return pad->connected; -} - -// Ensure to send EOF to each input pad, so the graph can be drained properly. -static void send_global_eof(struct lavfi *c) -{ - for (int n = 0; n < c->num_pads; n++) { - struct lavfi_pad *pad = c->pads[n]; - if (!pad->buffer || pad->dir != LAVFI_IN || pad->buffer_is_eof) - continue; - - if (av_buffersrc_add_frame(pad->buffer, NULL) < 0) - MP_FATAL(c, "could not send EOF to filter\n"); - - pad->buffer_is_eof = true; - } -} - -// libavfilter allows changing some parameters on the fly, but not -// others. -static bool is_aformat_ok(struct mp_aframe *a, struct mp_aframe *b) -{ - struct mp_chmap ca = {0}, cb = {0}; - mp_aframe_get_chmap(a, &ca); - mp_aframe_get_chmap(b, &cb); - return mp_chmap_equals(&ca, &cb) && - mp_aframe_get_rate(a) == mp_aframe_get_rate(b) && - mp_aframe_get_format(a) == mp_aframe_get_format(b); -} -static bool is_vformat_ok(struct mp_image *a, struct mp_image *b) -{ - return a->imgfmt == b->imgfmt && - a->w == b->w && a->h && b->h && - a->params.p_w == b->params.p_w && a->params.p_h == b->params.p_h; -} - -static void check_format_changes(struct lavfi *c) -{ - // check each pad for new input format - for (int n = 0; n < c->num_pads; n++) { - struct lavfi_pad *pad = c->pads[n]; - if (!pad->buffer || pad->dir != LAVFI_IN) - continue; - - if (pad->type == STREAM_AUDIO && pad->pending_a && pad->in_fmt_a) { - c->draining_new_format |= !is_aformat_ok(pad->pending_a, - pad->in_fmt_a); - } - if (pad->type == STREAM_VIDEO && pad->pending_v && pad->in_fmt_v) { - c->draining_new_format |= !is_vformat_ok(pad->pending_v, - pad->in_fmt_v); - } - } - - if (c->initialized && c->draining_new_format) - send_global_eof(c); -} - -// Attempt to initialize all pads. Return true if all are initialized, or -// false if more data is needed (or on error). -static bool init_pads(struct lavfi *c) -{ - if (!c->graph) - goto error; - - for (int n = 0; n < c->num_pads; n++) { - struct lavfi_pad *pad = c->pads[n]; - if (pad->buffer) - continue; - - if (!pad->filter) - goto error; // can happen if pad reassociation fails - - if (pad->dir == LAVFI_OUT) { - const AVFilter *dst_filter = NULL; - if (pad->type == STREAM_AUDIO) { - dst_filter = avfilter_get_by_name("abuffersink"); - } else if (pad->type == STREAM_VIDEO) { - dst_filter = avfilter_get_by_name("buffersink"); - } else { - assert(0); - } - - if (!dst_filter) - goto error; - - char name[256]; - snprintf(name, sizeof(name), "mpv_sink_%s", pad->name); - - if (avfilter_graph_create_filter(&pad->buffer, dst_filter, - name, NULL, NULL, c->graph) < 0) - goto error; - - if (avfilter_link(pad->filter, pad->filter_pad, pad->buffer, 0) < 0) - goto error; - } else { - TA_FREEP(&pad->in_fmt_v); // potentially cleanup previous error state - - pad->input_eof |= !pad->connected; - - if (pad->pending_a) { - assert(pad->type == STREAM_AUDIO); - pad->in_fmt_a = mp_aframe_new_ref(pad->pending_a); - if (!pad->in_fmt_a) - goto error; - mp_aframe_unref_data(pad->in_fmt_a); - } else if (pad->pending_v) { - assert(pad->type == STREAM_VIDEO); - pad->in_fmt_v = mp_image_new_ref(pad->pending_v); - if (!pad->in_fmt_v) - goto error; - mp_image_unref_data(pad->in_fmt_v); - } else if (pad->input_eof) { - // libavfilter makes this painful. Init it with a dummy config, - // just so we can tell it the stream is EOF. - if (pad->type == STREAM_AUDIO) { - pad->in_fmt_a = mp_aframe_create(); - mp_aframe_set_format(pad->in_fmt_a, AF_FORMAT_FLOAT); - mp_aframe_set_chmap(pad->in_fmt_a, - &(struct mp_chmap)MP_CHMAP_INIT_STEREO); - mp_aframe_set_rate(pad->in_fmt_a, 48000); - } else if (pad->type == STREAM_VIDEO) { - pad->in_fmt_v = talloc_zero(NULL, struct mp_image); - mp_image_setfmt(pad->in_fmt_v, IMGFMT_420P); - mp_image_set_size(pad->in_fmt_v, 64, 64); - } - } else { - // no input data, format unknown, can't init, wait longer. - pad->input_needed = true; - return false; - } - - AVBufferSrcParameters *params = av_buffersrc_parameters_alloc(); - if (!params) - goto error; - - char *filter_name = NULL; - if (pad->type == STREAM_AUDIO) { - params->time_base = pad->timebase = - (AVRational){1, mp_aframe_get_rate(pad->in_fmt_a)}; - params->format = - af_to_avformat(mp_aframe_get_format(pad->in_fmt_a)); - params->sample_rate = mp_aframe_get_rate(pad->in_fmt_a); - struct mp_chmap chmap = {0}; - mp_aframe_get_chmap(pad->in_fmt_a, &chmap); - params->channel_layout = mp_chmap_to_lavc(&chmap); - filter_name = "abuffer"; - } else if (pad->type == STREAM_VIDEO) { - params->time_base = pad->timebase = AV_TIME_BASE_Q; - params->format = imgfmt2pixfmt(pad->in_fmt_v->imgfmt); - params->width = pad->in_fmt_v->w; - params->height = pad->in_fmt_v->h; - params->sample_aspect_ratio.num = pad->in_fmt_v->params.p_w; - params->sample_aspect_ratio.den = pad->in_fmt_v->params.p_h; - params->hw_frames_ctx = pad->in_fmt_v->hwctx; - filter_name = "buffer"; - } else { - assert(0); - } - - const AVFilter *filter = avfilter_get_by_name(filter_name); - if (filter) { - char name[256]; - snprintf(name, sizeof(name), "mpv_src_%s", pad->name); - - pad->buffer = avfilter_graph_alloc_filter(c->graph, filter, name); - } - if (!pad->buffer) { - av_free(params); - goto error; - } - - if (pad->type == STREAM_AUDIO) { - char layout[80]; - snprintf(layout, sizeof(layout), "%lld", - (long long)params->channel_layout); - av_opt_set(pad->buffer, "channel_layout", layout, - AV_OPT_SEARCH_CHILDREN); - } - - int ret = av_buffersrc_parameters_set(pad->buffer, params); - av_free(params); - if (ret < 0) - goto error; - - if (avfilter_init_str(pad->buffer, NULL) < 0) - goto error; - - if (avfilter_link(pad->buffer, 0, pad->filter, pad->filter_pad) < 0) - goto error; - } - } - - return true; -error: - MP_FATAL(c, "could not initialize filter pads\n"); - c->failed = true; - return false; -} - -static void dump_graph(struct lavfi *c) -{ -#if LIBAVFILTER_VERSION_MICRO >= 100 - MP_VERBOSE(c, "Filter graph:\n"); - char *s = avfilter_graph_dump(c->graph, NULL); - if (s) - MP_VERBOSE(c, "%s\n", s); - av_free(s); -#endif -} - -void lavfi_pad_set_hwdec_devs(struct lavfi_pad *pad, - struct mp_hwdec_devices *hwdevs) -{ - // We don't actually treat this per-pad. - pad->main->hwdec_devs = hwdevs; -} - -// Initialize the graph if all inputs have formats set. If it's already -// initialized, or can't be initialized yet, do nothing. -static void init_graph(struct lavfi *c) -{ - assert(!c->initialized); - - if (init_pads(c)) { - if (c->hwdec_devs) { - struct mp_hwdec_ctx *hwdec = hwdec_devices_get_first(c->hwdec_devs); - for (int n = 0; n < c->graph->nb_filters; n++) { - AVFilterContext *filter = c->graph->filters[n]; - if (hwdec && hwdec->av_device_ref) - filter->hw_device_ctx = av_buffer_ref(hwdec->av_device_ref); - } - } - - // And here the actual libavfilter initialization happens. - if (avfilter_graph_config(c->graph, NULL) < 0) { - MP_FATAL(c, "failed to configure the filter graph\n"); - free_graph(c); - c->failed = true; - return; - } - - for (int n = 0; n < c->num_pads; n++) { - struct lavfi_pad *pad = c->pads[n]; - if (pad->dir == LAVFI_OUT) - pad->timebase = pad->buffer->inputs[0]->time_base; - } - - c->initialized = true; - - dump_graph(c); - } -} - -static void feed_input_pads(struct lavfi *c) -{ - assert(c->initialized); - - for (int n = 0; n < c->num_pads; n++) { - struct lavfi_pad *pad = c->pads[n]; - if (pad->dir != LAVFI_IN) - continue; - - pad->input_needed = false; - pad->input_eof |= !pad->connected; - -#if LIBAVFILTER_VERSION_MICRO >= 100 - if (!av_buffersrc_get_nb_failed_requests(pad->buffer)) - continue; -#endif - - if (c->draining_recover_eof || c->draining_new_format) - continue; - - if (pad->buffer_is_eof) - continue; - - AVFrame *frame = NULL; - double pts = 0; - bool eof = false; - if (pad->pending_v) { - pts = pad->pending_v->pts; - frame = mp_image_to_av_frame_and_unref(pad->pending_v); - pad->pending_v = NULL; - } else if (pad->pending_a) { - pts = mp_aframe_get_pts(pad->pending_a); - frame = mp_aframe_to_avframe_and_unref(pad->pending_a); - pad->pending_a = NULL; - } else { - if (!pad->input_eof) { - pad->input_needed = true; - continue; - } - eof = true; - } - - if (!frame && !eof) { - MP_FATAL(c, "out of memory or unsupported format\n"); - continue; - } - - if (frame) - frame->pts = mp_pts_to_av(pts, &pad->timebase); - - pad->buffer_is_eof = !frame; - - if (av_buffersrc_add_frame(pad->buffer, frame) < 0) - MP_FATAL(c, "could not pass frame to filter\n"); - av_frame_free(&frame); - - pad->input_again = false; - pad->input_eof = eof; - pad->input_waiting = eof; // input _might_ come again in the future - } -} - -static void read_output_pads(struct lavfi *c) -{ - assert(c->initialized); - - for (int n = 0; n < c->num_pads; n++) { - struct lavfi_pad *pad = c->pads[n]; - - if (pad->dir != LAVFI_OUT) - continue; - - // If disconnected, read and discard everything (passively). - if (pad->connected && !pad->output_needed) - continue; - - assert(pad->buffer); - assert(!pad->pending_v && !pad->pending_a); - - int flags = pad->output_needed ? 0 : AV_BUFFERSINK_FLAG_NO_REQUEST; - int r = AVERROR_EOF; - if (!pad->buffer_is_eof) - r = av_buffersink_get_frame_flags(pad->buffer, c->tmp_frame, flags); - if (r >= 0) { - pad->output_needed = false; - double pts = mp_pts_from_av(c->tmp_frame->pts, &pad->timebase); - if (pad->type == STREAM_AUDIO) { - pad->pending_a = mp_aframe_from_avframe(c->tmp_frame); - if (pad->pending_a) - mp_aframe_set_pts(pad->pending_a, pts); - } else if (pad->type == STREAM_VIDEO) { - pad->pending_v = mp_image_from_av_frame(c->tmp_frame); - if (pad->pending_v) - pad->pending_v->pts = pts; - } else { - assert(0); - } - av_frame_unref(c->tmp_frame); - if (!pad->pending_v && !pad->pending_a) - MP_ERR(c, "could not use filter output\n"); - pad->output_eof = false; - if (!pad->connected) - drop_pad_data(pad); - } else if (r == AVERROR(EAGAIN)) { - // We expect that libavfilter will request input on one of the - // input pads (via av_buffersrc_get_nb_failed_requests()). - pad->output_eof = false; - } else if (r == AVERROR_EOF) { - pad->output_needed = false; - pad->buffer_is_eof = true; - if (!c->draining_recover_eof && !c->draining_new_format) - pad->output_eof = true; - } else { - // Real error - ignore it. - MP_ERR(c, "error on filtering (%d)\n", r); - } - } -} - -// Process filter input and outputs. Return if progress was made (then the -// caller should repeat). If it returns false, the caller should go to sleep -// (as all inputs are asleep as well and no further output can be produced). -bool lavfi_process(struct lavfi *c) -{ - check_format_changes(c); - - if (!c->initialized) - init_graph(c); - - if (c->initialized) { - read_output_pads(c); - feed_input_pads(c); - } - - bool all_waiting = true; - bool any_needs_input = false; - bool any_needs_output = false; - bool all_output_eof = true; - bool all_input_eof = true; - - // Determine the graph state - for (int n = 0; n < c->num_pads; n++) { - struct lavfi_pad *pad = c->pads[n]; - - if (pad->dir == LAVFI_IN) { - all_waiting &= pad->input_waiting; - any_needs_input |= pad->input_needed; - all_input_eof &= pad->input_eof; - } else if (pad->dir == LAVFI_OUT) { - all_output_eof &= pad->buffer_is_eof; - any_needs_output |= pad->output_needed; - } - } - - if (all_output_eof && !all_input_eof) { - free_graph(c); - precreate_graph(c); - all_waiting = false; - any_needs_input = true; - } - - c->all_waiting = all_waiting; - return (any_needs_input || any_needs_output) && !all_waiting; -} - -bool lavfi_has_failed(struct lavfi *c) -{ - return c->failed; -} - -// Request an output frame on this output pad. -// Returns req_status -static int lavfi_request_frame(struct lavfi_pad *pad) -{ - assert(pad->dir == LAVFI_OUT); - - if (pad->main->failed) - return DATA_EOF; - - if (!(pad->pending_a || pad->pending_v)) { - pad->output_needed = true; - lavfi_process(pad->main); - } - - if (pad->pending_a || pad->pending_v) { - return DATA_OK; - } else if (pad->output_eof) { - return DATA_EOF; - } else if (pad->main->all_waiting) { - return DATA_WAIT; - } - return DATA_STARVE; -} - -// Try to read a new frame from an output pad. Returns one of the following: -// DATA_OK: a frame is returned -// DATA_STARVE: needs more input data -// DATA_WAIT: needs more input data, and all inputs in LAVFI_WAIT state -// DATA_EOF: no more data -int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_aframe **out_aframe) -{ - int r = lavfi_request_frame(pad); - *out_aframe = pad->pending_a; - pad->pending_a = NULL; - return r; -} - -// See lavfi_request_frame_a() for remarks. -int lavfi_request_frame_v(struct lavfi_pad *pad, struct mp_image **out_vframe) -{ - int r = lavfi_request_frame(pad); - *out_vframe = pad->pending_v; - pad->pending_v = NULL; - return r; -} - -bool lavfi_needs_input(struct lavfi_pad *pad) -{ - assert(pad->dir == LAVFI_IN); - lavfi_process(pad->main); - return pad->input_needed; -} - -// A filter user is supposed to call lavfi_needs_input(), and if that returns -// true, send either a new status or a frame. A status can be one of: -// DATA_STARVE: a new frame/status will come, caller will retry -// DATA_WAIT: a new frame/status will come, but caller goes to sleep -// DATA_EOF: no more input possible (in near time) -// If you have a new frame, use lavfi_send_frame_ instead. -// Calling this without lavfi_needs_input() returning true before is not -// allowed. -void lavfi_send_status(struct lavfi_pad *pad, int status) -{ - assert(pad->dir == LAVFI_IN); - assert(pad->input_needed); - assert(status != DATA_OK); - assert(!pad->pending_v && !pad->pending_a); - - pad->input_waiting = status == DATA_WAIT || status == DATA_EOF; - pad->input_again = status == DATA_STARVE; - pad->input_eof = status == DATA_EOF; -} - -static void lavfi_sent_frame(struct lavfi_pad *pad) -{ - assert(pad->dir == LAVFI_IN); - assert(pad->input_needed); - assert(pad->pending_a || pad->pending_v); - pad->input_waiting = pad->input_again = pad->input_eof = false; - pad->input_needed = false; -} - -// See lavfi_send_status() for remarks. -void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_aframe *aframe) -{ - assert(pad->type == STREAM_AUDIO); - assert(!pad->pending_a); - pad->pending_a = aframe; - lavfi_sent_frame(pad); -} - -// See lavfi_send_status() for remarks. -void lavfi_send_frame_v(struct lavfi_pad *pad, struct mp_image *vframe) -{ - assert(pad->type == STREAM_VIDEO); - assert(!pad->pending_v); - pad->pending_v = vframe; - lavfi_sent_frame(pad); -} - diff --git a/player/lavfi.h b/player/lavfi.h deleted file mode 100644 index 92c32954d7..0000000000 --- a/player/lavfi.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef MP_LAVFI -#define MP_LAVFI - -struct mp_log; -struct lavfi; -struct lavfi_pad; -struct mp_image; -struct mp_aframe; - -enum lavfi_direction { - LAVFI_IN = 1, - LAVFI_OUT, -}; - -struct lavfi *lavfi_create(struct mp_log *log, char *graph_string); -const char *lavfi_get_graph(struct lavfi *c); -void lavfi_destroy(struct lavfi *c); -struct lavfi_pad *lavfi_find_pad(struct lavfi *c, char *name); -enum lavfi_direction lavfi_pad_direction(struct lavfi_pad *pad); -enum stream_type lavfi_pad_type(struct lavfi_pad *pad); -void lavfi_set_connected(struct lavfi_pad *pad, bool connected); -bool lavfi_get_connected(struct lavfi_pad *pad); -bool lavfi_process(struct lavfi *c); -bool lavfi_has_failed(struct lavfi *c); -void lavfi_seek_reset(struct lavfi *c); -void lavfi_pad_set_hwdec_devs(struct lavfi_pad *pad, - struct mp_hwdec_devices *hwdevs); -int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_aframe **out_aframe); -int lavfi_request_frame_v(struct lavfi_pad *pad, struct mp_image **out_vframe); -bool lavfi_needs_input(struct lavfi_pad *pad); -void lavfi_send_status(struct lavfi_pad *pad, int status); -void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_aframe *aframe); -void lavfi_send_frame_v(struct lavfi_pad *pad, struct mp_image *vframe); - -#endif diff --git a/player/loadfile.c b/player/loadfile.c index aa35d38ddf..ad1b20ac8e 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -45,6 +45,8 @@ #include "audio/decode/dec_audio.h" #include "audio/out/ao.h" +#include "filters/f_lavfi.h" +#include "filters/filter_internal.h" #include "demux/demux.h" #include "stream/stream.h" #include "sub/dec_sub.h" @@ -971,8 +973,8 @@ static void deassociate_complex_filters(struct MPContext *mpctx) mpctx->vo_chain->filter_src = NULL; if (mpctx->ao_chain) mpctx->ao_chain->filter_src = NULL; - lavfi_destroy(mpctx->lavfi); - mpctx->lavfi = NULL; + TA_FREEP(&mpctx->lavfi); + TA_FREEP(&mpctx->lavfi_graph); } // Close all decoders and sinks (AO/VO) that are not connected to either @@ -1012,8 +1014,8 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit) char *graph = mpctx->opts->lavfi_complex; bool have_graph = graph && graph[0] && !force_uninit; if (have_graph && mpctx->lavfi && - strcmp(graph, lavfi_get_graph(mpctx->lavfi)) == 0 && - !lavfi_has_failed(mpctx->lavfi)) + strcmp(graph, mpctx->lavfi_graph) == 0 && + !mp_filter_has_failed(mpctx->lavfi)) return 0; if (!mpctx->lavfi && !have_graph) return 0; @@ -1031,12 +1033,17 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit) goto done; } - mpctx->lavfi = lavfi_create(mpctx->log, graph); - if (!mpctx->lavfi) + struct mp_lavfi *l = + mp_lavfi_create_graph(mpctx->filter_root, 0, false, NULL, graph); + if (!l) goto done; + mpctx->lavfi = l->f; + mpctx->lavfi_graph = talloc_strdup(NULL, graph); - if (lavfi_has_failed(mpctx->lavfi)) - goto done; + mp_filter_set_error_handler(mpctx->lavfi, mpctx->filter_root); + + for (int n = 0; n < mpctx->lavfi->num_pins; n++) + mp_pin_disconnect(mpctx->lavfi->pins[n]); for (int n = 0; n < mpctx->num_tracks; n++) { struct track *track = mpctx->tracks[n]; @@ -1050,14 +1057,12 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit) } snprintf(label, sizeof(label), "%cid%d", prefix, track->user_tid); - struct lavfi_pad *pad = lavfi_find_pad(mpctx->lavfi, label); + struct mp_pin *pad = mp_filter_get_named_pin(mpctx->lavfi, label); if (!pad) continue; - if (lavfi_pad_type(pad) != track->type) - continue; - if (lavfi_pad_direction(pad) != LAVFI_IN) + if (mp_pin_get_dir(pad) != MP_PIN_IN) continue; - if (lavfi_get_connected(pad)) + if (mp_pin_is_connected(pad)) continue; assert(!track->sink); @@ -1067,14 +1072,12 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit) goto done; } track->sink = pad; - lavfi_set_connected(pad, true); + mp_pin_set_manual_connection(pad, true); track->selected = true; } - struct lavfi_pad *pad = lavfi_find_pad(mpctx->lavfi, "vo"); - if (pad && lavfi_pad_type(pad) == STREAM_VIDEO && - lavfi_pad_direction(pad) == LAVFI_OUT) - { + 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"); @@ -1085,16 +1088,13 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit) if (!mpctx->vo_chain) goto done; } - lavfi_set_connected(pad, true); + mp_pin_set_manual_connection(pad, true); struct vo_chain *vo_c = mpctx->vo_chain; vo_c->filter_src = pad; - lavfi_pad_set_hwdec_devs(vo_c->filter_src, vo_c->hwdec_devs); } - pad = lavfi_find_pad(mpctx->lavfi, "ao"); - if (pad && lavfi_pad_type(pad) == STREAM_AUDIO && - lavfi_pad_direction(pad) == LAVFI_OUT) - { + pad = mp_filter_get_named_pin(mpctx->lavfi, "ao"); + if (pad && mp_pin_get_dir(pad) == MP_PIN_OUT) { if (mpctx->ao_chain) { if (mpctx->ao_chain->audio_src) { MP_ERR(mpctx, "Pad ao tries to connect to already used AO.\n"); @@ -1105,7 +1105,7 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit) if (!mpctx->ao_chain) goto done; } - lavfi_set_connected(pad, true); + mp_pin_set_manual_connection(pad, true); mpctx->ao_chain->filter_src = pad; } @@ -1121,6 +1121,17 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit) } } + // Don't allow unconnected pins. Libavfilter would make the data flow a + // real pain anyway. + for (int n = 0; n < mpctx->lavfi->num_pins; n++) { + struct mp_pin *pin = mpctx->lavfi->pins[n]; + if (!mp_pin_is_connected(pin)) { + MP_ERR(mpctx, "Pad %s is not connected to anything.\n", + mp_pin_get_name(pin)); + goto done; + } + } + success = true; done: diff --git a/player/playloop.c b/player/playloop.c index 610bbcdcbf..a75263cc5f 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -212,14 +212,12 @@ void add_step_frame(struct MPContext *mpctx, int dir) // Clear some playback-related fields on file loading or after seeks. void reset_playback_state(struct MPContext *mpctx) { - if (mpctx->lavfi) - lavfi_seek_reset(mpctx->lavfi); - 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; } mp_filter_reset(mpctx->filter_root); @@ -1081,16 +1079,21 @@ static void handle_complex_filter_decoders(struct MPContext *mpctx) struct track *track = mpctx->tracks[n]; if (!track->selected) continue; - if (!track->sink || !lavfi_needs_input(track->sink)) + if (!track->sink || !mp_pin_in_needs_data(track->sink)) continue; if (track->d_audio) { audio_work(track->d_audio); struct mp_aframe *fr; int res = audio_get_frame(track->d_audio, &fr); if (res == DATA_OK) { - lavfi_send_frame_a(track->sink, fr); - } else { - lavfi_send_status(track->sink, res); + mp_pin_in_write(track->sink, MAKE_FRAME(MP_FRAME_AUDIO, 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); } } if (track->d_video) { @@ -1098,9 +1101,14 @@ static void handle_complex_filter_decoders(struct MPContext *mpctx) struct mp_image *fr; int res = video_get_frame(track->d_video, &fr); if (res == DATA_OK) { - lavfi_send_frame_v(track->sink, fr); - } else { - lavfi_send_status(track->sink, res); + 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); } } } @@ -1123,12 +1131,8 @@ void run_playloop(struct MPContext *mpctx) handle_vo_events(mpctx); handle_command_updates(mpctx); - if (mpctx->lavfi) { - if (lavfi_process(mpctx->lavfi)) - mp_wakeup_core(mpctx); - if (lavfi_has_failed(mpctx->lavfi)) - mpctx->stop_play = AT_END_OF_FILE; - } + if (mpctx->lavfi && mp_filter_has_failed(mpctx->lavfi)) + mpctx->stop_play = AT_END_OF_FILE; fill_audio_out_buffers(mpctx); write_video(mpctx); diff --git a/player/video.c b/player/video.c index 8cf193368c..483043f358 100644 --- a/player/video.c +++ b/player/video.c @@ -102,6 +102,8 @@ static void vo_chain_reset_state(struct vo_chain *vo_c) // 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) @@ -152,7 +154,7 @@ static void vo_chain_uninit(struct vo_chain *vo_c) } if (vo_c->filter_src) - lavfi_set_connected(vo_c->filter_src, false); + mp_pin_disconnect(vo_c->filter_src); mp_image_unrefp(&vo_c->input_mpi); mp_image_unrefp(&vo_c->cached_coverart); @@ -212,7 +214,7 @@ int init_video_decoder(struct MPContext *mpctx, struct track *track) err_out: if (track->sink) - lavfi_set_connected(track->sink, false); + mp_pin_disconnect(track->sink); track->sink = NULL; video_uninit(track->d_video); track->d_video = NULL; @@ -356,7 +358,21 @@ static int decode_image(struct MPContext *mpctx) int res = DATA_EOF; if (vo_c->filter_src) { - res = lavfi_request_frame_v(vo_c->filter_src, &vo_c->input_mpi); + 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 && diff --git a/wscript_build.py b/wscript_build.py index 7aeac23dea..24bdcea12e 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -298,7 +298,6 @@ def build(ctx): ( "player/loadfile.c" ), ( "player/main.c" ), ( "player/misc.c" ), - ( "player/lavfi.c" ), ( "player/lua.c", "lua" ), ( "player/javascript.c", "javascript" ), ( "player/osd.c" ), |