第一章 课程导学与准备工作
1-1 课程导学
1-2 音视频的应用范围与播放器架构讲解
未来+高薪
- 音视频具有更加广阔的未来
- 优秀音视频技术人才奇缺
- 自学成本高
课程安排
-
FFmpeg 常用命令
- 视频录制命令
- 多媒体文件的分解、复用命令
- 裁剪与合并命令
- 图片、视频互转命令
- 直播相关命令
- 各种滤镜命令
-
FFmpeg基本开发
- C语言回顾
- FFmpeg核心概念与常用结构体
- 实战-多媒体文件的分解与复用
- 实战-多媒体格式的互转
- 实战-从MP4剪一段视频
- 作业-实现一个简单的小咖秀
-
音视频编解码实战
- 实战-H264解码
- 实战-H264编码
- 实战-音频AAC解码
- 实战-音频AAC编码
- 实战-视频转图片
-
音视频渲染实战
- SDL事件处理
- SDL视频纹理渲染
- SDL音频渲染
- 实战1-实现YUV视频播放器
- 实战2-YUV视频倍速播放
- 实战3-实现PCM播放器
-
FFmpeg开发播放器核心功能
- 实战-实现MP4文件的视频播放
- 实战-实现MP4文件的音频播放
- 实战-实现一个初级播放器
- 实战-音视频同步
- 实战-实现播放器内核
-
Android Q中实战FFmpeg
- 编译Android端可以使用的FFmpeg
- Java与C语言相互调用
- 实战-Android调用FFmpeg
适合人群
- 从事音视频相关工作,想提高技能的人
- 想转行到音视频行业的人
- 刚刚毕业或即将毕业还没有确定未来方向的人
学习建议
- 牢牢抓住音视频的处理机制,了解其本质
- 勤加练习,熟能生巧
- 带着问题去学习,事半功倍
1-3 音视频的应用范围与播放器架构讲解
音视频的广泛应用
- 直播类:音视频会议、教育直播、娱乐/游戏直播等
- 短视频类:抖音、快手、小咖秀等
- 网络视频:优酷、腾讯视频、爱奇艺等
- 视频通话:微信、QQ、Skype等
- 视频监控
- 人工智能:人脸识别,智能音箱等,更关注算法
播放器架构
渲染流程
1-4 什么是FFmpeg,它能做什么?
FFmpeg从何而来?
- 2000年,由法布里斯·贝拉(FabriceBellard)创建
- 2004年,迈克尔(Michael Niedermayer)接管
- 2011年,Libav从FFmpeg分离
法布里斯·贝拉
- 高中就读期间开发了著名的可执行压缩程序LzEXE
- 2000年创建了FFmpeg项目
- 2011年,他用JavaScript写了一个Linux虚拟机(JSLinux)
- 他还是QEMU,TinyCC的作者
FFmpeg都能做啥?
- FFmpeg是一个非常优秀的多媒体框架
- FFmpeg可以运行在Linux,Mac,Windows等品台上
- 能够解码,编码,转码,复用,解复用,过滤音视频数据
耻辱柱
- 偷用了FFmpeg开源代码却违背FFmpeg遵守的开源协议
- GPL的核心思想是基于GPL协议的代码都必须开源
- QQ影音、暴风影音、格式工厂等都被钉在了耻辱柱上。
1-5 FFmpeg下载编译与安装
FFmpeg下载编译与安装
- git clone https://git.ffmpeg.org/ffmpeg.git
- config --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
遇到报错:ERROR: videotoolbox requested, but not all dependencies are satisfied: corefoundation coremedia corevideo
解决方法:安装NVIDIA headers
git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git
make
sudo make install
遇到报错:
说某个静态库未找到,但你确实可以找到。
解决方法:
在/etc/ld.so.conf
文件中加入两行:
/usr/local/ffmpeg/lib
/usr/local/lib
这样操作后就正常了
安装过程中还安装了:
fdk-aac
speex
nasm
x264
cmake
tar -xzf [cmake-3.7.0-rc1.tar.gz](https://cmake.org/files/v3.7/cmake-3.7.0-rc1.tar.gz)
make && make install
tar -xzf x265_2.1.tar.gz
$ cd x265/build/linux
$ ./make-Makefiles.bash
$ make && make install
注:安装中,有些事mac系统特有的库,可不用装或者用替代品
1-6 Windows下安装FFmpeg
https://www.imooc.com/article/247113
1-7 FFmpeg 命令大全文档
第二章 FFmpeg常用命令实战
2-1 FFmpeg常用命令
FFmpeg命令分类
- 基本信息查询命令
- 录制命令
- 分解、复用命令
- 处理原始数据命令
- 裁剪与合并命令
- 图片、视频互转命令
- 直播相关命令
- 各种滤镜命令
2-2 FFmpeg处理流程
FFmpeg处理音视频流程
2-3 FFmpeg基本信息查询命令实战
FFmpeg基本信息查询命令
-version 显示版本
分解与复用命令
-demuxers 显示可用的demuxers
-muxers 显示可用的muxers
查询设备
-devices 显示可用设备
编解码相关
-codecs 显示所有编解码器
-decoders 显示所有的解码器
-encoders 显示所有的编码器
比特流处理
-bsfs 显示比特流filter
格式信息
-formats 显示可用的格式
网络协议
-protocols 显示可用的协议
滤镜
-filters 显示可用的过滤器
像素格式
-pix_fmts 显示可用的像素格式
其他
-sample_fmts 显示可用的采样格式
-layouts 显示channel名称
-colors 显示识别的颜色名称
2-4 FFmpeg录制命令实战
FFmpeg录屏命令
ffmpeg -f avfoundation -i 1 -r 30 out.yuv
-f:指定使用 AVfoundation 采集数据
-i:输入,指定从哪儿采集数据,1是一个文件索引号
-r:指定帧率
实战:
./ffmpeg -f gdigrab -i desktop -r 30 out.yuv
windows下录制屏幕
./ffplay -s 3840x1080 -pix_fmt bgra ./out.yuv
播放录制的文件
-s 输入播放尺寸
-pix_fmt 像素格式,录制的时候有显示查询可用设备:
ffmpeg -f avfoundation -list_devices true -i ''
试了没用
FFmpeg录音命令
ffmpeg -f avfoundation -i :0 out.wav
:0 代表录音设备
课后答疑任务:同时录制视频与音频
2-5 FFmpeg分解与复用命令实战
分解与复用
多媒体格式转换
ffmpeg -i out.mp4 -vcodec copy -acodec copy out.flv
-i
:输入文件
-vcodec
:设置视频编码处理方式为copy复制
-acodec
:设置音频编码处理方式为copy复制
抽取视频数据:
ffmpeg -i f35.mov -an -vcodec copy out.h264
抽取音频:
ffmpeg -i f35.mov -acodec copy -vn out.aac
2-6 ffmpeg 处理原始数据命令实战
提取YUV数据
ffmpeg -i input.mp4 -an -c:v rawvideo -pix_fmts yuv420p out.yuv
-an
: 不提取音频
-c:v
: 对视频进行编码,使用rawvideo(原始视频)格式进行编码
-pix_fmt yuv420p
: 输出的YUV像素格式
FFmpeg提取PCM数据
ffmpeg -i out.mp4 -vn -ar 44100 -ac 2 -f s16le out.pcm
-ar
: audiu rate音频采样率
-ac
: audiu channel 音频声道数为2
-f
:音频的数据存储格式s16le
: s:有符号16位lettle end
2-7 ffmpeg滤镜命令实战
回顾FFmpeg处理音视频流程
ffmpeg滤镜
ffmpeg滤镜命令
ffmpeg -i in.mov -vf crop=in_w-200:in_h-200 -c:v libx264 -c:a copy out.mp4
-vf
视频滤镜(video filter)
crop
滤镜名,格式:out_w:out_h:x:y(xy代表剪切的起始位置,默认为视频中心点)
in_w
视频本身宽度
in_h
视频本身高度
-c:v
指定视频解码器为libx264(codec video)
-c:a
指定音频处理方式为copy(codec audio)
2-8 FFmpeg音视频裁剪与合并命令实战
音视频裁剪
ffmpeg -i in.mp4 -ss 00:00:00 -t 10 out.ts
-ss
视频裁剪的起始时间,格式时:分:秒
-t
裁剪的时长s
音视频合并
ffmpeg -f concat -i inputs.txt out.flv
inputs.txt 需要合并的文件列表
格式: file '文件名'
out.flv: 输出的文件名
2-9 ffmpeg 图片与视频互转实战
FFmpeg视频转图片
ffmpeg -i input.flv -r 1 -f image2 image-%3d.jpeg
-r
指定转换图片的帧率
-f
指定输入文件转成的格式
ffmpeg -f image2 -r 1 -i bmp%d.jpeg -loop 1 -pix_fmt yuv420p -video_size 1280x720 -r 10 -c:v libx264 out.mp4
FFmpeg 图片转视频
ffmpeg -i image-%3d.jpeg output.mp4
2-10 ffmpeg 直播相关的命令实战
直播推/拉流
直播推流
ffmpeg -re -i input.mp4 -c copy -f flv rtmp://server/live/streamName
-re
降低帧率
-c
对音视频进行编解码(-a 音频 -v 视频)
-f
推出的文件格式直播拉流
ffmpeg -i rtmp://server/live/streamName -c copy dump.flv
rtmp、rtsp、http视频协议直播流地址
https://blog.csdn.net/github_30662571/article/details/72466091
第三章 基础开发
3-1 FFmpeg 基础开发概述
基础开发内容
- Vim编辑器
- C语言回顾,重点介绍指针概念
- Linux/Mac C语言的编译与调试
- Linux/Mac 常用开发工具介绍
3-2 Vim 模式及创建文件
Vim 处理模式
-
命令模式
拷贝、删除、粘贴等,通过i/a等键切换到编辑模式 -
编辑模式
编辑字符,通过Esc键进行切换
Vim 常用命令
- 创建文件: vim filename
- 保存文件: :w
- 关闭文件: :q
Vim 拷贝、粘贴与删除
- 拷贝 yy/yw 拷贝一行或一个词
- 粘贴 p
- 删除 dd/dw
Vim 光标移动
- 左下上右 h/j/k/l
- 调到文件头 gg
- 跳到文件尾 G
Vim 行内光标移动
- 移动到行首 ^
- 移动到行尾 $
- 按单词移动 向前w/ 2w/,向后 b/ 2b
Vim 查找与替换
- 查找关键字
/关键字
- 查找与替换:
:%s/关键字/替换字/gc
% 表示查找文档内所有的行,如果需要查找某个范围内的则用:行号,行号
表示
s 代表Search
g 查找所有
c 每次替换需要确认
Vim 多窗口
- 分窗口: :split/vsplit
- 窗口之间的跳转: Ctrl + ww/w[hjkl]
- 关闭窗口:
:close
- 最大化窗口:Ctrl + w + |
- 上下屏窗口调整: Ctrl + w + _
- 窗口平均分配:Ctrl + w + =
第四章 C语言基础
4-1 Hello World
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("Hello World!\n");
return 0;
}
编译代码,gcc -g -o helloworld helloworld.c
将helloworld.c编译为helloworld可执行程序
常用基本类型
- short、int、long(字节:2,4,4)
- float、double(32bit,32bit,前者精度低,后者精度高)
- char(单个字符)
- void 无符号型
4-2 C语言中的常量与变量
变量与常量
- int a=0; // 变量,可以再赋值
- const int len= 256; // 常量定义
内存管理
实践
#include <stdio.h>
int main (int argc, char* argv[]) {
int a = 0;
const int b = 10;
printf("a=%d\n", a);
printf("b=%d\n", b);
a = 10;
printf("a=%d\n", a);
return 0;
}
4-3 C语言中的指针
指针与数组
- 指针就是内存地址: void、char
- 数组(连续的同一类型的空间),如: char c[2]、int arr[10]
指针
数组
4-4 C语言中的指针-2(实践)
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char *argv[])
{
int *a, *b;
printf("addr of a:%p\n", &a);
printf("addr of b:%p"\n, &b);
return 0;
}
#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, %d, %d, %d\n", c, c[0], c[1], c[2]);
return 0;
}
C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。
sizeof()是获得类型的内存大小,字节为单位
4-5 C语言结构体
结构体
struct st{
int a; // 成员a
int b; // 成员b
};
枚举类型
#include <stdio.h>
enum e_type {
red = 0,
green,
blue
};
int main (int argc, char* argv[]) {
enum e_type et;
et = red;
printf("the color is %d\n", et);
return 0;
}
4-6 C语言中的if_else
算术运算与比较运算
- +、-、*、/、%
- >、 ==、 <、 >=、 <=、 !=
if/else 语句
if (a>b){
} else {
}
4-7 C语言中的for_while
for语句
for(int i = 0; i<100;i++){
}
while 语句
while(1){
}
4-8 C语言中的函数
函数
void func (int a) {
}
实操:
#include <stdio.h>
int sum (int a, int b) {
return (a + b);
}
void printInfo () {
printf("help: this is program of calc a+b \n\n");
}
int main (int argc, char* argv[]) {
printInfo();
int result;
result = sum(3, 5);
printf("3 + 5 = %d\n", result);
return 0;
}
4-9 C语言中的文件操作
文件操作
- 文件类型 FILE* file;
- 打开文件 FILE* fopen(path, mode);
- 关闭文件 fclose(FILE*);
#include<stdio.h>
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);//将游标放到文件的开头
fread(buf, 1, 10, file);
fclose(file);
printf("buf: %s \n", buf);
return 0;
}
4-10 再论C语言指针
指针的物理意义
- 它就是内存中的一个地址
- 指针本身运算
- 指针所指向内容的操作
操作系统是如何管理内存的?
- 栈空间,局部变量,如函数内
- 堆空间,存放程序
- 内存映射,数据库广泛应用
内存的分配与释放
- 分配内存 void* mem = malloc(size);
- 释放内存 free( mem );
内存泄露与野指针
- 不断的向系统申请内存
- 申请的内存不用,也不释放
- 释放看还使用该指针,占用别人的内存称为野指针
函数指针
- 返回值类型 (*指针变量名)([形参列表]);
int func(int x); /* 声明一个函数 */
int (*f)(int x); /* 声明一个函数指针 */
f = func; /* 将func 函数的首地址赋给指针f */
实践:
#include <stdio.h>
int sum (int a, int b) {
return (a + b);
}
void printInfo () {
printf("help: this is program of calc a+b \n\n");
}
int main (int argc, char* argv[]) {
printInfo();
int result;
int (*f)(int, int);
f = sum;
result = f(3, 5);
printf("3 + 5 = %d\n", result);
return 0;
}
4-11 C语言编译器
GCC/CLANG
-
gcc/clang -g -O 2 -o test test.c -I... -L... -l
-g 输出文件中的调试信息
-O 对输出文件做指令优化设置优化级别,默认为1;程序发行时使用
-o 输出的可执行文件名
-I (大写的i)指定头文件(位置)
-L 指定库文件位置
-l (小写的L)指定使用哪个库
编译过程
- 预编译,拷贝头文件
- 编译
- 链接,动态链接、静态链接
// add.c
int add (int a, int b) {
return (a + b);
}
gcc -g -c add.c
// 编译库文件
// 生成库文件
libtool -static -o libmylib.a add.o
// 命令无效
ar rcs libtestlib.a testlib.o
// 在Linux下的命令
// add.h
int add(int a, int b);
// testLib.c
#include <stdio.h>
#include "add.h"
int main(int argc, char* argv[]) {
printf("add=$d\n", add(3, 3));
return 0;
}
gcc -g -o testLib add.c -I . -L . -l mylib
方法二:先编译再链接
gcc -g -c testLib.c
gcc -o testLib testLib.o -L . -l mylib
待学习解决
在Linux下生成静态库用ar命令,动态库用gcc;
在Mac下用libtool
4-12 C语言调试器
调试器原理
- 编译输出带调试信息的程序
- 调试信息包含:指令地址、对应源代码及行号
- 指令完成后,回调
Gdb/lldb
命令 | gdb(Linux) | lldb(mac) |
---|---|---|
设置断点 | b | b |
运行程序 | r | r |
单步执行 | n | n |
跳入函数 | s | s |
跳出函数 | finish | finish |
打印内容 | p | p |
continue | c | c |
退出 | q | - |
gdb testLib
b main
info breakpoints // lldb: break list
第五章 ffmpeg 多媒体文件处理
5-1 FFmpeg 初级开发介绍
初级开发内容
- FFmpeg 日志的使用及目录操作
- 介绍FFmpeg 的基本概念及常用结构体
- 对复用/解复用及流操作的各种实战
FFmpeg代码结构
模块 | 介绍 |
---|---|
libavcodec | 提供了一系列编码器的实现 |
libavformat | 实现在流协议,容器格式及其本IO访问 |
libavutil | 包括了hash器,解码器和各种工具函数 |
libavfilter | 提供了各种音视频过滤器 |
libavdevice | 提供了访问捕获设备和回放设备的接口 |
libswresample | 实现了混音和重采样 |
libswscale | 实现了色彩转换和缩放功能 |
5-2 FFmpeg 日志系统
- include <libavutil/log.h> 第一步,引入头文件
- av_log_set_level(AV_LOG_DEBUG) 第二步,设置日志级别
- av_log(NULL, AV_LOG_INFO, " ...%s\n", op) 第三步,打印日志,第二个参数,日志级别,第三个参数日志模板字符串,
常用日志级别
- AV_LOG_ERROR
- AV_LOG_WARNING
- AV_LOG_INFO
- AV_LOG_DEBUG
实战:
#include <stdio.h>
#include <libavutil/log.h>
int main(int argc, char* argv[]) {
av_log_set_level(AV_LOG_DEBUG);
av_log(NULL, AV_LOG_DEBUG, "Hello world!%d\n", 2020);
return 0;
}
gcc -g -o ff_log ff_log.c -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -l avutil
或:
gcc -g -o ff_log ff_log.c `pkg-config --cflags --libs
// 需要事先配置环境变量
--cflags: 获取你所要指定库的地址目录
--libs:指定库的名字
5-3 FFmpeg文件的删除与重命名
FFmpeg文件与目录操作
文件的删除与重命名
- avpriv_io_delete()
- avpriv_io_move()
#include <libavutil/log.h>
#include <libavformat/avformat.h>
int main(int argc, char* argv[]) {
int res;
char* fileName = "./mytestfile.txt";
res = avpriv_io_move("test1.txt", "test2.txt");
if (res < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to rename\n");
return -1;
}
av_log(NULL, AV_LOG_INFO, "Success to rename\n");
// url
res = avpriv_io_delete(fileName);
if (res < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to delete file %s\n", fileName);
return -1;
}
av_log(NULL, AV_LOG_INFO, "Success to delete %s\n", fileName);
return 0;
}
gcc -g -o ff_file ff_file.c -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -l avutil -l avformat
5-4~5-5 FFmpeg操作目录及list的实现
操作目录的重要函数
- avio_open_dir(context, path, )
- avio_read_dir()
- avio_close_dir()
操作目录重要结构体
- AVIODirContext
操作目录的上下文 - AVIODirEntry
目录项。用于存放文件名、文件大小等信息
【实战】简单的ls命令
#include <libavutil/log.h>
#include <libavformat/avformat.h>
int main (int argc, char* argv[]) {
int res;
AVIODirContext *ctx = NULL;
AVIODirEntry *entry = NULL;
av_log_set_level(AV_LOG_INFO);
res = avio_open_dir(&ctx, "./", NULL);
if (res < 0) {
av_log(NULL, AV_LOG_ERROR, "Can't open dir:%s\n", av_err2str(res));
goto __fail;
}
while (1) {
res = avio_read_dir(ctx, &entry);
if (res < 0) {
av_log(NULL, AV_LOG_ERROR, "Can't read dir: %s\n", av_err2str(res));
goto __fail;
}
if (!entry) {
break;
}
av_log(NULL, AV_LOG_INFO, "%12"PRId64" %s \n", entry -> size, entry -> name);
avio_free_directory_entry(&entry);
}
__fail:
avio_close_dir(&ctx);
return 0;
}
gcc -g -o ff_dir_list ff_file_list.c -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -l avutil -l avformat
./ff_dir_list
注: PRld:是一种跨平台的书写方式,主要是为了通知支持32位和64位操作系统。PRld64表示64位整数,在32位系统中表示long long int,在64位系统中表示long int。相当于:
printf("%" "ld" "\n", value); // 64 bit OS
printf("%" "lld" "\n", value); // 64 bit OS
5-6 ffmpeg 处理流数据的基本概念
多媒体文件的基本概念
- 多媒体文件其实是个容器
- 在容器里有很多流(Stream/Track)
- 每种流是由不同的编码器编码的
- 从流中读出的数据称为包
- 在一个包中包含着一个或多个帧
几个重要的结构体
- AVFormatContext
- AVStream
- AVPacket
ffmpeg 操作流数据的基本步骤
解复用 --> 获取流 --> 读取数据包 --> 释放资源
5-7 ffmpeg 打印音视频Meta信息
【实战】打印音/视频信息
- av_register_all()
- avformat_open_input()/avformat_close_input()
- av_dump_format()
#include <libavutil/log.h>
#include <libavformat/avformat.h>
int main (int argc, char* argv[]) {
av_log_set_level(AV_LOG_INFO);
av_register_all();
int res;
AVFormatContext *fmt_ctx = NULL;
res = avformat_open_input(&fmt_ctx, "./test.mp4", NULL, NULL);
if (res < 0) {
av_log(NULL, AV_LOG_ERROR, "Can't open file: %s\n", av_err2str(res));
return -1;
}
av_dump_format(fmt_ctx, 0, "./test.mp4", 0);
avformat_close_input(&fmt_ctx);
return 0;
}
结果:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from './test.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf58.9.100
Duration: 00:05:26.13, bitrate: N/A
Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none, 1920x1080, 48 kb/s, 14.90 fps, 15360 tbn (default)
Metadata:
handler_name : VideoHandler
Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, 2 channels, 128 kb/s (default)
Metadata:
handler_name : SoundHandler
5-8 FFmpeg抽取音频数据
【实战】抽取音频数据
- av_init_packet()
- av_find_best_stream()
- av_read_frame() / av_packet_unref()
#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avformat.h>
int main (int argc, char* argv[]) {
av_log_set_level(AV_LOG_INFO);
av_register_all();
AVPacket pkt;
int audio_index;
// 1. read two params from console
char* src = NULL;
char* dst = NULL;
if (argc < 3) {
av_log(NULL, AV_LOG_ERROR, "the count of params should be more than 3!\n");
return -1;
}
src = argv[1];
dst = argv[2];
if (!src || !dst) {
av_log(NULL, AV_LOG_ERROR, "src or dst is null!\n");
return 1;
}
int res;
AVFormatContext *fmt_ctx = NULL;
res = avformat_open_input(&fmt_ctx, src, NULL, NULL);
if (res < 0) {
av_log(NULL, AV_LOG_ERROR, "Can't open file: %s\n", av_err2str(res));
avformat_close_input(&fmt_ctx);
return -1;
}
FILE* dst_fd = fopen(dst, "wb");
if (!dst_fd) {
av_log(NULL, AV_LOG_ERROR, "Can't open out file!\n");
return -1;
}
av_dump_format(fmt_ctx, 0, src, 0);
// 2. get stream
res = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (res < 0) {
av_log(NULL, AV_LOG_ERROR, "Can't find the best stream!\n");
avformat_close_input(&fmt_ctx);
fclose(dst_fd);
return -1;
}
audio_index = res;
av_init_packet(&pkt);
while(av_read_frame(fmt_ctx, &pkt) >= 0) {
if (pkt.stream_index == audio_index) {
int len = fwrite(pkt.data, 1, pkt.size, dst_fd);
if (len != pkt.size){
av_log(NULL, AV_LOG_WARNING, "warning, length of data is not equal size of pkt!\n");
}
}
av_packet_unref(&pkt);
}
// 3. write audio data to aac file
avformat_close_input(&fmt_ctx);
if (dst_fd) {
fclose(dst_fd);
}
return 0;
}
gcc -g -o ExtraAudio extraAudio.c -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -l avutil -l avformat -l avcodec
注:抽取出来的aac文件需要加adts头才能正常播放
5-11~5-13 FFmpeg抽取视频H264数据
- Start code
- SPS、PPS
- codec -> extradata
5-14~5-15 FFmpeg将MP4转成flv
【实战】将MP4转成flv
- 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()