mirror of
https://github.com/mpv-player/mpv
synced 2024-10-30 04:46:41 +01:00
76276c9210
Get rid of the old vf.c code. Replace it with a generic filtering framework, which can potentially handle more than just --vf. At least reimplementing --af with this code is planned. This changes some --vf semantics (including runtime behavior and the "vf" command). The most important ones are listed in interface-changes. vf_convert.c is renamed to f_swscale.c. It is now an internal filter that can not be inserted by the user manually. f_lavfi.c is a refactor of player/lavfi.c. The latter will be removed once --lavfi-complex is reimplemented on top of f_lavfi.c. (which is conceptually easy, but a big mess due to the data flow changes). The existing filters are all changed heavily. The data flow of the new filter framework is different. Especially EOF handling changes - EOF is now a "frame" rather than a state, and must be passed through exactly once. Another major thing is that all filters must support dynamic format changes. The filter reconfig() function goes away. (This sounds complex, but since all filters need to handle EOF draining anyway, they can use the same code, and it removes the mess with reconfig() having to predict the output format, which completely breaks with libavfilter anyway.) In addition, there is no automatic format negotiation or conversion. libavfilter's primitive and insufficient API simply doesn't allow us to do this in a reasonable way. Instead, filters can use f_autoconvert as sub-filter, and tell it which formats they support. This filter will in turn add actual conversion filters, such as f_swscale, to perform necessary format changes. vf_vapoursynth.c uses the same basic principle of operation as before, but with worryingly different details in data flow. Still appears to work. The hardware deint filters (vf_vavpp.c, vf_d3d11vpp.c, vf_vdpaupp.c) are heavily changed. Fortunately, they all used refqueue.c, which is for sharing the data flow logic (especially for managing future/past surfaces and such). It turns out it can be used to factor out most of the data flow. Some of these filters accepted software input. Instead of having ad-hoc upload code in each filter, surface upload is now delegated to f_autoconvert, which can use f_hwupload to perform this. Exporting VO capabilities is still a big mess (mp_stream_info stuff). The D3D11 code drops the redundant image formats, and all code uses the hw_subfmt (sw_format in FFmpeg) instead. Although that too seems to be a big mess for now. f_async_queue is unused.
145 lines
6.5 KiB
C
145 lines
6.5 KiB
C
#pragma once
|
|
|
|
#include <stddef.h>
|
|
|
|
#include "filter.h"
|
|
|
|
// Flag the thread as needing mp_filter_process() to be called. Useful for
|
|
// (some) async filters only. Idempotent.
|
|
// Explicitly thread-safe.
|
|
void mp_filter_wakeup(struct mp_filter *f);
|
|
|
|
// Same as mp_filter_wakeup(), but skip the wakeup, and only mark the filter
|
|
// as requiring processing to possibly update pin states changed due to async
|
|
// processing.
|
|
// Explicitly thread-safe.
|
|
void mp_filter_mark_async_progress(struct mp_filter *f);
|
|
|
|
// Flag the thread as needing mp_filter_process() to be called. Unlike
|
|
// mp_filter_wakeup(), not thread-safe, and must be called from the process()
|
|
// function of f (in exchange this is very light-weight).
|
|
// In practice, this means process() is repeated.
|
|
void mp_filter_internal_mark_progress(struct mp_filter *f);
|
|
|
|
// Flag the filter as having failed, and propagate the error to the parent
|
|
// filter. The error propagation stops either at the root filter, or if a filter
|
|
// has an error handler set.
|
|
// Must be called from f's process function.
|
|
void mp_filter_internal_mark_failed(struct mp_filter *f);
|
|
|
|
// If handler is not NULL, then if filter f errors, don't propagate the error
|
|
// flag to its parent. Also invoke the handler's process() function, which is
|
|
// supposed to use mp_filter_has_failed(f) to check any filters for which it has
|
|
// set itself as error handler.
|
|
// A filter must manually unset itself as error handler if it gets destroyed
|
|
// before the filter f, otherwise dangling pointers will occur.
|
|
void mp_filter_set_error_handler(struct mp_filter *f, struct mp_filter *handler);
|
|
|
|
// Add a pin. Returns the private handle (same as f->ppins[f->num_pins-1]).
|
|
// The name must be unique across all filter pins (you must verify this
|
|
// yourself if filter names are from user input). name=="" is not allowed.
|
|
// Never returns NULL. dir should be the external filter direction (a filter
|
|
// input will use dir==MP_PIN_IN, and the returned pin will use MP_PIN_OUT,
|
|
// because the internal pin is the opposite end of the external one).
|
|
struct mp_pin *mp_filter_add_pin(struct mp_filter *f, enum mp_pin_dir dir,
|
|
const char *name);
|
|
|
|
// Remove and deallocate a pin. The caller must be sure that nothing else
|
|
// references the pin anymore. You must pass the private pin (from
|
|
// mp_filter.ppin). This removes/deallocates the public paired pin as well.
|
|
void mp_filter_remove_pin(struct mp_filter *f, struct mp_pin *p);
|
|
|
|
// Free all filters which have f as parent. (This has nothing to do with
|
|
// talloc.)
|
|
void mp_filter_free_children(struct mp_filter *f);
|
|
|
|
struct mp_filter_params;
|
|
|
|
struct mp_filter_info {
|
|
// Informational name, in some cases might be used for filter discovery.
|
|
const char *name;
|
|
|
|
// mp_filter.priv is set to memory allocated with this size (if > 0)
|
|
size_t priv_size;
|
|
|
|
// Called during mp_filter_create(). Optional, can be NULL if use of a
|
|
// constructor function is required, which sets up the real filter after
|
|
// creation. Actually turns out nothing uses this.
|
|
bool (*init)(struct mp_filter *f, struct mp_filter_params *params);
|
|
|
|
// Free internal resources. Optional.
|
|
void (*destroy)(struct mp_filter *f);
|
|
|
|
// Called if any mp_pin was signalled (i.e. likely new data to process), or
|
|
// an async wakeup was received some time earlier.
|
|
// Generally, the implementation would consist of 2 stages:
|
|
// 1. check for the pin states, possibly request/probe for input/output
|
|
// 2. if data flow can happen, read a frame, perform actual work, write
|
|
// result
|
|
// The process function will usually run very often, when pin states are
|
|
// updated, so the generic code can determine where data flow can happen.
|
|
// The common case will be that process() is called running stage 1 a bunch
|
|
// of times, until it finally can run stage 2 too.
|
|
// Optional.
|
|
void (*process)(struct mp_filter *f);
|
|
|
|
// Clear internal state and buffers (e.g. on seeks). Filtering can restart
|
|
// after this, and all settings are preserved. It makes sense to preserve
|
|
// internal resources for further filtering as well if you can.
|
|
// Input/output pins are always cleared by the common code before invoking
|
|
// this callback.
|
|
// Optional, can be NULL for filters without state.
|
|
// Don't create or destroy filters in this function, don't reconnect pins,
|
|
// don't access pins.
|
|
void (*reset)(struct mp_filter *f);
|
|
|
|
// Send a command to the filter. Highly implementation specific, usually
|
|
// user-initiated. Optional.
|
|
bool (*command)(struct mp_filter *f, struct mp_filter_command *cmd);
|
|
};
|
|
|
|
// Create a filter instance. Returns NULL on failure.
|
|
// Destroy/free with talloc_free().
|
|
// This is for filter implementers only. Filters are created with their own
|
|
// constructor functions (instead of a generic one), which call this function
|
|
// to create the filter itself.
|
|
// parent is never NULL; use mp_filter_create_root() to create a top most
|
|
// filter.
|
|
// The parent does not imply anything about the position of the filter in
|
|
// the dataflow (only the mp_pin connections matter). The parent exists for
|
|
// convenience, which includes:
|
|
// - passing down implicit and explicit parameters (such as the filter driver
|
|
// loop)
|
|
// - auto freeing child filters if the parent is free'd
|
|
// - propagating errors
|
|
// - setting the parent as default manual connection for new external filter
|
|
// pins
|
|
// The parent filter stays valid for the lifetime of any filters having it
|
|
// directly or indirectly as parent. If the parent is free'd, all children are
|
|
// automatically free'd.
|
|
// All filters in the same parent tree must be driven in the same thread (or be
|
|
// explicitly synchronized otherwise).
|
|
// Driving the parent (or root) filter with mp_filter_run() will make sure this
|
|
// filter is driven too, without having to resort to recursion.
|
|
struct mp_filter *mp_filter_create(struct mp_filter *parent,
|
|
const struct mp_filter_info *info);
|
|
|
|
struct mp_filter_params {
|
|
// Identifies the filter and its implementation. The pointer must stay
|
|
// valid for the life time of the created filter instance.
|
|
const struct mp_filter_info *info;
|
|
|
|
// Must be set if global==NULL. See mp_filter_create() for remarks.
|
|
struct mp_filter *parent;
|
|
|
|
// Must be set if parent==NULL, can otherwise be NULL.
|
|
struct mpv_global *global;
|
|
|
|
// Filter specific parameters. Most filters will have a constructor
|
|
// function, and pass in something internal.
|
|
void *params;
|
|
};
|
|
|
|
// Same as mp_filter_create(), but technically more flexible.
|
|
struct mp_filter *mp_filter_create_with_params(struct mp_filter_params *params);
|