络达开发-音频流处理-AAC/loopbackTest为例

音频流主要是在DSP中进行处理,此处以aac_dec_interfaces.c和audio_loopback_test_interface.c两个源码文件来实例来分析介绍。两文件的路径分别是:

bta_sdk\dsp\middleware\MTK\dspalg\aac_decoder\src
bta_sdk\dsp\middleware\MTK\dspalg\audio_loopback_test\src

其中aac_dec_interface.c在另一个位置也有:bta_sdk\dsp\middleware\third_party\dspalg\aac_decoder\src

从路径可以看出这是第三方ACC编码函数。

1、aac_dec_interfaces.c[MTK\dspalg\aac_decoder\src]

 函数的功能把输入的音频数据经AAC编码后发送到左右两个输出声道;若AAC处理模块尚未初始化,则先初始化然后接着处理数据;文件中接口函数为:

bool stream_codec_decoder_aac_process (void *para);

函数的流程如下图所示:

 

AAC编码函数为:

int AIR_AAC_decoder(void *AAC_INSTANCE, uint8_t *input_buf, int aac_packet_len , int32_t *output_L, int32_t *output_R, int *decoded_len);

参数AAC_INSTANCE用于存储AAC功能模块初始化的实例对像,其值由AAC初始化函数的第一个参数返回:

int AIR_AAC_init(void *AAC_INSTANCE, uint8_t *input_buf, int aac_packet_len , int16_t *ch, uint32_t *srate, int16_t *brate);

参数aac_packet_len为input_buf待处理音频流的长度;

参数input_buf为输入的音频数据,来自去蓝牙通道,比如从手机传过来的声音流;

参数output_L为指向左声道输出的音频流buffer;

参数output_R为指向右声道输出的音频流buffer;

参数decoded_len表示AAC编码实际处理的数据长度;

从中可以知道,音频流的来源为第1 个输入通道,流向了第1和第2个出通道,其接口函数分别为:

void* stream_codec_get_1st_input_buffer(void* para)
void* stream_codec_get_1st_output_buffer(void* para)
void* stream_codec_get_2nd_output_buffer(void* para)

接口函数的位置为:

bta_sdk\dsp\middleware\MTK\dspfw\port\chip\ab156x\src\dsp_lower_layer\dsp_interface\ dsp_feature_interface.c

分析代码可以知道,输出通道一共有6个,由下面宏定义:

#define CALLBACK_OUTPUT_PORT_MAX_NUM  6

输入通道共有5个,由下面宏定义:

#define CALLBACK_INPUT_PORT_MAX_NUM   5

其中通用的获取指定通道的缓存区地址的函数为:

void* stream_codec_get_input_buffer(void* para, uint32_t channel)
void* stream_codec_get_output_buffer(void* para, uint32_t channel)

下面函数,可以用来获取输入数据流的长度:

uint16_t stream_codec_get_input_size(void* para)

另外还有获取输出通道的数据宽度,为是16位的还是32位的数据格式:

uint8_t stream_codec_get_output_resolution(void* para)

通过这些函数来获取对应通道数据的缓存首地址,然后再获取数据的长度,这样就可以对这些数据进行操作了。

2、audio_loopback_test_interface.c[bta_sdk\dsp\middleware\MTK\dspalg\audio_loopback_test\src]

 

 

 

函数调用流程:

 

3、输入输出缓存区的定义

 那么,另一个问题,下面的这些接口所获取的存储取的是如何定义的,即第1个inout buffer,第1个input buffer指向的buffer是哪里,谁会往这个buffer里放数据呢?

stream_function_get_1st_inout_buffer
stream_function_get_2nd_inout_buffer
void* stream_codec_get_1st_input_buffer(void* para)
void* stream_codec_get_1st_output_buffer(void* para)
void* stream_codec_get_2nd_output_buffer(void* para)

通过代码可以知道,存储区的首地址均由下面结构体中的out_ptr[]和in_ptr[]所定义:

typedef struct DSP_Entry_Para_s {
    VOID*                   mem_ptr;

    U16                     in_malloc_size;
    U16                     in_size;
    VOID*                   in_ptr[CALLBACK_INPUT_PORT_MAX_NUM];

    U16                     out_malloc_size;
    U16                     codec_out_size;
    VOID*                   out_ptr[CALLBACK_OUTPUT_PORT_MAX_NUM];

    U8                      in_channel_num;
    U8                      in_sampling_rate;
    U8                      out_channel_num;
    U8                      device_out_channel_num;
    U8                      software_handled_channel_num;
    U8                      codec_out_sampling_rate;

    TaskHandle_t            DSPTask;

    U8                      with_encoder;
    U16                     encoder_out_size;

    U8                      with_src;
    U8                      src_out_sampling_rate;
    U16                     src_out_size;

    U16                     pre_codec_out_size;
    U8                      pre_codec_out_sampling_rate;
    U8                      skip_process;
    U8                      bypass_mode;
    bool                    force_resume;
    DSP_FEATURE_RESOLUTION  resolution;
    DSP_FEATURE_NUMBERING   number;
    DSP_PKT_INFO_STRU       pkt_info;
    #ifdef PRELOADER_ENABLE
    VOID*                   feature_ptr;
    #endif
} DSP_ENTRY_PARA, *DSP_ENTRY_PARA_PTR;

这两个指定的初始化,是在文件dsp_callback.c->DSP_Callback_ParaSetup(DSP_STREAMING_PARA_PTR)中完成,如下代码所示。

 

if (stream->callback.FeatureTablePtr->FeatureType != CODEC_PCM_COPY) {
        /* malloc in_ptr */
        configASSERT(stream->callback.EntryPara.in_channel_num<=CALLBACK_INPUT_PORT_MAX_NUM);
        mallocSize = stream->callback.EntryPara.in_malloc_size*stream->callback.EntryPara.in_channel_num;
        mem_ptr = DSPMEM_tmalloc(stream->callback.EntryPara.DSPTask, mallocSize, stream);
        memset(mem_ptr, 0, mallocSize);
        for (i=0 ; i<stream->callback.EntryPara.in_channel_num ; i++)
        {
            stream->callback.EntryPara.in_ptr[i] = mem_ptr;
            mem_ptr += stream->callback.EntryPara.in_malloc_size;
        }
        /* malloc out_ptr */
        chNum = MAX(stream->callback.EntryPara.out_channel_num, 2);
        configASSERT(chNum<=CALLBACK_OUTPUT_PORT_MAX_NUM);
        mallocSize = stream->callback.EntryPara.out_malloc_size*chNum;
        mem_ptr = DSPMEM_tmalloc(stream->callback.EntryPara.DSPTask, mallocSize, stream);
        memset(mem_ptr, 0, mallocSize);
        for (i=0 ; i<chNum ; i++)
        {
            stream->callback.EntryPara.out_ptr[i]= mem_ptr;
            mem_ptr += stream->callback.EntryPara.out_malloc_size;
        }
     } else {
        //CODEC_PCM_COPY
        chNum = MAX(MAX(stream->callback.EntryPara.out_channel_num, 1), stream->callback.EntryPara.in_channel_num);
        configASSERT(chNum<=MIN(CALLBACK_INPUT_PORT_MAX_NUM, CALLBACK_OUTPUT_PORT_MAX_NUM));
        frameSize = MAX(stream->callback.EntryPara.in_malloc_size, stream->callback.EntryPara.out_malloc_size);
        stream->callback.EntryPara.in_malloc_size = stream->callback.EntryPara.out_malloc_size = frameSize;
        stream->callback.EntryPara.out_channel_num = chNum;

        mallocSize = frameSize*chNum;
        if ((frameSize & 3) && (chNum > 1))
        {
            DSP_MW_LOG_I("[DSP] Unaligned Callback Frame Size:%d!!", 1, frameSize);
        }
        mem_ptr = DSPMEM_tmalloc(stream->callback.EntryPara.DSPTask, mallocSize, stream);
        memset(mem_ptr, 0, mallocSize);
        for (i=0 ; i<chNum ; i++)
        {
            stream->callback.EntryPara.out_ptr[i]= mem_ptr;
            stream->callback.EntryPara.in_ptr[i] = mem_ptr;
            mem_ptr += frameSize;
        }
        DSP_MW_LOG_I("[DSP] Callback stream Setup codec is CODEC_PCM_COPY, Frame Size:%d, channel_num:%d", 2, frameSize, chNum);
    }

函数stream_codec_decoder_aac_process(…)在文件dsp_sdk.c中被索引到数据stream_feature_table[]中,如下图所示:

 

那么通过分析,可以知道CODEC_DECODER_AAC的这个类型,是在A2DP中被使用,并且是在解码的时候使用,因为可以猜出,AAC就是服务于A2DP过来的音频进行解码,然后通过Speaker播放出来。

 

因此就不难理解,

bool stream_codec_decoder_aac_process (void *para)

函数中的stream_codec_get_1st_input_buffer 就是指的A2DP的Source过来的音频流,而stream_codec_get_1st_output_buffer

stream_codec_get_2nd_output_buffer

则是左右Speaker的输出数据流,流向左右Speaker两个通道;

  1. 对于A2DP, in_channel_num=1,定义如下图所示:
  2. 对于A2DP, out_channel_num=2,如下图所示:

 如红线所标识,out_channel_num为2或者audio.channel_num(至于channel_num的值是多少,暂未考究,猜测应该为1或者2)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值