如何基于海思芯片快速搭建Agora RTC應(yīng)用

????????前兩篇講了從業(yè)以來(lái)對(duì)于RTC技術(shù)悼枢,應(yīng)用場(chǎng)景以及音頻編解碼發(fā)展的理解埠忘,主要是從理論層面,所謂光說(shuō)不練假把式馒索,今天來(lái)點(diǎn)干貨莹妒,介紹一個(gè)實(shí)際應(yīng)用案例,在海思芯片上集成Aogra-RTSA SDK的開(kāi)發(fā)實(shí)踐绰上,本文設(shè)定場(chǎng)景為應(yīng)用程序控制海思芯片通過(guò)攝像頭獲取輸入視頻旨怠,經(jīng)過(guò)編碼,通過(guò)RTSA的視頻通道發(fā)送給瀏覽器觀(guān)看渔期;同時(shí)瀏覽器可以通過(guò)控制指令通知海思芯片上應(yīng)用程序运吓,抓拍圖片,然后通過(guò)數(shù)據(jù)通道發(fā)回到瀏覽器側(cè)疯趟。(實(shí)際業(yè)務(wù)使用場(chǎng)景恕本人不方便介紹)

????????首先簡(jiǎn)單介紹下海思芯片拘哨,芯片組成是ARM+編解碼協(xié)處理器,支持?jǐn)z像頭視頻接入物理接口信峻,支持圖像處理倦青,支持H.264/H.265解碼和編碼,而且在芯片里集成了視頻檢測(cè)協(xié)處理器功能模塊盹舞。

Hi芯片架構(gòu)圖

????????海思芯片的視頻及圖片處理流程如下


????????話(huà)不多說(shuō)产镐,開(kāi)始集成處理流程介紹

????????首先,搭建海思芯片的交叉編譯環(huán)境踢步,海思開(kāi)發(fā)包包含了片載運(yùn)行操作系統(tǒng)癣亚,交叉編譯開(kāi)發(fā)包,編解碼SDK包获印,已經(jīng)開(kāi)發(fā)手冊(cè)述雾,(ARM只是授權(quán)指令集和架構(gòu),不實(shí)際生產(chǎn)兼丰,且ARM架構(gòu)系統(tǒng)很多玻孟,需要各自不同的交叉編譯環(huán)境),此處選用在虛擬機(jī)上安裝ubuntu進(jìn)行部署(Linux ubuntu 4.15.0-128-generic #131~16.04.1-Ubuntu)鳍征,這些由于跟RTC應(yīng)用關(guān)系不大黍翎,按照開(kāi)發(fā)包中指導(dǎo)書(shū)進(jìn)行操作即可。

處理代碼主要包含5個(gè)部分

1 海思芯片硬件系統(tǒng)的初始化

2 Agora通訊通道建立

3 視頻輸入/編碼通道建立及視頻傳輸

4 抓拍通道初始化

5 抓拍指令接收及圖片傳輸

下面按照代碼對(duì)處理流程進(jìn)行一步步的講解

->首先Main函數(shù)將上述指令串接起來(lái)

HI_VOIDmain(HI_VOID)

{

???????? HI_S32 sChnCount = 1;

???????? HI_S32 s32Ret;


???????? // step:海思系統(tǒng)初始化

???????? s32Ret = Agora_Hi_SysInit();

???????? if (HI_SUCCESS != s32Ret)

???????? {

?????????????????? SAMPLE_PRT("Sys Initfailed!\n");

?????????????????? return;

???????? }



??? /******************************************

???? step : agora init and join into thechannel.

???? 初始化RTC參數(shù)并加入通訊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.

???? 創(chuàng)建視頻輸入艳丛,編碼參數(shù)匣掸,取視頻流且傳輸?shù)揭曨l通道處理

??? ******************************************/

???????? 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);

???????? }


}


->海思芯片硬件系統(tǒng)的初始化代碼如下

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;

}

->視頻輸入/編碼通道建立及視頻傳輸

????????視頻數(shù)據(jù)傳輸流程如下圖


????????編碼及傳輸最主要的有兩個(gè)步驟旺聚,1 Agora_Hi_EncInit初始化VI接收,VPSS圖像處理以及Encode編碼引擎眶蕉;2 Agora_Enc_GetVencStreamProc(線(xiàn)程處理函數(shù))砰粹,循環(huán)接收從編碼器編碼后的視頻流數(shù)據(jù),通過(guò)Agora SDK的視頻通道傳輸?shù)絊erver造挽,供用戶(hù)端觀(guān)看碱璃。

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;


}


->抓拍指令接收及圖片傳輸

圖片抓拍指令接收及圖片傳輸


抓拍處理分為下面幾個(gè)處理:1 注冊(cè)數(shù)據(jù)通道回調(diào)饭入,打開(kāi)數(shù)據(jù)通道嵌器;2 接收抓拍指令啟動(dòng)抓拍;3 從編碼通道接收抓拍圖片谐丢,通過(guò)數(shù)據(jù)通道傳輸給用戶(hù)存檔使用爽航。

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.

???? 啟動(dòng)抓拍蚓让,獲取圖片并發(fā)送到數(shù)據(jù)通道

??? ******************************************/

??? 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上源碼。

????????最后是代碼的實(shí)際編譯讥珍,由于海思芯片集成的是armv7架構(gòu)芯片历极,因此在選擇sdk包時(shí),使用下面鏈接的sdk包衷佃,不然會(huì)報(bào)格式不對(duì)的錯(cuò)誤趟卸。https://download.agora.io/rtsasdk/release/AGORA-RTSALite-license-arm-linux-uclibceabi.tgz

????????Agora使用cmake編譯,按照README介紹氏义,修改toolchain.cmake中的交叉編譯器锄列,按照?qǐng)D中部分


????????更新CMakelist.txt中的.c文件以及include和so庫(kù)路徑

編譯成功,就可以運(yùn)行啦9哂啤A谟省!

本文介紹了海思芯片和Agora

RTSA SDK結(jié)合完成的一個(gè)監(jiān)控視頻及圖片回傳的場(chǎng)景克婶,在后續(xù)應(yīng)用場(chǎng)景中還可以增加音頻互通以及VDA檢測(cè)后自動(dòng)抓拍回傳圖片的操作饶囚。

最后說(shuō)下整體開(kāi)發(fā)的感受:視頻/圖片編碼及獲取上,海思芯片在監(jiān)控領(lǐng)域占絕對(duì)地位鸠补,軟硬件接口豐富萝风,可以快速獲取視頻流數(shù)據(jù),同時(shí)由于其硬件協(xié)處理器完成視頻轉(zhuǎn)碼處理紫岩,不占用ARM CPU的消耗规惰;RTC通訊上,由于A(yíng)gora的嵌入式SDK封裝的很好泉蝌,使用起來(lái)非常簡(jiǎn)單歇万,同時(shí)在瀏覽器側(cè)也有很好的支持,而且提供了多款A(yù)RM類(lèi)型的嵌入式側(cè)的SDK封裝勋陪;因此這兩個(gè)相結(jié)合可以很簡(jiǎn)單的將視頻處理和RTC傳輸應(yīng)用結(jié)合起來(lái)贪磺,搭建成一個(gè)視頻監(jiān)控傳輸系統(tǒng)(可以應(yīng)用在IPC AI防控、無(wú)人機(jī)等領(lǐng)域)诅愚。

本文GitHub源碼地址https://github.com/HansonSs82/MRTC寒锚,可參考。

本文為個(gè)人原創(chuàng)违孝,首發(fā)于聲網(wǎng)開(kāi)發(fā)者社區(qū)https://rtcdeveloper.com/t/topic/20517

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末刹前,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子雌桑,更是在濱河造成了極大的恐慌喇喉,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件校坑,死亡現(xiàn)場(chǎng)離奇詭異拣技,居然都是意外死亡千诬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)膏斤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)徐绑,“玉大人,你說(shuō)我怎么就攤上這事掸绞。” “怎么了耕捞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵衔掸,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我俺抽,道長(zhǎng)敞映,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任磷斧,我火速辦了婚禮振愿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弛饭。我一直安慰自己冕末,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布侣颂。 她就那樣靜靜地躺著档桃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪憔晒。 梳的紋絲不亂的頭發(fā)上藻肄,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音拒担,去河邊找鬼嘹屯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛从撼,可吹牛的內(nèi)容都是我干的州弟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼低零,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呆馁!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起毁兆,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤浙滤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后气堕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體纺腊,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畔咧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了揖膜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片誓沸。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖壹粟,靈堂內(nèi)的尸體忽然破棺而出拜隧,到底是詐尸還是另有隱情,我是刑警寧澤趁仙,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布洪添,位于F島的核電站,受9級(jí)特大地震影響雀费,放射性物質(zhì)發(fā)生泄漏干奢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一盏袄、第九天 我趴在偏房一處隱蔽的房頂上張望忿峻。 院中可真熱鬧,春花似錦辕羽、人聲如沸逛尚。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)黑低。三九已至,卻和暖如春酌毡,著一層夾襖步出監(jiān)牢的瞬間克握,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工枷踏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留菩暗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓旭蠕,卻偏偏與公主長(zhǎng)得像停团,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掏熬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容