写点什么

如何基于海思芯片快速搭建 Agora RTC 应用

用户头像
邵帅
关注
发布于: 2021 年 01 月 24 日

前两篇讲了从业以来对于 RTC 技术,应用场景以及音频编解码发展的理解,主要是从理论层面,所谓光说不练假把式,今天来点干货,介绍一个实际应用案例,在海思芯片上集成 Aogra-RTSA SDK 的开发实践,本文设定场景为应用程序控制海思芯片通过摄像头获取输入视频,经过编码,通过 RTSA 的视频通道发送给浏览器观看;同时浏览器可以通过控制指令通知海思芯片上应用程序,抓拍图片,然后通过数据通道发回到浏览器侧。(实际业务使用场景恕本人不方便介绍)

首先简单介绍下海思芯片,芯片组成是 ARM+编解码协处理器,支持摄像头视频接入物理接口,支持图像处理,支持 H.264/H.265 解码和编码,而且在芯片里集成了视频检测协处理器功能模块。

海思芯片的视频及图片处理流程如下

话不多说,开始集成处理流程介绍

首先,搭建海思芯片的交叉编译环境,海思开发包包含了片载运行操作系统,交叉编译开发包,编解码 SDK 包,已经开发手册,(ARM 只是授权指令集和架构,不实际生产,且 ARM 架构系统很多,需要各自不同的交叉编译环境),此处选用在虚拟机上安装 ubuntu 进行部署(Linux ubuntu 4.15.0-128-generic #131~16.04.1-Ubuntu),这些由于跟 RTC 应用关系不大,按照开发包中指导书进行操作即可。

处理代码主要包含 5 个部分

1.海思芯片硬件系统的初始化

2.Agora 通讯通道建立

3. 视频输入/编码通道建立及视频传输

4. 抓拍通道初始化

5.抓拍指令接收及图片传输

下面按照代码对处理流程进行一步步的讲解

->首先 Main 函数将上述指令串接起来

HIVOID main(HI_VOID)

{

         HI_S32 sChnCount = 1;

         HI_S32 s32Ret;

 

         // step: 海思系统初始化

         s32Ret = AgoraHiSysInit();

         if (HI_SUCCESS != s32Ret)

         {

                   SAMPLE_PRT("Sys Init failed!\n");

                   return;

         }

 

    /******************************************

     step : agora init and join into the channel.

     初始化 RTC 参数并加入通讯 channel

    ******************************************/

         s32Ret = AgoraChnInit()

         if (HI_SUCCESS != s32Ret)

         {

                   SAMPLEPRT("AgoraChn_Init Init failed!\n");

                   return;

         }

                  

 

    /******************************************

     step : stream venc init &process -- create chn, get stream, then send to agora channel.

     创建视频输入,编码参数,取视频流且传输到视频通道处理

    ******************************************/

         s32Ret = AgoraHiEncInit();

         if (HI_SUCCESS != s32Ret)

         {

                   SAMPLE_PRT("enc Init failed!\n");

                   return;

         }

 

         s32Ret = AgoraEncStartGetStream(sChnCount);

         if (HI_SUCCESS != s32Ret)

         {

                   SAMPLE_PRT("Start Venc failed!\n");

                   return;

         }

 

    /******************************************

     step : stream pic init 初始化图片抓拍通道

    ******************************************/

         s32Ret = AgoraHiPicInit();

         if (HI_SUCCESS != s32Ret)

         {

                   SAMPLE_PRT("pic Init failed!\n");

                   return;

         }

        

         while(1)

         {

                   // TODO: recv the config cmd from tcplink

 

                   // 释放 CPU,休眠 100ms,处理配置不需要那么快

                   usleep(100000);

         }

        

}

->海思芯片硬件系统的初始化代码如下

HIS32 AgoraHi_SysInit(HI_VOID)

{

    HIS32 s32Ret = HISUCCESS;

    VBCONFS stVbConf;

        

    /******************************************

     step  : init sys variable

    ******************************************/

    memset(&stVbConf,0,sizeof(VBCONFS));

        

    stVbConf.u32MaxPoolCnt = 128;

    /video buffer/

    u32BlkSize = AgoraSysCalcPicVbBlkSize(gs_enNorm,\

                enSize, PIXELFORMATYUV_SEMIPLANAR_420, 16,COMPRESSMODESEG);

    stVbConf.astCommPool[0].u32BlkSize = u32BlkSize;

    stVbConf.astCommPool[0].u32BlkCnt = 32;

 

    /******************************************

     step : mpp system init.

    ******************************************/

    s32Ret = MPPSysInit(&stVbConf);

    if (HI_SUCCESS != s32Ret)

    {

        AGORATESTPRT("system init failed with %d!\n", s32Ret);

        return -1;

    }

 

         return HI_SUCCESS;

 

}

 

->AgoraRTC 通道建立代码如下

HIS32 AgoraChn_Init(int32_t argc, char **argv)

{

        

         int32_t rval;

 

    SAMPLEPRT("%s Welcome to RTSA SDK v%s", TAGAPP, agorartcget_version());

        

    // 3. API: init agora rtc sdk

    int32t appidlen = strlen(gstAgoraConfig.pappid);

    void p_appid = (void )(appidlen == 0 ? NULL : gstAgoraConfig.p_appid);

    rval = agorartcinit(pappid, gstAgoraConfig.uid, &eventhandler, gstAgoraConfig.psdklog_dir);

    if (rval < 0) {

        LOGE("%s agora sdk init failed, rval=%d error=%s", TAGAPI, rval, agorartc_err_2_str(rval));

        return -1;

    }

 

    // 4. API: join channel

    int32t tokenlen = strlen(gstAgoraConfig.ptoken);

    void p_token = (void )(tokenlen == 0 ? NULL : gstAgoraConfig.p_token);

 

    rval = agorartcjoin_channel(g_stAgoraConfig.pchannel, ptoken, token_len);

    if (rval < 0) {

        LOGE("%s join channel %s failed, rval=%d error=%s", TAGAPI, gstAgoraConfig.pchannel, rval, agorartc_err_2_str(rval));

        return -1;

    }

 

    // 5. wait until join channel success or Ctrl-C trigger stop

    while (1) {

        if (bjoinsuccess_flag) {

            break;

        }

        usleep(10000);

    }

 

    return rval;

}

->视频输入/编码通道建立及视频传输

视频数据传输流程如下图

编码及传输最主要的有两个步骤,1 AgoraHi_EncInit 初始化 VI 接收,VPSS 图像处理以及 Encode 编码引擎;2 AgoraEncGetVencStreamProc(线程处理函数),循环接收从编码器编码后的视频流数据,通过 Agora SDK 的视频通道传输到 Server,供用户端观看。

HI_S32 Agora_HiEncInit(HI_VOID)

{

PAYLOADTYPEE enPayLoad = PT_H264;

PICSIZEE enSize = PIC_HD1080;

HI_U32 u32Profile = 0;

        

SAMPLEVIMODE_E enViMode = SAMPLE_VIMODE8_1080P;

HI_S32 s32VpssGrpCnt = 8;

   

VPSS_GRP VpssGrp;

VPSS_CHN VpssChn;

VPSSGRPATTR_S stVpssGrpAttr = {0};

VENC_CHN VencChn;

SAMPLERCE enRcMode= SAMPLERCCBR;

        

HIS32 s32Ret = HISUCCESS;

HI_U32 u32BlkSize;

SIZE_S stSize;

char c;

 

/******************************************

     step : start vi dev & chn to capture

    ******************************************/

    s32Ret = AgoraViCapture(enViMode,gs_enNorm);

    if (HI_SUCCESS != s32Ret)

    {

        AGORATESTPRT("start vi failed!\n");

        return -1;

    }

   

    /******************************************

     step : start vpss and vi bind vpss

    ******************************************/

    s32Ret = AgoraSysGetPicSize(gs_enNorm, enSize, &stSize);

    if (HI_SUCCESS != s32Ret)

    {

        SAMPLEPRT("SAMPLECOMM_SYS_GetPicSize failed!\n");

        return -1;

    }

 

    VpssGrp = 0;

     memset(&stVpssGrpAttr,0,sizeof(VPSSGRPATTR_S));

         stVpssGrpAttr.u32MaxW        = stSize.u32Width;

         stVpssGrpAttr.u32MaxH         = stSize.u32Height;

         stVpssGrpAttr.bNrEn       = HI_TRUE;

    stVpssGrpAttr.enDieMode = VPSSDIEMODE_NODIE;

         stVpssGrpAttr.enPixFmt          = SAMPLEPIXELFORMAT;

         s32Ret = AgoraVPSSStart(s32VpssGrpCnt, &stSize, 1, &stVpssGrpAttr);

    if (HI_SUCCESS != s32Ret)

    {

             SAMPLE_PRT("Start Vpss failed!\n");

        return -1;

    }

 

    s32Ret = AgoraViBindVpss();

    if (HI_SUCCESS != s32Ret)

    {

             SAMPLE_PRT("Vi bind Vpss failed!\n");

        return -1;

    }

 

    /******************************************

     step : start stream venc

    ******************************************/

    /* HD1080P /

 

    VpssGrp = 0;

    VpssChn = 0;

    VencChn = 0;

        

    s32Ret = AgoraEncStart(VencChn, enPayLoad,\

                                   gs_enNorm, enSize, enRcMode,u32Profile);

    if (HI_SUCCESS != s32Ret)

    {

        SAMPLE_PRT("Start Venc failed!\n");

        return -1;

    }

 

    s32Ret = AgoraEncBindVpss(VencChn, VpssGrp, VpssChn);

    if (HI_SUCCESS != s32Ret)

    {

        SAMPLE_PRT("Start Venc failed!\n");

        return -1;

    }

 

    return HI_SUCCESS;

  

}

HI_S32 Agora_Enc_StartGetStream(HI_S32 s32Cnt)

{

gs_stPara.bThreadStart = HI_TRUE;

gs_stPara.s32Cnt = s32Cnt;

return pthread_create(&gs_VencPid, 0, Agora_Enc_GetVencStreamProc, (HI_VOID*)&gs_stPara);

}

Agora_Enc_GetVencStreamProc 代码可参考 GitHub 上的源码。

->抓拍通道初始化

HI_S32 Agora_Hi_PicInit(HI_VOID)

{

    HI_S32 s32Ret;

    HI_S32 VpssGrp = 0;

    HI_S32 VpssChn = 1;

    HI_S32 VencChn = 1;

    SIZE_S stSize;

 

         // 1080P 图像

         stSize.u32Height = 1080;

         stSize.u32Width = 1920;

         s32Ret = AgoraEncSnapStart(VencChn, &stSize);

    if (HI_SUCCESS != s32Ret)

    {

        SAMPLE_PRT("Start snap failed!\n");

        return -1;

    }

 

         s32Ret = AgoraEncBindVpss(VencChn, VpssGrp, VpssChn);

    if (HI_SUCCESS != s32Ret)

    {

        SAMPLE_PRT("Start snap bind failed!\n");

        return -1;

    }

 

         return HI_SUCCESS;

 

}

 

->抓拍指令接收及图片传输

图片抓拍指令接收及图片传输

抓拍处理分为下面几个处理:1 注册数据通道回调,打开数据通道;2 接收抓拍指令启动抓拍;3 从编码通道接收抓拍图片,通过数据通道传输给用户存档使用。

static void AgoraHidchanavailability(const char *channel, int isavailable)

{

    LOGD("%s ch=%s isavailable=%d", TAGEVENT, channel, is_available);

    if (is_available)

    {

         strncpy(datachannel, channel, 31);

    }

}

 

static void AgoraHijoin_channel_success(const char *channel, int32_t elapsed)

{

    bjoinsuccess_flag = 1;

    LOGI("%s join success, channel=%s elapsed=%d", TAG_EVENT, channel, elapsed);

}

 

static void AgoraHion_cmd(const char channel, uint32_t uid, int cmd, const void param_ptr,

                                        sizet paramlen)

{

         HI_S32 sRet;

    /******************************************

     step : get picture, then send to agora data channel.

     启动抓拍,获取图片并发送到数据通道

    ******************************************/

    sRet = AgoraPicSnapProcess();

    LOGI("AgoraPicSnapProcess, sRet=%d", sRet);

         return;

}

 

static agorartceventhandlert event_handler = {

    .onjoinchannelsuccess     = AgoraHi_join_channel_success,

    .onrdtavailabilitychanged = AgoraHi_dchan_availability,

         .oncmd                                                = AgoraHi_on_cmd

};

Agora_Pic_SnapProcess 可参考 Github 上源码。

最后是代码的实际编译,由于海思芯片集成的是 armv7 架构芯片,因此在选择 sdk 包时,使用下面链接的 sdk 包,不然会报格式不对的错误。https://download.agora.io/rtsasdk/release/AGORA-RTSALite-license-arm-linux-uclibceabi.tgz

Agora 使用 cmake 编译,按照 README 介绍,修改 toolchain.cmake 中的交叉编译器,按照图中部分

更新 CMakelist.txt 中的.c 文件以及 include 和 so 库路径

编译成功,就可以运行啦!!!

本文介绍了海思芯片和 Agora RTSA SDK 结合完成的一个监控视频及图片回传的场景,在后续应用场景中还可以增加音频互通以及 VDA 检测后自动抓拍回传图片的操作。

最后说下整体开发的感受:视频/图片编码及获取上,海思芯片在监控领域占绝对地位,软硬件接口丰富,可以快速获取视频流数据,同时由于其硬件协处理器完成视频转码处理,不占用 ARM CPU 的消耗;RTC 通讯上,由于 Agora 的嵌入式 SDK 封装的很好,使用起来非常简单,同时在浏览器侧也有很好的支持,而且提供了多款 ARM 类型的嵌入式侧的 SDK 封装;因此这两个相结合可以很简单的将视频处理和 RTC 传输应用结合起来,搭建成一个视频监控传输系统(可以应用在 IPC AI 防控、无人机等领域)。

本文 GitHub 源码地址https://github.com/HansonSs82/MRTC,可参考。

本文为个人原创,首发于声网开发者社区https://rtcdeveloper.com/t/topic/20517


发布于: 2021 年 01 月 24 日阅读数: 13
用户头像

邵帅

关注

还未添加个人签名 2017.10.29 加入

还未添加个人简介

评论

发布
暂无评论
如何基于海思芯片快速搭建Agora RTC应用