如何使用libavfilter库给输入文件input.yuv添加视频滤镜?
一.视频滤镜初始化
本次代码实现的是给输入视频文件添加水平翻转滤镜,在视频滤镜初始化部分我们可以分为以下几步进行:
1.创建滤镜图结构
视频滤镜功能最核心的结构为滤镜图结构,即AVFilterGraph结构,我们调用avfilter_graph_alloc()函数就可以创建一个滤镜图结构。
2.创建滤镜实例结构
仅创建一个空的滤镜图显然是无法完成任何工作的,因此必须根据需求向滤镜图中添加相应的滤镜实例。这里,我们添加buffer滤镜和buffersink滤镜作为视频滤镜的输入和输出。滤镜由AVFilter结构实现,调用avfilter_get_by_name()函数即可获得相应的滤镜。在获取了这两个滤镜后,接下来,需要创建对应的滤镜实例,滤镜实例由AVFilterContext结构实现,通过调用avfilter_graph_create_filter()函数就能将滤镜实例添加到创建好的滤镜图中。
3.创建和配置滤镜接口
对于创建好的滤镜,需要将相应的接口连接后方可正常工作,滤镜接口类型定义为AVFilterInOut结构,其本质是一个链表的节点,创建输入输出接口可以调用avfilter_inout_alloc()函数,创建好之后,将滤镜对象和接口绑定即可。
4.根据滤镜描述解析并配置滤镜图
在完成滤镜图,相关滤镜和接口结构的创建后,接下来需要根据字符串类型的滤镜描述信息对整体的滤镜图进行解析和配置,这一步需要先后调用avfilter_graph_parse_ptr()和avfilter_graph_config()函数。
完整的初始化代码如下:
//video_filter_core.cpp
#define STREAM_FRAME_RATE 25
AVFilterContext *buffersink_ctx;
AVFilterContext *buffersrc_ctx;
AVFilterGraph *filter_graph;
AVFrame *input_frame= nullptr,*output_frame= nullptr; static int32_t init_frames(int32_t width,int32_t height,enum AVPixelFormat pix_fmt){
int result=0;
input_frame=av_frame_alloc();
output_frame=av_frame_alloc();
if(!input_frame||!output_frame){
cerr<<"Error:frame allocation failed."<<endl;
return -1;
}
input_frame->width=width;
input_frame->height=height;
input_frame->format=pix_fmt;
result= av_frame_get_buffer(input_frame,0);
if(result<0){
cerr<<"Error:av_frame_get_buffer failed."<<endl;
return -1;
}
result= av_frame_make_writable(input_frame);
if(result<0){
cerr<<"Error:av_frame_make_writable failed."<<endl;
return -1;
}
return 0;
} int32_t init_video_filter(int32_t width,int32_t height,const char *filter_descr){
int32_t result=0;
char args[512]={0};
const AVFilter *buffersrc= avfilter_get_by_name("buffer");
const AVFilter *buffersink= avfilter_get_by_name("buffersink");
AVFilterInOut *outputs=avfilter_inout_alloc();
AVFilterInOut *inputs=avfilter_inout_alloc();
enum AVPixelFormat pix_fmts[]={AV_PIX_FMT_YUV420P,AV_PIX_FMT_NONE};
do{
filter_graph=avfilter_graph_alloc();
if(!outputs||!inputs||!filter_graph){
cerr<<"Error:creating filter graph failed."<<endl;
result=AVERROR(ENOMEM);
break;
}
snprintf(args,sizeof(args),"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",width,height,AV_PIX_FMT_YUV420P,1,STREAM_FRAME_RATE,1,1);
result= avfilter_graph_create_filter(&buffersrc_ctx,buffersrc,"in",args, nullptr,filter_graph);
if(result<0){
cerr<<"Error:could not create source filter."<<endl;
break;
}
result= avfilter_graph_create_filter(&buffersink_ctx,buffersink,"out", nullptr,nullptr,filter_graph);
if(result<0){
cerr<<"Error:could not create sink filter."<<endl;
break;
}
result= av_opt_set_int_list(buffersink_ctx,"pix_fmts",pix_fmts,AV_PIX_FMT_NONE,AV_OPT_SEARCH_CHILDREN);
if(result<0){
cerr<<"Error:could not set output pixel format."<<endl;
break;
}
outputs->name=av_strdup("in");
outputs->filter_ctx=buffersrc_ctx;
outputs->pad_idx=0;
outputs->next= nullptr;
inputs->name=av_strdup("out");
inputs->filter_ctx=buffersink_ctx;
inputs->pad_idx=0;
inputs->next= nullptr;
//根据滤镜描述解析并配置滤镜图
if((result= avfilter_graph_parse_ptr(filter_graph,filter_descr,&inputs,&outputs, nullptr))<0){
cerr<<"Error:avfilter_graph_parse_ptr failed."<<endl;
break;
}
//在解析滤镜描述后,需要验证滤镜图整体配置的有效性
if((result=avfilter_graph_config(filter_graph, nullptr))<0){
cerr<<"Error:Graph config invalid."<<endl;
break;
}
result= init_frames(width,height,AV_PIX_FMT_YUV420P);
if(result<0){
cerr<<"Error:init frames failed."<<endl;
break;
}
}while(0);
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
return result;
}
static void free_frames(){
av_frame_free(&input_frame);
av_frame_free(&output_frame);
}
void destroy_video_filter(){
free_frames();
avfilter_graph_free(&filter_graph);
}
二.循环编辑视频帧
在这一步主要用到av_buffersrc_add_frame_flags()和av_buffersink_get_frame()这两个函数,它们的功能分别是将输入图像添加到滤镜图和从sink滤镜中获取编辑后的图像。代码如下:
//video_filter_core.cpp
static int32_t filter_frame(){
int32_t result=0;
if((result= av_buffersrc_add_frame_flags(buffersrc_ctx,input_frame,AV_BUFFERSRC_FLAG_KEEP_REF))<0){
cerr<<"Error:add frame to buffer src failed."<<endl;
return result;
}
while(true){
result= av_buffersink_get_frame(buffersink_ctx,output_frame);
if(result==AVERROR(EAGAIN)||result==AVERROR_EOF){
return 1;
}
else if(result<0){
cerr<<"Error:buffersink_get_frame failed."<<endl;
return result;
}
cout<<"Frame filtered,width:"<<output_frame->width<<",height:"<<output_frame->height<<endl;
write_frame_to_yuv(output_frame);
av_frame_unref(output_frame);
}
return result;
} int32_t filtering_video(int32_t frame_cnt){
int32_t result=0;
for(size_t i=0;i<frame_cnt;i++){
result= read_yuv_to_frame(input_frame);
if(result<0){
cerr<<"Error:read_yuv_to_frame failed."<<endl;
return result;
}
result=filter_frame();
if(result<0){
cerr<<"Error:filter_frame failed."<<endl;
return result;
}
}
return result;
}
下面是数据读入和数据写出代码:
//io_data.cpp
static FILE* input_file= nullptr;
static FILE* output_file= nullptr; int32_t open_input_output_files(const char* input_name,const char* output_name){
if(strlen(input_name)==0||strlen(output_name)==0){
cout<<"Error:empty input or output file name."<<endl;
return -1;
}
close_input_output_files();
input_file=fopen(input_name,"rb");//rb:读取一个二进制文件,该文件必须存在
if(input_file==nullptr){
cerr<<"Error:failed to open input file."<<endl;
return -1;
}
output_file=fopen(output_name,"wb");//wb:打开或新建一个二进制文件,只允许写
if(output_file== nullptr){
cout<<"Error:failed to open output file."<<endl;
return -1;
}
return 0;
}
void close_input_output_files(){
if(input_file!= nullptr){
fclose(input_file);
input_file= nullptr;
}
if(output_file!= nullptr){
fclose(output_file);
output_file= nullptr;
}
}
int32_t read_yuv_to_frame(AVFrame* frame){
int32_t frame_width=frame->width;
int32_t frame_height=frame->height;
int32_t luma_stride=frame->linesize[0];
int32_t chroma_stride=frame->linesize[1];
int32_t frame_size=frame_width*frame_height*3/2;
int32_t read_size=0;
if(frame_width==luma_stride){
//如果width等于stride,则说明frame中不存在padding字节,可整体读取
read_size+=fread(frame->data[0],1,frame_width*frame_height,input_file);
read_size+=fread(frame->data[1],1,frame_width*frame_height/4,input_file);
read_size+=fread(frame->data[2],1,frame_width*frame_height/4,input_file);
}
else{
//如果width不等于stride,则说明frame中存在padding字节
//对三个分量应该逐行读取
for(size_t i=0;i<frame_height;i++){
read_size+=fread(frame->data[0]+i*luma_stride,1,frame_width,input_file);
}
for(size_t uv=1;uv<=2;uv++){
for(size_t i=0;i<frame_height/2;i++){
read_size+=fread(frame->data[uv]+i*chroma_stride,1,frame_width/2,input_file);
}
}
}
if(read_size!=frame_size){
cerr<<"Error:Read data error,frame_size:"<<frame_size<<",read_size:"<<read_size<<endl;
return -1;
}
return 0;
}
int32_t write_frame_to_yuv(AVFrame* frame){
uint8_t** pBuf=frame->data;
int* pStride=frame->linesize;
for(size_t i=0;i<3;i++){
int32_t width=(i==0?frame->width:frame->width/2);
int32_t height=(i==0?frame->height:frame->height/2);
for(size_t j=0;j<height;j++){
fwrite(pBuf[i],1,width,output_file);
pBuf[i]+= pStride[i];
}
}
return 0;
}
main函数实现:
int main(){
const char *input_file_name="../input.yuv";
int32_t pic_width=1920;
int32_t pic_height=1080;
int32_t total_frame_cnt=250;
const char *filter_descr="hflip";
const char *output_file_name="../output.yuv";
int32_t result= open_input_output_files(input_file_name,output_file_name);
if(result<0){
return result;
}
result= init_video_filter(pic_width,pic_height,filter_descr);
if(result<0){
return result;
}
result= filtering_video(total_frame_cnt);
if(result<0){
return result;
}
close_input_output_files();
destroy_video_filter();
return 0;
}
最后,可以以下指令测试输出的output.yuv文件:
ffplay -f rawvideo -video_size 1920x1080 -i output.yuv
如何使用libavfilter库给输入文件input.yuv添加视频滤镜?的更多相关文章
- 标准库 - 输入输出处理(input and output facilities) lua
标准库 - 输入输出处理(input and output facilities)责任编辑:cynthia作者:来自ITPUB论坛 2008-02-18 文本Tag: Lua [IT168 技术文档] ...
- luvcview,使用mplayer查看摄像头和luvcview保存YUV图像视频的播放(转)
luvcview,使用mplayer查看摄像头和luvcview保存YUV图像视频的播放 在mplayer中查看摄像头,可使用如下命令:mplayer tv:// -tv driver=v4l2:in ...
- chart.js图表库案例赏析,饼图添加文字
chart.js图表库案例赏析,饼图添加文字 Chart.js 是一个令人印象深刻的 JavaScript 图表库,建立在 HTML5 Canvas 基础上.目前,它支持6种图表类型(折线图,条形图, ...
- 给input元素添加float. 去除IE6 下input的空隙
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- input输入框添加内部图标
有可能在制作网页的过程中遇到各种美化表单设计,这次我们来试着做一个demo 将input输入框添加内部图标 话不多说,看一下最终效果 我们的思路是,在一个div中,加入一个div和一个input标签, ...
- jquery语言中获取input标签后如何给input标签添加disabled的属性
jquery语言中获取input标签后如何给input标签添加disabled的属性 1.使用JQuery函数prop():$("input").prop("disabl ...
- 表单input项使用label,同时引用Bootstrap库,导致input点击效果区增大
产品姐姐想法多,点击input项才能聚焦进行操作,点击外部不能有反应 好了...直入正题 为了让标签更加语义化,在表单项中,我们往往会使用label进行包裹 <label for="l ...
- 为input输入框添加圆角并去除阴影
<input type="text" name="bianhao" value="" placeholder="请输入商品编 ...
- Input checkbox 添加样式背景
<style type="text/css"> .chk_1 { width: 20px; height: 20px; position: absolute; top: ...
- SharePoint 2007 文档库中的文档添加评论功能
背景:接到一个项目,要求文档管理,当然文档库就可以了,但是要求文档需要大家去读,读完以后还可以发表评论,这Moss貌似就有点困难了.和同事一起合计,想来想去也没有太好的办法,后来想到传统开发,两个表的 ...
随机推荐
- [Windows/Linux]Linux下的正斜杠"/"和"\"的区别 [转载]
执行某一条Linux命令时,遇到了此问题,甚为不解.[文由] 本篇属于全文转载自: Linux下的正斜杠"/"和""的区别 - 博客园 >>> ...
- devops|中小公司效率为王,没必要度量
之前写过一篇文章<devops|中小公司不要做研发效能度量>,主要是从基础设施方向考虑,因为很多条件都不具备,贸然高投入去做研发效能度量可能达不到我们的预期效果,给出的建议是先做好当下打好 ...
- windows下MinGW编译ffmpeg
windows下MinGW编译ffmpeg 1.官网下载MinGW并安装 1)下载 ,下载网址: https://sourceforge.net/projects/mingw/files/ ...
- 高阶组件——withRouter的原理和用法
作用: 高阶组件中的withRouter, 作用是将一个组件包裹进Route里面, 然后react-router的三个对象history, location, match就会被放进这个组件的props ...
- [Pytorch框架] 2.1.1 PyTorch 基础 : 张量
文章目录 PyTorch 基础 : 张量 张量(Tensor) 基本类型 Numpy转换 设备间转换 初始化 常用方法 PyTorch 基础 : 张量 在第一章中我们已经通过官方的入门教程对PyTor ...
- ChatGPT Plugin 插件开发:基于 ASP.NET Core Minimal API
前言 这是一篇ChatGPT插件开发教程,描述如何使用 ASP.NET Core Minimal API 开发 ChatGPT 插件,以最简单的 Todo List 指导示例作为入门教程. 这个Tod ...
- 【Redis】Setninel 哨兵机制
一.Sentinel 哨兵工作原理 Redis在2.6+以后引入哨兵机制,在2.8版本后趋于稳定状态,在生产环境中建议使用2.8版本以上的sentinel服务.sentinel集群用于监控redis集 ...
- 一篇文章搞定什么是nodeJs它和NPM关系与应用
现在前端的入门门槛越来越高了,不再是单纯 html+css+js,各种前端框架 层出不穷,各种ui组件库层出不穷. 模块化,打包化,各种工具库层出不穷,前端变成大前端 ,甚至前端可以搞定整个项目,通过 ...
- Go windows 环境搭建
下载地址 官网下载地址:https://golang.google.cn/dl/ 1.下载完之后 双击msi进行安装 路径可以不用改, 继续next 安装完之后就需要配置环境变量, 找到环境变量 GO ...
- 2022-06-06:大妈一开始手上有x个鸡蛋,她想让手上的鸡蛋数量变成y, 操作1 : 从仓库里拿出1个鸡蛋到手上,x变成x+1个, 操作2 : 如果手上的鸡蛋数量是3的整数倍,大妈可以直接把三分之
2022-06-06:大妈一开始手上有x个鸡蛋,她想让手上的鸡蛋数量变成y, 操作1 : 从仓库里拿出1个鸡蛋到手上,x变成x+1个, 操作2 : 如果手上的鸡蛋数量是3的整数倍,大妈可以直接把三分之 ...