海思3519 Audio Sample例程

Audio Sample例程

在audio中存放音频相关的例程。
在这里插入图片描述

在解析源码前最好先了解《HiMPP V4.0 媒体处理软件开发参考》中的音频部分。

一如既往先看该例程的功能,可以明显看出例程演示了AI直接输出到AO或经过编码器再输出到AO,或从音频文件获取数据流解码并输出,也有音质增加等等。

HI_VOID SAMPLE_AUDIO_Usage(HI_VOID)
{
    printf("\n\n/Usage:./sample_audio <index>/\n");
    printf("\tindex and its function list below\n");
    printf("\t0:  start AI to AO loop\n");
    printf("\t1:  send audio frame to AENC channel from AI, save them\n");
    printf("\t2:  read audio stream from file, decode and send AO\n");
    printf("\t3:  start AI(VQE process), then send to AO\n");
    printf("\t4:  start AI to AO(Hdmi) loop\n");
    printf("\t5:  start AI to AO(Syschn) loop\n");
    printf("\t6:  start AI to Extern Resampler\n");
}

一、Main

main函数中初始操作还是一样的对参数的判断、定义好信号处理函数以及对MMP系统进行初始化。

/******************************************************************************
* function : main
******************************************************************************/
#ifdef __HuaweiLite__
HI_S32 app_main(int argc, char* argv[])
#else
HI_S32 main(int argc, char* argv[])
#endif
{
    HI_S32 s32Ret = HI_SUCCESS;
    VB_CONFIG_S stVbConf;
    HI_U32 u32Index = 0;

    if (2 != argc)
    {
        SAMPLE_AUDIO_Usage();
        return HI_FAILURE;
    }

    u32Index = atoi(argv[1]);

    if (u32Index > 6)
    {
        SAMPLE_AUDIO_Usage();
        return HI_FAILURE;
    }

    signal(SIGINT, SAMPLE_AUDIO_HandleSig);
    signal(SIGTERM, SAMPLE_AUDIO_HandleSig);

    memset(&stVbConf, 0, sizeof(VB_CONFIG_S));
    s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
    if (HI_SUCCESS != s32Ret)
    {
        printf("%s: system init failed with %d!\n", __FUNCTION__, s32Ret);
        return HI_FAILURE;
    }

Sample默认采用的是AAC编解码,所以需要对编码器进行初始化,Aac编解码器中注册相应的打开编解码器、执行编解码、获取编解码信息、关闭、复位编码器等函数指针,其实现是调用第三方aac源码库来实现的。

HI_MPI_AENC_AacInit();
HI_MPI_ADEC_AacInit();

接着就是各个音频示例功能的接口实现,下面逐个分析每个接口。

switch (u32Index)
{
    case 0:
    {
        SAMPLE_AUDIO_AiAo();
        break;
    }
    case 1:
    {
        SAMPLE_AUDIO_AiAenc();
        break;
    }
    case 2:
    {
        SAMPLE_AUDIO_AdecAo();
        break;
    }
    case 3:
    {
        SAMPLE_AUDIO_AiVqeProcessAo();
        break;
    }
    case 4:
    {
        SAMPLE_AUDIO_AiHdmiAo();
        break;
    }
    case 5:
    {
        SAMPLE_AUDIO_AiToAoSysChn();
        break;
    }
    case 6:
    {
        SAMPLE_AUDIO_AiToExtResample();
        break;
    }
    default:
    {
        break;
    }
}

二、SAMPLE_AUDIO_AiAo()

AiAo的配置要与audio codec一致才能正确的接收音频数据,具体的可以参考《HiMPP V4.0 媒体处理软件开发参考》手册,里面讲的很清楚。如果需要添加其它外部audio codec可以搜索HI_ACODEC_TYPE_TLV320AIC31宏将内容改为新的外部audio codec的就可以了,流程是通用的。

/******************************************************************************
* function : Ai -> Ao(with fade in/out and volume adjust)
******************************************************************************/
HI_S32 SAMPLE_AUDIO_AiAo(HI_VOID)
{
    HI_S32 s32Ret;
    HI_S32 s32AiChnCnt;
    HI_S32 s32AoChnCnt;
    AI_CHN      AiChn = 0;
    AO_CHN      AoChn = 0;
    AIO_ATTR_S stAioAttr;

#ifdef HI_ACODEC_TYPE_TLV320AIC31
    AUDIO_DEV   AiDev = SAMPLE_AUDIO_EXTERN_AI_DEV;
    AUDIO_DEV   AoDev = SAMPLE_AUDIO_EXTERN_AO_DEV;
    stAioAttr.enSamplerate   = AUDIO_SAMPLE_RATE_48000;
    stAioAttr.enBitwidth     = AUDIO_BIT_WIDTH_16;
    stAioAttr.enWorkmode     = AIO_MODE_I2S_MASTER;
    stAioAttr.enSoundmode    = AUDIO_SOUND_MODE_MONO;
    stAioAttr.u32EXFlag      = 0;
    stAioAttr.u32FrmNum      = 30;
    stAioAttr.u32PtNumPerFrm = AACLC_SAMPLES_PER_FRAME;
    stAioAttr.u32ChnCnt      = 1;
    stAioAttr.u32ClkSel      = 1;
#else //  inner acodec
    AUDIO_DEV   AiDev = SAMPLE_AUDIO_INNER_AI_DEV;
    AUDIO_DEV   AoDev = SAMPLE_AUDIO_INNER_AO_DEV;
    stAioAttr.enSamplerate   = AUDIO_SAMPLE_RATE_48000;
    stAioAttr.enBitwidth     = AUDIO_BIT_WIDTH_16;
    stAioAttr.enWorkmode     = AIO_MODE_I2S_MASTER;
    stAioAttr.enSoundmode    = AUDIO_SOUND_MODE_STEREO;
    stAioAttr.u32EXFlag      = 0;
    stAioAttr.u32FrmNum      = 30;
    stAioAttr.u32PtNumPerFrm = AACLC_SAMPLES_PER_FRAME;
    stAioAttr.u32ChnCnt      = 2;
    stAioAttr.u32ClkSel      = 0;
    stAioAttr.enI2sType = AIO_I2STYPE_INNERCODEC;
#endif

这里不需要采用重采样功能,所以采样频率不需要配置。

gs_bAioReSample = HI_FALSE;
/* config ao resample attr if needed */
if (HI_TRUE == gs_bAioReSample)
{
    /* ai 48k -> 32k */
    enOutSampleRate = AUDIO_SAMPLE_RATE_32000;

    /* ao 32k -> 48k */
    enInSampleRate  = AUDIO_SAMPLE_RATE_32000;
}
else
{
    enInSampleRate  = AUDIO_SAMPLE_RATE_BUTT;
    enOutSampleRate = AUDIO_SAMPLE_RATE_BUTT;
}

/* resample and anr should be user get mode */
gs_bUserGetMode = (HI_TRUE == gs_bAioReSample) ? HI_TRUE : HI_FALSE;
gs_bUserGetMode = HI_TRUE;

/* config internal audio codec */
s32Ret = SAMPLE_COMM_AUDIO_CfgAcodec(&stAioAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_DBG(s32Ret);
    goto AIAO_ERR3;
}

分析一下接口SAMPLE_COMM_AUDIO_CfgAcodec(),可以很清晰的看出接口中通过预编译宏来控制不同audio codec的配置

/* config codec */
HI_S32 SAMPLE_COMM_AUDIO_CfgAcodec(AIO_ATTR_S* pstAioAttr)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_BOOL bCodecCfg = HI_FALSE;

#ifdef HI_ACODEC_TYPE_INNER
    /*** INNER AUDIO CODEC ***/
    s32Ret = SAMPLE_INNER_CODEC_CfgAudio(pstAioAttr->enSamplerate);
    if (HI_SUCCESS != s32Ret)
    {
        printf("%s:SAMPLE_INNER_CODEC_CfgAudio failed\n", __FUNCTION__);
        return s32Ret;
    }
    bCodecCfg = HI_TRUE;
#endif

#ifdef HI_ACODEC_TYPE_TLV320AIC31
    /*** ACODEC_TYPE_TLV320 ***/
    s32Ret = SAMPLE_Tlv320_CfgAudio(pstAioAttr->enWorkmode, pstAioAttr->enSamplerate);
    if (HI_SUCCESS != s32Ret)
    {
        printf("%s: SAMPLE_Tlv320_CfgAudio failed\n", __FUNCTION__);
        return s32Ret;
    }
    bCodecCfg = HI_TRUE;
#endif

    if (!bCodecCfg)
    {
        printf("Can not find the right codec.\n");
        return HI_FALSE;
    }
    return HI_SUCCESS;
}

SAMPLE_INNER_CODEC_CfgAudio()实现对音频驱动配置,调用的驱动是/dev/acodec,内置的相关ioctrl命令在《HiMPP V4.0 媒体处理软件开发参考》手册中可以搜到,这里主要是采用回复默认配置后设置一个采样频率与输入方式的选择。

HI_S32 SAMPLE_INNER_CODEC_CfgAudio(AUDIO_SAMPLE_RATE_E enSample)
{
    HI_S32 fdAcodec = -1;
    HI_S32 ret = HI_SUCCESS;
    ACODEC_FS_E i2s_fs_sel = 0;
    int iAcodecInputVol = 0;
    ACODEC_MIXER_E input_mode = 0;

    fdAcodec = open(ACODEC_FILE, O_RDWR);
    if (fdAcodec < 0)
    {
        printf("%s: can't open Acodec,%s\n", __FUNCTION__, ACODEC_FILE);
        return HI_FAILURE;
    }
    if (ioctl(fdAcodec, ACODEC_SOFT_RESET_CTRL))
    {
        printf("Reset audio codec error\n");
    }

    switch (enSample)
    {
        case AUDIO_SAMPLE_RATE_8000:
            i2s_fs_sel = ACODEC_FS_8000;
            break;

        case AUDIO_SAMPLE_RATE_16000:
            i2s_fs_sel = ACODEC_FS_16000;
            break;

        case AUDIO_SAMPLE_RATE_32000:
            i2s_fs_sel = ACODEC_FS_32000;
            break;

        case AUDIO_SAMPLE_RATE_11025:
            i2s_fs_sel = ACODEC_FS_11025;
            break;
.........................

启动Ai接口HI_S32 SAMPLE_COMM_AUDIO_StartAi()的参数从接口参数名可以清楚得知,后面4个参数是关于重采样以及VQE音质增强功能的,当前示例功能没用到,需要注意的是目前内置audio codec是配置成双声道只需要初始化左声道对应的通道另外右声道的部分SDK内部会补齐。

/* enable AI channle */
s32AiChnCnt = stAioAttr.u32ChnCnt;
s32Ret = SAMPLE_COMM_AUDIO_StartAi(AiDev, s32AiChnCnt, &stAioAttr, enOutSampleRate, gs_bAioReSample, NULL, 0);
if (s32Ret != HI_SUCCESS)
{
    SAMPLE_DBG(s32Ret);
    goto AIAO_ERR3;
}

Ao的启动与Ai基本一致,不同的是Vo不需要音质增强以及Vo还可以通过Hdmi输出。

/* enable AO channle */
s32AoChnCnt = stAioAttr.u32ChnCnt;
s32Ret = SAMPLE_COMM_AUDIO_StartAo(AoDev, s32AoChnCnt, &stAioAttr, enInSampleRate, gs_bAioReSample);
if (s32Ret != HI_SUCCESS)
{
    SAMPLE_DBG(s32Ret);
     goto AIAO_ERR2;
}

这里是两种音频输出方式,一种是AI与AO模块不绑定创建线程,实时将AI获取到的数据发送到AO中,另外一种是将AI与AO进行绑定自动将AI获取到的数据进行输出。

/* bind AI to AO channle */
if (HI_TRUE == gs_bUserGetMode)
{
    s32Ret = SAMPLE_COMM_AUDIO_CreatTrdAiAo(AiDev, AiChn, AoDev, AoChn);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_DBG(s32Ret);
         goto AIAO_ERR1;
    }
}
else
{
    s32Ret = SAMPLE_COMM_AUDIO_AoBindAi(AiDev, AiChn, AoDev, AoChn);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_DBG(s32Ret);
        goto AIAO_ERR1;
    }
}

该函数中调用的是SAMPLE_COMM_AUDIO_CreatTrdAiAo()接口。

/******************************************************************************
* function : Create the thread to get frame from ai and send to ao
******************************************************************************/
HI_S32 SAMPLE_COMM_AUDIO_CreatTrdAiAo(AUDIO_DEV AiDev, AI_CHN AiChn, AUDIO_DEV AoDev, AO_CHN AoChn)
{
    SAMPLE_AI_S* pstAi = NULL;

    pstAi = &gs_stSampleAi[AiDev * AI_MAX_CHN_NUM + AiChn];
    pstAi->bSendAenc = HI_FALSE;
    pstAi->bSendAo = HI_TRUE;
    pstAi->bStart = HI_TRUE;
    pstAi->AiDev = AiDev;
    pstAi->AiChn = AiChn;
    pstAi->AoDev = AoDev;
    pstAi->AoChn = AoChn;

    pthread_create(&pstAi->stAiPid, 0, SAMPLE_COMM_AUDIO_AiProc, pstAi);

    return HI_SUCCESS;
}

线程的执行函数是SAMPLE_COMM_AUDIO_AiProc,SAMPLE_COMM_AUDIO_AiProc设置音频帧的缓存长度为30。

/******************************************************************************
* function : get frame from Ai, send it  to Aenc or Ao
******************************************************************************/
void* SAMPLE_COMM_AUDIO_AiProc(void* parg)
{
    HI_S32 s32Ret;
    HI_S32 AiFd;
    SAMPLE_AI_S* pstAiCtl = (SAMPLE_AI_S*)parg;
    AUDIO_FRAME_S stFrame;
    AEC_FRAME_S   stAecFrm;
    fd_set read_fds;
    struct timeval TimeoutVal;
    AI_CHN_PARAM_S stAiChnPara;

    s32Ret = HI_MPI_AI_GetChnParam(pstAiCtl->AiDev, pstAiCtl->AiChn, &stAiChnPara);
    if (HI_SUCCESS != s32Ret)
    {
        printf("%s: Get ai chn param failed\n", __FUNCTION__);
        return NULL;
    }

    stAiChnPara.u32UsrFrmDepth = 30;

之后就一个循环每秒判断Ai有没有数据输入。

    FD_ZERO(&read_fds);
    AiFd = HI_MPI_AI_GetFd(pstAiCtl->AiDev, pstAiCtl->AiChn);
    FD_SET(AiFd, &read_fds);

    while (pstAiCtl->bStart)
    {
        TimeoutVal.tv_sec = 1;
        TimeoutVal.tv_usec = 0;

        FD_ZERO(&read_fds);
        FD_SET(AiFd, &read_fds);

        s32Ret = select(AiFd + 1, &read_fds, NULL, NULL, &TimeoutVal);
        if (s32Ret < 0)
        {
            break;
        }
        else if (0 == s32Ret)
        {
            printf("%s: get ai frame select time out\n", __FUNCTION__);
            break;
        }

        if (FD_ISSET(AiFd, &read_fds))
        {
            /* get frame from ai chn */
            memset(&stAecFrm, 0, sizeof(AEC_FRAME_S));
            s32Ret = HI_MPI_AI_GetFrame(pstAiCtl->AiDev, pstAiCtl->AiChn, &stFrame, &stAecFrm, HI_FALSE);
            if (HI_SUCCESS != s32Ret )
            {
#if 0
                printf("%s: HI_MPI_AI_GetFrame(%d, %d), failed with %#x!\n", \
                       __FUNCTION__, pstAiCtl->AiDev, pstAiCtl->AiChn, s32Ret);
                pstAiCtl->bStart = HI_FALSE;
                return NULL;
#else
                continue;
#endif
            }

接收到Ai输入数据后根据是否需要发送到Aenv或Ao将音频数据送到相应目标。

/* send frame to encoder */
if (HI_TRUE == pstAiCtl->bSendAenc)
{
    s32Ret = HI_MPI_AENC_SendFrame(pstAiCtl->AencChn, &stFrame, &stAecFrm);
    if (HI_SUCCESS != s32Ret )
    {
        printf("%s: HI_MPI_AENC_SendFrame(%d), failed with %#x!\n", \
               __FUNCTION__, pstAiCtl->AencChn, s32Ret);
        pstAiCtl->bStart = HI_FALSE;
        return NULL;
    }
}

/* send frame to ao */
if (HI_TRUE == pstAiCtl->bSendAo)
{
    s32Ret = HI_MPI_AO_SendFrame(pstAiCtl->AoDev, pstAiCtl->AoChn, &stFrame, 1000);
    if (HI_SUCCESS != s32Ret )
    {
        printf("%s: HI_MPI_AO_SendFrame(%d, %d), failed with %#x!\n", \
               __FUNCTION__, pstAiCtl->AoDev, pstAiCtl->AoChn, s32Ret);
        pstAiCtl->bStart = HI_FALSE;
        return NULL;
    }

}

/* finally you must release the stream */
s32Ret = HI_MPI_AI_ReleaseFrame(pstAiCtl->AiDev, pstAiCtl->AiChn, &stFrame, &stAecFrm);
if (HI_SUCCESS != s32Ret )
{
    printf("%s: HI_MPI_AI_ReleaseFrame(%d, %d), failed with %#x!\n", \
           __FUNCTION__, pstAiCtl->AiDev, pstAiCtl->AiChn, s32Ret);
    pstAiCtl->bStart = HI_FALSE;
    return NULL;
}

剩下就是释放资源,将启动的线程关闭,将Vi和Vo开启的通道等关闭。

AIAO_ERR0:

    if (HI_TRUE == gs_bUserGetMode)
    {
        s32Ret = SAMPLE_COMM_AUDIO_DestoryTrdAi(AiDev, AiChn);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_DBG(s32Ret);
        }
    }
    else
    {
        s32Ret = SAMPLE_COMM_AUDIO_AoUnbindAi(AiDev, AiChn, AoDev, AoChn);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_DBG(s32Ret);
        }
    }

AIAO_ERR1:

    s32Ret |= SAMPLE_COMM_AUDIO_StopAo(AoDev, s32AoChnCnt, gs_bAioReSample);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_DBG(s32Ret);
    }

AIAO_ERR2:
    s32Ret |= SAMPLE_COMM_AUDIO_StopAi(AiDev, s32AiChnCnt, gs_bAioReSample, HI_FALSE);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_DBG(s32Ret);
    }

AIAO_ERR3:

    return s32Ret;
}

三、SAMPLE_AUDIO_AiAenc

与上个函数不同的是对Ai输入的音频进行编码。

/********************************************
  step 3: start Aenc
********************************************/
s32AencChnCnt = stAioAttr.u32ChnCnt >> stAioAttr.enSoundmode;
s32Ret = SAMPLE_COMM_AUDIO_StartAenc(s32AencChnCnt, &stAioAttr, gs_enPayloadType);
if (s32Ret != HI_SUCCESS)
{
    SAMPLE_DBG(s32Ret);
    goto AIAENC_ERR5;
}

可以根据相应的编码协议类型进行初始化编码器。

/******************************************************************************
* function : Start Aenc
******************************************************************************/
HI_S32 SAMPLE_COMM_AUDIO_StartAenc(HI_S32 s32AencChnCnt, AIO_ATTR_S *pstAioAttr, PAYLOAD_TYPE_E enType)
{
    AENC_CHN AeChn;
    HI_S32 s32Ret, i;
    AENC_CHN_ATTR_S stAencAttr;
    AENC_ATTR_ADPCM_S stAdpcmAenc;
    AENC_ATTR_G711_S stAencG711;
    AENC_ATTR_G726_S stAencG726;
    AENC_ATTR_LPCM_S stAencLpcm;
    AENC_ATTR_AAC_S  stAencAac;

    /* set AENC chn attr */

    stAencAttr.enType = enType;
    stAencAttr.u32BufSize = 30;
    stAencAttr.u32PtNumPerFrm = pstAioAttr->u32PtNumPerFrm;

    if (PT_ADPCMA == stAencAttr.enType)
    {
        stAencAttr.pValue       = &stAdpcmAenc;
        stAdpcmAenc.enADPCMType = AUDIO_ADPCM_TYPE;
    }
    else if (PT_G711A == stAencAttr.enType || PT_G711U == stAencAttr.enType)
    {
        stAencAttr.pValue       = &stAencG711;
    }
    else if (PT_G726 == stAencAttr.enType)
    {
        stAencAttr.pValue       = &stAencG726;
        stAencG726.enG726bps    = G726_BPS;
    }
    else if (PT_LPCM == stAencAttr.enType)
    {
        stAencAttr.pValue = &stAencLpcm;
    }

这里一样还是提供两种方式将AI的数据发送到AENC,该函数里最终调用的是SAMPLE_COMM_AUDIO_AencBindAi。

/********************************************
  step 4: Aenc bind Ai Chn
********************************************/
for (i = 0; i < s32AencChnCnt; i++)
{
    AeChn = i;
    AiChn = i;

    if (HI_TRUE == gs_bUserGetMode)
    {
        s32Ret = SAMPLE_COMM_AUDIO_CreatTrdAiAenc(AiDev, AiChn, AeChn);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_DBG(s32Ret);
            for (j=0; j<i; j++)
            {
                SAMPLE_COMM_AUDIO_DestoryTrdAi(AiDev, j);
            }
            goto AIAENC_ERR4;
        }
    }
    else
    {
        s32Ret = SAMPLE_COMM_AUDIO_AencBindAi(AiDev, AiChn, AeChn);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_DBG(s32Ret);
            for (j=0; j<i; j++)
            {
                SAMPLE_COMM_AUDIO_AencUnbindAi(AiDev, j, j);
            }
            goto AIAENC_ERR4;
        }
    }
    printf("Ai(%d,%d) bind to AencChn:%d ok!\n", AiDev , AiChn, AeChn);
}

这里实现了两个功能,一个是将编码器通道0启动,打开指定音频文件,SAMPLE_COMM_AUDIO_CreatTrdAencAdec()接口创建线程,线程执行的函数为SAMPLE_COMM_AUDIO_AencProc(),该函数每秒查看Ai是否有数据传输给编码器编码完成,有数据则按指定类型解码后保存到指定文件中。

另一个功能时将编码器与Ao绑定,将编码后的音频直接播放。

/********************************************
  step 5: start Adec & Ao. ( if you want )
********************************************/
if (HI_TRUE == bSendAdec)
{
    s32Ret = SAMPLE_COMM_AUDIO_StartAdec(AdChn, gs_enPayloadType);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_DBG(s32Ret);
        goto AIAENC_ERR3;
    }

    s32AoChnCnt = stAioAttr.u32ChnCnt;
    s32Ret = SAMPLE_COMM_AUDIO_StartAo(AoDev, s32AoChnCnt, &stAioAttr, enInSampleRate, gs_bAioReSample);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_DBG(s32Ret);
        goto AIAENC_ERR2;
    }

    pfd = SAMPLE_AUDIO_OpenAencFile(AdChn, gs_enPayloadType);
    if (!pfd)
    {
        SAMPLE_DBG(HI_FAILURE);
        goto AIAENC_ERR1;
    }
    s32Ret = SAMPLE_COMM_AUDIO_CreatTrdAencAdec(AeChn, AdChn, pfd);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_DBG(s32Ret);
        goto AIAENC_ERR1;
    }

    s32Ret = SAMPLE_COMM_AUDIO_AoBindAdec(AoDev, AoChn, AdChn);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_DBG(s32Ret);
        goto AIAENC_ERR0;
    }

    printf("bind adec:%d to ao(%d,%d) ok \n", AdChn, AoDev, AoChn);
}

printf("\nplease press twice ENTER to exit this sample\n");
getchar();
getchar();

剩下的依旧是依次释放资源。

四、SAMPLE_AUDIO_AdecAo

大致的流程都相同,从配置audio codec到开启编码器、开启Ao然后绑定Ao与编码器,不同的是开启了一个线程从文件中读取音频数据并传输给编码器然后播放,一直循环。

SAMPLE_COMM_AUDIO_AdecProc()函数作为线程执行函数,函数实现循环读取音频文件进行解码,如果需要只播放一次的话,可以在读取完成后直接退出并延时一段时间等Ao将音频播放完成后结束任务。

while (HI_TRUE == pstAdecCtl->bStart)
{
    /* read from file */
    stAudioStream.pStream = pu8AudioStream;
    u32ReadLen = fread(stAudioStream.pStream, 1, u32Len, pfd);
    if (u32ReadLen <= 0)
    {
        s32Ret = HI_MPI_ADEC_SendEndOfStream(s32AdecChn, HI_FALSE);
        if (HI_SUCCESS != s32Ret)
        {
            printf("%s: HI_MPI_ADEC_SendEndOfStream failed!\n", __FUNCTION__);
        }
        (HI_VOID)fseek(pfd, 0, SEEK_SET);/*read file again*/
        continue;
    }

    /* here only demo adec streaming sending mode, but pack sending mode is commended */
    stAudioStream.u32Len = u32ReadLen;
    s32Ret = HI_MPI_ADEC_SendStream(s32AdecChn, &stAudioStream, HI_TRUE);
    if (HI_SUCCESS != s32Ret)
    {
        printf("%s: HI_MPI_ADEC_SendStream(%d) failed with %#x!\n", \
               __FUNCTION__, s32AdecChn, s32Ret);
        break;
    }
}

五、SAMPLE_AUDIO_AiVqeProcessAo

该功能与SAMPLE_AUDIO_AiAo的区别在于增加了音质增强功能,具体是增加了音质增强VQE的属性配置,这里使用的是Common场景模式,具体参数配置可以根据《HiMPP V4.0 媒体处理软件开发参考》手册进行修改。

if (1 == u32AiVqeType)
{
    memset(&stAiVqeRecordAttr, 0, sizeof(AI_RECORDVQE_CONFIG_S));
    stAiVqeRecordAttr.s32WorkSampleRate    = AUDIO_SAMPLE_RATE_48000;
    stAiVqeRecordAttr.s32FrameSample       = SAMPLE_AUDIO_PTNUMPERFRM;
    stAiVqeRecordAttr.enWorkstate          = VQE_WORKSTATE_COMMON;
    stAiVqeRecordAttr.s32InChNum           = 2;
    stAiVqeRecordAttr.s32OutChNum           = 2;
    stAiVqeRecordAttr.enRecordType           = VQE_RECORD_NORMAL;
    stAiVqeRecordAttr.stDrcCfg.bUsrMode    = HI_FALSE;
    stAiVqeRecordAttr.stRnrCfg.bUsrMode    = HI_FALSE;
    stAiVqeRecordAttr.stHdrCfg.bUsrMode    = HI_FALSE;
    stAiVqeRecordAttr.stHpfCfg.bUsrMode    = HI_TRUE;
    stAiVqeRecordAttr.stHpfCfg.enHpfFreq   = AUDIO_HPF_FREQ_80;

    stAiVqeRecordAttr.u32OpenMask = AI_RECORDVQE_MASK_DRC | AI_RECORDVQE_MASK_HDR | AI_RECORDVQE_MASK_HPF | AI_RECORDVQE_MASK_RNR;

    pAiVqeAttr = (HI_VOID *)&stAiVqeRecordAttr;
}
else
{
    pAiVqeAttr = HI_NULL;
}

然后在启动Ai的接口中根据参数设置VQE的属性并开启该功能。

/********************************************
  step 2: start Ai
********************************************/
s32AiChnCnt = stAioAttr.u32ChnCnt;
s32Ret = SAMPLE_COMM_AUDIO_StartAi(AiDev, s32AiChnCnt, &stAioAttr, enOutSampleRate, gs_bAioReSample, pAiVqeAttr, u32AiVqeType);
if (s32Ret != HI_SUCCESS)
{
    SAMPLE_DBG(s32Ret);
    goto VQE_ERR2;
}

六、SAMPLE_AUDIO_AiHdmiAo

该功能与SAMPLE_AUDIO_AiAo的区别在于使用HDMI播放音频,所以需要增加HDMI的配置。

通过配置Aodev为宏SAMPLE_AUDIO_INNER_HDMI_AO_DEV表示。

/******************************************************************************
* function : Ai -> Ao(Hdmi)
******************************************************************************/
HI_S32 SAMPLE_AUDIO_AiHdmiAo(HI_VOID)
{
    HI_S32 s32Ret, s32AiChnCnt;
    AUDIO_DEV   AiDev = SAMPLE_AUDIO_INNER_AI_DEV;
    AI_CHN      AiChn = 0;
    AUDIO_DEV   AoDev = SAMPLE_AUDIO_INNER_HDMI_AO_DEV;
    AO_CHN      AoChn = 0;

    AIO_ATTR_S stAioAttr;
    AIO_ATTR_S stHdmiAoAttr;

    stAioAttr.enSamplerate   = AUDIO_SAMPLE_RATE_48000;
    stAioAttr.enBitwidth     = AUDIO_BIT_WIDTH_16;
    stAioAttr.enWorkmode     = AIO_MODE_I2S_MASTER;
    stAioAttr.enSoundmode    = AUDIO_SOUND_MODE_STEREO;
    stAioAttr.u32EXFlag      = 1;
    stAioAttr.u32FrmNum      = 30;
    stAioAttr.u32PtNumPerFrm = SAMPLE_AUDIO_PTNUMPERFRM;
    stAioAttr.u32ChnCnt      = 2;
    stAioAttr.u32ClkSel      = 0;
    stAioAttr.enI2sType = AIO_I2STYPE_INNERCODEC;

    stHdmiAoAttr.enSamplerate   = AUDIO_SAMPLE_RATE_48000;
    stHdmiAoAttr.enBitwidth     = AUDIO_BIT_WIDTH_16;
    stHdmiAoAttr.enWorkmode     = AIO_MODE_I2S_MASTER;
    stHdmiAoAttr.enSoundmode    = AUDIO_SOUND_MODE_STEREO;
    stHdmiAoAttr.u32EXFlag      = 1;
    stHdmiAoAttr.u32FrmNum      = 30;
    stHdmiAoAttr.u32PtNumPerFrm = SAMPLE_AUDIO_PTNUMPERFRM;
    stHdmiAoAttr.u32ChnCnt      = 2;
    stHdmiAoAttr.u32ClkSel      = 0;
    stHdmiAoAttr.enI2sType = AIO_I2STYPE_INNERHDMI;

在启动Ao接口中判断Aodev的类型并启动HDMI。

/* enable AO channle */
s32Ret = SAMPLE_COMM_AUDIO_StartAo(AoDev, stHdmiAoAttr.u32ChnCnt, &stHdmiAoAttr, stHdmiAoAttr.enSamplerate, HI_FALSE);
if (s32Ret != HI_SUCCESS)
{
    SAMPLE_DBG(s32Ret);
    return HI_FAILURE;
}
/******************************************************************************
* function : Start Ao
******************************************************************************/
HI_S32 SAMPLE_COMM_AUDIO_StartAo(AUDIO_DEV AoDevId, HI_S32 s32AoChnCnt,
                                 AIO_ATTR_S* pstAioAttr, AUDIO_SAMPLE_RATE_E enInSampleRate, HI_BOOL bResampleEn)
{
    HI_S32 i;
    HI_S32 s32Ret;

    if (SAMPLE_AUDIO_INNER_HDMI_AO_DEV == AoDevId)
    {
#ifdef HI_ACODEC_TYPE_HDMI
        pstAioAttr->u32ClkSel = 0;

        SAMPLE_COMM_AUDIO_StartHdmi(pstAioAttr);
#endif
    }

启动HDMI中配置HDMI属性,根据接口时序去调用进行初始化HDMI的接口SAMPLE_COMM_VO_HdmiStart()。

HI_S32 SAMPLE_COMM_AUDIO_StartHdmi(AIO_ATTR_S *pstAioAttr)
{
    HI_S32 s32Ret;
    HI_HDMI_ATTR_S stHdmiAttr;
    HI_HDMI_ID_E enHdmi = HI_HDMI_ID_0;
    VO_PUB_ATTR_S stPubAttr;
    VO_DEV VoDev = 0;

    stPubAttr.u32BgColor = 0x000000ff;
    stPubAttr.enIntfType = VO_INTF_HDMI;
    stPubAttr.enIntfSync = VO_OUTPUT_1080P30;

    if(HI_SUCCESS != SAMPLE_COMM_VO_StartDev(VoDev, &stPubAttr))
    {
        printf("[Func]:%s [Line]:%d [Info]:%s\n", __FUNCTION__, __LINE__, "failed");
        return HI_FAILURE;
    }

    s32Ret = SAMPLE_COMM_VO_HdmiStart(stPubAttr.enIntfSync);
    if(HI_SUCCESS != s32Ret)
    {
        printf("[Func]:%s [Line]:%d [Info]:%s\n", __FUNCTION__, __LINE__, "failed");
        return HI_FAILURE;
    }

    s32Ret = HI_MPI_HDMI_Stop(enHdmi);
    if(HI_SUCCESS != s32Ret)
    {
        printf("[Func]:%s [Line]:%d [Info]:%s\n", __FUNCTION__, __LINE__, "failed");
        return HI_FAILURE;
    }

由于SAMPLE_COMM_VO_HdmiStart()是sample_comm_vo模块中通用的开启HDMI输出视频的配置,并没有配置HDMI输出音频所以需要修改HDMI的配置并重新启动。

   s32Ret = HI_MPI_HDMI_GetAttr(enHdmi, &stHdmiAttr);
    if(HI_SUCCESS != s32Ret)
    {
        printf("[Func]:%s [Line]:%d [Info]:%s\n", __FUNCTION__, __LINE__, "failed");
        return HI_FAILURE;
    }

    stHdmiAttr.bEnableAudio = HI_TRUE;        /**< if enable audio */
    stHdmiAttr.enSoundIntf = HI_HDMI_SND_INTERFACE_I2S; /**< source of HDMI audio, HI_HDMI_SND_INTERFACE_I2S suggested.the parameter must be consistent with the input of AO*/
    stHdmiAttr.enSampleRate = pstAioAttr->enSamplerate;        /**< sampling rate of PCM audio,the parameter must be consistent with the input of AO */
    stHdmiAttr.u8DownSampleParm = HI_FALSE;    /**< parameter of downsampling  rate of PCM audio, default :0 */

    stHdmiAttr.enBitDepth = 8 * (pstAioAttr->enBitwidth+1);   /**< bitwidth of audio,default :16,the parameter must be consistent with the config of AO */
    stHdmiAttr.u8I2SCtlVbit = 0;        /**< reserved, should be 0, I2S control (0x7A:0x1D) */

    stHdmiAttr.bEnableAviInfoFrame = HI_TRUE; /**< if enable  AVI InfoFrame*/
    stHdmiAttr.bEnableAudInfoFrame = HI_TRUE;; /**< if enable AUDIO InfoFrame*/

    s32Ret = HI_MPI_HDMI_SetAttr(enHdmi, &stHdmiAttr);
    if(HI_SUCCESS != s32Ret)
    {
        printf("[Func]:%s [Line]:%d [Info]:%s\n", __FUNCTION__, __LINE__, "failed");
        return HI_FAILURE;
    }

    s32Ret = HI_MPI_HDMI_Start(enHdmi);
    if(HI_SUCCESS != s32Ret)
    {
        printf("[Func]:%s [Line]:%d [Info]:%s\n", __FUNCTION__, __LINE__, "failed");
        return HI_FAILURE;
    }

    return HI_SUCCESS;
}

七、SAMPLE_AUDIO_AiToAoSysChn

该功能与SAMPLE_AUDIO_AiAo的区别在于使用系统音通道输出。

/* bind AI to AO channle */
s32Ret = SAMPLE_COMM_AUDIO_CreatTrdAiAo(AiDev, AiChn, AoDev, AO_SYSCHN_CHNID);
if (s32Ret != HI_SUCCESS)
{
    SAMPLE_DBG(s32Ret);
     goto AIAO_ERR1;
}
printf("ai(%d,%d) bind to ao(%d,%d) ok\n", AiDev, AiChn, AoDev, AO_SYSCHN_CHNID);

该通道在手册《HiMPP V4.0 媒体处理软件开发参考》1404/1770页中的定义如下:

在这里插入图片描述

八、SAMPLE_AUDIO_AiToExtResample

该接口实现将Ai收到的数据进行重采样处理后保存到相应重采样通道文件中。

/******************************************************************************
* function : Ai -> ExtResample -> file
******************************************************************************/
HI_S32 SAMPLE_AUDIO_AiToExtResample(HI_VOID)
{
    HI_S32 i, j, s32Ret;
    AI_CHN      AiChn;
    HI_S32      s32AiChnCnt;
    FILE*        pfd = NULL;
    AIO_ATTR_S stAioAttr;

为每一个通道创建线程去将ai的数据重采样后保存到文件中。

/********************************************
  step 4: Create thread to resample.
********************************************/
for (i = 0; i < s32AiChnCnt; i++)
{
    AiChn = i;

    pfd = SAMPLE_AUDIO_OpenResFile(AiChn);
    if (!pfd)
    {
        SAMPLE_DBG(HI_FAILURE);
        goto AIRES_ERR1;
    }

    s32Ret = SAMPLE_COMM_AUDIO_CreatTrdAiExtRes(&stAioAttr, AiDev, AiChn, enOutSampleRate, pfd);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_DBG(s32Ret);
        for (j=0; j<i; j++)
        {
            SAMPLE_COMM_AUDIO_DestoryTrdAi(AiDev, j);
        }
        goto AIRES_ERR1;
    }

    printf("Ai(%d,%d) extern resample ok!\n", AiDev , AiChn);
}

线程执行函数SAMPLE_COMM_AUDIO_AiExtResProc实现每秒去读取Ai的数据并写入到文件中。

/******************************************************************************
* function : get frame from Ai, send it  to Resampler
******************************************************************************/
void* SAMPLE_COMM_AUDIO_AiExtResProc(void* parg)
{
    HI_S32 s32Ret;
    HI_S32 AiFd;

本篇是摘至参考链接,如有侵权,请及时联系小编,小编会及时删除的。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ltqshs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值