【网络通信 -- 直播】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 的一般流程
关键函数
- 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 填充音频帧