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

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

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

Hi芯片架构图

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


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

        首先,搭建海思芯片的交叉编译环境,海思开发包包含了片载运行操作系统,交叉编译开发包,编解码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函数将上述指令串接起来

HI_VOIDmain(HI_VOID)

{

         HI_S32 sChnCount = 1;

         HI_S32 s32Ret;


         // step:海思系统初始化

         s32Ret = Agora_Hi_SysInit();

         if (HI_SUCCESS != s32Ret)

         {

                   SAMPLE_PRT("Sys Initfailed!\n");

                   return;

         }



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

     step : agora init and join into thechannel.

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

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

         s32Ret = Agora_Chn_Init()

         if (HI_SUCCESS != s32Ret)

         {

                   SAMPLE_PRT("Agora_Chn_InitInit failed!\n");

                   return;

         }



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

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

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

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

         s32Ret = Agora_Hi_EncInit();

         if (HI_SUCCESS != s32Ret)

         {

                   SAMPLE_PRT("enc Initfailed!\n");

                   return;

         }


         s32Ret =Agora_Enc_StartGetStream(sChnCount);

         if (HI_SUCCESS != s32Ret)

         {

                   SAMPLE_PRT("Start Vencfailed!\n");

                   return;

         }


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

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

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

         s32Ret = Agora_Hi_PicInit();

         if (HI_SUCCESS != s32Ret)

         {

                   SAMPLE_PRT("pic Initfailed!\n");

                   return;

         }


         while(1)

         {

                   // TODO: recv the config cmdfrom tcplink


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

                   usleep(100000);

         }


}


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

HI_S32Agora_Hi_SysInit(HI_VOID)

{

    HI_S32 s32Ret = HI_SUCCESS;

    VB_CONF_S stVbConf;


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

     step : init sys variable

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

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


    stVbConf.u32MaxPoolCnt = 128;

    /*video buffer*/

    u32BlkSize = Agora_Sys_CalcPicVbBlkSize(gs_enNorm,\

                enSize,PIXEL_FORMAT_YUV_SEMIPLANAR_420, 16,COMPRESS_MODE_SEG);

    stVbConf.astCommPool[0].u32BlkSize =u32BlkSize;

    stVbConf.astCommPool[0].u32BlkCnt = 32;



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

     step : mpp system init.

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

    s32Ret = MPP_Sys_Init(&stVbConf);

    if (HI_SUCCESS != s32Ret)

    {

        AGORA_TEST_PRT("system init failedwith %d!\n", s32Ret);

        return -1;

    }


         return HI_SUCCESS;


}


->AgoraRTC通道建立代码如下

HI_S32Agora_Chn_Init(int32_t argc, char **argv)

{


         int32_t rval;


    SAMPLE_PRT("%s Welcome to RTSA SDKv%s", TAG_APP, agora_rtc_get_version());


    // 3. API: init agora rtc sdk

    int32_t appid_len =strlen(g_stAgoraConfig.p_appid);

    void *p_appid = (void *)(appid_len == 0 ?NULL : g_stAgoraConfig.p_appid);

    rval = agora_rtc_init(p_appid,g_stAgoraConfig.uid, &event_handler, g_stAgoraConfig.p_sdk_log_dir);

    if (rval < 0) {

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

        return -1;

    }


    // 4. API: join channel

    int32_t token_len =strlen(g_stAgoraConfig.p_token);

    void *p_token = (void *)(token_len == 0 ?NULL : g_stAgoraConfig.p_token);


    rval =agora_rtc_join_channel(g_stAgoraConfig.p_channel, p_token, token_len);

    if (rval < 0) {

        LOGE("%s join channel %s failed,rval=%d error=%s", TAG_API, g_stAgoraConfig.p_channel, rval, agora_rtc_err_2_str(rval));

        return -1;

    }


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

    while (1) {

        if (b_join_success_flag) {

            break;

        }

        usleep(10000);

    }



    return rval;

}

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

        视频数据传输流程如下图


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

HI_S32Agora_Hi_EncInit(HI_VOID)

{

    PAYLOAD_TYPE_E enPayLoad = PT_H264;

    PIC_SIZE_E enSize = PIC_HD1080;

    HI_U32 u32Profile = 0;


    SAMPLE_VI_MODE_E enViMode =SAMPLE_VI_MODE_8_1080P;

    HI_S32 s32VpssGrpCnt = 8;


    VPSS_GRP VpssGrp;

    VPSS_CHN VpssChn;

    VPSS_GRP_ATTR_S stVpssGrpAttr ={0};

    VENC_CHN VencChn;

    SAMPLE_RC_E enRcMode=SAMPLE_RC_CBR;


    HI_S32 s32Ret = HI_SUCCESS;

    HI_U32 u32BlkSize;

    SIZE_S stSize;

    char c;


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

     step : start vi dev & chn to capture

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

    s32Ret = Agora_Vi_Capture(enViMode,gs_enNorm);

    if (HI_SUCCESS != s32Ret)

    {

        AGORA_TEST_PRT("start vifailed!\n");

        return -1;

    }


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

     step : start vpss and vi bind vpss

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

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

    if (HI_SUCCESS != s32Ret)

    {

       SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");

        return -1;

    }


    VpssGrp = 0;

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

         stVpssGrpAttr.u32MaxW       = stSize.u32Width;

         stVpssGrpAttr.u32MaxH        = stSize.u32Height;

         stVpssGrpAttr.bNrEn      = HI_TRUE;

    stVpssGrpAttr.enDieMode =VPSS_DIE_MODE_NODIE;

         stVpssGrpAttr.enPixFmt         = SAMPLE_PIXEL_FORMAT;

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

    if (HI_SUCCESS != s32Ret)

    {

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

        return -1;

    }


    s32Ret = Agora_Vi_BindVpss();

    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 = Agora_Enc_Start(VencChn,enPayLoad,\

                                   gs_enNorm,enSize, enRcMode,u32Profile);

    if (HI_SUCCESS != s32Ret)

    {

        SAMPLE_PRT("Start Vencfailed!\n");

        return -1;

    }


    s32Ret = Agora_Enc_BindVpss(VencChn,VpssGrp, VpssChn);

    if (HI_SUCCESS != s32Ret)

    {

        SAMPLE_PRT("Start Vencfailed!\n");

        return -1;

    }



    return HI_SUCCESS;


}


HI_S32Agora_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_S32Agora_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 = Agora_Enc_SnapStart(VencChn,&stSize);

    if (HI_SUCCESS != s32Ret)

    {

        SAMPLE_PRT("Start snapfailed!\n");

        return -1;

    }


         s32Ret = Agora_Enc_BindVpss(VencChn,VpssGrp, VpssChn);

    if (HI_SUCCESS != s32Ret)

    {

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

        return -1;

    }


         return HI_SUCCESS;


}


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

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


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

staticvoid Agora_Hi_dchan_availability(const char *channel, int is_available)

{

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

         if (is_available)

         {

                   strncpy(datachannel, channel,31);

         }

}


staticvoid Agora_Hi_join_channel_success(const char *channel, int32_t elapsed)

{

    b_join_success_flag = 1;

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

}


staticvoid Agora_Hi_on_cmd(const char *channel, uint32_t uid, int cmd, const void*param_ptr,

                                        size_t param_len)

{

         HI_S32 sRet;

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

     step : get picture, then send to agoradata channel.

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

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

    sRet = Agora_Pic_SnapProcess();

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

         return;

}



staticagora_rtc_event_handler_t event_handler = {

    .on_join_channel_success     = Agora_Hi_join_channel_success,

    .on_rdt_availability_changed =Agora_Hi_dchan_availability,

         .on_cmd                                               = Agora_Hi_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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容