一箫一剑平生意,负尽狂名十五年。这篇文章主要讲述基于RK3399OpenHarmony富设备软件音频解码方案相关的知识,希望能为你提供帮助。
文章图片
1.音频编解码原理数字音频是由 PCM(Pulse Code Modulation,脉冲编码调制)技术将模拟信号,主要经过抽样、量化、编码三个处理过程产生的,其中的编码就是按照一定的格式记录采样和量化后的数字数据,比如顺序存储或压缩存储。不经过编码的源音频数据量太大,所以编码最主要的工作就是压缩,即压缩掉冗余信号(指不能被人耳感知到的信号)。播放数字音频时需要进行解码,简单地说解码就是对应不同格式编码的逆向处理过程。音频解码分为硬件解码与软件解码2种方式:
硬件解码是通过声卡等设备专用的DSP芯片解码,功耗更低,解码质量、效率更高。
软件解码就是通过特定的软件解码,即使用CPU解码,由于要妥协解码设备的通用性,所以算法上对效率、质量有所折扣。
因当前基于扬帆的主板中没有相应的DSP芯片,我们将采用软件解码方式。
1.1.音频编码格式介绍 1.1.1.WAV(Waveform Audio File Format)
WAV是一款最接近无损的音频文件编码格式。由于WAV内部编码即PCM,并未对文件进行压缩,所以文件大小相对也比较大,理论上该文件格式可以在各种播放平台顺利编解码。
WAV编码就是在PCM数据格式的前面加上44字节,分别用来描述PCM的采样率、声道数、数据格式等信息。
特点:音质通透,支持软件广泛。
适用场合:多媒体开发的中间文件、保存音乐和音效素材。
1.1.2.MP3( Moving Picture Experts Group Audio Layer Ⅲ)
MP3是目前最流行的有损压缩音频编码格式。它设计用来大幅度地降低音频数据量,将音乐以1:10甚至1:12的压缩率,压缩成容量较小的文件。MP3文件大体分为三部分:TAG_V2(ID3V2)、音频数据、TAG_V1(ID3V1)。
特点:音质在128Kbit/s以上表现均衡,压缩比高,支持大量软件和硬件,兼容性好。
适用场合:高比特率下对兼容性有要求的音乐欣赏。
1.1.3.AAC(Advanced Audio Coding)
AAC是新一代的音频有损压缩技术,也是一种专为声音数据设计的文件压缩格式。与MP3不同,它采用了全新的算法进行编码,更加高效,具有更高的性价比。
特点:相对于MP3,AAC格式的音质更佳,文件更小。
适用场合:128Kbit/s以下的音频编码,多用于视频中音频轨的编码。
1.1.4.Ogg(OggVorbis)
Ogg是一种完全免费的且非常有潜力的音频多通道有损压缩编码技术。Ogg有着非常出色的算法,可以用更小的码率达到更好的音质。
特点:可以用比MP3更小的码率实现比MP3更好的音质,高中低码率下均有良好的表现,兼容性不够好,流媒体特性不支持。
适用场合:语音聊天的音频消息场景。
1.2.MP3解码流程同步及差错检查包括了头解码模块,在主控模块开始运行后,主控模块将比特流的数据缓冲区交给同步及差错检查模块,此模块包含两个功能,即头信息解码及帧边信息解码,根据它们的信息进行尺度因子解码及哈夫曼解码,得出的结果经过逆量化,立体声解码,混淆缩减,IMDCT,频率反转,合成多相滤波这几个模块之后,得出左右声道的PCM码流,再由主控模块将其放入输出缓冲区输出到声音播放设备。
MP3解码分同步方式和异步方式两种,所谓同步方式是指解码函数在解码完一帧后才返回并带回出错信息,异步方式是指解码函数在调用后立即返回,通过消息传递解码状态信息。
2.MP3软件解码数据结构与算法 2.1.MP3软件解码数据结构分析 2.1.1.struct UndecodeStream
【基于RK3399OpenHarmony富设备软件音频解码方案】此数据结构存放解码前的位流数据。
`````c
struct UndecodeStream
unsigned char const *inputBuf;
/* 输入位流缓冲区 */
unsigned char const *bufEnd;
/* 缓冲区结束 */
unsigned long skipLen;
/* 要在下一帧之前跳过的字节数 */int sync;
/* 流同步 */
unsigned long freeRate;
/* 自由比特率 */unsigned char const *currFrame;
/* 当前帧(的开始) */
unsigned char const *nextFrame;
/* 下一帧(的开始) */
struct BitPointer currPtr;
/* 当前处理位指针 */
struct BitPointer ancPtr;
/* 辅助位指针 */
unsigned int ancBitLen;
/* 辅助比特数 */int options;
/* 解码选项 */
enum AudioDeocdeErrCode error;
/* 错误码 */
;
struct BitPointer
unsigned char const *byte;
unsigned short cache;
unsigned short left;
;
enum AudioDeocdeErrCode
SUCCESS= 0x0000,/* no error */ERROR_BUFLEN= 0x0001,/* 输入缓冲区太小或EOF */
ERROR_BUFPTR= 0x0002,/* 无效的缓冲区指针 */ERROR_NOMEM= 0x0031,/* 没有足够的内存 */ERROR_LOSTSYNC= 0x0101,/* 失去同步 */
ERROR_BADLAYER= 0x0102,/* 保留头层值 */
ERROR_BADBITRATE= 0x0103,/* 禁止比特率值 */
ERROR_BADSAMPLERATE= 0x0104,/* 保留采样频率值 */
ERROR_BADEMPHASIS= 0x0105,/* 保留重要值 */ERROR_BADCRC= 0x0201,/* CRC检查失败 */
ERROR_BADBITALLOC= 0x0211,/* 禁止位分配值 */
ERROR_BADSCALEFACTOR = 0x0221,/* 错误的缩放因子索引 */
ERROR_BADMODE= 0x0222,/* 错误的比特率/模式组合 */
ERROR_BADFRAMELEN= 0x0231,/* 错误的帧长度 */
ERROR_BADBIGVALUES= 0x0232,/* 错误的大值计算 */
ERROR_BADBLOCKTYPE= 0x0233,/* 保留块类型 */
ERROR_BADSCFSI= 0x0234,/* 错误的缩放因子选择信息 */
ERROR_BADDATAPTR= 0x0235,/* 主数据开始指针错误 */
ERROR_BADPART3LEN= 0x0236,/* 音频数据长度错误 */
ERROR_BADHUFFTABLE= 0x0237,/* 哈夫曼表选择错误 */
ERROR_BADHUFFDATA= https://www.songbingjia.com/android/0x0238,/* 哈夫曼数据溢出 */
ERROR_BADSTEREO= 0x0239/* 块类型不兼容 */
2.1.2.struct SynthStream
此数据结构存放解码合成滤波后的PCM数据。
```c
struct SynthStream
fixed_t filter[2][2][2][16][8];
/* 多相滤波器组输出 [ch][eo][peo][s][v] */
unsigned int phase;
/* 当前处理阶段 */
struct PcmStream pcm;
/* PCM输出 */
;
typedefsigned long fixed_t;
struct PcmStream
unsigned int samplerate;
/* 采样频率(Hz) */
unsigned short channels;
/* 通道数 */
unsigned short length;
/* 每个通道的采样数 */
fixed_t samples[2][1152];
/* PCM输出样本[ch][样本] */
;
PcmStream定义了音频的采样率、声道个数和PCM 采样数据, 用这里面的信息来初始化音频设备。以帧(frame)为单位对MP3进行解码的,当正确的解码完一帧数据可以得到(每声道)1152个PCM 数据。一帧数据量可以用下面的公式来计算:
frameSize = (((mpegVersion == MPEG1 ? 144 : 72) bitRate) / samplingRate) + paddingBit
例如: bitRate = 128000, a samplingRate =44100, andpaddingBit = 1
frameSize = (144 128000) / 44100 + 1 = 417 bytes
也就是说,想解码一个比特率为128K,采样率为44.1K 的MP3 文件,最少一次读入内存417 bytes 以准备解码,通常需要读入的字节数要比一帧的数据量多一些,比如16K。
2.1.3.struct FrameDecodeStream
此数据结构存放MPEG帧解码后PCM 数据。
struct FrameDecodeStream
struct AudioHeader header;
/* MPEG音频报头 */
int options;
/* 解码选项(来此流) */
fixed_t sbsample[2][36][32];
/* 合成子带滤波器样本 */
fixed_t (*overlap)[2][32][18];
/* MP3(第三层)块重叠数据 */
;
struct AudioHeader
enum AudioCodeLayer layer;
/* audio layer (1, 2, or 3) */
enum AudioChannelMode mode;
/* channel mode */
int modeExtension;
/* 附加模式信息 */
enum AudioCodeEmphasis emphasis;
/* 不增强使用(信号还原) */unsigned long bitRate;
/* 流比特率(bps) */
unsigned int sampleRate;
/* 采样频率(Hz) */unsigned short crcCount;
/* 帧CRC累加器 */
unsigned short crcTargetSum;
/* 最终目标CRC校验和 */int flags;
/* */
int privateBits;
/* 专用位 */timer_t duration;
/* 帧的音频播放时间 */
;
enum AudioCodeLayer
LAYER_I= 1,/* Layer I */
LAYER_II= 2,/* Layer II */
LAYER_III = 3/* Layer III */
;
enum AudioChannelMode
MODE_SINGLE_CHANNEL = 0,/* single channel */
MODE_DUAL_CHANNEL= 1,/* dual channel */
MODE_JOINT_STEREO= 2,/* 联合(MS/强度)立体声 */
MODE_STEREO= 3/* 正常LR立体声 */
;
enum AudioCodeEmphasis
EMPHASIS_NONE= 0,/* no emphasis */
EMPHASIS_50_15_US= 1,/* 50/15微秒增强 */
EMPHASIS_CCITT_J_17 = 3,/* CCITT J.17 emphasis */
EMPHASIS_RESERVED= 2/* unknown emphasis */
;
typedef struct
signed long seconds;
/* 整秒 */
unsigned long fraction;
/* 定时器/分辨率秒 */
timer_t;
在layer 域中得到音频数据所采的层,在mode域中得到音频数据的声道个数,在birRate和sampleRate中得到音频数据的位率(128kbps、384kbps 等等)和采样率(22KHz、44.1KHz、48KHz等)。
2.1.4.struct AudioDecoder
此数据结构存放音频解码器功能数据。
struct AudioDecoder
enum AudioDecoderMode mode;
int options;
struct/* 异步信息 */
long pid;
int in;
int out;
async;
struct/* 同步信息 */
struct UndecodeStream undecodestream;
struct FrameDecodeStream frameDecodeStream;
struct SynthStream synthStrem;
*sync;
void *cbData;
/* 压缩的数据 */enum AudioDecodeFlow (*InputFunc)(void *, struct UndecodeStream *);
enum AudioDecodeFlow (*HeaderFunc)(void *, struct AudioHeader const *);
enum AudioDecodeFlow (*FilterFunc)(void *,
struct UndecodeStream const *, struct FrameDecodeStream *);
enum AudioDecodeFlow (*OutputFunc)(void *,
struct AudioHeader const *, struct PcmStream *);
enum AudioDecodeFlow (*ErrorFunc)(void *, struct UndecodeStream *, struct FrameDecodeStream *);
enum AudioDecodeFlow (*MessageFunc)(void *, void *, unsigned int *);
;
enum AudioDecoderMode
DECODER_MODE_SYNC= 0,
DECODER_MODE_ASYNC
;
enum AudioDecodeFlow
FLOW_CONTINUE = 0x0000,/* 正常继续 */
FLOW_STOP= 0x0010,/* 正常停止解码 */
FLOW_BREAK= 0x0011,/* 停止解码并发出错误信号 */
FLOW_IGNORE= 0x0020/* 忽略当前帧 */
;
2.2.MP3软件解码功能函数分析 2.2.1.MP3解码(Mp3Decode)
enum AudioDeocdeErrCode Mp3Decode(struct UndecodeStream *undecodeStream, struct FrameDecodeStream *frameDecodeStream, unsigned int channel)
//同步差错检查
...
for (ch = 0;
ch <
channel;
++ch)
//缩放因子解码
...
//哈夫曼解码
...
//逆向量化处理
...
//重新排序
...
//立体声解码
...
//混淆缩减处理
...
//IDCT处理
...
//频率反转
...
//合成多相滤波处理
......
2.2.2.解码器初始化(DecoderInit)
int32_t DecoderInit(struct AudioDecoder *decoder, void *data,
enum AudioDecodeFlow (*InputFunc)(void *, struct UndecodeStream *),
enum AudioDecodeFlow (*HeaderFunc)(void *, struct AudioHeader const *),
enum AudioDecodeFlow (*FilterFunc)(void *,
struct UndecodeStream const *, struct FrameDecodeStream *),
enum AudioDecodeFlow (*OutputFunc)(void *,
struct AudioHeader const *, struct PcmStream *),
enum AudioDecodeFlow (*ErrorFunc)(void *, struct UndecodeStream *, struct FrameDecodeStream *),
enum AudioDecodeFlow (*MessageFunc)(void *, void *, unsigned int *)decoder->
mode= -1;
decoder->
options= 0;
decoder->
async.pid= 0;
decoder->
async.in= -1;
decoder->
async.out= -1;
decoder->
sync= 0;
decoder->
cb_data= https://www.songbingjia.com/android/data;
decoder->
InputFunc= InputFunc;
decoder->
HeaderFunc = HeaderFunc;
decoder->
FilterFunc= FilterFunc;
decoder->
OutputFunc= OutputFunc;
decoder->
ErrorFunc= ErrorFunc;
decoder->
MessageFunc = MessageFunc;
...
2.2.3.解码器运行(DecoderRun)
int32_t DecoderRun(struct AudioDecoder *decoder, enum AudioDecoderMode mode)int result;
int (*run)(struct AudioDecoder *) = 0;
switch (decoder->
mode = mode)
case DECODER_MODE_SYNC:
run = SyncDecoding;
// 同步解码
break;
case DECODER_MODE_ASYNC:
# if defined(USE_ASYNC)
run = AsyncDecoding;
// 异步解码
# endif
break;
...result = run(decoder);
// 执行解码...return result;
static int32_t SyncDecoding(struct AudioDecoder *decoder)
...
//调用Mp3Decodestatic int32_t AsyncDecoding(struct AudioDecoder *decoder)
...
//调用Mp3Decode
2.2.4.解码器结束处理(DecoderFinish)
int32_t DecoderFinish(struct AudioDecoder *decoder)# if defined(USE_ASYNC)
if (decoder->
mode == DECODER_MODE_ASYNC &
&
decoder->
async.pid)
pid_t pid;
int status;
close(decoder->
async.in);
...close(decoder->
async.out);
...
decoder->
async.pid = 0;
decoder->
async.in= -1;
decoder->
async.out = -1;
...# endif
...
return 0;
2.2.5.解码器消息处理(DecoderMsg)
/*
* NAME:decoder->
message()
* DESCRIPTION: send a message to and receive a reply from the decoder process
*/
int32_t DecoderMsg(struct AudioDecoder *decoder, void *message, unsigned int *len)...
# if defined(USE_ASYNC)
if (decoder->
mode != DECODER_MODE_ASYNC ||
send(decoder->
async.out, message, *len) != FLOW_CONTINUE ||
receive(decoder->
async.in, &
message, len) != FLOW_CONTINUE)
return -1;
...
return 0;
# else
return -1;
# endifstatic enum AudioDecodeFlow send(int fd, void const *message, unsigned int size)...static enum AudioDecodeFlow receive(int fd, void **message, unsigned int *size)...
2.2.6.解码器功能操作函数集(DecoderOps)
该组函数在初始化解码器时自定义、实现与传入。
```c
static enum AudioDecodeFlow input(void *data, struct UndecodeStream *stream)struct buffer *buffer = data;
if (!buffer->
length)
return FLOW_STOP;
...buffer->
length = 0;
return FLOW_CONTINUE;
struct buffer
unsigned char const *start;
unsigned long length;
;
- HeaderFunc
- FilterFunc
- OutputFunc
static enum AudioDecodeFlow output(void *data, struct AudioHeader const *header,
struct PcmStream *pcm)unsigned int nchannels, nsamples;
fixed_t const *leftChannel, *rightChannel;
/* pcm->
samplerate contains the sampling frequency */
nchannels = pcm->
channels;
nsamples= pcm->
length;
leftChannel= pcm->
samples[0];
rightChannel= pcm->
samples[1];
...return FLOW_CONTINUE;
- ErrorFunc
- MessageFunc```c
2.3.HAL层实现软件解码设计将解码代码放在drivers/audio/hal/decoder目录中,构建为audio_decode.so。在播放流程(audio_render)中的调用解码函数(同步方式),循环传入MP3流数据,输出PCM流(每次16K数据)。
更多原创内容请关注:软通动力HarmonyOS学院想了解更多关于鸿蒙的内容,请访问:
51CTO和华为官方合作共建的鸿蒙技术社区
https://ost.51cto.com/#bkwz
::: hljs-center
文章图片
:::
推荐阅读
- 如何使用 .NET Core 安全地加/解密文件#yyds干货盘点#
- QT读取剪切板内容-实现复制粘贴文本和图片
- DVC - 数据版本控制入门详解
- 字节跳动流式数据集成基于Flink Checkpoint两阶段提交的实践和优化
- #yyds干货盘点# Map - LinkedHashSet&Map源码解析
- 一文了解 Python 中的对象析构函数(__del__)
- 阿里巴巴开源大规模稀疏模型训练/预测引擎DeepRec
- #yyds干货盘点# ant design中 table的表格行的拖拽实现
- #yyds干货盘点#运算溢出,负负得正