FFmpeg音视频核心技术精讲与实战

常见问题

第一章 课程导学与准备工作

1-1 课程导学

1-2 音视频的应用范围与播放器架构讲解

未来+高薪

  • 音视频具有更加广阔的未来
  • 优秀音视频技术人才奇缺
  • 自学成本高

课程安排

  • FFmpeg 常用命令

    1. 视频录制命令
    2. 多媒体文件的分解、复用命令
    3. 裁剪与合并命令
    4. 图片、视频互转命令
    5. 直播相关命令
    6. 各种滤镜命令
  • FFmpeg基本开发

    1. C语言回顾
    2. FFmpeg核心概念与常用结构体
    3. 实战-多媒体文件的分解与复用
    4. 实战-多媒体格式的互转
    5. 实战-从MP4剪一段视频
    6. 作业-实现一个简单的小咖秀
  • 音视频编解码实战

    1. 实战-H264解码
    2. 实战-H264编码
    3. 实战-音频AAC解码
    4. 实战-音频AAC编码
    5. 实战-视频转图片
  • 音视频渲染实战

    1. SDL事件处理
    2. SDL视频纹理渲染
    3. SDL音频渲染
    4. 实战1-实现YUV视频播放器
    5. 实战2-YUV视频倍速播放
    6. 实战3-实现PCM播放器
  • FFmpeg开发播放器核心功能

    1. 实战-实现MP4文件的视频播放
    2. 实战-实现MP4文件的音频播放
    3. 实战-实现一个初级播放器
    4. 实战-音视频同步
    5. 实战-实现播放器内核
  • Android Q中实战FFmpeg

    1. 编译Android端可以使用的FFmpeg
    2. Java与C语言相互调用
    3. 实战-Android调用FFmpeg

适合人群

  • 从事音视频相关工作,想提高技能的人
  • 想转行到音视频行业的人
  • 刚刚毕业或即将毕业还没有确定未来方向的人

学习建议

  • 牢牢抓住音视频的处理机制,了解其本质
  • 勤加练习,熟能生巧
  • 带着问题去学习,事半功倍

1-3 音视频的应用范围与播放器架构讲解

音视频的广泛应用

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

播放器架构

播放器架构

渲染流程

2019_8_16_15_40_52.png

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

x265

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命令分类

  1. 基本信息查询命令
  2. 录制命令
  3. 分解、复用命令
  4. 处理原始数据命令
  5. 裁剪与合并命令
  6. 图片、视频互转命令
  7. 直播相关命令
  8. 各种滤镜命令

2-2 FFmpeg处理流程

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复制

image.png

抽取视频数据:
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()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259