FFMPEG源码之过滤器

功能介绍

FFmpeg的过滤器是用于对音视频流进行处理和转换的模块。它可以对输入流进行各种操作,如调整大小、调节亮度、对比度、裁剪、旋转等。

操作步骤

实现对某个视频的放大操作的详细步骤如下:

  1. 配置和初始化FFmpeg:在开始编写代码之前,需要配置和初始化FFmpeg库。这包括引入头文件、注册相关的编解码器和过滤器等。
  2. 打开输入文件:使用avformat_open_input函数打开要处理的视频文件,并获取音视频流信息。
  3. 配置输入流上下文和输出流上下文:使用avcodec_parameters_to_context函数从输入流中获取编解码器参数,并将其配置给相应的解码器上下文。创建输出流上下文,指定输出文件格式及其编码参数。
  4. 配置过滤器图:使用avfilter_graph_alloc函数创建过滤器图。
  5. 添加输入过滤器:使用avfilter_graph_create_filter函数为过滤器图添加输入过滤器。输入过滤器的参数是输入流上下文中的解码器上下文。
  6. 添加输出过滤器:使用avfilter_graph_create_filter函数为过滤器图添加输出过滤器。输出过滤器的参数是输出流上下文中的编码器上下文。
  7. 创建过滤器链:使用avfilter_graph_parse2函数将过滤器图中的过滤器链接起来,形成过滤器链。
  8. 配置过滤器链:使用avfilter_graph_config函数对过滤器链进行配置,包括设置过滤器参数和连接输入输出。
  9. 创建帧对象:使用av_frame_alloc函数创建待处理的帧对象。
    从输入文件中读取帧:使用av_read_frame函数从输入文件中循环读取帧数据,直到所有帧读取完毕。
  10. 处理帧数据:在帧数据处理循环中,将读取到的帧数据传入过滤器链进行处理。
  11. 将处理后的帧数据写入输出文件:使用av_frame_get_best_effort_timestamp函数获取帧的时间戳,然后使用av_interleaved_write_frame函数向输出文件写入帧数据。
  12. 释放资源:循环结束后,释放所有资源,包括释放帧对象、关闭输入文件、关闭输出文件等。

源码实现

下面是使用FFmpeg过滤器功能实现将input.mp4文件中的视频宽度扩大两倍并水平翻转的代码流程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>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);// ...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/5620.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线性表之链表

1、链表概述 链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 顺序表的存储位置可以用一个简单直观的公式表示&#xff0c;它可以随机存取表中任意一个元素&#xff0c;但插入和删除需要移动大量元素。链式…

深入理解 PostgreSQL 的架构和内部工作原理

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Postman怎么做接口测试-以简单的登录接口为例

我们就以登录某测试系统为例子&#xff0c;实现在Postman上做接口测试 一、首先打开系统首页首页&#xff0c;做一个登录操作&#xff08;目的是获取接口url及参数&#xff09;&#xff1a;一般在公司做接口测试的时候页面还没有出来&#xff0c;我们需要根据接口文档进行接口…

kafka第三课-可视化工具、生产环境问题总结以及性能优化

一、可视化工具 https://pan.baidu.com/s/1qYifoa4 密码&#xff1a;el4o 下载解压之后&#xff0c;编辑该文件&#xff0c;修改zookeeper地址&#xff0c;也就是kafka注册的zookeeper的地址&#xff0c;如果是zookeeper集群&#xff0c;以逗号分开 vi conf/application.conf 启…

Python 逻辑回归:理论与实践

文章目录 1. 介绍1.1 什么是逻辑回归&#xff1f;1.2 逻辑回归的应用领域 2. 逻辑回归的原理2.1 Sigmoid 函数2.2 决策边界2.3 损失函数 3. 逻辑回归的实现3.1 数据准备3.2 创建逻辑回归模型3.3 模型训练3.4 模型预测3.5 模型评估 4. 可视化决策边界4.1 绘制散点图4.2 绘制决策…

基于SaaS模式的Java基层卫生健康云HIS系统源码【运维管理+运营管理+综合监管】

云HIS综合管理平台 一、模板管理 模板分为两种&#xff1a;病历模板和报表模板。模板管理是运营管理的核心组成部分&#xff0c;是基层卫生健康云中各医疗机构定制电子病历和报表的地方&#xff0c;各医疗机构可根据自身特点特色定制电子病历和报表&#xff0c;制作的电子病历…

php 手机加*星 【字符串】

场景&#xff1a;展示手机号时&#xff0c;避免暴露隐私信息&#xff0c;因此需要给手机号加*号 代码 /*** 手机号码隐私加星* param string $mobile 手机号*/ function mobileToStar(string $mobile) { // 正则检测手机号if(!preg_match(/^1[3456789]\d{9}$/,$mobile)){// …

Docker 容器生命周期:创建、启动、暂停与停止----从创建到停止多角度分析

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Sysbench测试工具详解

文章目录 Sysbench安装准备测试数据使用yum安装的路径进行测试测试结果 Sysbench Sysbench 是一个常用的多线程性能测试工具&#xff0c;可用于评估数据库系统和硬件的性能。它支持多种基准测试&#xff0c;包括 OLTP (Online Transaction Processing)、CPU、文件 I/O、内存等。…

代码随想录| 图论04 查并集 ●查并集理论知识 ●1971.寻找图中是否存在路径 ●684.冗余连接 ●685.冗余连接II

#查并集理论知识 并查集用处&#xff1a;解决连通性问题 将两个元素添加到一个集合中。判断两个元素在不在同一个集合 思路&#xff1a;将三个元素A&#xff0c;B&#xff0c;C &#xff08;分别是数字&#xff09;放在同一个集合&#xff0c;其实就是将三个元素连通在一起&a…

Python 算法基础篇:插入排序和希尔排序

Python 算法基础篇&#xff1a;插入排序和希尔排序 引言 1. 插入排序算法概述2. 插入排序算法实现实例1&#xff1a;插入排序 3. 希尔排序算法概述4. 希尔排序算法实现实例2&#xff1a;希尔排序 5. 插入排序与希尔排序的对比总结 引言 插入排序和希尔排序是两种常用的排序算法…

017-从零搭建微服务-系统服务(四)

写在最前 如果这个项目让你有所收获&#xff0c;记得 Star 关注哦&#xff0c;这对我是非常不错的鼓励与支持。 源码地址&#xff08;后端&#xff09;&#xff1a;https://gitee.com/csps/mingyue 源码地址&#xff08;前端&#xff09;&#xff1a;https://gitee.com/csps…

【实战】 七、Hook,路由,与 URL 状态管理(下) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(十三)

文章目录 一、项目起航&#xff1a;项目初始化与配置二、React 与 Hook 应用&#xff1a;实现项目列表三、TS 应用&#xff1a;JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…

el-upload将上传的图片转为base64

el-upload将上传的图片转为base64 文章目录 el-upload将上传的图片转为base641. el-upload属性设置2. 图片转base64格式方法3. 在绑定的方法中调用 elementui使用el-upload时将选择的图片转为base64格式,然后再上传 1. el-upload属性设置 使用组件&#xff0c;然后on-change绑定…

【NLP】如何使用Hugging-Face-Pipelines?

一、说明 随着最近开发的库&#xff0c;执行深度学习分析变得更加容易。其中一个库是拥抱脸。Hugging Face 是一个平台&#xff0c;可为 NLP 任务&#xff08;如文本分类、情感分析等&#xff09;提供预先训练的语言模型。 本博客将引导您了解如何使用拥抱面部管道执行 NLP 任务…

代码随想录算法训练营day45 70.爬楼梯(进阶) 322.零钱兑换 279.完全平方数

题目链接70.爬楼梯(进阶) class Solution {public int climbStairs(int n) {int[] dp new int[n1];int m 2;dp[0] 1;for(int i 1; i < n; i){for(int j 1; j < m; j){if(i > j)dp[i] dp[i-j];}}return dp[n];} }题目链接322.零钱兑换 class Solution {public …

超详细图文教程:3DS Max 中创建低多边形游戏长剑模型

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 在此&#xff0c;由两部分组成的教程的第一部分中&#xff0c;我将向您展示如何&#xff1a; 对剑柄进行建模剑的护手模型剑刃建模 1. 如何制作剑柄 步骤 1 在本教程中使用正交视图。要更改视图&#x…

python安装OpenCV

安装OpenCV pip install opencv-pythonpython OpenCV 打开摄像头 import cv2WIDTH 1080 HEIGHT 720cap cv2.VideoCapture(0, cv2.CAP_DSHOW) cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)while True:ok,frame cap.read()# pri…

阿里巴巴前端开发规范

前言 规范的目的是为了编写高质量的代码&#xff0c;让你的团队成员每天的心情都是愉悦的&#xff0c;大家在一起是快乐的。 现在软件架构的复杂性需要协同开发完成&#xff0c;如何高效地协同呢&#xff1f;无规矩不成方圆&#xff0c;无规范难以协同&#xff0c;比如&#xf…

机器学习概念

文章目录 一、机器学习概念1. 机器学习基本概念2. 基于规则的学习3. 基于模型学习4. 机器学习数据集描述二、机器学习分类1. 监督学习1.1 分类问题1.2 回归问题2. 无监督学习2.1 聚类问题2.2 数据降维3. 半监督学习4. 强化学习三、拟合问题1. 欠拟合2. 过拟合3. 奥卡姆剃刀原则…