ffmpeg学习笔记-初识ffmpeg

时间:2022-08-19 00:30:54

ffmpeg用来对音视频进行处理,那么在使用ffmpeg前就需要ffmpeg有一个大概的了解,这里使用雷神的ppt素材进行整理,以便于复习

音视频基础知识

视频播放器的原理

  • 播放视频的流程大致如下:

    ffmpeg学习笔记-初识ffmpeg

  • 常用播放器

    • 跨平台
      • VLC,Mplayer,ffplay等
    • Windows平台
      • 完美解码,终极解码,暴风影音
  • 信息查看工具

    • 综合信息查看:MediaInfo
    • 二进制信息查看:UltraEdit
    • 单项详细信息分析
      • 封装格式:Elecard Format Analyzer
      • 视频编码数据:Elecard Stream Eye
      • 视频像素数据:YUV Player
      • 音频采样数据:Adobe Audition

封装格式

封装格式的作用:视频码流和音频码流按照一定的格式存储在一个文件中

封装格式分析工具:Elecard Format Analyzer

ffmpeg学习笔记-初识ffmpeg

名称 推出机构 目前使用领域
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构成

    ffmpeg学习笔记-初识ffmpeg

  • FLV格式简介

    包含文件头。数据由大小不固定的Tag构成

    ffmpeg学习笔记-初识ffmpeg

视频编码数据

  • 视频编码的作用:将视频像素数据(RGBYUV等)压缩成为视频码流,从而降低视频的数据量

  • 视频编码分析工具: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帧画面的压缩编码后的数据

    ffmpeg学习笔记-初识ffmpeg
  • H.264压缩方法

    比较复杂。包含了帧内预测、帧间预测、熵编码、环路滤波等环节构成

    可以将图像数据压缩100倍以上

音频编码数据

音频编码的作用:将音频采样数据(PCM等)压缩成为音频码流,从而降低音频的数据量

名称 推出机构 推出时间 目前使用领域
AAC MPEG 1997 各个领域(新)
AC-3 Dolby Inc. 1992 电影
MP3 MPEG 1993 各个领域(旧)
WMA Microsoft Inc. 1999 微软平台
  • AAC格式简介

    数据由大小不固定的ADTS构成

    ffmpeg学习笔记-初识ffmpeg
  • AAC压缩方法

    比较复杂

    可以将音频数据压缩10倍以上

视频像素数据

  • 视频像素数据作用:保存了屏幕上每个像素点的像素值

  • 格式:

    常见的像素数据格式有RGB24RGB32YUV420PYUV422PYUV444P

    压缩编码中一般使用的是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为例,图像像素数据的存储方式如下:

    ffmpeg学习笔记-初识ffmpeg

    从图中可以看出,RGB24依次存储了每个像素点的R、G、B信息

    PS:BMP文件中存储的就是RGB格式的像素数据

  • YUV格式简介

    相关实验表明,人眼对亮度敏感而对色度不敏感。因而可以将亮度信息和色度信息分离,并对色度信息采用更“狠”一点的压缩方案,从而提高压缩效率

    YUV格式中,Y只包含亮度信息,而UV只包含色度信息

    YUV420P为例,图像像素数据的存储方式如图所示

    ffmpeg学习笔记-初识ffmpeg

    从图中可以看出,YUV420P首先存储了整张图像的Y信息,然后存储整张图像的U信息,最后存储了整张图像的V信息

音频采样数据

  • 音频采样数据作用:保存了音频中每个采样点的值

  • 特点

    音频采样数据体积很大,一般情况下一首4分钟的PCM格式的歌曲体积为:

    4*60*44100*2*2=42.3MByte

    PS:这里假定采样率为44100Hz,采样精度为16bit

  • 音频采样数据查看工具:Adobe Audition

  • PCM格式简介

    单声道的情况下按照顺序存储每个采样点的数据

    双声道的情况下按照“左右、左右”的顺序存储每个采样点两个声道的数据

    ffmpeg学习笔记-初识ffmpeg

在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

    打开命令行工具,切换到ffmpegbin文件夹

    命令行中输入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"

  • 一般的视频解码流程

    视频码流一般存储在一定的封装格式(例如MP4AVI等)中。封装格式中通常还包含音频码流等内容

    对于封装格式中的视频,需要先从封装格式中提取中视频码流,然后再进行解码

    例如解码MKV格式的视频文件,就是"MKV -> H.264码流 -> YUV"

VC下FFmpeg开发环境的搭建

  • 新建控制台工程

    打开VC++

    文件 -> 新建 -> 项目 -> Win32控制台应用程序

  • 拷贝FFmpeg开发文件

    头文件(*.h)拷贝至项目文件夹的include子文件夹下

    导入库文件(*.lib)拷贝至项目文件夹的lib子文件夹下

    动态库文件(*.dll)拷贝至项目文件夹下

    PS:如果直接使用官网上下载的FFmpeg开发文件。则可能还需要将MinGW安装目录中的inttypes.hstdint.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学习笔记-初识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学习笔记-初识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格式的视频像素数据保存在AVFramedata[0]data[1]data[2]中。但是这些像素值并不是连续存储的,每行有效像素之后存储了一些无效像素。以亮度Y数据为例,data[0]中一共包含了linesize[0]*height个数据。但是出于优化等方面的考虑,linesize[0]实际上并不等于宽度width,而是一个比宽度大一些的值。因此需要使用sws_scale()进行转换。转换后去除了无效数据,widthlinesize[0]取值相等

    ffmpeg学习笔记-初识ffmpeg

SDL显示

视频显示知识

  • 视频显示的流程

    视频显示的流程,就是将像素数据"画"在屏幕上的过程

    例如显示YUV,就是将YUV"画"在系统的窗口中

SDL简介

  • 作用

    SDL(Simple DirectMedia Layer)库的作用说白了就是封装了复杂的视音频底层交互工作,简化了视音频处理的难度

  • 特点

    跨平台

    开源

  • 结构

    SDL结构如下所示

    可以看出它实际上还是调用了DirectX等底层的API完成了和硬件的交互

    ffmpeg学习笔记-初识ffmpeg

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\n", SDL_GetError());
} else {
printf("Success init SDL");
}
return 0;
}

如果运行无误,则代表SDL已经配置完成

SDL视频显示的函数

  • SDL视频显示的流程图如下所示

    ffmpeg学习笔记-初识ffmpeg

  • 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视频显示的数据结构如下所示

    ffmpeg学习笔记-初识ffmpeg

  • 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 CounterARGument 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()方法将控件显示出来