标签:查看 输出 overlay strong frame 需要 clu fread width
1. 简介
FFmpeg filter提供了很多?视频特效处理的功能,?如视频缩放、截取、翻转、叠加等。
其中定义了很多的filter,例如以下常?的?些filter。
scale:视频/图像的缩放
overlay:视频/图像的叠加
crop:视频/图像的裁剪
trim:截取视频的?段
rotate:以任意?度旋转视频
?持的filter的列表可以通过以下命令获得。
ffmpeg -filters
也可以查看?档[2],具体某个版本的?持情况以命令?获取到的结果为准。
以下是filter的?个简单的应?示例,对视频的宽和?减半。
ffmpeg -i input -vf scale=iw/2:ih/2 output
2. filter使用方法
FFmpeg中filter包含三个层次,filter->filterchain->filtergraph。
2.1 filter语法
??个字符串描述filter的组成,形式如下
[in_link_1]…[in_link_N]filter_name=parameters[out_link_1]…[out_link_M]
参数说明:
1. [in_link_N]、[out_link_N]:?来标识输?和输出的标签。in_link_N是标签名,标签名可以任意命名,需使??括号括起来。在filter_name的前?的标签?于标识输?,在filter_name后?的?于标识输出。?个filter可以有多个输?和多个输出,没有输?的filter称为source filter,没有输出的filter称为sink filter。对输?或输出打标签是可选的,打上标签是为了连接其他filter时使?。
2. filter_name:filter的名称。
3. “=parameters”:包含初始化filter的参数,是可选的。
“=parameters”有以下?种形式
1. 使?‘:‘字符分隔的?个“键=值”对列表。如下所示。
ffmpeg -i input -vf scale=w=iw/2:h=ih/2 output ffmpeg -i input -vf scale=h=ih/2:w=iw/2 output
2. 使?‘:‘字符分割的“值”的列表。在这种情况下,键按照声明的顺序被假定为选项名。例如,scale filter 的前两个选项分别是w和h,当参数列表为“iw/2:ih/2”时,iw/2的值赋给w,ih/2的值赋给h。如下所示。
ffmpeg -i input -vf scale=iw/2:ih/2 output
3. 使?‘:‘ 字符分隔混合“值”和“键=值”对的列表。“值”必须位于“键=值”对之前,并遵循与前?点相同的约束顺序。之后的“键=值”对的顺序不受约束。如下所示。
ffmpeg -i input -vf scale=iw/2:h=ih/2 output
2.2 filterchain的语法
??个字符串描述filterchain的组成,形式如下
"filter1, filter2, ... filterN-1, filterN"
说明:
1. 由?个或多个filter的连接?成,filter之间以逗号“,”分隔。
2. 每个filter都连接到序列中的前?个filter,即前?个filter的输出是后?个filter的输?。
?如示例
ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
示例说明:
1. crop、vflip在同?个filterchain中
2.3 filtergraph的语法
??个字符串描述filtergraph的组成,形式如下
"filterchain1;filterchain2;...filterchainN-1;fiterchainN"
说明:
1. 由?个或多个filter的组合?成,filterchain之间?分号";"分隔。
2. filtergraph是连接filter的有向图。它可以包含循环,?对filter之间可以有多个连接。
3. 当在filtergraph中找到两个相同名称的标签时,将创建相应输?和输出之间的连接。
4. 如果输出没有被打标签,则默认将其连接到filterchain中下?个filter的第?个未打标签的输?。例如
以下filterchain中。
nullsrc, split[L1], [L2]overlay, nullsink
说明:split filter有两个输出,overlay filter有两个输?。split的第?个输出标记为“L1”,overlay的第?个输?pad标记为“L2”。split的第?个输出将连接到overlay的第?个输?。
5. 在?个filter描述中,如果没有指定第?个filter的输?标签,则假定为“In”。如果没有指定最后?个filter的输出标签,则假定为“out”。
6. 在?个完整的filterchain中,所有没有打标签的filter输?和输出必须是连接的。如果所有filterchain的所有filter输?和输出pad都是连接的,则认为filtergraph是有效的[2]。
?如示例
ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
其中有三个filterchain, 分别是:
1. "split [main][tmp]"。它只有?个filter,即 split,它有?个默认的输?,即INPUT解码后的frame。有两个输出, 以 [main], [tmp] 标识。
2. "[tmp] crop=iw:ih/2:0:0, vflip [flip]"。它由两个filter组成,crop和vflip,crop的输? 为[tmp],vflip的输出标识为[flip]。
3. "[main][flip] overlay=0:H/2"。它由?个filter组成,即overlay。有两个输?,[main]和[flip]。有?个默认的输出。
3. 基本结构
我们把?整个滤波的流程称为滤波过程。下?是?个滤波过程的结构
图中简要指示出了滤波所?到的各个结构体,各个结构体有如下作?:
AVFilterGraph | ?于统合这整个滤波过程的结构体。 |
AVFilter | 滤波器,滤波器的实现是通过AVFilter以及位于其下的结构体/函数来维护的。 |
AVFilterContext | ?个滤波器实例,即使是同?个滤波器,但是在进?实际的滤波时,也会由于输?的参数不同?有不同的滤波效果,AVFilterContext就是在实际进?滤波时?于维护滤波相关信息的实体。 |
AVFilterLink | 滤波器链,作?主要是?于连接相邻的两个AVFilterContext。为了实现?个滤波过程,可能会需要多个滤波器协同完成,即?个滤波器的输出可能会是另?个滤波器的输?,AVFilterLink的作?是串联两个相邻的滤波器实例,形成两个滤波器之间的通道。 |
AVFilterPad | 滤波器的输?输出端?,?个滤波器可以有多个输?以及多个输出端?,相邻滤波器之间是通过AVFilterLink来串联的,?位于AVFilterLink两端的分别就是前?个滤波器的输出端?以及后?个滤波器的输?端?。 |
buffersrc | ?个特殊的滤波器,这个滤波器的作?就是充当整个滤波过程的??,通过调?该滤波器提供的函数(如av_buffersrc_add_frame)可以把需要滤波的帧传输进?滤波过程。在创建该滤波器实例的时候需要提供?些关于所输?的帧的格式的必要参数(如:time_base、图像的宽?、图像像素格式等)。 |
buffersink | ?个特殊的滤波器,这个滤波器的作?就是充当整个滤波过程的出?,通过调?该滤波器提供的函数(如av_buffersink_get_frame)可以提取出被滤波过程滤波完成后的帧。 |
demo
#include <stdio.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavfilter/avfilter.h> #include <libavfilter/buffersink.h> #include <libavfilter/buffersrc.h> #include <libavutil/opt.h> #include <libavutil/imgutils.h> AVFilterContext *mainsrc_ctx = NULL; AVFilterContext *resultsink_ctx = NULL; AVFilterGraph *filter_graph = NULL; int init_filters(const int width, const int height, const int format) { int ret = 0; AVFilterInOut *inputs = NULL; AVFilterInOut *outputs = NULL; char filter_args[1024] = { 0 }; filter_graph = avfilter_graph_alloc(); if (!filter_graph) { printf("Error: allocate filter graph failed\n"); return -1; } // snprintf(filter_args, sizeof(filter_args), // "buffer=video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d[v0];" // Parsed_buffer_0 // "[v0]split[main][tmp];" // Parsed_split_1 // "[tmp]crop=iw:ih/2:0:0,vflip[flip];" // Parsed_crop_2 Parsed_vflip_3 // "[main][flip]overlay=0:H/2[result];" // Parsed_overlay_4 // "[result]buffersink", // Parsed_buffersink_5 // width, height, format, 1, 25, 1, 1); snprintf(filter_args, sizeof(filter_args), "buffer=video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d[v0];" // Parsed_buffer_0 "[v0]split[main][tmp];" // Parsed_split_1 "[tmp]crop=iw:ih/2:0:0,vflip[flip];" // Parsed_crop_2 Parsed_vflip_3 "[main]buffersink;" // Parsed_buffersink_4 "[flip]buffersink", // Parsed_buffersink_5 width, height, format, 1, 25, 1, 1); ret = avfilter_graph_parse2(filter_graph, filter_args, &inputs, &outputs); if (ret < 0) { printf("Cannot parse graph\n"); return ret; } ret = avfilter_graph_config(filter_graph, NULL); // 提交过滤器 if (ret < 0) { printf("Cannot configure graph\n"); return ret; } // Get AVFilterContext from AVFilterGraph parsing from string mainsrc_ctx = avfilter_graph_get_filter(filter_graph, "Parsed_buffer_0"); if(!mainsrc_ctx) { printf("avfilter_graph_get_filter Parsed_buffer_0 failed\n"); return -1; } resultsink_ctx = avfilter_graph_get_filter(filter_graph, "Parsed_buffersink_4"); if(!resultsink_ctx) { printf("avfilter_graph_get_filter Parsed_buffersink_5 failed\n"); return -1; } printf("sink_width:%d, sink_height:%d\n", av_buffersink_get_w(resultsink_ctx), av_buffersink_get_h(resultsink_ctx)); return 0; } // ffmpeg -i 9.5.flv -vf "split[main][tmp];[tmp]crop=iw:ih/2:0:0,vflip [flip];[main][flip]overlay=0:H/2" -b:v 500k -vcodec libx264 9.5_out.flv int main(int argc, char** argv) { int ret = 0; int in_width = 768; int in_height = 320; avfilter_register_all(); if(init_filters(in_width, in_height, AV_PIX_FMT_YUV420P) < 0) { printf("init_filters failed\n"); return -1; } // input yuv FILE* inFile = NULL; const char* inFileName = "768x320.yuv"; fopen_s(&inFile, inFileName, "rb+"); if (!inFile) { printf("Fail to open file\n"); return -1; } // output yuv FILE* outFile = NULL; const char* outFileName = "out_crop_vfilter_4.yuv"; fopen_s(&outFile, outFileName, "wb"); if (!outFile) { printf("Fail to create file for output\n"); return -1; } char *graph_str = avfilter_graph_dump(filter_graph, NULL); FILE* graphFile = NULL; fopen_s(&graphFile, "graphFile.txt", "w"); // 打印filtergraph的具体情况 fprintf(graphFile, "%s", graph_str); av_free(graph_str); AVFrame *frame_in = av_frame_alloc(); unsigned char *frame_buffer_in = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width, in_height, 1)); av_image_fill_arrays(frame_in->data, frame_in->linesize, frame_buffer_in, AV_PIX_FMT_YUV420P, in_width, in_height, 1); AVFrame *frame_out = av_frame_alloc(); unsigned char *frame_buffer_out = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width, in_height, 1)); av_image_fill_arrays(frame_out->data, frame_out->linesize, frame_buffer_out, AV_PIX_FMT_YUV420P, in_width, in_height, 1); frame_in->width = in_width; frame_in->height = in_height; frame_in->format = AV_PIX_FMT_YUV420P; uint32_t frame_count = 0; while (1) { // 读取yuv数据 if (fread(frame_buffer_in, 1, in_width*in_height * 3 / 2, inFile) != in_width*in_height * 3 / 2) { break; } //input Y,U,V frame_in->data[0] = frame_buffer_in; frame_in->data[1] = frame_buffer_in + in_width*in_height; frame_in->data[2] = frame_buffer_in + in_width*in_height * 5 / 4; if (av_buffersrc_add_frame(mainsrc_ctx, frame_in) < 0) { printf("Error while add frame.\n"); break; } // filter内部自己处理 /* pull filtered pictures from the filtergraph */ ret = av_buffersink_get_frame(resultsink_ctx, frame_out); if (ret < 0) break; //output Y,U,V if (frame_out->format == AV_PIX_FMT_YUV420P) { for (int i = 0; i < frame_out->height; i++) { fwrite(frame_out->data[0] + frame_out->linesize[0] * i, 1, frame_out->width, outFile); } for (int i = 0; i < frame_out->height / 2; i++) { fwrite(frame_out->data[1] + frame_out->linesize[1] * i, 1, frame_out->width / 2, outFile); } for (int i = 0; i < frame_out->height / 2; i++) { fwrite(frame_out->data[2] + frame_out->linesize[2] * i, 1, frame_out->width / 2, outFile); } } ++frame_count; if(frame_count % 25 == 0) printf("Process %d frame!\n",frame_count); av_frame_unref(frame_out); } fclose(inFile); fclose(outFile); av_frame_free(&frame_in); av_frame_free(&frame_out); avfilter_graph_free(&filter_graph); // 内部去释放AVFilterContext产生的内存 printf("finish\n"); return 0; }
标签:查看 输出 overlay strong frame 需要 clu fread width
原文地址:https://www.cnblogs.com/vczf/p/13638051.html