ffmpeg用来对音视频进行处理,那么在使用ffmpeg前就需要ffmpeg有一个大概的了解,这里使用雷神的ppt素材进行整理,以便于复习
音视频基础知识
视频播放器的原理
-
播放视频的流程大致如下:
-
常用播放器
- 跨平台
VLC,Mplayer,ffplay等
- Windows平台
完美解码,终极解码,暴风影音
- 跨平台
-
信息查看工具
- 综合信息查看:
MediaInfo
- 二进制信息查看:
UltraEdit
- 单项详细信息分析
- 封装格式:
Elecard Format Analyzer
- 视频编码数据:
Elecard Stream Eye
- 视频像素数据:
YUV Player
- 音频采样数据:
Adobe Audition
- 封装格式:
- 综合信息查看:
封装格式
封装格式的作用:视频码流和音频码流按照一定的格式存储在一个文件中
封装格式分析工具:Elecard Format Analyzer
名称 | 推出机构 | 目前使用领域 |
---|---|---|
AVI |
Microsoft Inc. |
BT下载影视 |
MP4 |
MPEG |
互联网视频网站 |
TS |
MPEG |
IPTV,数字电视 |
FLV |
Adobe Inc. |
互联网视频网站 |
MKV |
CoreCodec Inc. |
互联网视频网站 |
RMVB |
Real Networks Inc. |
BT下载影视 |
-
MPEG2-TS格式简介
不包含文件头。数据大小固定(188Byte
)的TS Packet
构成
-
FLV格式简介
包含文件头。数据由大小不固定的Tag
构成
视频编码数据
-
视频编码的作用:将视频像素数据(
RGB
,YUV
等)压缩成为视频码流,从而降低视频的数据量 -
视频编码分析工具:
Elecard Stream Eye
名称 | 推出机构 | 推出时间 | 目前使用领域 |
---|---|---|---|
HEVC(H.265) |
MPEG/ITU-T |
2013 |
研发中 |
H.264 |
MPEG/ITU-T |
2003 |
各个领域 |
MPEG4 |
MPEG |
2001 |
不温不火 |
MPEG2 |
MPEG |
1994 |
数字电视 |
VP9 |
Google |
2013 |
研发中 |
VP8 |
Google |
2008 |
不普及 |
VC-1 |
Microsoft Inc. |
2006 |
微软平台 |
- H.264格式简介
数据由大小不固定的NALU构成
最常见的情况下,1个NALU
存储了1帧画面的压缩编码后的数据
- H.264压缩方法
比较复杂。包含了帧内预测、帧间预测、熵编码、环路滤波等环节构成
可以将图像数据压缩100倍以上
音频编码数据
音频编码的作用:将音频采样数据(PCM
等)压缩成为音频码流,从而降低音频的数据量
名称 | 推出机构 | 推出时间 | 目前使用领域 |
---|---|---|---|
AAC |
MPEG |
1997 |
各个领域(新) |
AC-3 |
Dolby Inc. |
1992 |
电影 |
MP3 |
MPEG |
1993 |
各个领域(旧) |
WMA |
Microsoft Inc. |
1999 |
微软平台 |
- AAC格式简介
数据由大小不固定的ADTS
构成
- AAC压缩方法
比较复杂
可以将音频数据压缩10倍以上
视频像素数据
-
视频像素数据作用:保存了屏幕上每个像素点的像素值
-
格式:
常见的像素数据格式有RGB24
,RGB32
,YUV420P
,YUV422P
,YUV444P
等
压缩编码中一般使用的是YUV
格式的像素数据,最为常见的格式为YUV420P
-
特点
频像素数据体积很大,一般情况下1小时高清视频的RGB24格式的数据体积为:
3600*25*1920*1080*3=559.9GByte
PS:这里假定帧率为25Hz
,取样精度8bit
-
YUV格式像素数据查看工具:
YUV Player
-
RGB格式简介
Red、Green、Blue三种颜色,可以混合成世界上所有的颜色
彩色图像中每个点,由R、G、B三个分量组成
以RGB24
为例,图像像素数据的存储方式如下:
从图中可以看出,RGB24
依次存储了每个像素点的R、G、B信息
PS:BMP文件中存储的就是RGB格式的像素数据 -
YUV格式简介
相关实验表明,人眼对亮度敏感而对色度不敏感。因而可以将亮度信息和色度信息分离,并对色度信息采用更“狠”一点的压缩方案,从而提高压缩效率
YUV格式中,Y只包含亮度信息,而UV只包含色度信息
以YUV420P
为例,图像像素数据的存储方式如图所示
从图中可以看出,YUV420P
首先存储了整张图像的Y信息,然后存储整张图像的U信息,最后存储了整张图像的V信息
音频采样数据
-
音频采样数据作用:保存了音频中每个采样点的值
-
特点
音频采样数据体积很大,一般情况下一首4分钟的PCM格式的歌曲体积为:
4*60*44100*2*2=42.3MByte
PS:这里假定采样率为44100Hz
,采样精度为16bit
-
音频采样数据查看工具:
Adobe Audition
-
PCM格式简介
单声道的情况下按照顺序存储每个采样点的数据
双声道的情况下按照“左右、左右”的顺序存储每个采样点两个声道的数据
在Windows下使用
背景
-
使用广泛
使用FFmpeg作为内核的视频播放器
Mplayer,射手播放器,暴风影音,KMPlayer,QQ影音...
-
使用FFmpeg作为内核的转码器
格式工厂,狸窝视频转换器,暴风转码...
总而言之,FFmpeg是视频行业中的"瑞士军刀"
- 特点
基于命令行:FFmpeg界面不太人性化,操作相对复杂,但是也更加灵活
开源:可以吸引全世界优秀的开发者加入其中进行开发
FFmpeg命令行工具的获取
-
下载地址
访问FFmpeg官网
ffmpeg官网
→选择Download
→选择 Windows Package
→进入Zeranoe FFmpeg网站
注意不要直接从FFmpeg官网下载源代码 -
版本说明
Zeranoe
网站中的FFmpeg分为3个版本:
Static
:只包含3个体积很大的exe
Shared
:除了3个体积较小的exe
之外,还包含了dll
动态库文件
Dev
:只包含了开发用的头文件(*.h
)和导入库文件(*.lib
)
PS: 命令行使用的时候下载Static
或者Shared
版本就可以了 -
使用
将下载下来的压缩包解压到任意目录(例如D:ffmpeg
)
打开命令行工具,切换到ffmpeg
的bin
文件夹
命令行中输入ffmpeg.exe
,查看弹出的信息
ffmpeg.exe的使用
-
命令格式
功能ffmpeg.exe用于视频的转码
最简单的命令ffmpeg -i input.avi -b:v 640k output.ts
该命令将当前文件夹下的input.avi
文件转换为output.ts
文件,并将output.ts
文件视频的码率设置为640kbps
-
命令格式
ffmpeg -i {输入文件路径} -b:v {输出视频码率} {输出文件路径}
所有的参数都是以键值对的形式指定的。例如输入文件参数是"-i
",而参数值是文件路径;输出视频码率参数是"-b:v
",而参数值是视频的码率值。但是注意位于最后面的输出文件路径前面不包含参数名称 -
命令参数
参数 | 说明 |
---|---|
-h |
帮助 |
-i filename |
输入文件 |
-t duration |
设置处理时间,格式为hh:mm:ss |
-ss position |
设置起始时间,格式为hh:mm:ss |
-b:v bitrate |
设置视频码率 |
-b:a bitrate |
设置音频码率 |
-r fps |
设置帧率 |
-s wxh |
设置帧大小,格式为WxH |
-c:v codec |
设置视频编码器 |
-c:a codec |
设置音频编码器 |
-ar freq |
设置音频采样率 |
PS:详细的参数可以访问http://ffmpeg.org/ffmpeg.html
ffplay.exe的使用
-
命令格式
功能ffplay.exe用于视频的播放
最简单的命令ffplay input.avi
该命令将播放当前文件夹下的input.avi
文件 -
命令格式
ffplay {输入文件路径}
ffplay.exe
的参数格式和ffmpeg.exe
是类似的。所有的参数都是以键值对的形式指定的(由于不包含输出文件,所以只能指定输入参数)。注意位于最后面的输入文件路径前面不包含参数名称 -
快捷键
快捷键 | 说明 |
---|---|
q, ESC | 退出 |
f | 全屏 |
p, 空格 | 暂停 |
鼠标点击屏幕 | 跳转到指定位置 |
PS:详细的参数可以访问http://ffmpeg.org/ffplay.html
视频解码器
视频解码知识
-
纯净的视频解码流程
压缩编码数据 -> 像素数据
例如解码H.264
,就是"H.264码流 -> YUV
" -
一般的视频解码流程
视频码流一般存储在一定的封装格式(例如MP4
、AVI
等)中。封装格式中通常还包含音频码流等内容
对于封装格式中的视频,需要先从封装格式中提取中视频码流,然后再进行解码
例如解码MKV
格式的视频文件,就是"MKV -> H.264码流 -> YUV
"
VC下FFmpeg开发环境的搭建
-
新建控制台工程
打开VC++
文件 -> 新建 -> 项目 -> Win32控制台应用程序
-
拷贝FFmpeg开发文件
头文件(*.h)拷贝至项目文件夹的include子文件夹下
导入库文件(*.lib)拷贝至项目文件夹的lib子文件夹下
动态库文件(*.dll)拷贝至项目文件夹下
PS:如果直接使用官网上下载的FFmpeg
开发文件。则可能还需要将MinGW
安装目录中的inttypes.h
,stdint.h
,_mingw.h
三个文件拷贝至项目文件夹的include
子文件夹下 -
配置开发文件
打开属性面板
解决方案资源管理器 -> 右键单击项目 -> 属性
头文件配置
配置属性 -> C/C++ -> 常规 -> 附加包含目录,输入"include"(刚才拷贝头文件的目录)
导入库配置
配置属性 -> 链接器 -> 常规 -> 附加库目录,输入"lib"(刚才拷贝库文 件的目录
配置属性 -> 链接器 -> 输入 -> 附加依赖项,输入"avcodec.lib; avformat.lib; avutil.lib; avdevice.lib; avfilter.lib; postproc.lib; swresample.lib; swscale.lib"(导入库的文件名)
动态库不用配置 -
测试
创建源代码文件
在工程中创建一个包含
main()函数的C/C++文件(如果已经有了可以跳过这一步)
包含头文件
如果是C语言中使用FFmpeg,则直接使用下面代码
# include "libavcodec/avcodec.h"
如果是C++语言中使用FFmpeg,则使用下面代码
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include "libavcodec/avcodec.h"
}
main()
中调用一个FFmpeg的接口函数
例如下面代码打印出了FFmpeg的配置信息
int main(int argc, char* argv [])
{
printf("%s", avcodec_configuration());
return 0;
}
如果运行无误,则代表FFmpeg已经配置完成
FFmpeg库简介 FFmpeg一共包含8个库
avcodec
:编解码(最重要的库)
avformat
:封装格式处理
avfilter
:滤镜特效处理
avdevice
:各种设备的输入输出
avutil
:工具库(大部分库都需要这个库的支持)
postproc
:后加工
swresample
:音频采样数据格式转换
swscale
:视频像素数据格式转换
FFmpeg解码的函数
-
FFmpeg解码的流程图如下所示
-
FFmpeg解码函数简介
av_register_all()
:注册所有组件
avformat_open_input()
:打开输入视频文件
avformat_find_stream_info()
:获取视频文件信息
avcodec_find_decoder()
:查找解码器
avcodec_open2()
:打开解码器
av_read_frame()
:从输入文件读取一帧压缩数据
avcodec_decode_video2()
:解码一帧压缩数据
avcodec_close()
:关闭解码器
avformat_close_input()
:关闭输入视频文件
FFmpeg解码的数据结构
-
FFmpeg解码的数据结构如下所示
-
FFmpeg数据结构简介
AVFormatContext
:封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息
AVInputFormat
:每种封装格式(例如FLV
,MKV
,MP4
,AVI
)对应一个该结构体
AVStream
:视频文件中每个视频(音频)流对应一个该结构体
AVCodecContext
:编码器上下文结构体,保存了视频(音频)编解码相关信息
AVCodec
:每种视频(音频)编解码器(例如H.264
解码器)对应一个该结构体
AVPacket
:存储一帧压缩编码数据
AVFrame
:存储一帧解码后像素(采样)数据 -
FFmpeg数据结构分析
AVFormatContext
iformat
:输入视频的AVInputFormat
nb_streams
:输入视频的AVStream
个数streams
:输入视频的AVStream []
数组duration
:输入视频的时长(以微秒为单位)bit_rate
:输入视频的码率
AVInputFormat
name
:封装格式名称long_name
:封装格式的长名称extensions
:封装格式的扩展名id
:封装格式ID- 一些封装格式处理的接口函数
-
FFmpeg数据结构分析
AVStream
id
:序号codec
:该流对应的AVCodecContext
time_base
:该流的时基r_frame_rate
:该流的帧率
AVCodecContext
codec
:编解码器的AVCodec
width, height
:图像的宽高(只针对视频)pix_fmt
:像素格式(只针对视频)sample_rate
:采样率(只针对音频)channels
:声道数(只针对音频)sample_fmt
:采样格式(只针对音频)
AVCodec
name
:编解码器名称long_name
:编解码器长名称type
:编解码器类型id
:编解码器ID- 一些编解码的接口函数
-
FFmpeg数据结构分析
AVPacket
pts
:显示时间戳dts
:解码时间戳data
:压缩编码数据size
:压缩编码数据大小stream_index
:所属的AVStream
AVFrame
data
:解码后的图像像素数据(音频采样数据)linesize
:对视频来说是图像中一行像素的大小;对音频来说是整个音频帧的大小width, height
:图像的宽高(只针对视频)key_frame
:是否为关键帧(只针对视频)pict_type
:帧类型(只针对视频)。例如I,P,B
补充小知识
- 解码后的数据为什么要经过
sws_scale()
函数处理?
解码后YUV格式的视频像素数据保存在AVFrame
的data[0]
、data[1]
、data[2]
中。但是这些像素值并不是连续存储的,每行有效像素之后存储了一些无效像素。以亮度Y
数据为例,data[0]
中一共包含了linesize[0]*height
个数据。但是出于优化等方面的考虑,linesize[0]
实际上并不等于宽度width
,而是一个比宽度大一些的值。因此需要使用sws_scale()
进行转换。转换后去除了无效数据,width
和linesize[0]
取值相等
SDL显示
视频显示知识
- 视频显示的流程
视频显示的流程,就是将像素数据"画"在屏幕上的过程
例如显示YUV,就是将YUV"画"在系统的窗口中
SDL简介
-
作用
SDL(Simple DirectMedia Layer)
库的作用说白了就是封装了复杂的视音频底层交互工作,简化了视音频处理的难度 -
特点
跨平台
开源 -
结构
SDL结构如下所示
可以看出它实际上还是调用了DirectX
等底层的API
完成了和硬件的交互
VC下SDL开发环境的搭建
-
新建控制台工程
打开VC++
文件 -> 新建 -> 项目 -> Win32控制台应用程序
-
拷贝SDL开发文件
头文件(*.h)拷贝至项目文件夹的include子文件夹下
导入库文件(*.lib)拷贝至项目文件夹的lib子文件夹下
动态库文件(*.dll)拷贝至项目文件夹下
-
配置开发文件
打开属性面板
解决方案资源管理器 -> 右键单击项目 -> 属性
头文件配置
配置属性 -> C/C++ -> 常规 -> 附加包含目录,输入"include"(刚才拷贝文件的目录)
导入库配置
配置属性 -> 链接器 -> 常规 -> 附加库目录,输入"lib"(刚才拷贝文件的目录)
配置属性 -> 链接器 -> 输入 -> 附加依赖项,输入"SDL2.lib; SDL2main.lib"(导入库的文件名)
动态库不用配置 -
测试
创建源代码文件
在工程中创建一个包含main()
函数的C/C++文件(如果已经有了可以跳过这一步),后续步骤在该文件中编写源代码
包含头文件
如果是C语言中使用SDL,则直接使用下面代码
#include "SDL2/SDL.h"
如果是C++语言中使用SDL,则使用下面代码
extern "C"
{
#include "SDL2/SDL.h"
}
main()
中调用一个SDL的接口函数
例如下面代码初始化了SDL
int main(int argc, char* argv [])
{
if(SDL_Init(SDL_INIT_VIDEO))
{
printf("Could not initialize SDL - %s
", SDL_GetError());
} else {
printf("Success init SDL");
}
return 0;
}
如果运行无误,则代表SDL已经配置完成
SDL视频显示的函数
-
SDL视频显示的流程图如下所示
-
SDL视频显示函数简介
SDL_Init()
:初始化SDL系统
SDL_CreateWindow()
:创建窗口SDL_Window
SDL_CreateRenderer()
:创建渲染器SDL_Renderer
SDL_CreateTexture()
:创建纹理SDL_Texture
SDL_UpdateTexture()
:设置纹理的数据
SDL_RenderCopy()
:将纹理的数据拷贝给渲染器
SDL_RenderPresent()
:显示
SDL_Delay()
:工具函数,用于延时
SDL_Quit()
:退出SDL系统
SDL视频显示的数据结构
-
SDL视频显示的数据结构如下所示
-
SDL数据结构简介
SDL_Window
:代表了一个"窗口"
SDL_Renderer
:代表了一个"渲染器"
SDL_Texture
:代表了一个"纹理"
SDL_Rect
:一个简单的矩形结构
进阶-SDL中事件和多线程
-
SDL多线程
函数:SDL_CreateThread()
:创建一个线程
数据结构:SDL_Thread
:线程的句柄 -
SDL事件
函数
SDL_WaitEvent()
等待一个事件
SDL_PushEvent()
发送一个事件
数据结构
SDL_Event
:代表一个事件
fffmpeg + SDL视频播放器
FFmpeg和SDL整合实现视频播放器
- 整合方式
FFmpeg解码器实现了:视频文件 -> YUV
SDL视频显示实现了:YUV -> 屏幕
FFmpeg+SDL整合之后实现了:视频文件 -> YUV -> 屏幕
进阶:脱离开发环境的独立播放器
main()
函数的参数
argc argv
:全称为ARGument Counter
和ARGument Vector
。其中argv
存储了来自于命令行的参数;而argc
存储了参数的个数
例如在命令行中输入ffmpeg -i test.mkv test.ts
,则argc
取值为4,而argv[]
数组取值如下:
argv [0] = "ffmpeg";
argv [1] = "-i";
argv [2] = "test.mkv";
argv [3] = "test.ts";
动态链接库(*.dll
)
动态链接库不能被编译进应用程序。因而使用应用程序的时候必须在相同目录下保存用到的动态链接库文件
MFC知识
-
创建MFC工程的方法
打开VC++
文件 -> 新建 -> 项目 -> MFC应用程序
应用程序类型 -> 基于对话框
取消勾选"使用Unicode库"(暂不详细介绍)
-
设置控件
找到"工具箱",就可以将相应的控件拖拽至应用程序对话框中
常用控件有:Button,Edit Control,Static Text等
找到"属性"选项卡
可以在"Caption"属性上修改控件上的文字
可以在"ID"属性上修改控件上的ID(ID是控件的标识,不可重复)
-
添加消息响应函数
双击Button控件,就可以给该控件添加消息响应函数
在菜单栏的"项目 -> 类向导"处,可以添加更多种类的消息响应函数
MFC最简单的弹出消息框的函数是AfxMessageBox("HelloWorld")
FFmpeg + SDL + MFC实现图形界面视频播放器
-
FFmpeg解码器与MFC的整合
需要将视频文件路径从MFC界面上的Edit Control
控件传递给FFmpeg解码器
GetWindowText()
-
SDL与MFC的整合
需要将SDL显示的画面绘制到MFC的Picture Control
控件上
SDL_CreateWindowFrom()
PS:SDL2
有一个Bug。在系统退出的时候会把显示图像的控件隐藏起来,因此需要调用该控件的ShowWindow()
方法将控件显示出来