基于RK3399OpenHarmony富设备软件音频解码方案 原创 精华

软通动力HOS
发布于 2022-3-15 16:54
浏览
10收藏

基于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

此数据结构存放解码前的位流数据。

`````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	   = 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输出 */
};

typedef   signed 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      = 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){
    ...
    //调用Mp3Decode
}

static 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;
    # endif
}

static 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学院

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-3-15 16:54:17修改
11
收藏 10
回复
举报
5条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

学习到了,感谢老师分享。

回复
2022-3-16 09:52:29
软通小精灵
软通小精灵

感谢大家支持

回复
2022-3-16 14:36:33
软通田可辉
软通田可辉

目前扬帆开发板有活动哦

回复
2022-3-18 09:05:37
芒果爱学习
芒果爱学习

支持支持

回复
2022-3-18 10:15:26
wx5ba093dcdfce0
wx5ba093dcdfce0

很棒👍🏻

回复
2022-3-18 10:56:48
回复
    相关推荐