FFmpeg 的开源协议要求基于其开发的播放器也需要开源,很多我们熟知的播放器如 QQ 影音、暴风影音、KMPlayer 都曾因未开源而登上 FFmpeg 的 Hall of Shame。该页面目前已停止更新,但足以让我们了解这一神器在视频行业举足轻重的作用。

音视频的广泛应用

直播类:音视频会议、教育直播、娱乐/游戏直播等

短视频:抖音、快手、小咖秀等

网络视频:优酷、腾讯视频、爱奇艺等

音视频通话:微信、QQ、Skype 等

视频监控

人工智能:人脸识别、智能音箱等,更关注算法

include <libavutil/log.h>// 设置日志级别av_log_set_level(AV_LOG_DEBUG)// 打印日志av_log(NULL, AV_LOG_INFO,"...%s\n",op)

播放器架构 & 渲染流程

本文主要内容

FFmpeg常用命令实战

FFmpeg开发必备C语言回顾

FFmpeg多媒体文件处理

FFmpeg编解码实战

FFmpeg SDL音视频渲染实战

git clone  FFmpeg./configure --help./configure --prefix=/usr/local/ffmpeg  --enable-gpl --enable-nonfree --enable-libfdk-aac --enable-libx264 --enable-libx265 --enable-filter=delogo --enable-debug --disable-optimizations --enable-libspeex --enable-videotoolbox --enable-shared --enable-pthreads --enable-version3 --enable-hardcoded-tables --cc=clang --host-cflags= --host-ldflags=make && make install make && make install# Mac如若权限不足可使用 sudo make install 或使用 sudo -i 切换为root# 编译执行应使用软链确保可直接执行命令ln -s /usr/local/ffmpeg/bin/ffmpeg /usr/local/bin/ln -s /usr/local/ffmpeg/bin/ffplay /usr/local/bin/# Linux 编译./configure --prefix=/usr/local/ffmpeg --enable-gpl --enable-nonfree --enable-libfdk-aac --enable-libx264 --enable-libx265 --enable-filter=delogo --enable-debug --disable-optimizations --enable-libspeex --enable-shared --enable-pthreadsWindows 下安装 FFmpeg: http://t.cn/EbYol3C//FFmpeg常用命令实战//

# 基本信息查询命令-version 显示版本-demuxer 显示可用的 demuxers-muxers 显示可用的 muxers-devices 显示可用的设备-codecs 显示所有编解码器-decoders 显示可用的解码器-encoders 显示所有的编码器-bsfs 显示比特流 filter-formats 显示可用的格式-protocols 显示可用的协议-filters 显示可用的过滤器-pix_fmts 显示可用的像素格式-sample_fmts 显示可用的采样格式-layouts 显示 channel 名称-colors 显示识别的颜色名称# 录制命令# 录制视频ffmpeg -f avfoundation -i 1 -r 30 out.yuv-f 指定使用 avfoundation 采集数据-i 指定从哪儿采集数据,它是一个文件索引号[1] Capture screen 0-r 指定帧率# 查看录制音频、视频设备号ffmpeg -f avfoundation -list_devices true -i ""# 根据录制时的分辨率和像素格式指定进行播放操作# brew 安装的话请使用brew reinstall ffmpeg --with-sdl2安装 ffplayffplay -video_size 2880x1800 -pixel_format uyvy422 out.yuv# 录制音频命令ffmpeg -f avfoundation -i :0 out.wavffplay out.wav:0 代表音频设备# 分解/复用命令# 多媒体格式转换ffmpeg -i input.mp4 -vcodec copy -acodec copy out.flv-i 输入文件-vcodec 视频编解码处理方式-acodec 音频编解码处理方式# 抽取视频ffmpeg -i input.mov -an -vcodec copy out.h264# 抽取音频(假设音频为 aac)ffmpeg -i input.mov -acodec copy  -vn out.aac# 处理原始数据命令# FFmpeg 提取 YUV 数据ffmpeg -i input.mp4 -an -c:v rawvideo -pix_fmt yuv420p out.yuv# FFmpeg 提取 PCM数据ffmpeg -i input.mp4 -vn -ar 44100 -ac 2 -f s16le out.pcm-r 采样率-f 格式 s 有符号,16位,le 小于# 播放:ffplay -ar 44100 -ac 2 -f s16le out.pcm# 裁剪与合并命令# 裁剪ffmpeg -i input.mp4 -ss 00:00:00 -t 10 out.ts-ss 起始时间 -t 时长(秒)# 合并ffmpeg -f concat -i inputs.txt out.flv# inputs.txt 的内容(filename 可以是1.ts 等)file filename # 图片/视频互转命令ffmpeg -i in.flv -r 1 -f image2 image-%3d.jpeg-r 帧率,1为每秒一张-f 图片格式%3d 三个数字# 图片转视频ffmpeg -i image-%3d.jpeg out.mp4# 直播相关命令# 直播推流ffmpeg -re -i out.mp4 -c copy -f flv rtmp:server/live/streamName-re 减慢帧率速度,保持同步# 直播拉流ffmpeg -i rtmp://server/live/streamName -c copy dump.flv# 各种滤镜命令# 裁切cropffmpeg -i input.mov -vf crop=in_w-200:in_h-200 -c:v libx264 -c:a copy out.mp4-200表示宽高分别减200

# helloworld.c#include <stdio.h>int main(int argc, char* argv[]){     int a=1; #变量    const int b=2; #常量    printf("a=%d\n",a);    printf("Hello World!\n");    return 0;}# Mac 下编译clang -g -o helloworld helloworld.c-g 调试信息-o 输出文件# Linux下使用的命令是 gcc

常用基本类型

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

指针就是内存地址:void*、char*

数组:char c[2]、int arr[10]

include <stdio.h>#include <stdlib.h>int main(int argc, char *argv[]){    int *a, *b;    a = (int*)malloc(sizeof(int));    b = (int*)malloc(sizeof(int));    *a = 1;    *b = 2;    int c[3] = {0,1,2};// 加&符表示指针地址,指向存储空间地址    printf("addr of a:%p, %p, %d\n", &a, a, *a);    printf("addr of b:%p, %p, %d\n", &b, b, *b);    printf("addr of c:%p, %p, %d, %d, %d\n", &c, c, c[0], c[1], c[2]);    return 0;}// 输出结果,注意数组的指针和存储空间地址相同addr of a:0x7ffee6b95700, 0x7fbdc6402bf0, 1addr of b:0x7ffee6b956f8, 0x7fbdc6402c00, 2addr of c:0x7ffee6b9571c, 0x7ffee6b9571c, 0, 1, 2

结构体、枚举类型

// 结构体struct st{};// 枚举类型enum em{};

文件操作

文件类型 FILE* file;

打开文件 FILE* fopen(path, mode); // a+

关闭文件 fclose(FILE*);

int main(int argc, char* argv[]){    FILE* file;    char buf[1024] = {0,};// 打开文件    file = fopen("1.txt", "a+");// 写入文件    fwrite("hello, world!", 1, 13, file);// 回到文件头    rewind(file);// 读取文件到 buffer 中    fread(buf,1,26,file);// 关闭文件    fclose(file);    printf("buf:%s\n",buf);    return 0;}

内存的分配和释放

目前道歉文件前面的potplayer 64位资源付费让步

分配内存 void* mem = malloc(size);

释放内存 free(mem);

内存泄漏和野指针

不断地向系统申请内存

申请的内存不用,也不释放

占用别人的内存称作野指针

int sum(int a, int b){    return(a+b);}int main(int argc, char* argv[]){    int result;// 指针函数    int(*f)(int, int);// 赋值为对就函数    f = sum;    result = f(3,5);    printf("3+5=%d\n",result);    return result;}

C语言编译器

gcc/clang -g -O2 -o test test.c -I... -L... -l-g 调试信息-O 指令优化级别指定-o 输出文件-I 指定头文件位置-L 指定所引用第三方库文件的位置-l 具体使用的库// 仅编译为中间文件,生成的为 add.o 文件clang -g -c add.c// 生成静态库 myliblibtool -static -o libmylib.a add.o# 编译文件clang -g -o libtest libtest.c -I . -L . -lmylib

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

调试器(gdb/lldb)

lldb libtestb main # 为 main 函数设置断点break list...dwarfdump 读取.dSYM目录下文件如:libtest.dSYM/Contents/Resources/DWARF/libtest# 相关命令b 设置断点r 运行程序n 单步执行s 跳入函数finish 跳出函数p 打印内容//FFmpeg多媒体文件处理//

libavcodec: 提供了一系列编解码器的实现

libavformat: 实现在流协议,容器格式及其本IO访问

libavutil: 包括了hash器,解码器和各利工具函数

libavfilter: 提供了各种音视频过滤器

libavdevice: 提供了访问捕获设备和回放设备的接口

libswresample: 实现了混音和重采样

libswscale: 实现了色彩转换和缩放工能

# FFmpeg日志系统include <libavutil/log.h>// 设置日志级别:AV_LOG_ERROR,AV_LOG_WARNING,AV_LOG_INFO,AV_LOG_DEBUGav_log_set_level(AV_LOG_DEBUG>// 打印日志av_log(NULL,AV_LOG_INFO,"...%s\n",op)# 编译语句clang -g -o ffmpeg_log ffmpeg_log.c -lavutil# 文件的删除与重命名avpriv_io_delete()avpriv_io_move()# 编译语句clang -g -o ffmpeg_del ffmpeg_file.c `pkg-config --libs libavformat`# 操作目录的函数avio_open_dir() avio_read_dir()avio_close_dir()AVIODirContext // 操作目录的上下文AVIODirEntry // 目录项,用于存放文件名、文件大小等信息# 编译语句:clang -g -o list ffmpeg_list.c `pkg-config --libs libavformat libavutil`

多媒体文件的基本概念

多媒体文件其实是个容器

在容器里有很多流(Stream/Track)

每种流是由不同的编码器编码的

从流中读出的数据称为包

在一个包中包含着一个或多个帧

见个重要的结构体

AVFormatContext

FFmpeg 操作流数据的基本步骤

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

打印音/视频信息

av_register_all()&nbsp;// deprecated&nbsp;avformat_open_input()/avformat_close_input()av_dump_format()# 编译命令clang -g -o mediainfo media_info.c `pkg-config --libs libavutil libavformat`

抽取音频数据

av_init_packet() // 初始化数据包结构体av_find_best_stream()av_read_frame()/av_packet_unref()# 编译命令clang -g -o extract_audio extract_audio.c `pkg-config --libs libavutil libavformat`# 提取音频./extract_audio test.mp4 ./test.aac

抽取视频数据

◆Start code◆SPS/PPS◆codec→extradata

# 编译命令clang -g -o extract_video extract_video.c `pkg-config --libs libavutil libavformat`# 抽取视频./extract_video test.mp4 test.h264格式互转

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()

#编译命令clang -g -o remuxing remuxing.c `pkg-config --libs libavformat`# mp4转 flv./remuxing trailer.mp4 trailer.flv

视频截取

av_seek_frame()

# 编译命令 clang -g -o cutvideo cutvideo.c `pkg-config --libs libavutil libavformat libavcodec`# 剪辑./cutvideo 10 20 trailer.mp4 trailer-cut.mp4//FFmpeg编解码实战//

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)

H264编码流程

查找编码器(avcodec_find_encoder_by_name)

设置编码参数,并打开编码器(avcodec_open2)

编码(avcodec_encode_video2)

clang -g -o encode_video encode_video.c `pkg-config --libs libavcodec`./encode_video 1.h264 libx264

实战-视频转图片

clang -g -o decode_video decode_video.c `pkg-config --libs libavformat libavcodec libavutil libswscale`./decode_video /workspace/trailer.mp4 ./

FFmpeg AAC 编解码

查找编码器(avcodec_find_encoder_by_name)

设置编码参数,并打开编码器(avcodec_open2)

编码(avcodec_encode_audio2)

clang -g -o encode_audio encode_audio.c `pkg-config --libs libavcodec`./encode_audio 2.aac//FFmpeg SDL音视频渲染实战//

编译安装

# 下载源码进入目录编译安装./configure --prefix=/usr/local# -j 指定线程数sudo make -j 8 && make install

使用 SDL基本步骤

添加头文件#include <SDL.h>

初始化 SDL(SDL_Init)

退出 SDL(SDL_Quit)

SDL_CreateWindow()/SDL_DestroyWindow()

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

clang -g -o first_sdl first_sdl.c `pkg-config --cflags --libs sdl2`clang -g -o event_sdl event_sdl.c `pkg-config --cflags --libs sdl2`

SDL事件种类

SDL_WindowEvent:窗口事件

SDL_KeyboardEvent:键盘事件

SDL_MouseMotionEvent:鼠标事件

事件处理:SDL_PollEvent, SDL_WaitEvent, SDL_WaitEventTimeout

SDL渲染基本原理

SDL纹理相关 API

SDL_CreateTexture()

format:YUV, RGB

access: Texture 类型, Target, Stream

SDL_DestroyTexture()

SDL_SetRenderTarget()

SDL_RenderClear()

SDL_RenderCopy()

SDL_RenderPresent()

clang -g -o texture_sdl texture_sdl.c `pkg-config --cflags --libs sdl2`YUV 视频播放器

创建线程

SDL_CreateThread

fn:线程执行函数

name:线程名

data:执行函数参数

SDL 更新纹理

SDL_UpdateTexture()

SDL_UpdateYUVTexture()

# 执行时需改代码中的宽高和 yuv 文件路径clang -g -o yuv_player yuv_player.c `pkg-config --cflags --libs sdl2`SDL音频处理

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

声卡向你要数据而不是你主动推给声卡

数据的多少由音频参数决定的

SDL 音频 API