FFmpeg常用API与示例(四)——过滤器实战
1.filter
在多媒体处理中,filter 的意思是被编码到输出文件之前用来修改输入文件内容的一个软件工具。如:视频翻转,旋转,缩放等。
语法:[input_link_label1]… filter_name=parameters [output_link_label1]…
1、视频过滤器 -vf
如 input.mp4 视频按顺时针方向旋转 90 度
ffplay -i input.mp4 -vf transpose=1
如 input.mp4 视频水平翻转(左右翻转)
ffplay -i input.mp4 -vf hflip
2、音频过滤器 -af
实现慢速播放,声音速度是原始速度的 50%
offplay input.mp3 -af atempo=0.5
过滤器链(Filterchain)
Filterchain = 逗号分隔的一组 filter
语法:“filter1,filter2,filter3,…filterN-2,filterN-1,filterN”
顺时针旋转 90 度并水平翻转
ffplay -i input.mp4 -vf transpose=1,hflip
过滤器图(Filtergraph)
第一步: 源视频宽度扩大两倍。
ffmpeg -i jidu.mp4 -t 10 -vf pad=2*iw output.mp4
第二步:源视频水平翻转
ffmpeg -i jidu.mp4 -t 10 -vf hflip output2.mp4
第三步:水平翻转视频覆盖 output.mp4
ffmpeg -i output.mp4 -i output2.mp4 -filter_complex overlay=w compare.mp4
是不是很复杂?
用带有链接标记的过滤器图(Filtergraph)只需一条命令
基本语法
Filtergraph = 分号分隔的一组 filterchain
“filterchain1;filterchain2;…filterchainN-1;filterchainN”
Filtergraph 的分类
1、简单(simple) 一对一
2、复杂(complex)多对一, 多对多
过滤器图处理流程:

对于刚才用三步处理的方式,用过滤器图可以这样做:
ffplay -f lavfi -i testsrc -vf split[a][b];[a]pad=2*iw[1];[b]hflip[2];[1][2]overlay=w
F1: split 过滤器创建两个输入文件的拷贝并标记为[a],[b]
F2: [a]作为 pad 过滤器的输入,pad 过滤器产生 2 倍宽度并输出到[1].
F3: [b]作为 hflip 过滤器的输入,vflip 过滤器水平翻转视频并输出到[2].
F4: 用 overlay 过滤器把 [2]覆盖到[1]的旁边.
filter 涉及的结构体,主要包括:
-
FilterGraph, AVFilterGraph
-
InputFilter, InputStream, OutputFilter, OutputStream
-
AVFilter, AVFilterContext
-
AVFilterLink
-
AVFilterPad;
2.DirectShow
DirectShow(简称 DShow) 是一个 Windows 平台上的流媒体框架,提供了高质量的多媒体流采集和回放功能。它支持多种多样的媒体文件格式,包括 ASF、MPEG、AVI、MP3和 WAV 文件,同时支持使 动或早期的 VFW 驱动来进行多媒体流的采集。
DirectShow 大大简化了媒体回放、格式转换和采集工作。但与此同时,它也为用户自定义的解决方案提供了底层流控制框架,从而使用户可以自行创建支持新的文件格式或其他用户的 DirectShow 组件。
DirectShow 是基于**组件对象模型(COM)**的,因此当你编写 DirectShow 应用程序时,你必须具备 COM 客户端程序编写的知识。对于大部分的应用程序,你不需要实现自己的COM 对象,DirectShow 提供了大部分你需要的 DirectShow 组件,但是假如你需要编写自己的 DirectShow 组件来进行扩充,那么你必须编写实现 COM 对象。
使用 DirectShow 编写的典型应用程序包括:DVD 播放器、视频编辑程序、AVI 到 ASF转换器、 MP3 播放器和数字视频采集应用。

DirectShow 的架构如下图所示:

/*** @file* API example for decoding and filtering* @example filtering_video.c*/#define _XOPEN_SOURCE 600 /* for usleep */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>const char *filter_descr = "scale=640:480,hflip";
/* other way:scale=78:24 [scl]; [scl] transpose=cclock // assumes "[in]" and "[out]" to be input output pads respectively*/static AVFormatContext *fmt_ctx;
static AVCodecContext *dec_ctx;
AVFilterContext *buffersink_ctx;
AVFilterContext *buffersrc_ctx;
AVFilterGraph *filter_graph;
static int video_stream_index = -1;
static int64_t last_pts = AV_NOPTS_VALUE;static int open_input_file(const char *filename)
{int ret;AVCodec *dec;if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");return ret;}if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");return ret;}/* select the video stream */ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n");return ret;}video_stream_index = ret;/* create decoding context */dec_ctx = avcodec_alloc_context3(dec);if (!dec_ctx)return AVERROR(ENOMEM);avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[video_stream_index]->codecpar);/* init the video decoder */if ((ret = avcodec_open2(dec_ctx, dec, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n");return ret;}return 0;
}static int init_filters(const char *filters_descr)
{char args[512];int ret = 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();AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base;enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };filter_graph = avfilter_graph_alloc();if (!outputs || !inputs || !filter_graph) {ret = AVERROR(ENOMEM);goto end;}/* buffer video source: the decoded frames from the decoder will be inserted here. */snprintf(args, sizeof(args),"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,time_base.num, time_base.den,dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",args, NULL, filter_graph);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");goto end;}/* buffer video sink: to terminate the filter chain. */ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",NULL, NULL, filter_graph);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");goto end;}/** Set the endpoints for the filter graph. The filter_graph will* be linked to the graph described by filters_descr.*//** The buffer source output must be connected to the input pad of* the first filter described by filters_descr; since the first* filter input label is not specified, it is set to "in" by* default.*/outputs->name = av_strdup("in");outputs->filter_ctx = buffersrc_ctx;outputs->pad_idx = 0;outputs->next = NULL;/** The buffer sink input must be connected to the output pad of* the last filter described by filters_descr; since the last* filter output label is not specified, it is set to "out" by* default.*/inputs->name = av_strdup("out");inputs->filter_ctx = buffersink_ctx;inputs->pad_idx = 0;inputs->next = NULL;if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,&inputs, &outputs, NULL)) < 0)goto end;if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)goto end;end:avfilter_inout_free(&inputs);avfilter_inout_free(&outputs);return ret;
}/// 将yuv帧写入文件:yuv420p格式
FILE * g__file_fd;
static void write_frame(const AVFrame *frame)
{static int printf_flag = 0;if(!printf_flag){printf_flag = 1;printf("frame widht=%d,frame height=%d\n",frame->width,frame->height);if(frame->format==AV_PIX_FMT_YUV420P){printf("format is yuv420p\n");}else{printf("formet is = %d \n",frame->format);}}fwrite(frame->data[0],1,frame->width*frame->height,g__file_fd);fwrite(frame->data[1],1,frame->width/2*frame->height/2,g__file_fd);fwrite(frame->data[2],1,frame->width/2*frame->height/2,g__file_fd);
}int main(int argc, char **argv)
{int ret;AVPacket packet;AVFrame *frame;AVFrame *filt_frame;if (argc != 2) {fprintf(stderr, "Usage: %s file\n", argv[0]);exit(1);}g__file_fd = fopen("yuv888.yuv", "w");frame = av_frame_alloc();filt_frame = av_frame_alloc();if (!frame || !filt_frame) {perror("Could not allocate frame");exit(1);}if ((ret = open_input_file(argv[1])) < 0)goto end;if ((ret = init_filters(filter_descr)) < 0)goto end;/* read all packets */while (1) {if ((ret = av_read_frame(fmt_ctx, &packet)) < 0)break;if (packet.stream_index == video_stream_index) {ret = avcodec_send_packet(dec_ctx, &packet);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n");break;}while (ret >= 0) {ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame from the decoder\n");goto end;}frame->pts = frame->best_effort_timestamp;/* push the decoded frame into the filtergraph */if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) {av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");break;}/* pull filtered frames from the filtergraph */while (1) {ret = av_buffersink_get_frame(buffersink_ctx, filt_frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)break;if (ret < 0)goto end;write_frame(filt_frame);av_frame_unref(filt_frame);}av_frame_unref(frame);}}av_packet_unref(&packet);}
end:avfilter_graph_free(&filter_graph);avcodec_free_context(&dec_ctx);avformat_close_input(&fmt_ctx);av_frame_free(&frame);av_frame_free(&filt_frame);fclose(g__file_fd);if (ret < 0 && ret != AVERROR_EOF) {fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));exit(1);}exit(0);
}
相关文章:
FFmpeg常用API与示例(四)——过滤器实战
1.filter 在多媒体处理中,filter 的意思是被编码到输出文件之前用来修改输入文件内容的一个软件工具。如:视频翻转,旋转,缩放等。 语法:[input_link_label1]… filter_nameparameters [output_link_label1]… 1、视…...
解决springboot项目的网站静态页面显示不全问题
在通过springboot搭建项目时,为了能够访问静态的前端页面,我们考虑到访问的优先级问题,通常选择将资源放在recourses/static的目录下,如下: 这时可能会出现类似于下面这种图片无法加载、没有按照指定位置显示的情况&am…...
表面的相似,本质的不同
韩信与韩王信,两个韩信的结局都是被刘邦所杀,似乎结局类似。但是,略加分析,就会发现其中存在本质的区别。 韩信属于必杀。他的王位是要来的,有居功自傲的本意,功高震主而且毫不避讳。而且年轻,…...
问题:幂等性 分布式session
web项目中请求线程到service层的时候远程调用服务之前是串行化执行每个任务都要get阻塞等待任务完成,举例当用户在购物车页面点击去结算就会请求后台toTrade请求获取订单确认的详情数据并渲染到订单详情页,现在在toTrade请求中使用异步任务编排Completab…...
Golang | Leetcode Golang题解之第66题加一
题目: 题解: func plusOne(digits []int) []int {n : len(digits)for i : n - 1; i > 0; i-- {if digits[i] ! 9 {digits[i]for j : i 1; j < n; j {digits[j] 0}return digits}}// digits 中所有的元素均为 9digits make([]int, n1)digits[0]…...
c++ STL 之栈—— stack 详解
vector 是 stl 的一个关联容器,名叫“栈”,何为“栈”?其实就是一个数组,但有了数组何必还需栈,这是一个高深的问题。 一、简介 1. 定义 栈,是一个柔性数组(可变长数组),可以变大变小…...
鸿蒙开发接口Ability框架:【(窗口扩展能力)】
窗口扩展能力 WindowExtensionAbility基于ExtensionAbility,WindowExtensionAbility中展示的内容作为一个控件(AbilityComponent)内容展示在其他应用窗口中,实现在一个窗口中展示多个应用程序内容的功能。 说明: 本模块首批接口从API versio…...
AutoCAD中密集的填充打散后消失的问题
有时候在AutoCAD中,图案填充的填充面积过大或填充太过密集时,将该填充打散,也就是执行Explode时,会发现填充图案消失了。 原因是打散后线条太大,系统就不显示了。可以通过设置:HPMAXLINES 值,来…...
基于Matplotlib的模型性能可视化工作
一、项目简介 本项目是科技考古墓葬识别工作的中间过程,因为需要大量复用所以另起一章好了。 主要涉及到数据读取、数据可视化和少量的数据处理过程。 二、相关知识 PandasMatplotlib 三、实验过程 1. 数据探索性分析 1.1 准备工作–导入模块 import pandas…...
KAN网络最全解析——比肩MLP和Transformer?
1 基本思路 1.1 MLP与Spline的优缺点 多层感知器 (MLP)是深度学习的基础理论模块,是目前可用于逼近非线性函数的默认模型,其表征能力已由通用逼近定理证明。但MLP也有明显的缺点,例如在 Transformer中,MLP 的参数量巨大…...
ASP.NET学生信息管理系统
摘 要 本文介绍了在ASP.net环境下采用“自上而下地总体规划,自下而上地应用开发”的策略开发一个管理信息系统的过程。通过分析某一学校学生管理的不足,创建了一套行之有效的计算机管理学生的方案。文章介绍了学生管理信息系统的系统分析部分,…...
图片改大小尺寸怎么改?几招教你搞定图片修改
在社交媒体平台上发布图片时,调整图片的尺寸大小可以确保图片适合平台的要求,不同的社交媒体平台可能对图片的尺寸有不同的要求,通过调整图片尺寸,可以更加完美的展现出来,那么有没有比较简单的图片改大小的方法呢&…...
Scala编程入门:从零开始的完整教程
目录 引言环境准备创建第一个Scala项目基本语法高阶概念进阶资源结语 引言 Scala是一种强大的、静态类型的、多范式编程语言,它结合了面向对象和函数式编程的特点。本教程将指导您如何从零开始学习Scala,并搭建一个简单的开发环境。让我们开始探索Scala…...
Proxmox VE 8 SDN创建VLAN隔离用户网络
作者:田逸(formyz) 在上一篇文章中,我们用SDN的Simple对租户(用户)网络实现了隔离功能,但它有个限制,仅仅能在单个物理节点上进行通信,而不能跨越物理节点(除…...
API低代码平台介绍3-异构数据源的数据查询功能
异构数据源的数据查询功能 在上一篇文章中我们通过API平台定义了一个最基本的数据查询接口,本篇文章我们将上升难度,在原有接口的基础上,实现在MySQL数据库和Oracle数据库同时进行数据查询。 什么场景会需要同时对异构数据源进行查询&…...
【Linux】-网络请求和下载、端口[6]
目录 一、网络请求和下载 1、ping命令 2、wget命令 3、curl命令 二、端口 1、虚拟端口 2、查看端口占用 一、网络请求和下载 1、ping命令 可以通过ping命令,检查指定的网络服务器是否可联通状态 语法:ping [ -c num ] ip或主机名 选项&…...
Github2024-05-10开日报 Top10
根据Github Trendings的统计,今日(2024-05-10统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目4TypeScript项目4JavaScript项目1Lua项目1C项目1Rust项目1Dart项目1 RustDesk: 用Rust编写的开源远…...
2016-2021年全国范围的2.5m分辨率的建筑屋顶数据
一、论文介绍 摘要:大规模且多年的建筑屋顶面积(BRA)地图对于解决政策决策和可持续发展至关重要。此外,作为人类活动的细粒度指标,BRA可以为城市规划和能源模型提供帮助,为人类福祉带来好处。然而…...
Gitea 上传用户签名
在 Gitea 的用户管理部分,有一个 SSH 和 GPG 的选项。 单击这个选项,可以在选项上添加 Key。 Key 的来源 如是 Windows 的用户,可以选择 Kleopatra 这个软件。 通过这个软件生成的 Key 的界面中有一个导出功能。 单击这个导出,…...
【原创】springboot+mysql物资库存管理系统设计与实现
个人主页:程序猿小小杨 个人简介:从事开发多年,Java、Php、Python、前端开发均有涉猎 博客内容:Java项目实战、项目演示、技术分享 文末有作者名片,希望和大家一起共同进步,你只管努力,剩下的交…...
深度解析:如何用UE Viewer高效处理虚幻引擎1-4代游戏资源
深度解析:如何用UE Viewer高效处理虚幻引擎1-4代游戏资源 【免费下载链接】UEViewer Viewer and exporter for Unreal Engine 1-4 assets (UE Viewer). 项目地址: https://gitcode.com/gh_mirrors/ue/UEViewer UE Viewer是一款强大的开源虚幻引擎资源查看与导…...
Weka数据预处理:离散化与虚拟变量实战指南
1. 数据预处理在机器学习中的重要性在机器学习项目中,原始数据很少能直接用于建模。就像一位经验丰富的厨师不会直接把刚从菜市场买来的食材扔进锅里一样,数据科学家也需要对原始数据进行适当的处理和转换。Weka作为一款强大的机器学习工具,提…...
GoFr框架:加速微服务开发的Go语言利器
目录 一、核心特性:简化微服务开发的五大支柱 1.1 零配置启动与约定优于配置 1.2 全栈可观测性:日志、追踪、指标一体化 1.3 多数据源支持与弹性扩展 二、技术架构:分层设计与模块化组件 三、未来展望:持续演进的云原生生态…...
迷你电吹风速修
署名 浙江 方位机主的一台迷你电吹风不能开机。观察电吹风,机主已经拆过进风网罩,动过电源进线,但是没有修复。1.拆手柄:用镊子工具撬出开关1,按钮2。再用力将手柄外壳用力推出,即可拆出内壳结构及电机。由…...
全栈聚合应用ChattyPlay-Agent:从架构演进到工程化实战
1. 项目概述:一个全栈开发者的“瑞士军刀”是如何炼成的作为一名在前后端领域摸爬滚打了十多年的开发者,我见过也做过不少“聚合型”应用。但像ChattyPlay-Agent这样,能把视频解析、AI对话、金融数据、漫画阅读、论文工具、闲鱼助手等十几个看…...
抖音无水印视频下载神器:DouYinBot 让你的视频创作更高效 [特殊字符]
抖音无水印视频下载神器:DouYinBot 让你的视频创作更高效 🚀 【免费下载链接】DouYinBot 抖音无水印下载 项目地址: https://gitcode.com/gh_mirrors/do/DouYinBot 还在为抖音视频的水印烦恼吗?想保存喜欢的视频却苦于找不到无水印版本…...
避开这些坑!蓝桥杯单片机操作24C02存储器的5个常见错误与调试技巧
避开这些坑!蓝桥杯单片机操作24C02存储器的5个常见错误与调试技巧 在蓝桥杯单片机竞赛中,24C02存储器的使用是一个常见但容易出错的环节。许多参赛者在实现按键次数存储功能时,往往会遇到数据读取异常、写入失败或显示乱码等问题。本文将针对…...
3步永久备份你的QQ空间记忆:GetQzonehistory让你轻松掌握青春时光
3步永久备份你的QQ空间记忆:GetQzonehistory让你轻松掌握青春时光 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 还记得那些年你在QQ空间写下的青春絮语吗?那些…...
SARIMA模型:季节性时间序列预测实战指南
1. 理解SARIMA:季节性时间序列预测的核心工具时间序列分析中最常见的挑战之一就是处理具有明显季节性波动的数据。传统ARIMA模型在非季节性数据上表现优异,但遇到像月度销售额、季度气温变化这类具有固定周期特征的数据时就会捉襟见肘。这正是SARIMA(Sea…...
OpenRGB:告别多品牌RGB软件混乱,一站式跨平台灯光控制解决方案
OpenRGB:告别多品牌RGB软件混乱,一站式跨平台灯光控制解决方案 【免费下载链接】OpenRGB Open source RGB lighting control that doesnt depend on manufacturer software. Supports Windows, Linux, MacOS. Mirror of https://gitlab.com/CalcProgramm…...
