当前位置: 首页 > news >正文

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工具操作,这些方法不灵活,很难满足个性化的需求。本文从视频最基本的原理剖析,实现了图片合成视频的功能;这样就为上层丰富多彩的应用打开了大门。

       比如 将两个视频文件合成到一个播放画面;处理过程为:同时读取两个视频源的文件,将两个图片拼接,再放入视频容器。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 深度学习自编码器 - 随机编码器和解码器篇
  • 深度学习之线性代数预备知识点
  • 幼儿园自动分班工具:使用Python进行实现
  • 如何在kotlin中给空字符串(””)和null值设置默认值问题?
  • Docker数据挂载本地目录
  • STM32单片机 内存 字 字节 位关系详细讲解
  • 【鸿蒙】HarmonyOS NEXT星河入门到实战6-组件化开发-样式结构重用常见组件
  • 关于安卓App自动化的一些想法
  • HTML5中下拉框标签`<select>`深入全面解析
  • 学习笔记 韩顺平 零基础30天学会Java(2024.9.16)
  • C语言 | Leetcode C语言题解之第405题数字转换为十六进制数
  • Flutter Error: Type ‘UnmodifiableUint8ListView‘ not found
  • sqli-labs靶场自动化利用工具——第10关
  • 新增的标准流程
  • vue3项目实现全局国际化
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • bootstrap创建登录注册页面
  • CSS 提示工具(Tooltip)
  • CSS相对定位
  • ESLint简单操作
  • FineReport中如何实现自动滚屏效果
  • idea + plantuml 画流程图
  • Java IO学习笔记一
  • Koa2 之文件上传下载
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • seaborn 安装成功 + ImportError: DLL load failed: 找不到指定的模块 问题解决
  • Spring核心 Bean的高级装配
  • Sublime Text 2/3 绑定Eclipse快捷键
  • 你不可错过的前端面试题(一)
  • 深入浅出Node.js
  • 正则表达式-基础知识Review
  • 智能情侣枕Pillow Talk,倾听彼此的心跳
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • "无招胜有招"nbsp;史上最全的互…
  • #VERDI# 关于如何查看FSM状态机的方法
  • #基础#使用Jupyter进行Notebook的转换 .ipynb文件导出为.md文件
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (1)虚拟机的安装与使用,linux系统安装
  • (2024)docker-compose实战 (8)部署LAMP项目(最终版)
  • (42)STM32——LCD显示屏实验笔记
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (二)hibernate配置管理
  • (机器学习的矩阵)(向量、矩阵与多元线性回归)
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (学习日记)2024.01.19
  • (一) 初入MySQL 【认识和部署】
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes
  • .NET 使用配置文件
  • .NET 中使用 Mutex 进行跨越进程边界的同步
  • .netcore如何运行环境安装到Linux服务器
  • .Net程序帮助文档制作