mirror of https://github.com/mpv-player/mpv
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.
This commit is contained in:
parent
b9f804b566
commit
0366ba2531
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
819
player/lavfi.c
819
player/lavfi.c
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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)
|
||||
if (mp_pin_get_dir(pad) != MP_PIN_IN)
|
||||
continue;
|
||||
if (lavfi_pad_direction(pad) != LAVFI_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:
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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" ),
|
||||
|
|
Loading…
Reference in New Issue