mirror of
https://github.com/baresip/baresip
synced 2026-08-07 00:26:06 -04:00
200 lines
4.8 KiB
C
200 lines
4.8 KiB
C
/**
|
|
* @file avfilter/filter.c Video filter using libavfilter -- filtering
|
|
*
|
|
* Copyright (C) 2020 Mikhail Kurkov
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <libavformat/avformat.h>
|
|
#include <libavfilter/buffersink.h>
|
|
#include <libavfilter/buffersrc.h>
|
|
#include <libavutil/opt.h>
|
|
#include <re.h>
|
|
#include <rem.h>
|
|
#include <baresip.h>
|
|
#include "avfilter.h"
|
|
#include "util.h"
|
|
|
|
|
|
int filter_init(struct avfilter_st *st, const char *filter_descr,
|
|
struct vidframe *frame)
|
|
{
|
|
const AVFilter *buffersrc, *buffersink;
|
|
AVFilterInOut *outputs, *inputs;
|
|
enum AVPixelFormat src_format = vidfmt_to_avpixfmt(frame->fmt);
|
|
char args[512];
|
|
int err = 0;
|
|
|
|
if (!str_isset(filter_descr)) {
|
|
st->enabled = false;
|
|
return 0;
|
|
}
|
|
|
|
buffersrc = avfilter_get_by_name("buffer");
|
|
buffersink = avfilter_get_by_name("buffersink");
|
|
outputs = avfilter_inout_alloc();
|
|
inputs = avfilter_inout_alloc();
|
|
|
|
st->filter_graph = avfilter_graph_alloc();
|
|
st->vframe_in = av_frame_alloc();
|
|
st->vframe_out = av_frame_alloc();
|
|
if (!outputs || !inputs || !st->filter_graph ||
|
|
!st->vframe_in || !st->vframe_out) {
|
|
err = AVERROR(ENOMEM);
|
|
goto end;
|
|
}
|
|
|
|
/* buffer video source */
|
|
snprintf(args, sizeof(args),
|
|
"video_size=%dx%d:pix_fmt=%d:"
|
|
"time_base=%d/%d:pixel_aspect=1/1",
|
|
frame->size.w, frame->size.h, src_format, 1, VIDEO_TIMEBASE);
|
|
|
|
err = avfilter_graph_create_filter(
|
|
&st->buffersrc_ctx, buffersrc, "in", args, NULL,
|
|
st->filter_graph);
|
|
if (err < 0) {
|
|
warning("avfilter: cannot create buffer source\n");
|
|
goto end;
|
|
}
|
|
|
|
/* buffer video sink: to terminate the filter chain. */
|
|
err = avfilter_graph_create_filter(
|
|
&st->buffersink_ctx, buffersink, "out", NULL, NULL,
|
|
st->filter_graph);
|
|
if (err < 0) {
|
|
warning("avfilter: cannot create buffer sink\n");
|
|
goto end;
|
|
}
|
|
|
|
err = av_opt_set_bin(st->buffersink_ctx, "pix_fmts",
|
|
(uint8_t *)&src_format, sizeof(src_format),
|
|
AV_OPT_SEARCH_CHILDREN);
|
|
if (err < 0) {
|
|
warning("avfilter: cannot set output pixel format\n");
|
|
goto end;
|
|
}
|
|
|
|
outputs->name = av_strdup("in");
|
|
outputs->filter_ctx = st->buffersrc_ctx;
|
|
outputs->pad_idx = 0;
|
|
outputs->next = NULL;
|
|
|
|
inputs->name = av_strdup("out");
|
|
inputs->filter_ctx = st->buffersink_ctx;
|
|
inputs->pad_idx = 0;
|
|
inputs->next = NULL;
|
|
|
|
err = avfilter_graph_parse_ptr(st->filter_graph, filter_descr,
|
|
&inputs, &outputs, NULL);
|
|
if (err < 0) {
|
|
warning("avfilter: error parsing filter description: %s\n",
|
|
filter_descr);
|
|
goto end;
|
|
}
|
|
|
|
err = avfilter_graph_config(st->filter_graph, NULL);
|
|
if (err < 0) {
|
|
warning("avfilter: filter graph config failed\n");
|
|
goto end;
|
|
}
|
|
|
|
st->size = frame->size;
|
|
st->format = frame->fmt;
|
|
st->enabled = true;
|
|
|
|
info("avfilter: filter graph initialized for %s\n", filter_descr);
|
|
|
|
end:
|
|
avfilter_inout_free(&inputs);
|
|
avfilter_inout_free(&outputs);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
void filter_reset(struct avfilter_st *st)
|
|
{
|
|
if (!st)
|
|
return;
|
|
|
|
if (!st->enabled)
|
|
return;
|
|
if (st->filter_graph)
|
|
avfilter_graph_free(&st->filter_graph);
|
|
if (st->vframe_in)
|
|
av_frame_free(&st->vframe_in);
|
|
if (st->vframe_out)
|
|
av_frame_free(&st->vframe_out);
|
|
st->enabled = false;
|
|
info("avfilter: filter graph reset\n");
|
|
}
|
|
|
|
|
|
bool filter_valid(const struct avfilter_st *st, const struct vidframe *frame)
|
|
{
|
|
bool res = !st->enabled ||
|
|
((st->size.h == frame->size.h) &&
|
|
(st->size.w == frame->size.w) &&
|
|
(st->format == frame->fmt));
|
|
return res;
|
|
}
|
|
|
|
|
|
int filter_encode(struct avfilter_st *st, struct vidframe *frame,
|
|
uint64_t *timestamp)
|
|
{
|
|
unsigned i;
|
|
int err;
|
|
|
|
if (!frame)
|
|
return 0;
|
|
|
|
if (!st->enabled) {
|
|
return 0;
|
|
}
|
|
|
|
/* fill the source frame */
|
|
st->vframe_in->format = vidfmt_to_avpixfmt(frame->fmt);
|
|
st->vframe_in->width = frame->size.w;
|
|
st->vframe_in->height = frame->size.h;
|
|
st->vframe_in->pts = *timestamp;
|
|
|
|
for (i=0; i<4; i++) {
|
|
st->vframe_in->data[i] = frame->data[i];
|
|
st->vframe_in->linesize[i] = frame->linesize[i];
|
|
}
|
|
|
|
/* push source frame into the filter graph */
|
|
err = av_buffersrc_add_frame_flags(
|
|
st->buffersrc_ctx, st->vframe_in, AV_BUFFERSRC_FLAG_KEEP_REF);
|
|
if (err < 0) {
|
|
warning("avfilter: error while feeding the filtergraph\n");
|
|
goto out;
|
|
}
|
|
|
|
/* pull filtered frames from the filtergraph */
|
|
av_frame_unref(st->vframe_out);
|
|
err = av_buffersink_get_frame(st->buffersink_ctx, st->vframe_out);
|
|
if (err == AVERROR(EAGAIN) || err == AVERROR_EOF)
|
|
goto out;
|
|
if (err < 0) {
|
|
warning("avfilter: error while getting"
|
|
" filtered frame from the filtergraph\n");
|
|
goto out;
|
|
}
|
|
|
|
avframe_ensure_topdown(st->vframe_out);
|
|
|
|
/* Copy filtered frame back to the input frame */
|
|
for (i=0; i<4; i++) {
|
|
frame->data[i] = st->vframe_out->data[i];
|
|
frame->linesize[i] = st->vframe_out->linesize[i];
|
|
}
|
|
frame->size.h = st->vframe_out->height;
|
|
frame->size.w = st->vframe_out->width;
|
|
frame->fmt = avpixfmt_to_vidfmt(st->vframe_out->format);
|
|
|
|
out:
|
|
return err;
|
|
}
|