FFmpeg的C++封装:FFmpegWrapper

什么是FFmpeg?

FFmpeg 是一套完整的录制、转换、流化音视频的解决方案,也是一个在LGPL协议 下的开源项目。它包含了业界领先的音视频编解码库。FFmpeg是在Linux操作系统下开发的,但它也能在其他操作系统下编译,包括Windows。

整个项目由以下几个部分组成:

  • ffmpeg:一个用来转换视频文件格式的命令行工具,它也支持从电视卡中实时的抓取和编码视频。
  • ffserver:一个基于HTTP协议(基于RTSP的版本正在开发中)用于实时广播的多媒体服务器,它也支持实时广播的时间平移。
  • ffplay:一个用SDL 和FFmpeg库开发的简单的媒体播放器。
  • libavcodec:一个包含了所有FFmpeg音视频编解码器的库。为了保证最优的性能和高可复用性,大多数编解码器都是从头开发的。
  • libavformat:一个包含了所有的普通音视频格式的解析器和产生器的库。

FFmpegWrapper仅使用了libavcodec和libavformat这两部分。

什么是FFmpegWrapper?

FFmpegWrapper:

  • 是一个在Windows下用VS2005编写的C++ Win32动态库。
  • 用面向对象的方法封装了FFmpeg库中常用的API,使之易于使用,不需要开发人员了解很多音视频编解码的知识。
  • 其中99%的代码符合C++标准,很容易移植到其他平台。
  • 由farthinker 开发和维护。

为什么要使用FFmpegWrapper?

对于一个对视频编解码不太了解的开发者来说,用FFmpeg的库来编写应用绝对是一件痛苦的事情。首先需要编译从SVN下载的源代码(FFmpeg官方只提供源代码……)。如果是在Windows下编译 ,麻烦就开始了(当然你也可以直接使用别人编译好的SDK , 跳过这一步)。当你好不容易编译好一个可以使用的动态库之后,你会发现很难找到合适的文档来学习如何使用FFmpeg的库,你只能一边参考示例代码一边摸 索使用方法。然后你会发现问题一个接一个的出现,你又不知从何处下手来解决。总之,使用FFmpeg的学习成本是很高的。

FFmpegWrapper的目的就在于让FFmpeg的调用过程简单化、面向对象化,降低使用FFmpeg的学习成本,让对视频编解码不太了解的 开发人员也能轻松的使用FFmpeg。但是,简化使用的同时也在一定程度上简化了功能,FFmpegWrapper很难继承FFmpeg库的所有功能。所 以FFmpegWrapper适合一些编解码需求相对简单的应用,而不适合那些需求复杂灵活、扩展性很强的应用。

如何使用FFmpegWrapper来编解码音视频?

准备工作

首先下载FFmpegWrapper的库文件(若是在非Windows平台下使用,则需要下载源代码自己编译),然后将FFmpegWrapper 部署到项目中。部署的过程中需要注意的是,最好不要改变ffmpeg文件夹相对于FFmpegWrapper.h的路径,若必须要改变,组需要修改 FFmpegWrapper.h中#include “ffmpeg/include/avformat.h”的路径。调用动态库的具体方法这里就不赘述了。

使用FFmpegWrapper编码音视频

指定音视频参数

首先需要指定一些音视频的参数。FFmpegWrapper中用FFmpegVideoParam和FFmpegAudioParam这两个类来表示音视频的参数。下面的例子指定了一个flv视频的参数:

//指定视频参数,从左到右分别是:宽、高、像素格式、比特率、帧率
FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);
//指定视频参数,从左到右分别是:比特率、采样率、声道数
FFmpegAudioParam audioParam(64000, 44100, 2);
//指定视频参数,从左到右分别是:宽、高、像素格式、比特率、帧率
FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);
//指定视频参数,从左到右分别是:比特率、采样率、声道数
FFmpegAudioParam audioParam(64000, 44100, 2);

若视频中没有视频流(音频流),则可以初始化一个空的FFmpegVideoParam(FFmpegAudioParam):

FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);
//没有音频流,初始化一个空的音频参数对象
FFmpegAudioParam audioParam();

初始化FFmpegEncoder对象

用音视频参数初始化FFmpegEncoder对象:

FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);
FFmpegAudioParam audioParam(64000, 44100, 2);
//参数从左到右分别是:FFmpegVideoParam、FFmpegAudioParam 、编码输出文件名
FFmpegEncoder testEncoder(videoParam, audioParam, "test.flv" );

其中第三个参数包含了输出文件的路径、名字和后缀,并且是可选的参数,也就是说可以没有输出文件。但是在没有输出文件的时候需要音视频参数中指定codec的名称,因为FFmpegEncoder不能从文件后缀判断出使用什么codec:

FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25, "flv" );
FFmpegAudioParam audioParam(64000, 44100, 2, "libmp3lame" );
//参数从左到右分别是:FFmpegVideoParam、FFmpegAudioParam 、编码输出文件名
FFmpegEncoder testEncoder(videoParam, audioParam);

逐帧编码音视频

开始编码之前还需要先调用FFmpegEncoder对象的open方法,打开相应的codec和输出文件:

FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);
FFmpegAudioParam audioParam(64000, 44100, 2);
FFmpegEncoder testEncoder(videoParam, audioParam, "test.flv" );testEncoder.open();

然后就可以调用FFmpegEncoder对象的writeVideoFrame(writeAudioFrame)方法来逐帧的编码并输出视(音)频了:

//其中videoFrameData是uint8_t *(unsigned char *)类型的参数
testEncoder.writeVideoFrame(videoFrameData);//其中audioFrameData是short *类型的参数
testEncoder.writeAudioFrame(audioFrameData);
//其中videoFrameData是uint8_t *(unsigned char *)类型的参数
testEncoder.encodeVideoFrame(videoFrameData);
uint8_t *encodedVideo = testEncoder.getVideoBuffer();//其中audioFrameData是short *类型的参数
testEncoder.encodeAudioFrame(audioFrameData);
uint8_t *encodedAudio = testEncoder.getAudioBuffer();

编码的过程中,还可以获得音视频的时间戳(pts)来处理音视频同步(暂不适用于没有输出文件的情况),下面是一个例子:

short *audioData;
uint8_t *videoData;
double videoPts, audioPts;videoPts = testEncoder.getVideoPts();
audioPts = testEncoder.getAudioPts();/* output 5 seconds test video file */
while (audioPts < 5) {if (audioPts <= videoPts) {audioData = getTestAudioData();testEncoder.writeAudioFrame(audioData);} else {videoData = getVideoFrame();testEncoder.writeVideoFrame(videoData);}audioPts = testEncoder.getAudioPts();videoPts = testEncoder.getVideoPts();
}

完成编码后还需要调用FFmpegEncoder对象的close方法,关闭codec和输出文件并释放资源:

testEncoder.close();

使用FFmpegWrapper解码音视频

初始化FFmpegDecoder对象

首先初始化一个FFmpegDecoder对象,并传入输入文件的名称(包括路径、名字和后缀):

FFmpegDecoder testDecoder("test.flv");

逐帧解码音视频

开始解码之前还需要先调用FFmpegDecoder对象的open方法,打开相应的codec和输入文件:

FFmpegDecoder testDecoder( "test.flv" );testDecoder.open();

然后就可以调用FFmpegDecoder对象的decodeFrame方法来逐帧的解码音视频文件了:

//decodeFrame的返回值表示当前解码的帧的状态:
//   0 - 视频帧
//   1 - 音频帧
// -1 - 文件末尾或解码出错
int signal = testDecoder.decodeFrame()

解码之后可以通过FFmpegDecoder对象的getVideoFrame(getAudioFrame)方法来获得解码后的视(音)频数据,下面是一个完整的例子:

int signal;
uint8_t *decodedVideo;
short *decodedAudio;
FFmpegDecoder testDecoder("test.flv" );
ffencoder.open();while ( true ) {signal = testDecoder.decodeFrame();if (signal == 0) {decodedVideo = ffdecoder.getVideoFrame();//处理解码之后的视频数据} else if (signal == 1) {decodedAudio = ffdecoder.getAudioFrame();//处理解码之后的音频数据} else if (signal == -1) {break ;}
}

完成编码后还需要调用FFmpegDecoder对象的close方法,关闭codec和输入文件并释放资源:

testDecoder.close();

注意

  • FFmpegWrapper暂时没有完整的转码功能,如有需要请使用FFmpeg提供的格式转换工具ffmpeg.exe。
  • 上面的介绍只涉及到一部分FFmpegWrapper的公共API,详细的API介绍和其他细节见FFmpegWrapper API参考(upcoming)。
  • farthinker只是一个web开发者,对音视频的了解实在有限,所以FFmpegWrapper肯定存在一些潜在的问题,欢迎大家积极批评指正。

本文福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

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

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

相关文章

java常用字符串工具方法封装

Java常用的字符串工具方法有很多&#xff0c;以下是一些常见的封装&#xff1a; 判断字符串是否为空或null public static boolean isNullOrEmpty(String str) {return str null || str.trim().isEmpty(); }判断字符串是否为数字 public static boolean isNumeric(String s…

跨境代采怎么实现(代采网站)

中国代购作为一种新型的业务形式&#xff0c;此类服务能够帮助消费者购买来自全球的商品&#xff0c;同时也为商家提供了在线销售机会。代购行业的兴起&#xff0c;有助于打破传统的地域和跨境限制&#xff0c;促进了国际贸易和经济发展。 一、中国代购的定义和特点 代购可以被…

冬季吃得过饱?羊大师教你几招消食的小妙招!

冬季吃得过饱&#xff1f;羊大师教你几招消食的小妙招&#xff01; 冬季是人们容易吃得过饱的季节&#xff0c;美食诱惑频出&#xff0c;很容易导致胃口过大&#xff0c;饭量过多&#xff0c;造成消化不良甚至影响身体健康。所以&#xff0c;如何消食&#xff0c;让胃得到缓解…

如何拆解Unity 2022.3版本的AssetBundle

1&#xff09;如何拆解Unity 2022.3版本的AssetBundle 2&#xff09;Unity 2022 LTS版本的稳定性 3&#xff09;关于AssetBundle禁用TypeTree之后的一些可序列化的问题 这是第363篇UWA技术知识分享的推送&#xff0c;精选了UWA社区的热门话题&#xff0c;涵盖了UWA问答、社区帖…

long转int类型转换问题

在业务代码中排序时需要根据日期排序&#xff0c;写了如下代码 sorted((o1, o2) -> {String str1 null;String str2 null;try {Field field getField(fieldMap, configBO.getCodeName());str1 String.valueOf(field.get(o1));str2 String.valueOf(field.get(o2));} ca…

【Qt开发流程】之对象模型1:信号和槽

Qt对象模型 标准c对象模型为对象范型提供了非常有效的运行时支持。但是它的静态特性在某些问题领域是不灵活的。图形用户界面编程是一个既需要运行时效率又需要高度灵活性的领域。Qt通过结合c的速度和Qt对象模型的灵活性提供了这一点。 Qt将这些特性添加到c中: 一个非常强大的…

基于Go语言实现简易Web应用

目录 前言Go语言特点写在使用Go语言实现Web应用前面创建Web服务器声明一个结构体操作加入中间件的使用使用静态文件服务器最后 前言 在编程语言中&#xff0c;近几年问世的几个新语言都是非常不错的&#xff0c;比如Go、Python、 Rust等等。其中&#xff0c;Go语言(Golang)作…

CookieSession Redis 到JWT会话管理历史

单应用时期&#xff0c;通常使用 Cookies 和 Session 进行会话管理。 用户登录后&#xff0c;服务器创建一个唯一的会话标识符&#xff08;Session ID&#xff09;&#xff0c;将其存储在浏览器的 Cookies 中&#xff0c;并在服务端维护一个关联该标识符的会话对象。 这种方…

基因名潘多拉

同一基因可能会有多种命名&#xff0c;如Ensembl gene ID、NCBI Entrez gene ID、Gene Symbol等 基因有哪些名儿&#xff0c;你知道嘛&#xff1f; Ensembl gene ID&#xff1a;Ensembl数据库中对基因的命名&#xff0c;如ENSMUSG00000000538。ENS是固定字符&#xff0c;表示…

全国停车位收费标准接口API

1) 请求地址 接口地址https://psbg.jparking.cn/cw-gateway/cwzg/v1/near_park 2) 调用方式&#xff1a;HTTP post 3) 接口描述&#xff1a; 数据来源捷停车 不可用于商用 概不负责 4) 请求参数: {"latitude": "29.563009", //坐标"longitude&quo…

基于SSM的零食销售系统

基于SSM的零食销售系统 零食商城系统是一个面向用户销售零食的在线平台&#xff0c;用户可以在该平台上浏览、搜索、选择和购买各种零食产品。零食商城系统的背景是随着生活水平的提高和消费观念的改变&#xff0c;人们对于各种健康、美味和方便的零食产品的需求不断增加。传统…

Verilog基础:编译指令`timescale

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 timescale编译指令用于指定指令后模块的时间单位和时间精度。时间单位是时间值的度量单位&#xff0c;例如延迟值和仿真时间&#xff1b;而仿真精度决定了最小可分…

【独家OD2023C卷真题】20天拿下华为OD笔试2023C-小明能到达的最大坐标值【欧弟算法】全网注释最详细分类最全的华为OD真题题解

题目描述与示例 题目描述 小明在玩一个游戏&#xff0c;游戏规则如下&#xff1a;在游戏开始前&#xff0c;小明站在坐标轴原点处&#xff08;坐标值为 0&#xff09;给定一组指令和一个幸运数&#xff0c;每个指令都是一个整数&#xff0c;小明按照指定的要求前进或者后退指…

集成测试如何做?

今天学习下如何进行集成测试。 什么是集成测试? 集成测试被定义为一种测试类型&#xff0c;其中软件模块在逻辑上集成并作为一个组进行测试。一个典型的软件项目由多个软件模块组成&#xff0c;由不同的程序员编码。此级别测试的目的是在集成这些软件模块时&#xff0c;暴露…

C语言还会存在多久

一、C语言的生命力 在当前的科技发展和就业市场需求下&#xff0c;可以肯定地说C语言并没有像一些新兴语言&#xff08;如Python、JavaScript等&#xff09;那样受到大量的关注。然而&#xff0c;并不意味着学习C语言的人会越来越少。 首先&#xff0c;C语言作为一种深受尊重…

全网最新最全的Appium自动化:Appium常用操作之H5页面操作 --待补充!

手机chrome浏览器操作&#xff1a; 手机端chrome浏览器一般用于打开H5手机版网站&#xff0c;它的操作方式与PC端的浏览器操作&#xff08;即selenium对浏览器的操作&#xff09;是一模一样的&#xff0c;由于切换后的WebView页面也属于网页 下述的方法中部分支持在webview页面…

什么是JVM的内存模型?详细阐述Java中局部变量、常量、类名等信息在JVM中的存储位置

导航&#xff1a; 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/黑马旅游/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码-CSDN博客 目录 一、JVM基本介绍 二、JVM内存模型 2.0 概述 2.1 类加载子系统 2.2 运行时数据区 2.2.0 基本…

python中的输入输出

文章目录 输入函数input()例子1.如何输入获得两个字符串?&#xff08;若输入abc def或abc,def)2.如何输入获得两个整数?&#xff08;若输入34,567)3.如何输入后获得一个元素均为数值型的列表?&#xff08;若输入12,3.4,567或[12,3.4,567]&#xff09; 输出输出函数print()pr…

从无人驾驶汽车到虚拟助手:人工智能如何改变我们的世界

人工智能对我们的生活影响有多大 近年来&#xff0c;人工智能迅速发展&#xff0c;成为影响社会各个领域的重要技术。本文将深入探讨人工智能在无人驾驶汽车、虚拟助手等领域的应用和影响&#xff0c;剖析人工智能对我们的生活、工作和社交等方面所带来的深刻变革。 目录 人…

【上海大学《面向对象程序设计A》课程小项目报告】抽象向量类模板及其派生类

1 项目内容及要求 本项目通过设计一个抽象向量类模板&#xff0c;以及一个通用的向量类模板和一个字符串类作为其派生类&#xff0c;以满足各种应用场景中的数据存储和处理需求。 项目内容&#xff1a; 抽象向量类模板。派生向量类。派生字符串类。测试及异常处理。联合测试…