原理
1.将压缩后的每一帧数据进行解码
2.对解码后的数据进行计算
3.再将处理好的数据进行编码
简单滤镜
ffplay -i /Users/king/Desktop/ffmpeg/audio/cut.mp4 -vf "drawbox=x=30:y=30:w=60:h=60:c=red"
drawbox 滤镜名字
后边用等号连接参数,参数使用冒号进行分割
查看滤镜
ffmpeg -h filter=drawbox
ffmpeg -filters 支持的滤镜
filter语法
在avfilter参数之间用":"来分割 多个filter串联分割使用","分割。多个filter之间没有关联用";"进行分割
ffplay -i /Users/xxxxx/Desktop/ffmpeg/audio/cut.mp4 -vf "
//
// filter_test.c
// ffmpegaudio
//
// Created by KING on 2023/12/23.
//
/**
* Set a binary option to an integer list.
*
* @param obj AVClass object to set options on
* @param name name of the binary option
* @param val pointer to an integer list (must have the correct type with
* regard to the contents of the list)
* @param term list terminator (usually 0 or -1)
* @param flags search flags
*/
//ffplay -i /Users/king/Desktop/ffmpeg/audio/filter.yuv -pixel_format GRAY8 -video_size 1920x1080
#include "filter_test.h"
#include "libavutil/avutil.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavformat/avformat.h"
#include "libavutil/opt.h"
#include "libavformat/avformat.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
const char *filter_desc = "drawbox=30:10:64:64:red";
static int init_filters(const char *filter_desc,AVFormatContext *fmt_context,AVCodecContext *codec_context,AVFilterGraph **graph, AVFilterContext **buf_ctx,AVFilterContext **buf_sink_ctx, int video_stream_index) {
int ret = -1;
char args[512] = {0,};
AVRational time_base = fmt_context->streams[video_stream_index]->time_base;
AVFilterInOut *inputs = avfilter_inout_alloc();
AVFilterInOut *output = avfilter_inout_alloc();
if (!inputs || !output){
printf("output inputs failed\n");
return AVERROR(ENOMEM);
}
*graph = avfilter_graph_alloc();
if (!*graph){
printf("graph create failed\n");
return AVERROR(ENOMEM);
}
//"[in]drawbox=xxx[out]"
const AVFilter *bufsrc = avfilter_get_by_name("buffer");
if (!bufsrc) {
printf("buffer src create failed\n");
return ret;
}
const AVFilter *bufsinck= avfilter_get_by_name("buffersink");
if (!bufsinck) {
printf("bufsinck create failed\n");
return ret;
}
/// 输入buffer filter创建
snprintf(args,
512,
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",codec_context->width,codec_context->height,codec_context->pix_fmt,time_base.num,time_base.den,codec_context->sample_aspect_ratio.num,codec_context->sample_aspect_ratio.den);
ret = avfilter_graph_create_filter(buf_ctx, bufsrc, "in", args, NULL, *graph);
if (ret < 0) {
printf("avfilter_graph_create_filter in failed\n");
goto __ERROR;
}
///输出 buffer filter 创建
enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P,AV_PIX_FMT_GRAY8,AV_PIX_FMT_NONE};
ret = avfilter_graph_create_filter(buf_sink_ctx, bufsinck, "out", NULL, NULL, *graph);
if (ret < 0) {
printf("avfilter_graph_create_filter out failed\n");
goto __ERROR;
}
av_opt_set_int_list(*buf_sink_ctx,"pix_fmts",pix_fmts,AV_PIX_FMT_NONE,AV_OPT_SEARCH_CHILDREN);
// create inout
///输出 这里的name 是反的
inputs->name = av_strdup("out");
inputs->filter_ctx = *buf_sink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
/// 输入
output->name = av_strdup("in");
output->filter_ctx = *buf_ctx;
output->pad_idx = 0;
output->next = NULL;
// create filter and graph for filter desciption
avfilter_graph_parse_ptr(*graph, filter_desc, &inputs, &output,NULL);
ret = avfilter_graph_config(*graph, NULL);
if (ret < 0) {
printf("avfilter_graph_config failed\n");
}
__ERROR:
avfilter_inout_free(&inputs);
avfilter_inout_free(&output);
return ret;
}
int create_filter(char *file_name, AVFormatContext **fmt_context,AVCodecContext **codec_context, int *video_stream_index, AVCodec **dec) {
int ret = 0;
ret = avformat_open_input(fmt_context, file_name, NULL, NULL);
if (ret < 0) {
printf("open fail");
}
ret = avformat_find_stream_info(*fmt_context, NULL);
if (ret < 0) {
printf("avformat_find_stream_info fail");
}
ret = av_find_best_stream(*fmt_context, AVMEDIA_TYPE_VIDEO, -1, -1, dec,0);
if (ret < 0) {
printf("av_find_best_stream fail");
}
*video_stream_index = ret;
*codec_context = avcodec_alloc_context3(*dec);
if (!codec_context) {
printf("codec_context fdailed");
}
avcodec_parameters_to_context(*codec_context, (*fmt_context)->streams[*video_stream_index]->codecpar);
///打开解码器
ret = avcodec_open2(*codec_context, *dec, NULL);
if (ret < 0) {
printf("avcodec_open2 fail");
}
return ret;
}
static int do_frame(AVFrame *filter_frame, FILE *out) {
int ret = 0;
fwrite(filter_frame->data[0], 1, filter_frame->width * filter_frame->height, out);
fflush(out);
return ret;
}
static int filter_video(AVFilterContext*buf_ctx,AVFilterContext*buf_sink_ctx, AVFrame *frame, AVFrame *filter_frame,FILE *out) {
int ret = 0;
ret = av_buffersrc_add_frame(buf_ctx, frame);
if (ret < 0) {
printf("av_buffersrc_add_frame failed");
return ret;
}
while (1) {
ret = av_buffersink_get_frame(buf_sink_ctx, filter_frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
if (ret < 0) {
return ret;
}
do_frame(filter_frame,out);
av_frame_unref(filter_frame);
}
av_frame_unref(frame);
return ret;
}
static int decode_frame_and_filter(AVFilterContext*buf_ctx,AVFilterContext*buf_sink_ctx,AVCodecContext* codec_context,AVFrame *frame, AVFrame *filter_frame, FILE *out) {
int ret = 0;
ret = avcodec_receive_frame(codec_context, frame);
if (ret < 0) {
if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
printf("avcodec_receive_frame failed");
}
return ret;
}
return filter_video(buf_ctx,buf_sink_ctx,frame,filter_frame,out);
}
void player_filter_test() {
const char *filter_desc="drawbox=30:10:64:64:red";
// const char *filter_desc="movie=/xxx/docs/media/16324057162865/16324083424959.png,scale=64:64[wm];[in][wm]overlay=30:10";
AVFormatContext *fmt_context = NULL;
AVCodec *dec = NULL;
AVCodecContext *codec_context = NULL;
int video_stream_index = 0;
AVFilterContext *buf_ctx = NULL;
AVFilterContext *buf_sink_ctx = NULL;
AVFilterGraph *graph = NULL;
const char *file_name = "/Users/king/Desktop/ffmpeg/audio/cut.mp4";
AVPacket packet;
AVFrame *frame = NULL;
AVFrame *filter_frame = NULL;
FILE *out = NULL;
int ret = 0;
frame = av_frame_alloc();
filter_frame = av_frame_alloc();
out = fopen("/Users/king/Desktop/ffmpeg/audio/filter.yuv", "wb+");
if (!out) {
printf("FILE fail");
goto __ERROR;
}
if (!frame || !filter_frame) {
printf("init_frame fail");
goto __ERROR;
}
if ((ret = create_filter(file_name,&fmt_context,&codec_context,&video_stream_index,&dec)) < 0) {
printf("create_filter fail");
} else {
if ((ret = init_filters(filter_desc,fmt_context,codec_context,&graph,&buf_ctx,&buf_sink_ctx,video_stream_index)) < 0) {
printf("init_filters fail");
goto __ERROR;
}
}
while (1) {
if((ret = av_read_frame(fmt_context,&packet)) < 0) {
break;
}
if (packet.stream_index == video_stream_index) {
if ( (ret = avcodec_send_packet(codec_context, &packet)) < 0) {
printf("avcodec_send_packet failed\n");
break;
}
if ((ret = decode_frame_and_filter(buf_ctx,buf_sink_ctx,codec_context,frame,filter_frame,out)) < 0) {
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
continue;;
}
printf("decode_frame_and_filter failed\n");
break;
}
}
}
__ERROR:
if (out) {
fclose(out);
}
if (fmt_context) {
avformat_free_context(fmt_context);
}
if (codec_context) {
avcodec_free_context(&codec_context);
}
if (graph) {
avfilter_graph_free(&graph);
}
if (buf_ctx) {
avfilter_free(buf_ctx);
}
if (buf_sink_ctx) {
avfilter_free(buf_sink_ctx);
}
if (dec) {
av_free(dec);
}
printf("finish");
}
// 创建graph avfilter_graph_alloc()
// 创建buffer filter 和 buffersink filter
//分析 filter描述符号 并构建avfiltergraph
// 使创建好的avfiltergraph生效
/// 使用filter
/// 1.获得解码后的原始数据pcm/yuv
/// 2.将数据添加到bufferfilter中去
/// 3.从buffer sink中读取处理好的数据
/// 当所有的数据处理完之后 释放资源
//实现自己的filter
//找一个现成的filter为模版
//替换代码中所有filter name 关键字
/// 修改libavfilter 中的makefile 增加新的filter
/// 修改allfilter.c增加新的filter 并重新编译ffmpeg
///
"
通过api的方式使用filter的流程
1.设置filter描述符 (vf 后边的)
2.初始化filter
3.进行滤镜处理
4.释放filter资源
ffmpeg -i input.mp4 -vf "setpts=0.5*PTS[v]" speed2.0.mp4
特殊filter
buffer filter
buffersink filter