ffmpeg实现视频的合成与分割
视频合成与分割程序使用
作者开发了一款软件,可以实现对视频的合成和分割,界面如下:

播放时,可以选择多个视频源;在选中“保存视频”情况下,会将多个视频源合成一个视频。如果只取一个视频源中一段视频,就实现了视频分割。下载视频合成与分割程序。
对视频的处理采用了ffmpeg库。作者在此库的基础上,做了进一步封装,使用起来更加简便。
底层处理逻辑可用如下函数表示:
bool InitVideo(); bool AddImage(unsigned char* imageFileBuffer, int bufferSize); bool CloseVideo();
可见底层函数是十分简洁的; 但是ffmpeg函数调用复杂,使用起来不便; 将ffmpeg封装亦非易事;本文就讲述对ffmpeg封装的过程。
视频编码与解码
对视频的处理分为两种:解码和编码。视频播放属于解码,视频生成属于编码。视频播放方面的文章和例子很多;我也写过一篇文章《使用Emgu.CV开发视频播放器简述》。
视频其实就是连续的图片,编码的作用就是压缩图片,减小视频文件的占用。可以把视频文件想象成容器,把一些列图片放入容器,经过编码,生成标准格式的视频文件(如mp4),这个过程就是编码;
把不同视频来源的图片放入容器,就实现了视频的合成;把视频中某段包含的图片放入容器,就实现了视频的分割。只要实现了对多个图片到视频的编码,就实现了视频的合成和分割。

初始化编码器,包括选择编码器,生成输入流,写入文件头等操作。
bool ImageToVideo::InitVideo()
{InitFfmpeg();AVFormatContext* pFormatCtx = NULL;_errnum = avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, _destVideoFileName.c_str());if (_errnum < 0){av_strerror(_errnum, _errbuf, sizeof(_errbuf));return false;}_initFree.pFormatCtx = pFormatCtx;// h264视频编码器const AVCodec* vcodec = avcodec_find_encoder(AVCodecID::AV_CODEC_ID_H264);if (!vcodec){return false;}// 创建编码器上下文AVCodecContext* pVideoCodecCtx = avcodec_alloc_context3(vcodec);if (!pVideoCodecCtx){return false;}_initFree.pVideoCodecCtx = pVideoCodecCtx;// 比特率、宽度、高度pVideoCodecCtx->bit_rate = 4000000;pVideoCodecCtx->width = _videoWidth; // 视频宽度pVideoCodecCtx->height = _videoHeight; // 视频高度// 时间基数、帧率pVideoCodecCtx->time_base = { 1, 25 };pVideoCodecCtx->framerate = { 25, 1 };// 关键帧间隔pVideoCodecCtx->gop_size = 10;// 不使用b帧pVideoCodecCtx->max_b_frames = 0;// 帧、编码格式pVideoCodecCtx->pix_fmt = AVPixelFormat::AV_PIX_FMT_YUV420P;pVideoCodecCtx->codec_id = AVCodecID::AV_CODEC_ID_H264;// 预设:快速av_opt_set(pVideoCodecCtx->priv_data, "preset", "superfast", 0);// 全局头pVideoCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;_errnum = avcodec_open2(pVideoCodecCtx, vcodec, NULL);if (_errnum < 0){return false;}// 为封装器创建视频流AVStream* pVideoStream = avformat_new_stream(pFormatCtx, NULL);if (!pVideoStream){return false;}_initFree.pVideoStream = pVideoStream;pVideoStream->codec->codec_tag = 0;pVideoStream->codecpar->codec_tag = 0;// 配置视频流的编码参数avcodec_parameters_from_context(pVideoStream->codecpar, pVideoCodecCtx);// 打开输出流IO_errnum = avio_open(&pFormatCtx->pb, _destVideoFileName.c_str(), AVIO_FLAG_WRITE); // 打开AVIO流if (_errnum < 0){avio_close(pFormatCtx->pb);return false;}_errnum = avformat_write_header(pFormatCtx, NULL);if (_errnum < 0){return false;}return true;
}
添加图片
1 对添加的图片缩放处理:
SwsContext* pSwsCtx = sws_getContext(imageWidth, imageHeight, srcFormat, _initFree.pVideoCodecCtx->width, _initFree.pVideoCodecCtx->height, AVPixelFormat::AV_PIX_FMT_YUV420P, // 输出SWS_BICUBIC, 0, 0, 0);
2 发送frame ,接收编码后的packet
vframe->pts = vpts++;
_errnum = avcodec_send_frame(_initFree.pVideoCodecCtx, vframe);
if (_errnum < 0)
{// cout << "avcodec_send_frame failed" << endl;av_frame_free(&vframe);return false;
}// 视频编码报文
AVPacket* packet = av_packet_alloc();
int writeCount = 0;while (true)
{_errnum = avcodec_receive_packet(_initFree.pVideoCodecCtx, packet);if (_errnum < 0 || packet->size <= 0){int e1 = AVERROR_EOF;int e2 = AVERROR(EAGAIN);if (writeCount == 0){av_frame_free(&vframe);av_packet_free(&packet);// cout << "avcodec_receive_packet failed" << endl;return false;}else{break;}}
编码完成:写入文件尾数据,释放资源
_errnum = av_write_trailer(_initFree.pFormatCtx);
if (_errnum != 0)
{return false;
}if (pFormatCtx != NULL)
{avio_closep(&pFormatCtx->pb);avformat_close_input(&pFormatCtx);
}if (pVideoCodecCtx != NULL)
{avcodec_close(pVideoCodecCtx);avcodec_free_context(&pVideoCodecCtx);
}if (pSwsCtx != NULL)
{sws_freeContext(pSwsCtx);
}
后记:对于视频的合成和分割,网上有不少这方面的文章,大都是讲述如何使用ffmpeg工具操作,这些方法不灵活,很难满足个性化的需求。本文从视频最基本的原理剖析,实现了图片合成视频的功能;这样就为上层丰富多彩的应用打开了大门。
比如 将两个视频文件合成到一个播放画面;处理过程为:同时读取两个视频源的文件,将两个图片拼接,再放入视频容器。
相关文章:
ffmpeg实现视频的合成与分割
视频合成与分割程序使用 作者开发了一款软件,可以实现对视频的合成和分割,界面如下: 播放时,可以选择多个视频源;在选中“保存视频”情况下,会将多个视频源合成一个视频。如果只取一个视频源中一段视频…...
团体标准的十大优势
一、团体标准是什么 团体标准是指由社会团体(行业协会、联合会、企业联盟等)按照自己确立的制定程序,自主制定、发布、采纳,并由社会自愿采用的标准。简单的说,就是社会团体为了满足市场和创新需要,协调相…...
java spring boot 动态添加 cron(表达式)任务、动态添加停止单个cron任务
java spring boot 动态添加 cron(表达式)任务、动态添加停止单个cron任务 添加对应的maven <dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.0</version…...
sqlgun靶场漏洞挖掘
1.xss漏洞 搜索框输入以下代码,验证是否存在xss漏洞 <script>alert(1)</script> OK了,存在xss漏洞 2.SQL注入 经过测试,输入框存在SQL注入漏洞 查询数据库名 查询管理员账号密码 此处密码为MD5加密,解码内容如下 找…...
好用的 Markdown 编辑器组件
ByteMD bytedance/bytemd: ByteMD v1 repository (github.com) 这里由于我的项目是 Next,所以安装 bytemd/react, 阅读官方文档,执行命令来安装编辑器主体、以及 gfm(表格支持)插件、highlight 代码高亮插件…...
uniapp vite3 require导入commonJS 的js文件方法
vite3 导入commonJS 方式导出 在Vite 3中,你可以通过配置vite.config.js来实现导入CommonJS(CJS)风格的模块。Vite 默认支持ES模块导入,但如果你需要导入CJS模块,可以使用特定的插件,比如originjs/vite-pl…...
通义灵码用户说:“人工编写测试用例需要数十分钟,通义灵码以毫秒级的速度生成测试代码,且准确率和覆盖率都令人满意”
通过一篇文章,详细跟大家分享一下我在使用通义灵码过程中的感受。 一、定义 通义灵码,是一个智能编码助手,它基于通义大模型,提供代码智能生成、研发智能问答能力。 在体验过程中有任何问题均可点击下面的连接前往了解和学习。 …...
MySQL中的约束
约束概述 1.1 为什么需要约束 数据完整性(Data Integrity)是指数据的精确性(Accuracy)和可靠性(Reliability)。它是防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信…...
Leetcode 寻找重复数
可以使用 位运算 来解决这道题目。使用位运算的一个核心思想是基于数字的二进制表示,统计每一位上 1 的出现次数,并与期望的出现次数做比较。通过这种方法,可以推断出哪个数字重复。 class Solution { public:int findDuplicate(vector<i…...
大一新生以此篇开启你的算法之路
各位大一计算机萌新们,你们好,本篇博客会带领大家进行算法入门,给各位大一萌新答疑解惑。博客文章略长,可根据自己的需要观看,在博客中会有给大一萌新问题的解答,请不要错过。 入门简介: 算法…...
【AI大模型】ChatGPT模型原理介绍(上)
目录 🍔 什么是ChatGPT? 🍔 GPT-1介绍 2.1 GPT-1模型架构 2.2 GPT-1训练过程 2.2.1 无监督的预训练语言模型 2.2.2 有监督的下游任务fine-tunning 2.2.3 整体训练过程架构图 2.3 GPT-1数据集 2.4 GPT-1模型的特点 2.5 GPT-1模型总结…...
基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模
前言 本系列教程旨在使用UE5配置一个具备激光雷达深度摄像机的仿真小车,并使用通过跨平台的方式进行ROS2和UE5仿真的通讯,达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础,Nav2相关的学习教程可以参考本人的其他博…...
C++竞赛初阶L1-15-第六单元-多维数组(34~35课)557: T456507 图像旋转
题目内容 输入一个 n 行 m 列的黑白图像,将它顺时针旋转 90 度后输出。 输入格式 第一行包含两个整数 n 和 m,表示图像包含像素点的行数和列数。1≤n≤100,1≤m≤100。 接下来 n 行,每行 m 个整数,表示图像的每个像…...
无线领夹麦克风哪个牌子好?西圣、罗德、猛犸领夹麦克风深度评测
如今短视频和直播行业蓬勃发展,无线领夹麦克风成为了许多创作者不可或缺的工具。然而,市场上的无线领夹麦克风品牌众多、质量参差不齐,为了帮助大家挑选到满意的产品,我作为数码测评博主,对无线领夹麦克风市场进行了…...
React Native 0.76,New Architecture 将成为默认模式,全新的 RN 来了
关于 React Native 的 New Architecture 概念,最早应该是从 2018 年 RN 团队决定重写大量底层实现开始,因为那时候 React Native 面临各种结构问题和性能瓶颈,最终迫使 RN 团队开始进行重构。 而从 React Native 0.68 开始,New A…...
Java并发:互斥锁,读写锁,Condition,StampedLock
3,Lock与Condition 3.1,互斥锁 3.1.1,可重入锁 锁的可重入性(Reentrant Locking)是指在同一个线程中,已经获取锁的线程可以再次获取该锁而不会导致死锁。这种特性允许线程在持有锁的情况下,可…...
客户端负载均衡Ribbon实例
文章目录 一,概述二,实现过程三,项目源码1. 源码放送:2. 部署方式 四,功能演示五,其他 一,概述 一般来说,提到负载均衡,大家一般很容易想到浏览器 -> NGINX -> 反…...
MySQL数据库负载均衡
数据库负载均衡是通过将数据库请求分散到多个数据库服务器上,以提高数据库的处理能力和可用性。在高并发的场景下,使用数据库负载均衡器可以有效避免单点故障,提高系统的整体性能和可靠性。 数据库负载均衡器 数据库负载均衡器可以是硬件设…...
达梦CASE_SENSITIVE参数解析
1. 参数含义 标识符大小写敏感,默认值为 Y。 当大小写敏感时,小写的标识符应用双引号括起,否则被转换为大写;当大小写不敏感时,系统不自动转换标识符的大小写,在标识符比较时也不区分大小写。 CASE_SENS…...
酒店智能轻触开关工作原理
在现代化酒店中,智能轻触开关已成为提升宾客居住体验的重要设备之一。这些开关不仅操作便捷,而且功能丰富,能够实现对灯光、窗帘、空调等设备的精准控制。本文将深入探讨酒店智能轻触开关的工作原理。 一、智能轻触开关的基本概念 智能轻触开…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...
