【网络通信 -- 直播】FFMPEG 音频编码

【0】PCM 样本格式简介

PCM (Pulse Code Modulation,脉冲编码调制) 音频数据是未经压缩的音频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准数字音频数据;
描述 PCM 数据的 6 个参数

  • 1. Sample Rate : 采样频率;8kHz(电话)、44.1kHz(CD)、48kHz(DVD)
  • 2. Sample Size : 量化位数;通常该值为 16 bit
  • 3. Number of Channels : 通道个数。常见的音频有立体声(stereo)和单声道(mono)两种类型,立体声包含左声道和右声道;另外还有环绕立体声等其它不太常用的类型;
  • 4. Sign : 表示样本数据是否是有符号位,比如用一字节表示的样本数据,有符号的话表示范围为 -128 ~ 127,无符号是 0 ~ 255;有符号位 16bits 数据取值范围为 -32768~32767;
  • 5. Byte Ordering : 字节序,字节序是 little-endian 还是 big-endian;通常均为 little-endian;
  • 6. Integer Or Floating Point : 整形或浮点型;大多数格式的 PCM 样本数据使用整形表示,而在一些对精度要求高的应用方面,使用浮点类型表示 PCM 样本数据(浮点数 float 值域为 [-1.0, 1.0]

【0.1】FFMPEG 中 Packed 和 Planar 的 PCM 数据区别

FFmpeg 中音视频数据基本上都有 Packed 和 Planar 两种存储方式,对于双声道音频来说,Packed 方式为两个声道的数据交错存储;Planar 方式为两个声道分开存储;
假设一个 L/R 为一个采样点,数据存储的方式如下所示 :

  • Packed: L R L R L R L R ...
  • Planar: L L L L ... R R R R...

Packed 格式

1 AV_SAMPLE_FMT_U8, 	///< unsigned 8 bits
2 AV_SAMPLE_FMT_S16,	///< signed 16 bits
3 AV_SAMPLE_FMT_S32, 	///< signed 32 bits
4 AV_SAMPLE_FMT_FLT, 	///< float
5 AV_SAMPLE_FMT_DBL, 	///< double

仅仅保存在 AVFrame 的 uint8_t *data[0]; 音频格式 LRLRLR ...

Planar 格式

Planar 为 FFmpeg 内部存储音频使用的采样格式,所有的 Planar 格式后面都有字母 P 标识

1 AV_SAMPLE_FMT_U8P, 	///< unsigned 8 bits, planar
2 AV_SAMPLE_FMT_S16P, 	///< signed 16 bits, planar
3 AV_SAMPLE_FMT_S32P, 	///< signed 32 bits, planar
4 AV_SAMPLE_FMT_FLTP, 	///< float, planar
5 AV_SAMPLE_FMT_DBLP, 	///< double, planar
6 AV_SAMPLE_FMT_S64, 	///< signed 64 bits
7 AV_SAMPLE_FMT_S64P, 	///< signed 64 bits, planar

plane 0 : LLLLL...	对应 uint8_t *data[0]
plane 1 : RRRRR...	对应 uint8_t *data[1]

FFmpeg 音频解码后和编码前的数据是存放在 AVFrame 结构中的;

  • Packed 格式,frame.data[0] 或 frame.extended_data[0] 包含所有的音频数据
  • Planar 格式,frame.data[i] 或 frame.extended_data[i] 表示第 i 个声道的数据(假设声道 0 是第一个), AVFrame.data 数组大小固定为 8,如果声道数超过 8,需要从 frame.extended_data 获取声道数据

【1】FFMPEG 编码 PCM 为 AAC 的一般流程

Android FFmpng编码音频 ffmpeg音频编码参数_Android FFmpng编码音频

关键函数

  • avcodec_find_encoder : 根据指定的 AVCodecID 查找注册的编码器
  • avcodec_alloc_context3 : 为 AVCodecContext 分配内存
  • avcodec_open2 : 打开编码器
  • avcodec_send_frame : 将 AVFrame 非压缩数据给编码器
  • avcodec_receive_packet : 获取到编码后的 AVPacket 数据,收到的 packet 需要手动释放内存
  • av_frame_get_buffer : 为音频或视频帧分配新的 buffer,在调用这个函数之前,必须在 AVFame 上设置好以下属性:
  • format(视频为像素格式,音频为样本格式)
  • nb_samples(样本个数,针对音频)
  • channel_layout(通道类型,针对音频)
  • width/height(宽高,针对视频)
  • av_frame_make_writable : 确保 AVFrame 是可写的
  • 使用 av_frame_make_writable() 的问题是,在最坏的情况下,它会在您使用 encode 再次更改整个输入 frame 之前复制它;
  • 如果 frame 不可写,av_frame_make_writable() 将分配新的缓冲区,并复制这个输入 input frame 数据,避免和编码器需要缓存该帧时造成冲突;
  • av_samples_fill_arrays 填充音频帧