ADPCM编解码--FFmpeg源码移植ARM

前言

最近需要在ARM上做ADPCM音频编解码,遂查阅大量资料学习了相关的概念,最终对FFmpeg中MS ADPCM编解码进行移植,本文记录学习过程中所做的笔记。

一、概念

1. PCM概念

对一段音频模拟信号进行采样,横轴为时间,纵轴为信号强度,每秒采样8000个点,就表示PCM的采样频率为8kHz,每个采样点的值用16位的变量存储,表示PCM的位深等于16。最后的将采集的N个点的值写入文件流,就生成了PCM数据流。音频模拟信号采样量化编码如下图所示:
在这里插入图片描述

2. ADPCM编码思想

PCM数据流本质上是由一连串的二进制数据填充而成,在对PCM原始数据流编码时,每次取PCM数据流的16bit数据为一个PCM采样点,首先计算PCM相邻两点之间的差值,然后将该差值乘以编码算法的系数,使结果存在于-8到7区间,也就可以通过4bit数据来存储一个16bit的PCM数据。因此,ADPCM编码可以将PCM文件的大小压缩到1/4。但编码前的PCM数据块大小与编码后的ADPCM数据块大小并不是严格的4:1,因为每个ADPCM数据块除了压缩数据外,还有一个固定7字节长度的head头信息下文会对头信息数据结构进行解析。
在这里插入图片描述

二、MS ADPCM编解码

1. FFmpeg源码中的ADPCM编解码逻辑

(1) FFmpeg源码中ADPCM文件结构

adpcm.c :主要是ADPCM解码代码
adpcmemc.c :主要是ADPCM编码代码
adpcm.h :部分头文件

(2) FFmpeg源码中ADPCM编解码主要函数

在对FFmpeg源码移植时,实现以下几个编解码函数的逻辑,即可实现ADPCM编解码的基本功能,如果需要进行量化编码优化,则需关注adpcm_compress_trellis()函数,通过avctx->trellis变量可以设置优化等级。

adpcmenc.c中主要编码函数

adpcm_encode_init():编码初始化函数,初始化部分编码参数
adpcm_encode_frame():编码逻辑实现,内部通过switch case选择不同的编码算法
adpcm_encode_close():编码资源释放

adpcm.c中主要解码函数

adpcm_decode_init():解码初始化函数,初始化部分解码参数
adpcm_decode_frame():解码逻辑实现,内部通过switch case选择不同的解码算法
adpcm_decode_close():解码资源释放

2. PCM数据块与ADPCM数据块对应关系

【本文仅以MS ADPCM编解码算法举例,其他算法的数据结构与MS ADPCM算法的数据结构不同】

FFmpeg源码在读取PCM数据流时,一次性读取2^N字节的原始PCM数据,然后将该段PCM数据块编码压缩成ADPCM数据块,编码前PCM数据块的大小与编码后ADPCM数据块的大小关系计算公式如下:

pcm_data_size = ( (adpcm_data_size - 7)*4 + 4 )
pcm_data_size 表示PCM数据块大小,adpcm_data_size 表示ADPCM数据块大小

【举例如下】
一次读取2048字节的PCM数据流,通过MS ADPCM编码可以生成518字节的ADPCM数据块。一帧518字节ADPCM数据块由7字节HEAD头信息与511字节的压缩ADPCM_DATA数据组成。

2048字节的PCM数据块与518字节的ADPCM数据块的数据结构对应关系如下所示:
PCM与ADPCM数据块对应数据结构关系

  • 7字节的HEAD头信息包含3字节固定头与4字节PCM原始数据。
  • 511字节的ADPCM_DATA数据,是由MS ADPCM编码算法按4:1比例将PCM_DATA1的2044字节数据压缩而成。

3. ADPCM头信息数据结构

在MS ADPCM编解码算法中,不论ADPCM数据块大小为多少,ADPCM头信息固定为7字节,头信息数据结构如下:

uint8_t header[7] = {0};
header[0] = 0;
header[1] = 16;		// 16&0xFF
header[2] = 0;		// 16&0xFF00;
// 取pcm原始数据,按小端字节序写入
header[3] = samples[1] & 0xFF;	//samples为short int型PCM原始数据,samples[1]表示PCM数据块中的第2个short型数据
header[4] = samples[1] & 0xFF00;
header[5] = samples[0] & 0xFF;
header[6] = samples[0] & 0xFF00;

三、FFmpeg源码中部分参数含义

1. block_size

block_size 定义了在编码或解码过程中,音频数据被分割成的每个数据块的大小。每个块包含音频数据及可能的一些附加信息(如预测器状态、系数等)。
在编码过程中,音频样本被组织成大小为 block_size 的块进行处理和压缩。编码器对每个块进行压缩,并将压缩后的数据写入输出流。
在解码过程中,解码器从输入流中读取大小为 block_size 的块,解码这些数据以恢复原始音频样本。
以Microsoft ADPCM为例,常见的 block_size 设置为 512 字节。
block_size指的是一帧PCM数据压缩成adpcm数据后,一个adcpm数据块的字节数。

2. frame_size

frame_size指的是,压缩一个block_size大小的ADPCM数据块,需要frame_size个PCM样本,一个PCM样本为2字节。
因此,生成一个block_size字节的ADPCM数据块,需要frame_size * 2字节的PCM数据。
在MS ADPCM压缩编码算法中,frame_size的计算公式为:

avctx->frame_size = (s->block_size - 7 * channels) * 2 / channels + 2;

【举例】
当通道数channels=1,block_size=256时,frame_size==1000;表明生成一个256字节大小的ADPCM数据,需要读取1000字节的PCM数据,其中996字节的PCM数据按4:1压缩生成ADPCM数据块,4字节的原始PCM数据与3字节的标志数据共同组成7字节的ADPCM头信息。
因此,一帧256字节的ADPCM数据由7字节的头信息与249字节的ADPCM压缩数据组成。

四、WAV文件结构

1. PCM数据封装成WAV格式

其中WAV headers占44字节,可由通用软件解析播放。
wav headers数据结构如下:

typedef struct WAVE_HEADER{
    char    fccID[4];       //内容为""RIFF
    unsigned int dwSize;   //最后填写,WAVE格式音频的大小//unsigned long dwSize;
    char    fccType[4];     //内容为"WAVE"
}WAVE_HEADER;

typedef struct WAVE_FMT{
    char    fccID[4];          //内容为"fmt "
    unsigned int  dwSize;     //内容为WAVE_FMT占的字节数,为16
    unsigned short wFormatTag; //如果为PCM,改值为 1
    unsigned short wChannels;  //通道数,单通道=1,双通道=2
    unsigned int  dwSamplesPerSec;//采用频率
    unsigned int  dwAvgBytesPerSec;/* ==dwSamplesPerSec*wChannels*uiBitsPerSample/8 */
    unsigned short wBlockAlign;//==wChannels*uiBitsPerSample/8
    unsigned short uiBitsPerSample;//每个采样点的bit数,8bits=8, 16bits=16
}WAVE_FMT;

typedef struct WAVE_DATA{
    char    fccID[4];       //内容为"data"
    unsigned int dwSize;   //==NumSamples*wChannels*uiBitsPerSample/8
}WAVE_DATA;

2. ADPCM数据封装成WAV格式

其中WAV headers占124字节,也可由通用音频软件解析播放。
wav headers数据结构如下:

// 定义 WAV 文件头部结构
#pragma pack(push, 1)   // 开始禁用对齐
struct WAVHeader {
    // 12Byte
    char riff[4];                // "RIFF"
    uint32_t overall_size;       // 文件总大小 - 8 字节
    char wave[4];                // "WAVE"

    // 58Byte
    char fmt_chunk_marker[4];    // "fmt " (包括空格)
    uint32_t length_of_fmt;      // 格式子块的大小,16 for PCM. 50 for some ADPCM
    uint16_t format_Tag;        // 格式类型,1 for PCM, 0x11 for ADPCM
    uint16_t channels;           // 通道数
    uint32_t samplesPerSec;        // 采样率
    uint32_t avgBytesPerSec;           // 每秒字节数
    uint16_t block_align;        // 数据块对齐
    uint16_t bits_per_sample;    // 每个样本的位数
    uint16_t cbSize;            // 32
    uint32_t unknown[8];        // 0

    // 12Byte
    char    fact_chunk_marker[4];  // "fact"
    uint32_t length_of_fact;     // sample count大小
    uint32_t samplesCount;       // 样本数 480006,PCM原始文件除以2

    // 34Byte
    char    tag_list_marker[4];  // "LIST"
    uint32_t length_of_tag;     // 26
    char    tag_real_name[4];   // "INFO"
    char    encode_chunk_marker[4]; // "ISFT"
    uint32_t length_of_encode;  // 14
    char    encode_value[13];   // "Lavf61.5.101"
    char alignement;            // alignement

    // 8Byte
    char data_chunk_header[4];   // "data"
    uint32_t data_size;          // 数据大小
};
#pragma pack(pop)  // 恢复默认对齐

参考文章:

【1】 https://blog.youkuaiyun.com/littlezls/article/details/83501580
【2】 https://www.shaoguoji.cn/2019/08/04/hardcore-audio-2/
【3】 https://www.shaoguoji.cn/2019/08/05/hardcore-audio-1/
【4】 https://www.cnblogs.com/jinggege-0224/p/12744654.html
【5】 https://www.cnblogs.com/hwl1023/p/5580813.html

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值