FFmpeg的过滤器是用于对音视频流进行处理和转换的模块。它可以对输入流进行各种操作,如调整大小、调节亮度、对比度、裁剪、旋转等。
实现对某个视频的放大操作的详细步骤如下:
下面是使用FFmpeg过滤器功能实现将input.mp4文件中的视频宽度扩大两倍并水平翻转的代码流程
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[]) {
const char *filters = "scale=w=2*iw:h=ih, hflip";
const char *video_file = "input.mp4";
// 注册所有的过滤器
avfilter_register_all();
// 用于存储视频文件的格式上下文
AVFormatContext *format_ctx = NULL;
// 打开视频文件并获取格式上下文
if (avformat_open_input(&format_ctx, video_file, NULL, NULL) != 0) {
fprintf(stderr, "Unable to open video file.");
return -1;
}
// 检索流信息
if (avformat_find_stream_info(format_ctx, NULL) < 0) {
fprintf(stderr, "Unable to retrieve stream information.");
avformat_close_input(&format_ctx);
return -1;
}
// 查找视频流索引
int video_stream_index = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_stream_index == AVERROR_STREAM_NOT_FOUND) {
fprintf(stderr, "Unable to find video stream.");
avformat_close_input(&format_ctx);
return -1;
}
// 获取视频流解码器上下文
AVCodecContext *codec_ctx = format_ctx->streams[video_stream_index]->codec;
// 查找视频流解码器
AVCodec *codec = avcodec_find_decoder(codec_ctx->codec_id);
if (codec == NULL) {
fprintf(stderr, "Unable to find decoder.");
avformat_close_input(&format_ctx);
return -1;
}
// 打开视频流解码器
if (avcodec_open2(codec_ctx, codec, NULL) != 0) {
fprintf(stderr, "Unable to open decoder.");
avformat_close_input(&format_ctx);
return -1;
}
// 创建过滤器图
AVFilterGraph *filter_graph = avfilter_graph_alloc();
if (!filter_graph) {
fprintf(stderr, "Unable to create filter graph.\n");
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
return -1;
}
// 创建输入过滤器
AVFilter *buffer_src = avfilter_get_by_name("buffer");
AVFilterContext *buffer_src_ctx;
if (avfilter_graph_create_filter(&buffer_src_ctx, buffer_src, "in", NULL, NULL, filter_graph) < 0) {
fprintf(stderr, "Unable to create buffer source filter.\n");
avfilter_graph_free(&filter_graph);
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
return -1;
}
// 设置输入过滤器参数
if (av_opt_set_bin(buffer_src_ctx, "video_size", (uint8_t*)&codec_ctx->width, sizeof(codec_ctx->width), AV_OPT_SEARCH_CHILDREN) < 0) {
fprintf(stderr, "Unable to set video size.\n");
avfilter_graph_free(&filter_graph);
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
return -1;
}
// 创建输出过滤器
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
AVFilter *buffer_sink = avfilter_get_by_name("buffersink");
AVFilterContext *buffer_sink_ctx;
if (avfilter_graph_create_filter(&buffer_sink_ctx, buffer_sink, "out", NULL, NULL, filter_graph) < 0) {
fprintf(stderr, "Unable to create buffer sink filter.\n");
avfilter_graph_free(&filter_graph);
avfilter_inout_free(&outputs);
avfilter_inout_free(&inputs);
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
return -1;
}
// 创建输入过滤器参数
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
outputs->name = av_strdup("in");
outputs->filter_ctx = buffer_src_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = buffer_sink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
// 连接输入输出过滤器
if (avfilter_graph_parse_ptr(filter_graph, filters, &inputs, &outputs, NULL) < 0) {
fprintf(stderr, "Unable to parse filter graph.\n");
avfilter_graph_free(&filter_graph);
avfilter_inout_free(&outputs);
avfilter_inout_free(&inputs);
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
return -1;
}
// 配置过滤器图
if (avfilter_graph_config(filter_graph, NULL) < 0) {
fprintf(stderr, "Unable to configure filter graph.\n");
avfilter_graph_free(&filter_graph);
avfilter_inout_free(&outputs);
avfilter_inout_free(&inputs);
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
return -1;
}
// 创建输入帧和输出帧
AVFrame *frame = av_frame_alloc();
AVFrame *out_frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Unable to allocate frame.\n");
avfilter_graph_free(&filter_graph);
avfilter_inout_free(&outputs);
avfilter_inout_free(&inputs);
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
return -1;
}
// 打开输出文件
FILE *video_dst_file = fopen("output.mp4", "wb");
if (!video_dst_file) {
fprintf(stderr, "Unable to open output file.\n");
avfilter_graph_free(&filter_graph);
avfilter_inout_free(&outputs);
avfilter_inout_free(&inputs);
av_frame_free(&frame);
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
return -1;
}
// 初始化空的AVPacket结构体,用于存储解码之后的数据
AVPacket packet = { 0 };
// 读取每个视频包
while (av_read_frame(format_ctx, &packet) >= 0) {
// 检查是否是视频流
if (packet.stream_index == video_stream_index) {
// 将视频包发送到解码器进行解码
if (avcodec_send_packet(codec_ctx, &packet) != 0) {
fprintf(stderr, "Error sending packet to decoder.\n");
break;
}
// 解码每个解码帧
while (avcodec_receive_frame(codec_ctx, frame) == 0) {
// 将解码帧发送到过滤器图进行处理
if (av_buffersrc_add_frame_flags(buffer_src_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) != 0) {
fprintf(stderr, "Error adding frame to filter graph.\n");
break;
}
// 获取过滤后的帧
while (av_buffersink_get_frame(buffer_sink_ctx, out_frame) == 0) {
// 直接将帧数据写入输出文件
fwrite(out_frame->data[0], 1, out_frame->linesize[0] * out_frame->height, video_dst_file);
fwrite(out_frame->data[1], 1, out_frame->linesize[1] * out_frame->height / 2, video_dst_file);
fwrite(out_frame->data[2], 1, out_frame->linesize[2] * out_frame->height / 2, video_dst_file);
// 释放帧对象
av_frame_unref(out_frame);
}
}
// 释放帧对象
av_frame_unref(frame);
}
// 释放数据包对象
av_packet_unref(&packet);
}
// 关闭输出文件
fclose(video_dst_file);
// 释放资源
avfilter_graph_free(&filter_graph);
avfilter_inout_free(&outputs);
avfilter_inout_free(&inputs);
av_frame_free(&frame);
avcodec_free_context(&codec_ctx);
avformat_close_input(&format_ctx);
return 0;
}
如果有多个过滤器可以通过遍历过滤器源码数组来创建和连接多个过滤器。每次循环迭代,我们创建一个新的过滤器,并与前一个过滤器进行连接。最后一个过滤器将连接到输出过滤器。请注意,在每次迭代中,我们使用前一个过滤器的输出过滤器上下文作为当前过滤器的输入。
const char *filters[] = {
"scale=w=2*iw:h=ih",
"hflip",
"rotate=30*PI/180"
};
// ...
// 创建输入输出过滤器链表
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
// 创建输入过滤器
AVFilterContext *buffer_src_ctx = NULL;
if (avfilter_graph_create_filter(&buffer_src_ctx, buffer_src, "in", NULL, NULL, filter_graph) < 0) {
// 错误处理
}
// 设置输入过滤器参数
if (av_opt_set_bin(buffer_src_ctx, "video_size", (uint8_t*)&codec_ctx->width, sizeof(codec_ctx->width), AV_OPT_SEARCH_CHILDREN) < 0) {
// 错误处理
}
// 循环遍历过滤器源码数组,并创建过滤器
for (int i = 0; i < num_filters; i++) {
// 创建输出过滤器
AVFilterContext *filter_ctx = NULL;
if (avfilter_graph_create_filter(&filter_ctx, filter, "filter_name", NULL, NULL, filter_graph) < 0) {
// 错误处理
}
// 连接过滤器
if (avfilter_link(buffer_src_ctx, 0, filter_ctx, 0) < 0) {
// 错误处理
}
buffer_src_ctx = filter_ctx; // 更新buffer_src_ctx为当前过滤器的输出过滤器上下文
}
// 创建输出过滤器
AVFilterContext *buffer_sink_ctx = NULL;
if (avfilter_graph_create_filter(&buffer_sink_ctx, buffer_sink, "out", NULL, NULL, filter_graph) < 0) {
// 错误处理
}
// 连接输出过滤器
if (avfilter_link(buffer_src_ctx, 0, buffer_sink_ctx, 0) < 0) {
// 错误处理
}
// 配置过滤器图
if (avfilter_graph_config(filter_graph, NULL) < 0) {
// 错误处理
}
// ...
// 在解码和过滤循环中处理帧
// ...
// 释放资源
avfilter_graph_free(&filter_graph);
avfilter_inout_free(&outputs);
avfilter_inout_free(&inputs);
// ...