FFmpeg音视频核心技术与实战

Coding Alan 6年前 (2018-11-13) 11467次浏览 0个评论 扫描二维码

FF – Fast Forward

mpeg – Moving Picture Experts Group

音视频的广泛应用

  • 直播类:音视频会议、教育直播、娱乐/游戏直播等
  • 短视频:抖音、快手、小咖秀等
  • 网络视频:优酷、腾讯视频、爱奇艺等
  • 音视频通话:微信、QQ、Skype 等
  • 视频监控
  • 人工智能:人脸识别、智能音箱等,更关注算法

音视视频基础知识

音视频封装和编码格式

FFmpeg音视频核心技术与实战

像素格式

  • BGRA RGBA ARGB32 RGB32 YUV420
  • R = Y + 1.4075 * (V-128);
  • G = Y – 0.3455 * (U-128) – 0.7169*(V-128);
  • B = Y + 1.779 * (U-128);

Y 表示明亮度,也即灰度值;U 和 V 表示色度

PCM音频参数

  • 采样率 sample_rate 44100 (CD)
  • 通道 channels(左右声道)
  • 样本大小(格式)sample_size
    • AV_SAMPLE_FMT_S16
    • AV_SAMPLE_FMT_FLTP

MP4格式分析

box 类型说明
ftypfile type,表明文件类型
moovmetadata container,存放媒体信息的地方
mvhdmovie header,文件的总体信息,如时长,创建时间等
traktrack or stream container,存放视频/音频流的容量
tkhdtrack header,track 的总体信息,如时长,宽高等
mdiatrak media information container
mdhdmedia header,定论 TimeScale,trak需要通过 TimeScale 换算成真实时间
hdlrhandler,表明本 trak 类型,指明是 video/audio 还是 hint
minfmedia information container,数据在子 box 中
stblsample table box,存放时间/偏移的映射关系表,数据在子 box中
stsdsample descriptions
stts(decoding) time-to-sample,“时戳-sample 序号”的映射表
stscsample-to-chunk,sample 和 chunk 的映射表,这里的算法比较巧妙
stszsample size,每个 sample 的大小
stz2sample size,另一种 sample size 的存储算法,更节省空间
stsssync sample table,可随机访问的 sample 列表(关键帧列表)
stcochunk offset,每个 chunk 的偏移,sample 的偏移可根据其它 box 推算出来
co6464-bit chunk offset
mdatmedia data container,具体的媒体数据

http://download.qt.io/

FFmpeg 日志系统

播放器架构 & 渲染流程

音视频解码由 FFmpeg 完成,播放、渲染由 SDL 实现

FFmpeg音视频核心技术与实战

FFmpeg 从何而来?

  • 2000年,由法布里斯•贝拉(Fabrice Bellard)创建
  • 2004年,迈克尔(Michael Niedermayer)接管
  • 2011年,Libav从 FFmpeg 分离

FFmpeg 是一个非常优秀的多媒体框架,可运行在 Linux、Windows和 Mac 平台上,能够解码、编码、转码、复用、解复用、过滤音视频数据。

本文主要内容

安装

GitHub 地址:https://github.com/FFmpeg/FFmpeg

以上–enable-debug –disable-optimizations主要是便于开发调试,生产环境请勿使用

Windows 下安装 FFmpeg

FFmpeg常用命令实战

FFmpeg音视频核心技术与实战

基本信息查询命令

录制命令

参照如上命令在使用内置摄像头(-i 0)录制时会出现报错:

修改命令如下:

分解/复用命令

FFmpeg音视频核心技术与实战

处理原始数据命令

裁剪与合并命令

图片/视频互转命令

直播相关命令

测试流地址链接:https://blog.csdn.net/github_30662571/article/details/72466091

各种滤镜命令

FFmpeg音视频核心技术与实战

FFmpeg 常用命令

FFmpeg开发必备C语言回顾

Vim 编辑器常用命令

演示代码: GitHub仓库

常用基本类型

整型:short、int、long
浮点型:float、double
Char 型:char
无符号型:void

FFmpeg音视频核心技术与实战

指针与数组

指针就是内存地址:void*、char*,指针本身也可进行运算

数组(连续同一类型):char c[2]、int arr[10]

结构体、枚举类型

文件操作
文件类型 FILE* file;
打开文件 FILE* fopen(path, mode); // a+
关闭文件 fclose(FILE*);

操作系统管理内存:栈空间(4M, 8M,操作系统自动分配)、堆空间(4G)、内存映射

内存的分配和释放
分配内存 void* mem = malloc(size);
释放内存 free(mem);
内存泄漏和野指针
不断地向系统申请内存
申请的内存不用,也不释放
占用别人的内存称作野指针

C语言编译器


预编译→编译→链接(动态链接 /静态链接)

调试器(gdb/lldb)

调试信息包含:指令地址、对应源代码及行号

FFmpeg多媒体文件处理

演示代码:GitHub 仓库

FFmpeg 代码结构

  • libavcodec: 提供了一系列编解码器的实现
  • libavformat: 实现在流协议,容器格式及其本IO访问
  • libavutil: 包括了hash器,解码器和各利工具函数
  • libavfilter: 提供了各种音视频过滤器
  • libavdevice: 提供了访问捕获设备和回放设备的接口
  • libswresample: 实现了混音和重采样
  • libswscale: 实现了色彩转换和缩放工能

多媒体文件的基本概念

  • 多媒体文件其实是个容器
  • 在容器里有很多流(Stream/Track)
  • 每种流是由不同的编码器编码的
  • 从流中读出的数据称为包
  • 在一个包中包含着一个或多个帧

几个重要的结构体

  • AVFormatContext
  • AVStream
  • AVPacket

FFmpeg 操作流数据的基本步骤

解复用➞获取流➞读数据包➞释放资源

打印音/视频信息

抽取音频数据

抽取视频数据

◆Start code
◆SPS/PPS
◆codec→extradata

格式互转

  • avformat_alloc_output_context2()/avformat_free_context()
  • avformat_new_stream()
  • avcodec_parameters_copy()
  • avformat_write_header()
  • av_write_frame()/av_interleaved_write_frame()
  • av_write_trailer()

视频截取 

av_seek_frame()

FFmpeg编解码实战

演示代码:GitHub 仓库

FFmpeg H264解码

libavcodec/avcodec.h

常用数据结构

  • AVCodec 编码器结构体
  • AVCodecContext 编码器上下文
  • AVFrame 解码后的帧

结构体内存的分配与释放

  • av_frame_alloc/av_frame_free()
  • avcodec_alloc_context3()
  • avcodec_free_context()

解码步骤

  • 查找解码器(avcodec_find_decoder)
  • 打开解码器(avcodec_open2)
  • 解码(avcodec_decode_video2)

FFmpeg H264编码

H264编码流程

  • 查找编码器(avcodec_find_encoder_by_name)
  • 设置编码参数,并打开编码器(avcodec_open2)
  • 编码(avcodec_encode_video2)

实战-视频转图片

FFmpeg AAC 编解码

  • 查找编码器(avcodec_find_encoder_by_name)
  • 设置编码参数,并打开编码器(avcodec_open2)
  • 编码(avcodec_encode_audio2)

FFmpeg SDL音视频渲染实战

演示代码:GitHub 仓库

SDL(Simple DirectMedia Layer),是由 C 语言实现的跨平台的媒体开源库,多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。

http://www.libsdl.org/index.php

编译安装

使用 SDL基本步骤

  • 添加头文件#include <SDL.h>
  • 初始化 SDL(SDL_Init)
  • 退出 SDL(SDL_Quit)

SDL_CreateWindow()/SDL_DestroyWindow()

SDL_CreateRenderer()/SDL_DestroyRenderer()/SDL_RenderClear()/SDL_RenderPresent()

SDL将所有事件都存放在一个队列中;所有对事件的操作,其实就是对队列的操作。

SDL事件种类

  • SDL_WindowEvent:窗口事件
  • SDL_KeyboardEvent:键盘事件
  • SDL_MouseMotionEvent:鼠标事件

事件处理:SDL_PollEvent(轮询), SDL_WaitEvent, SDL_WaitEventTimeout

SDL渲染基本原理

FFmpeg音视频核心技术与实战

SDL纹理相关 API

  • SDL_CreateTexture()
    • format:YUV, RGB
    • access: Texture 类型, Target, Stream
  • SDL_DestroyTexture()
  • SDL_SetRenderTarget()
  • SDL_RenderClear()
  • SDL_RenderCopy()
  • SDL_RenderPresent()

YUV 视频播放器

创建线程

  • SDL_CreateThread
    • fn:线程执行函数
    • name:线程名
    • data:执行函数参数

SDL 更新纹理

  • SDL_UpdateTexture()
  • SDL_UpdateYUVTexture()

SDL音频处理

播放音频基本流程:打开音频设备->设置音频参数->向声卡喂数据->播放音频->关闭设备

播放音频的基本原则

  • 声卡向你要数据而不是你主动推给声卡
  • 数据的多少由音频参数决定的

SDL 音频 API

  • SDL_OpenAudio/SDL_CloseAudio
  • SDL_PauseAudio
  • SDL_MixAudio(混音)

FFmpeg播放器核心功能开发

演示代码:GitHub 仓库

最简单的播放器一

  • 该播放器只实现视频播放
  • 将 FFmpeg 与 SDL结合到一起
  • 通过 FFmpeg 解码视频数据
  • 通过 SDL 进行渲染

最简单的播放器二

  • 可以同时播放音频与视频
  • 使用队列存放音频包

线程的互斥与同步:通过操作系统的锁与信号量

锁的种类:读写锁、自旋锁、可重入锁

SDL创建线程

  • SDL_CreateThead
  • SDL_WaitThread

SDL锁

  • SDL_CreateMutex/SDL_DestroyMutex
  • SDL_LockMutex/SDL_UnlockMutex

SDL条件变量(信号量)

  • SDL_CreateCond/SDL_DestroyCond
  • SDL_CondWait/SDL_CondSignal

参考教程:http://dranger.com/ffmpeg/tutorial04.html

FFmpeg音视频核心技术与实战

 

线程的退出机制

  • 主线程接收到退出事件
  • 解复用线程在循环分流时对 quit进行判断
  • 视频解码线程从视频流队列中取包时对 quit进行判断
  • 音频解码从音频流队列中取包时对 quit进行判断
  • 音视频循环解码时对 quit 进行判断
  • 在收到信号量消息时对 quit 进行判断

音视频同步

  • 时间戳
    • PTS: Presentation timestamp
    • DTS: Decoding timestamp
    • I (intra)/B (bidrectional)/P (predicted)帧
  • 时间戳顺序
    • 实际帧顺序:I B B P
    • 存放帧顺序:I P B B
    • 解码时间戳:1 4 2 3
    • 展示时间戳:1 2 3 4
  • 从哪儿获得 PTS
    • AVPacket 中的 PTS
    • AVFrame 中的 PTS
    • av_frame_get_best_effort_timestamp()
  • 时间基
    • tbr:帧率(time base rate)
    • tbn:time base of stream
    • tbc:time base of codec
  • 计算当前帧的PTS
    • PTS = PTS * av_q2d(video_stream->time_base)
    • av_q2d(AVRotional a)
      { return a.num / (double)a.den;}
  • 计算下一帧的PTS
    • video_clock:预测的下一帧视频的 PTS
    • frame_delay:1/tbr
    • audio_clock:音频当前播放的时间戳
  • 音视频同步方式
    • 视频同步到音频
    • 音频同步到视频
    • 音频和视频都同步到系统时钟
  • 视频播放的基本思路:一般的做法,展示第一帧视频帧后,获得要显示的下一个视频帧的 PTS,然后设置一个定时器,当定时器超时后,刷新新的视频帧,如此反复操作。

如何在Android下使用FFmpeg

  • JAVA与 C 之间的相互调用
  • Android 下的 FFmpeg的编译
  • Android 下如何使用 FFmpeg

JNI基本概念

  • JNIEnv
  • JavaVM
  • 线程

安卓投屏至电脑:https://www.vysor.io/

JAVA 调用 C、C++的方法

  • 在 Java 层定义 native关键字函数
  • 方法一:在 C/C++层创建 Java_packname_classname_methodname 函数
  • 方法二:RegisterNative

Signature

  • Java 与 C/C++相互调用时,表示函数参数的描述符
  • 输入参数放在( )内,输出参数放在( )外
  • 多个参数之间顺序存放,且用“;”分割

原始类型的 Signature

Java 类型符号
booleanZ
byteB
charC
shortS
intI
longL
floatF
doubleD
voidV

类的 Signature

  • Java 对象参数“L包路径/类名
  • Java 数组“[”,
    • ([Student;)[Lstudent; -> Student[] Xxx(Student[])
    • ([Ljava/lang/String;)[Ljava/lang/Object -> Object[] Xxx(String[] s)

参考代码FirstJNI:GitHub仓库

C/C++调Java 方法

  • FindClass
  • GetMethodID/GetFieldID
  • NewObject
  • Call<TYPE> Method/[G/S]et<Type> Field

参考代码testc2j:GitHub仓库

安卓下使用 FFmpeg(需修改MainActivity中的videoPath):GitHub仓库

如何在iOS下使用FFmpeg

参考代码:GitHub 仓库

https://github.com/Bilibili/ijkplayer

音视频进阶

  • FFmpeg Filter 的使用与开发
  • FFmpeg 裁剪与优化
  • 视频渲染(OpenGL/Metal)
  • 声音的特效
  • 网络传输
  • WebRTC
  • AR技术
  • OpenCV

行业痛点

  • 回音消除
  • 降噪
  • 视频秒开
  • 多人多视频实时互动
  • PC 端/APP/网页实时视频互通
  • 实时互动与大并发负载

常见问题

1、nasm/yasm not found or too old. Use –disable-x86asm for a crippled build.

2、ERROR: libfdk_aac not found

3、warning: ‘av_register_all’ is deprecated

修改为:

4、MacOS Mojave ffplay 只播放声音不显示画面

解决办法:

  • 方法一:
    • 从 SDL下载 2.0.9版本或以后的SDL2代码。
    • 执行 ./configure
    • 执行 make && sudo make install
    • 执行 export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
    • [重新]编译ffmpeg
  • 方法二
    • SDL下载dmg包。
    • 执行 open SDL2-2.0.9.dmg
    • [重新]编译ffmpeg
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址