7

基于RK3399OpenHarmony富设备软件音频解码方案

 2 years ago
source link: https://os.51cto.com/article/704201.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

043940c75fcc36fcec19238ebe90509739e331.png

一、音频编解码原理

数字音频是由 PCM(Pulse Code Modulation,脉冲编码调制)技术将模拟信号,主要经过抽样、量化、编码三个处理过程产生的,其中的编码就是按照一定的格式记录采样和量化后的数字数据,比如顺序存储或压缩存储。不经过编码的源音频数据量太大,所以编码最主要的工作就是压缩,即压缩掉冗余信号(指不能被人耳感知到的信号)。播放数字音频时需要进行解码,简单地说解码就是对应不同格式编码的逆向处理过程。音频解码分为硬件解码与软件解码2种方式:

硬件解码是通过声卡等设备专用的DSP芯片解码,功耗更低,解码质量、效率更高。

软件解码就是通过特定的软件解码,即使用CPU解码,由于要妥协解码设备的通用性,所以算法上对效率、质量有所折扣。

因当前基于扬帆的主板中没有相应的DSP芯片,我们将采用软件解码方式。

1、音频编码格式介绍

1.1.WAV(Waveform Audio File Format)

WAV是一款最接近无损的音频文件编码格式。由于WAV内部编码即PCM,并未对文件进行压缩,所以文件大小相对也比较大,理论上该文件格式可以在各种播放平台顺利编解码。

WAV编码就是在PCM数据格式的前面加上44字节,分别用来描述PCM的采样率、声道数、数据格式等信息。

特点:音质通透,支持软件广泛。

适用场合:多媒体开发的中间文件、保存音乐和音效素材。

1.2.MP3( Moving Picture Experts Group Audio Layer Ⅲ)

MP3是目前最流行的有损压缩音频编码格式。它设计用来大幅度地降低音频数据量,将音乐以1:10甚至1:12的压缩率,压缩成容量较小的文件。MP3文件大体分为三部分:TAG_V2(ID3V2)、音频数据、TAG_V1(ID3V1)。

特点:音质在128Kbit/s以上表现均衡,压缩比高,支持大量软件和硬件,兼容性好。

适用场合:高比特率下对兼容性有要求的音乐欣赏。

1.3.AAC(Advanced Audio Coding)

AAC是新一代的音频有损压缩技术,也是一种专为声音数据设计的文件压缩格式。与MP3不同,它采用了全新的算法进行编码,更加高效,具有更高的性价比。

特点:相对于MP3,AAC格式的音质更佳,文件更小。

适用场合:128Kbit/s以下的音频编码,多用于视频中音频轨的编码。

1.4.Ogg(OggVorbis)

Ogg是一种完全免费的且非常有潜力的音频多通道有损压缩编码技术。Ogg有着非常出色的算法,可以用更小的码率达到更好的音质。

特点:可以用比MP3更小的码率实现比MP3更好的音质,高中低码率下均有良好的表现,兼容性不够好,流媒体特性不支持。

适用场合:语音聊天的音频消息场景。

2、MP3解码流程

同步及差错检查包括了头解码模块,在主控模块开始运行后,主控模块将比特流的数据缓冲区交给同步及差错检查模块,此模块包含两个功能,即头信息解码及帧边信息解码,根据它们的信息进行尺度因子解码及哈夫曼解码,得出的结果经过逆量化,立体声解码,混淆缩减,IMDCT,频率反转,合成多相滤波这几个模块之后,得出左右声道的PCM码流,再由主控模块将其放入输出缓冲区输出到声音播放设备。

MP3解码分同步方式和异步方式两种,所谓同步方式是指解码函数在解码完一帧后才返回并带回出错信息,异步方式是指解码函数在调用后立即返回,通过消息传递解码状态信息。

二、MP3软件解码数据结构与算法

1、MP3软件解码数据结构分析

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     /* 块类型不兼容 */
}

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。

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等)。

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、MP3软件解码功能函数分析

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.解码器初始化(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.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.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.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.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

3、HAL层实现软件解码设计

将解码代码放在drivers/audio/hal/decoder目录中,构建为audio_decode.so。在播放流程(audio_render)中的调用解码函数(同步方式),循环传入MP3流数据,输出PCM流(每次16K数据)。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

835deac15140f6314de278785ab1409433bd04.jpg


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK