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

Hanson

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

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

首先,搭建海思芯片的交叉编译环境,海思开发包包含了片载运行操作系统,交叉编译开发包,编解码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_VOID main(HI_VOID)

{

HI_S32 sChnCount = 1;

HI_S32 s32Ret;

// step: 海思系统初始化

s32Ret = Agora_Hi_SysInit();

if (HI_SUCCESS != s32Ret)

{

SAMPLE_PRT("Sys Init failed!n");

return;

}

/

step : agora init and join into the channel.

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

/

s32Ret = Agora_Chn_Init()

if (HI_SUCCESS != s32Ret)

{

SAMPLE_PRT("Agora_Chn_Init Init 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 Init failed!n");

return;

}

s32Ret = Agora_Enc_StartGetStream(sChnCount);

if (HI_SUCCESS != s32Ret)

{

SAMPLE_PRT("Start Venc failed!n");

return;

}

/

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

/

s32Ret = Agora_Hi_PicInit();

if (HI_SUCCESS != s32Ret)

{

SAMPLE_PRT("pic Init failed!n");

return;

}

while(1)

{

// TODO: recv the config cmd from tcplink

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

usleep(100000);

}

}

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

HI_S32 Agora_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 failed with %d!n", s32Ret);

return -1;

}

return HI_SUCCESS;

}

->AgoraRTC通道建立代码如下

HI_S32 Agora_Chn_Init(int32_t argc, char **argv)

{

int32_t rval;

SAMPLE_PRT("%s Welcome to RTSA SDK v%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 or Ctrl-C trigger stop

while (1) {

if (b_join_success_flag) {

break;

}

usleep(10000);

}

return rval;

}

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

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

HI_S32 Agora_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 vi failed!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 Venc failed!n");

return -1;

}

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

if (HI_SUCCESS != s32Ret)

{

SAMPLE_PRT("Start snap failed!n");

return -1;

}

s32Ret = Agora_Enc_BindVpss(VencChn, VpssGrp, VpssChn);

if (HI_SUCCESS != s32Ret)

{

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

return -1;

}

return HI_SUCCESS;

}

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

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

static void 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);
}

}

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

{

b_join_success_flag = 1;

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

}

static void 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 agora data channel.

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

/

sRet = Agora_Pic_SnapProcess();

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

return;

}

static agora_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/rts...

Agora使用cmake编译,按照README介绍,修改toolchain.cmake中的交叉编译器,按照图中部分
image
更新CMakelist.txt中的.c文件以及include和so库路径
image
编译成功,就可以运行啦!!!

本文介绍了海思芯片和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

阅读 61
0 声望
0 粉丝
0 条评论
0 声望
0 粉丝
宣传栏