mirror of
https://github.com/mpv-player/mpv
synced 2024-11-14 22:48:35 +01:00
61961d03f6
Can be used with mp_chain_filters() to combine multiple filters into a single one. This is a bit silly, but whatever. I'm making it an explicit separate filter, because it lets the user access mp_filter.ppins against all conventions.
312 lines
8.8 KiB
C
312 lines
8.8 KiB
C
#include "audio/aframe.h"
|
|
#include "video/mp_image.h"
|
|
|
|
#include "f_utils.h"
|
|
#include "filter_internal.h"
|
|
|
|
struct frame_duration_priv {
|
|
struct mp_image *buffered;
|
|
};
|
|
|
|
static void frame_duration_process(struct mp_filter *f)
|
|
{
|
|
struct frame_duration_priv *p = f->priv;
|
|
|
|
if (!mp_pin_can_transfer_data(f->ppins[1], f->ppins[0]))
|
|
return;
|
|
|
|
struct mp_frame frame = mp_pin_out_read(f->ppins[0]);
|
|
|
|
if (frame.type == MP_FRAME_EOF && p->buffered) {
|
|
mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_VIDEO, p->buffered));
|
|
p->buffered = NULL;
|
|
// Pass through the actual EOF in the next iteration.
|
|
mp_pin_out_repeat_eof(f->ppins[0]);
|
|
} else if (frame.type == MP_FRAME_VIDEO) {
|
|
struct mp_image *next = frame.data;
|
|
if (p->buffered) {
|
|
if (p->buffered->pts != MP_NOPTS_VALUE &&
|
|
next->pts != MP_NOPTS_VALUE &&
|
|
next->pts >= p->buffered->pts)
|
|
{
|
|
p->buffered->pkt_duration = next->pts - p->buffered->pts;
|
|
}
|
|
mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_VIDEO, p->buffered));
|
|
} else {
|
|
mp_pin_out_request_data(f->ppins[0]);
|
|
}
|
|
p->buffered = next;
|
|
} else {
|
|
mp_pin_in_write(f->ppins[1], frame);
|
|
}
|
|
}
|
|
|
|
static void frame_duration_reset(struct mp_filter *f)
|
|
{
|
|
struct frame_duration_priv *p = f->priv;
|
|
|
|
mp_image_unrefp(&p->buffered);
|
|
}
|
|
|
|
static const struct mp_filter_info frame_duration_filter = {
|
|
.name = "frame_duration",
|
|
.priv_size = sizeof(struct frame_duration_priv),
|
|
.process = frame_duration_process,
|
|
.reset = frame_duration_reset,
|
|
.destroy = frame_duration_reset,
|
|
};
|
|
|
|
struct mp_filter *mp_compute_frame_duration_create(struct mp_filter *parent)
|
|
{
|
|
struct mp_filter *f = mp_filter_create(parent, &frame_duration_filter);
|
|
if (!f)
|
|
return NULL;
|
|
|
|
mp_filter_add_pin(f, MP_PIN_IN, "in");
|
|
mp_filter_add_pin(f, MP_PIN_OUT, "out");
|
|
|
|
return f;
|
|
}
|
|
|
|
void mp_chain_filters(struct mp_pin *in, struct mp_pin *out,
|
|
struct mp_filter **filters, int num_filters)
|
|
{
|
|
for (int n = 0; n < num_filters; n++) {
|
|
if (!filters[n])
|
|
continue;
|
|
assert(filters[n]->num_pins == 2);
|
|
mp_pin_connect(filters[n]->pins[0], in);
|
|
in = filters[n]->pins[1];
|
|
}
|
|
mp_pin_connect(out, in);
|
|
}
|
|
|
|
// Make it repeat process().
|
|
static void mark_progress(struct mp_subfilter *sub)
|
|
{
|
|
// f == NULL is not really allowed, but at least don't crash.
|
|
struct mp_filter *f = mp_pin_get_manual_connection(sub->in);
|
|
if (f)
|
|
mp_filter_internal_mark_progress(f);
|
|
}
|
|
|
|
bool mp_subfilter_read(struct mp_subfilter *sub)
|
|
{
|
|
if (sub->filter) {
|
|
if (mp_pin_can_transfer_data(sub->out, sub->filter->pins[1])) {
|
|
struct mp_frame frame = mp_pin_out_read(sub->filter->pins[1]);
|
|
if (sub->draining && frame.type == MP_FRAME_EOF) {
|
|
sub->draining = false;
|
|
TA_FREEP(&sub->filter);
|
|
mark_progress(sub);
|
|
return false;
|
|
}
|
|
mp_pin_in_write(sub->out, frame);
|
|
return false;
|
|
}
|
|
if (sub->draining)
|
|
return false;
|
|
}
|
|
|
|
struct mp_pin *out = sub->filter ? sub->filter->pins[0] : sub->out;
|
|
|
|
if (sub->frame.type)
|
|
return mp_pin_in_needs_data(out);
|
|
|
|
if (!mp_pin_can_transfer_data(out, sub->in))
|
|
return false;
|
|
|
|
sub->frame = mp_pin_out_read(sub->in);
|
|
return true;
|
|
}
|
|
|
|
void mp_subfilter_reset(struct mp_subfilter *sub)
|
|
{
|
|
if (sub->filter && sub->draining)
|
|
TA_FREEP(&sub->filter);
|
|
sub->draining = false;
|
|
mp_frame_unref(&sub->frame);
|
|
}
|
|
|
|
void mp_subfilter_continue(struct mp_subfilter *sub)
|
|
{
|
|
struct mp_pin *out = sub->filter ? sub->filter->pins[0] : sub->out;
|
|
// It was made sure earlier that the pin is writable, unless the filter
|
|
// was newly created, or a previously existing filter (which was going to
|
|
// accept input) was destroyed. In those cases, essentially restart
|
|
// data flow.
|
|
if (!mp_pin_in_needs_data(out)) {
|
|
mark_progress(sub);
|
|
return;
|
|
}
|
|
mp_pin_in_write(out, sub->frame);
|
|
sub->frame = MP_NO_FRAME;
|
|
}
|
|
|
|
void mp_subfilter_destroy(struct mp_subfilter *sub)
|
|
{
|
|
TA_FREEP(&sub->filter);
|
|
sub->draining = false;
|
|
}
|
|
|
|
bool mp_subfilter_drain_destroy(struct mp_subfilter *sub)
|
|
{
|
|
if (!sub->draining && sub->filter) {
|
|
// We know the filter is writable (unless the user created a new filter
|
|
// and immediately called this function, which is invalid).
|
|
mp_pin_in_write(sub->filter->pins[0], MP_EOF_FRAME);
|
|
sub->draining = true;
|
|
}
|
|
return !sub->filter;
|
|
}
|
|
|
|
static const struct mp_filter_info bidir_nop_filter = {
|
|
.name = "nop",
|
|
};
|
|
|
|
struct mp_filter *mp_bidir_nop_filter_create(struct mp_filter *parent)
|
|
{
|
|
struct mp_filter *f = mp_filter_create(parent, &bidir_nop_filter);
|
|
if (!f)
|
|
return NULL;
|
|
|
|
mp_filter_add_pin(f, MP_PIN_IN, "in");
|
|
mp_filter_add_pin(f, MP_PIN_OUT, "out");
|
|
|
|
mp_pin_connect(f->ppins[1], f->ppins[0]);
|
|
|
|
return f;
|
|
}
|
|
|
|
static const struct mp_filter_info bidir_dummy_filter = {
|
|
.name = "dummy",
|
|
};
|
|
|
|
struct mp_filter *mp_bidir_dummy_filter_create(struct mp_filter *parent)
|
|
{
|
|
struct mp_filter *f = mp_filter_create(parent, &bidir_dummy_filter);
|
|
if (!f)
|
|
return NULL;
|
|
|
|
mp_filter_add_pin(f, MP_PIN_IN, "in");
|
|
mp_filter_add_pin(f, MP_PIN_OUT, "out");
|
|
|
|
return f;
|
|
}
|
|
|
|
struct fixed_aframe_size_priv {
|
|
int samples;
|
|
bool pad_silence;
|
|
struct mp_aframe *in;
|
|
struct mp_aframe *out;
|
|
int out_written; // valid samples in out
|
|
struct mp_aframe_pool *pool;
|
|
};
|
|
|
|
static void fixed_aframe_size_process(struct mp_filter *f)
|
|
{
|
|
struct fixed_aframe_size_priv *p = f->priv;
|
|
|
|
if (!mp_pin_in_needs_data(f->ppins[1]))
|
|
return;
|
|
|
|
if (p->in && !mp_aframe_get_size(p->in))
|
|
TA_FREEP(&p->in);
|
|
|
|
if (!p->in) {
|
|
struct mp_frame frame = mp_pin_out_read(f->ppins[0]);
|
|
if (frame.type == MP_FRAME_EOF) {
|
|
if (!p->out) {
|
|
mp_pin_in_write(f->ppins[1], frame);
|
|
return;
|
|
}
|
|
mp_pin_out_repeat_eof(f->ppins[0]);
|
|
} else if (frame.type == MP_FRAME_AUDIO) {
|
|
p->in = frame.data;
|
|
if (p->out && !mp_aframe_config_equals(p->out, p->in)) {
|
|
mp_pin_out_unread(f->ppins[0], frame);
|
|
p->in = NULL;
|
|
}
|
|
} else if (frame.type) {
|
|
MP_ERR(f, "unsupported frame type\n");
|
|
mp_filter_internal_mark_failed(f);
|
|
return;
|
|
} else {
|
|
return; // no new data yet
|
|
}
|
|
}
|
|
|
|
if (p->in) {
|
|
if (!p->out) {
|
|
p->out = mp_aframe_create();
|
|
mp_aframe_config_copy(p->out, p->in);
|
|
mp_aframe_copy_attributes(p->out, p->in);
|
|
if (mp_aframe_pool_allocate(p->pool, p->out, p->samples) < 0) {
|
|
mp_filter_internal_mark_failed(f);
|
|
return;
|
|
}
|
|
p->out_written = 0;
|
|
}
|
|
int in_samples = mp_aframe_get_size(p->in);
|
|
int copy = MPMIN(in_samples, p->samples - p->out_written);
|
|
if (!mp_aframe_copy_samples(p->out, p->out_written, p->in, 0, copy))
|
|
assert(0);
|
|
mp_aframe_skip_samples(p->in, copy);
|
|
p->out_written += copy;
|
|
}
|
|
|
|
// p->in not set means draining for EOF or format change
|
|
if ((!p->in && p->out_written) || p->out_written == p->samples) {
|
|
int missing = p->samples - p->out_written;
|
|
assert(missing >= 0);
|
|
if (missing) {
|
|
mp_aframe_set_silence(p->out, p->out_written, missing);
|
|
if (!p->pad_silence)
|
|
mp_aframe_set_size(p->out, p->out_written);
|
|
}
|
|
mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, p->out));
|
|
p->out = NULL;
|
|
p->out_written = 0;
|
|
} else {
|
|
mp_pin_out_request_data_next(f->ppins[0]);
|
|
}
|
|
}
|
|
|
|
static void fixed_aframe_size_reset(struct mp_filter *f)
|
|
{
|
|
struct fixed_aframe_size_priv *p = f->priv;
|
|
|
|
TA_FREEP(&p->in);
|
|
TA_FREEP(&p->out);
|
|
p->out_written = 0;
|
|
}
|
|
|
|
static const struct mp_filter_info fixed_aframe_size_filter = {
|
|
.name = "fixed_aframe_size",
|
|
.priv_size = sizeof(struct fixed_aframe_size_priv),
|
|
.process = fixed_aframe_size_process,
|
|
.reset = fixed_aframe_size_reset,
|
|
.destroy = fixed_aframe_size_reset,
|
|
};
|
|
|
|
struct mp_filter *mp_fixed_aframe_size_create(struct mp_filter *parent,
|
|
int samples, bool pad_silence)
|
|
{
|
|
if (samples < 1)
|
|
return NULL;
|
|
|
|
struct mp_filter *f = mp_filter_create(parent, &fixed_aframe_size_filter);
|
|
if (!f)
|
|
return NULL;
|
|
|
|
mp_filter_add_pin(f, MP_PIN_IN, "in");
|
|
mp_filter_add_pin(f, MP_PIN_OUT, "out");
|
|
|
|
struct fixed_aframe_size_priv *p = f->priv;
|
|
p->samples = samples;
|
|
p->pad_silence = pad_silence;
|
|
p->pool = mp_aframe_pool_create(p);
|
|
|
|
return f;
|
|
}
|