前言
由於hi3516a海思自帶的開發應用程序是通過攝像頭接口或HDMI接口獲取視頻數據後並進行存儲。然而,在實際應用中,多是獲取數據後直接通過網絡把數據發送出去。那麼本文章將開始學習hi3516a獲取數據後通過網線和RTP協議把數據實時發送出去。
背景:hi3516a開發板通過HDMI接口獲取BT1120數據後進行壓縮,並通過RTP協議進行實時的視頻直播。
硬件平臺:hi3516a
軟件平臺:Hi3516A_SDK_V1.0.5.0
視頻數據接口:HDMI
無私分享,從我做起!
源碼解析
下面首先看主程序的源碼,源碼來源於網絡,一步一步進行分析。
int main(int argc, char* argv[])
{
int s32MainFd,temp;
struct timespec ts = { 0, 200000000 }; //200000000ns=200000us=200ms=0.2s
pthread_t id;
ringmalloc(1920*1080);//分配緩衝區並進行初始化
printf("RTSP server START\n");
PrefsInit();//設置服務器信息全局變量,獲取主機name
printf("listen for client connecting...\n");
signal(SIGINT, IntHandl); //異常中止信號處理
s32MainFd = tcp_listen(SERVER_RTSP_PORT_DEFAULT); //以非阻塞方式偵聽554端口
printf("s32MainFd=%d\r\n",s32MainFd);
if (ScheduleInit() == ERR_FATAL)
//線程的結構體進行初始化,創建處理主線程schedule_do,schedule_do裏調用sched[i].play_action把數據發送出去
{
fprintf(stderr,"Fatal: Can't start scheduler %s, %i \nServer is aborting.\n", __FILE__, __LINE__);
return 0;
}
RTP_port_pool_init(RTP_DEFAULT_PORT); //554,初始化10個RTP port
pthread_create(&id,NULL,SAMPLE_VENC_1080P_CLASSIC_RTSP,NULL); //海思芯片內部獲取編碼後數據
while (!g_s32Quit)
{
nanosleep(&ts, NULL);
EventLoop(s32MainFd);//RTSP服務器連接處理函數入口
}
sleep(2);
ringfree();
printf("The Server quit!\n");
return 0;
}
(1)void ringmalloc(int size)
其中ringmalloc(1920*1080);是分配緩衝區並進行初始化,我的hdmi數據數據分辨率是1920X1080p,下面看下ringmalloc()函數源碼。主要是分配64個1920X1080p的fifo空間並初始化一些參數。
/* 環形緩衝區的地址編號計算函數,如果到達喚醒緩衝區的尾部,將繞回到頭部。
環形緩衝區的有效地址編號爲:0到(NMAX-1)
*/
void ringmalloc(int size)
{
int i;
for(i =0; i<NMAX; i++) //64
{
ringfifo[i].buffer = malloc(size);
ringfifo[i].size = 0;
ringfifo[i].frame_type = 0;
printf("FIFO INFO:idx:%d,len:%d,ptr:%x\n",i,ringfifo[i].size,(int)(ringfifo[i].buffer));
}
iput = 0; /* 環形緩衝區的當前放入位置 */
iget = 0; /* 緩衝區的當前取出位置 */
n = 0; /* 環形緩衝區中的元素總數量 */
}
(2)void PrefsInit()
接着查看PrefsInit()函數,主要是設置服務器信息全局變量,獲取主機name。
void PrefsInit()
{
int l;
//設置服務器信息全局變量
stPrefs.port = SERVER_RTSP_PORT_DEFAULT; //554
gethostname(stPrefs.hostname,sizeof(stPrefs.hostname));
l=strlen(stPrefs.hostname);
if (getdomainname(stPrefs.hostname+l+1,sizeof(stPrefs.hostname)-l)!=0)
{
stPrefs.hostname[l]='.';
}
#ifdef RTSP_DEBUG
printf("-----------------------------------\n");
printf("\thostname is: %s\n", stPrefs.hostname);
printf("\trtsp listening port is: %d\n", stPrefs.port);
printf("\tInput rtsp://hostIP:%d/test.264 to play this\n",stPrefs.port);
printf("\n");
#endif
}
(3)s32MainFd = tcp_listen(SERVER_RTSP_PORT_DEFAULT);
接下來,s32MainFd = tcp_listen(SERVER_RTSP_PORT_DEFAULT);//以非阻塞方式偵聽554端口,554 是rtsp的默認端口號;具體網絡編程這一塊的知識參考傳送門。
int tcp_listen(unsigned short port)
{
int f;
int on=1;
struct sockaddr_in s;
int v = 1;
/*創建套接字*/
if((f = socket(AF_INET, SOCK_STREAM, 0))<0) //AF_INET ipv4 SOCK_STREAM 流模式,tcp
{
fprintf(stderr, "socket() error in tcp_listen.\n");
return -1;
}
/*設置socket的可選參數*/
setsockopt(f, SOL_SOCKET, SO_REUSEADDR, (char *) &v, sizeof(int));
s.sin_family = AF_INET;
s.sin_addr.s_addr = htonl(INADDR_ANY);
s.sin_port = htons(port);
/*綁定socket*/
if(bind(f, (struct sockaddr *)&s, sizeof(s)))
{
fprintf(stderr, "bind() error in tcp_listen");
return -1;
}
//設置爲非阻塞方式
if(ioctl(f, FIONBIO, &on) < 0)
{
fprintf(stderr, "ioctl() error in tcp_listen.\n");
return -1;
}
/*監聽*/
if(listen(f, SOMAXCONN) < 0)
{
fprintf(stderr, "listen() error in tcp_listen.\n");
return -1;
}
return f;
}
(4)int ScheduleInit()
接下來是便是調用ScheduleInit()函數,該函數對線程的結構體進行初始化,創建處理主線程schedule_do,schedule_do裏調用sched[i].play_action把數據發送出去。ScheduleInit()函數是本文章分析的重點,下面來詳細分析該函數。
int ScheduleInit()
{
int i;
pthread_t thread=0;
/*初始化數據*/
for(i=0; i<MAX_CONNECTION; ++i) //MAX_CONNECTION=10,最大允許連接10個客戶端
{
sched[i].rtp_session=NULL;
sched[i].play_action=NULL;
sched[i].valid=0;
sched[i].BeginFrame=0;
}
/*創建處理主線程*/
pthread_create(&thread,NULL,schedule_do,NULL); //創建schedule_do線程
return 0;
}
schedule_do線程的源碼如下:
void *schedule_do(void *arg)
{
int i=0;
struct timeval now;
unsigned long long mnow;
char *pDataBuf, *pFindNal;
unsigned int ringbuffer;
struct timespec ts = {0,33333}; //33333ns=33.333us
int s32FileId;
unsigned int u32NaluToken;
char *pNalStart=NULL;
int s32NalSize;
int s32FindNal = 0;
int buflen=0,ringbuflen=0,ringbuftype;
struct ringbuf ringinfo;
#ifdef RTSP_DEBUG
printf("rtsputils.c %d :The pthread %s start\n",__LINE__ , __FUNCTION__);
#endif
do
{
nanosleep(&ts, NULL); //sleep()-------以秒爲單位,usleep()----以微秒爲單位,nanosleep( )---------以納秒爲單位
s32FindNal = 0; //客戶端連接標識清零
//如果有客戶端連接,則g_s32DoPlay大於零
//if(g_s32DoPlay>0)
{
ringbuflen = ringget(&ringinfo);
if(ringbuflen ==0)
continue ;
}
printf("ringbuflen=%d\r\n",ringbuflen);
s32FindNal = 1;//有客戶端連接,置位
for(i=0; i<MAX_CONNECTION; ++i) //10
{
if(sched[i].valid) //如果有某個客戶端連接了,那麼sched[i].valid會在schedule_add()函數中被置爲1
{
if(!sched[i].rtp_session->pause)
//若sched[i].rtp_session->pause爲0 ,即表示沒有被暫停,是在播放中,即進行下面的數據處理和發送
{
//計算時間戳
gettimeofday(&now,NULL);
mnow = (now.tv_sec*1000 + now.tv_usec/1000);//毫秒
if((sched[i].rtp_session->hndRtp)&&(s32FindNal))
//若sched[i].rtp_session->hndRtp不是空 且s32FindNal有客戶端連接
{
buflen=ringbuflen;
//調用play_action函數把數據發送出去
sched[i].play_action((unsigned int)(sched[i].rtp_session->hndRtp), ringinfo.buffer, ringinfo.size, mnow);
}
}
}
}
}
while(!stop_schedule); //如果stop_schedule被置位,則跳出do循環
cleanup:
//free(pDataBuf);
//close(s32FileId);
#ifdef RTSP_DEBUG
printf("The pthread %s end\n", __FUNCTION__);
#endif
return ERR_NOERROR;
}
sched[i].play_action是執行發送數據的函數,該函數在schedule_add函數中被配置爲RtpSend。
//把RTP會話添加進schedule中,錯誤返回-1,正常返回schedule隊列號
int schedule_add(RTP_session *rtp_session)
{
int i;
for(i=0; i<MAX_CONNECTION; ++i)
{
/*需是還沒有被加入到調度隊列中的會話*/
if(!sched[i].valid)
{
sched[i].valid=1;
sched[i].rtp_session=rtp_session;
//設置播放動作
sched[i].play_action=RtpSend;
printf("rtsputils.c **adding a schedule object action %s,%d**\n", __FILE__, __LINE__);
return i;
}
}
return ERR_GENERIC;
}
下面來分析RtpSend函數。
unsigned int RtpSend(unsigned int u32Rtp, char *pData, int s32DataSize, unsigned int u32TimeStamp)
{
int s32NalSize = 0;
char *pNalBuf, *pDataEnd;
HndRtp hRtp = (HndRtp)u32Rtp;
unsigned int u32NaluToken;
hRtp->u32TimeStampCurr = u32TimeStamp;
printf("rtputils.c %d: hRtp->emPayload=%x\r\n",__LINE__,hRtp->emPayload);
if(_h264 == hRtp->emPayload) //實際打印信息,hRtp->emPayload=101,不進入這個分支
{
pDataEnd = pData + s32DataSize;
//搜尋第一個nalu起始標誌0x01000000
for(; pData < pDataEnd-5; pData ++)
{
memcpy(&u32NaluToken, pData, 4 * sizeof(char));
if(0x01000000 == u32NaluToken)
{
//標記nalu起始位置
pData += 4;
pNalBuf = pData;
break;
}
}
//發送nalu
for(; pData < pDataEnd-5; pData ++)
{
//搜尋第二個nalu起始標誌0x01000000,找到nalu起始位置,發送該nalu數據
memcpy(&u32NaluToken, pData, 4 * sizeof(char));
if(0x01000000 == u32NaluToken)
{
s32NalSize = (int)(pData - pNalBuf);//二者一相減就是第一個nalu的內容
/*
if (first_in == 1)
{
tmp_file=fopen("tmpfile.mp4","wb");
first_in = 0;
}
fwrite(pNalBuf,s32NalSize,1,tmp_file);
fflush(tmp_file);
if(SendNalu264(hRtp, pNalBuf, s32NalSize) == -1)
{
return -1;
}*/
//標記nalu起始位置
pData += 4;
pNalBuf = pData;
}
}//while
if(pData > pNalBuf)//最後一個nalu
{
s32NalSize = (int)(pData - pNalBuf);
if(SendNalu264(hRtp, pNalBuf, s32NalSize) == -1)
{
return -1;
}
}
}
else if(_h264nalu == hRtp->emPayload) ////實際打印信息,hRtp->emPayload=101,進入這個分支中
{
if(SendNalu264(hRtp, pData, s32DataSize) == -1)//直接發送NALU單元,不需要分離NALU
{
//在rtsp的setup階段時創建RTP套接字時設置了負荷類型爲_h264nalu,所以程序執行這個分支
//原因3516編碼已經幫我們分開了每一個H264的slice,即直接是一個NALU,所以直接添加東西組成RTP即可
{
return -1;
}
}
}
else if(_g711 == hRtp->emPayload)
{
if(SendNalu711(hRtp, pData, s32DataSize) == -1)
{
return -1;
}
}
else
{
return -1;
}
return 0;
}
測試的打印信息如下:
SendNalu264函數則是把原始的h264編碼數據封包爲RTP數據包,並以udp協議發送出去。詳細分析見傳送門。
至此,ScheduleInit()函數的主線分析完成。
(5)SAMPLE_VENC_1080P_CLASSIC_RTSP
下面繼續分析SAMPLE_VENC_1080P_CLASSIC_RTSP線程,該線程從HDMI接口獲取數據進行編碼後,把H264原始碼流存入ringfifo中。
/******************************************************************************
* function : H.264@1080p@30fps
******************************************************************************/
HI_VOID* SAMPLE_VENC_1080P_CLASSIC_RTSP(HI_VOID* p)
{
PAYLOAD_TYPE_E enPayLoad = {PT_H264};
PIC_SIZE_E enSize = {PIC_HD1080};
HI_U32 u32Profile = 0;
VB_CONF_S stVbConf;
SAMPLE_VI_CONFIG_S stViConfig = {0};
VPSS_GRP VpssGrp;
VPSS_CHN VpssChn;
VPSS_GRP_ATTR_S stVpssGrpAttr;
VPSS_CHN_ATTR_S stVpssChnAttr;
VPSS_CHN_MODE_S stVpssChnMode;
VENC_CHN VencChn;
SAMPLE_RC_E enRcMode = SAMPLE_RC_CBR;
HI_S32 s32ChnNum;
HI_S32 s32Ret = HI_SUCCESS;
HI_U32 u32BlkSize;
SIZE_S stSize;
char c;
s32ChnNum = 1;
/******************************************
step 1: init sys variable
******************************************/
memset(&stVbConf, 0, sizeof(VB_CONF_S));
SAMPLE_COMM_VI_GetSizeBySensor(&enSize);
stVbConf.u32MaxPoolCnt = 128;
/*video buffer*/
u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm, \
enSize, SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
stVbConf.astCommPool[0].u32BlkSize = u32BlkSize;
stVbConf.astCommPool[0].u32BlkCnt = 20;
/******************************************
step 2: mpp system init.
******************************************/
s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("system init failed with %d!\n", s32Ret);
goto END_VENC_1080P_CLASSIC_0;
}
/******************************************
step 3: start vi dev & chn to capture
******************************************/
// stViConfig.enViMode = SENSOR_TYPE;
stViConfig.enViMode = SAMPLE_VI_MODE_BT1120_1080P;
stViConfig.enRotate = ROTATE_NONE;
stViConfig.enNorm = VIDEO_ENCODING_MODE_AUTO;
stViConfig.enViChnSet = VI_CHN_SET_NORMAL;
stViConfig.enWDRMode = WDR_MODE_NONE;
s32Ret = SAMPLE_COMM_VI_StartVi(&stViConfig);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("start vi failed!\n");
goto END_VENC_1080P_CLASSIC_1;
}
/******************************************
step 4: start vpss and vi bind vpss
******************************************/
s32Ret = SAMPLE_COMM_SYS_GetPicSize(gs_enNorm, enSize, &stSize);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
goto END_VENC_1080P_CLASSIC_1;
}
VpssGrp = 0;
stVpssGrpAttr.u32MaxW = stSize.u32Width;
stVpssGrpAttr.u32MaxH = stSize.u32Height;
stVpssGrpAttr.bIeEn = HI_FALSE;
stVpssGrpAttr.bNrEn = HI_TRUE;
stVpssGrpAttr.bHistEn = HI_FALSE;
stVpssGrpAttr.bDciEn = HI_FALSE;
stVpssGrpAttr.enDieMode = VPSS_DIE_MODE_NODIE;
stVpssGrpAttr.enPixFmt = SAMPLE_PIXEL_FORMAT;
s32Ret = SAMPLE_COMM_VPSS_StartGroup(VpssGrp, &stVpssGrpAttr);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("Start Vpss failed!\n");
goto END_VENC_1080P_CLASSIC_2;
}
s32Ret = SAMPLE_COMM_VI_BindVpss(stViConfig.enViMode);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("Vi bind Vpss failed!\n");
goto END_VENC_1080P_CLASSIC_3;
}
VpssChn = 1;
stVpssChnMode.enChnMode = VPSS_CHN_MODE_USER;
stVpssChnMode.bDouble = HI_FALSE;
stVpssChnMode.enPixelFormat = SAMPLE_PIXEL_FORMAT;
stVpssChnMode.u32Width = 1920;
stVpssChnMode.u32Height = 1080;
stVpssChnMode.enCompressMode = COMPRESS_MODE_NONE;
memset(&stVpssChnAttr, 0, sizeof(stVpssChnAttr));
stVpssChnAttr.s32SrcFrameRate = -1;
stVpssChnAttr.s32DstFrameRate = -1;
s32Ret = SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("Enable vpss chn failed!\n");
goto END_VENC_1080P_CLASSIC_4;
}
/******************************************
step 5: start stream venc
******************************************/
/*** HD1080P **/
printf("\t c) cbr.\n");
enRcMode = SAMPLE_RC_CBR;
VpssGrp = 0;
VpssChn = 1;
VencChn = 0;
s32Ret = SAMPLE_COMM_VENC_Start(VencChn, enPayLoad, \
gs_enNorm, PIC_HD1080, enRcMode, u32Profile);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("Start Venc failed!\n");
goto END_VENC_1080P_CLASSIC_5;
}
s32Ret = SAMPLE_COMM_VENC_BindVpss(VencChn, VpssGrp, VpssChn);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("Start Venc failed!\n");
goto END_VENC_1080P_CLASSIC_5;
}
/******************************************
step 6: stream venc process -- get stream, then save it to file.
******************************************/
s32Ret = SAMPLE_COMM_VENC_StartGetStream_RTSP(s32ChnNum);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("Start Venc failed!\n");
goto END_VENC_1080P_CLASSIC_5;
}
printf("please press twice ENTER to exit this sample\n");
getchar();
getchar();
/******************************************
step 7: exit process
******************************************/
SAMPLE_COMM_VENC_StopGetStream();
END_VENC_1080P_CLASSIC_5:
VpssGrp = 0;
VpssChn = 0;
VencChn = 0;
SAMPLE_COMM_VENC_UnBindVpss(VencChn, VpssGrp, VpssChn);
SAMPLE_COMM_VENC_Stop(VencChn);
SAMPLE_COMM_VI_UnBindVpss(stViConfig.enViMode);
END_VENC_1080P_CLASSIC_4: //vpss stop
VpssGrp = 0;
VpssChn = 0;
SAMPLE_COMM_VPSS_DisableChn(VpssGrp, VpssChn);
END_VENC_1080P_CLASSIC_3: //vpss stop
SAMPLE_COMM_VI_UnBindVpss(stViConfig.enViMode);
END_VENC_1080P_CLASSIC_2: //vpss stop
SAMPLE_COMM_VPSS_StopGroup(VpssGrp);
END_VENC_1080P_CLASSIC_1: //vi stop
SAMPLE_COMM_VI_StopVi(&stViConfig);
END_VENC_1080P_CLASSIC_0: //system exit
SAMPLE_COMM_SYS_Exit();
return;
}
繼續分析s32Ret = SAMPLE_COMM_VENC_StartGetStream_RTSP(s32ChnNum);
/******************************************************************************
* funciton : start get venc stream process thread
******************************************************************************/
HI_S32 SAMPLE_COMM_VENC_StartGetStream_RTSP(HI_S32 s32Cnt)
{
gs_stPara.bThreadStart = HI_TRUE;
gs_stPara.s32Cnt = s32Cnt;
return pthread_create(&gs_VencPid, 0, SAMPLE_COMM_VENC_GetVencStreamProc_RTSP, (HI_VOID*)&gs_stPara);
}
HI_VOID* SAMPLE_COMM_VENC_GetVencStreamProc_RTSP(HI_VOID* p)
{
HI_S32 i;
HI_S32 s32ChnTotal;
VENC_CHN_ATTR_S stVencChnAttr;
SAMPLE_VENC_GETSTREAM_PARA_S* pstPara;
HI_S32 maxfd = 0;
struct timeval TimeoutVal;
fd_set read_fds;
HI_S32 VencFd[VENC_MAX_CHN_NUM];
HI_CHAR aszFileName[VENC_MAX_CHN_NUM][FILE_NAME_LEN];
FILE* pFile[VENC_MAX_CHN_NUM];
char szFilePostfix[10];
VENC_CHN_STAT_S stStat;
VENC_STREAM_S stStream;
HI_S32 s32Ret;
VENC_CHN VencChn;
PAYLOAD_TYPE_E enPayLoadType[VENC_MAX_CHN_NUM];
pstPara = (SAMPLE_VENC_GETSTREAM_PARA_S*)p;
s32ChnTotal = pstPara->s32Cnt;
/******************************************
step 1: check & prepare save-file & venc-fd
******************************************/
if (s32ChnTotal >= VENC_MAX_CHN_NUM)
{
SAMPLE_PRT("input count invaild\n");
return NULL;
}
for (i = 0; i < s32ChnTotal; i++)
{
/* decide the stream file name, and open file to save stream */
VencChn = i;
s32Ret = HI_MPI_VENC_GetChnAttr(VencChn, &stVencChnAttr);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("HI_MPI_VENC_GetChnAttr chn[%d] failed with %#x!\n", \
VencChn, s32Ret);
return NULL;
}
enPayLoadType[i] = stVencChnAttr.stVeAttr.enType;
s32Ret = SAMPLE_COMM_VENC_GetFilePostfix(enPayLoadType[i], szFilePostfix);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("SAMPLE_COMM_VENC_GetFilePostfix [%d] failed with %#x!\n", \
stVencChnAttr.stVeAttr.enType, s32Ret);
return NULL;
}
snprintf(aszFileName[i], FILE_NAME_LEN, "stream_chn%d%s", i, szFilePostfix);
/*
pFile[i] = fopen(aszFileName[i], "wb");
if (!pFile[i])
{
SAMPLE_PRT("open file[%s] failed!\n",
aszFileName[i]);
return NULL;
}*/
/* Set Venc Fd. */
VencFd[i] = HI_MPI_VENC_GetFd(i);
if (VencFd[i] < 0)
{
SAMPLE_PRT("HI_MPI_VENC_GetFd failed with %#x!\n",
VencFd[i]);
return NULL;
}
if (maxfd <= VencFd[i])
{
maxfd = VencFd[i];
}
}
/******************************************
step 2: Start to get streams of each channel.
******************************************/
while (HI_TRUE == pstPara->bThreadStart)
{
FD_ZERO(&read_fds);
for (i = 0; i < s32ChnTotal; i++)
{
FD_SET(VencFd[i], &read_fds);
}
TimeoutVal.tv_sec = 2;
TimeoutVal.tv_usec = 0;
s32Ret = select(maxfd + 1, &read_fds, NULL, NULL, &TimeoutVal);
if (s32Ret < 0)
{
SAMPLE_PRT("select failed!\n");
break;
}
else if (s32Ret == 0)
{
SAMPLE_PRT("get venc stream time out, exit thread\n");
continue;
}
else
{
for (i = 0; i < s32ChnTotal; i++)
{
if (FD_ISSET(VencFd[i], &read_fds))
{
/*******************************************************
step 2.1 : query how many packs in one-frame stream.
*******************************************************/
memset(&stStream, 0, sizeof(stStream));
s32Ret = HI_MPI_VENC_Query(i, &stStat);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("HI_MPI_VENC_Query chn[%d] failed with %#x!\n", i, s32Ret);
break;
}
/*******************************************************
step 2.2 : malloc corresponding number of pack nodes.
*******************************************************/
stStream.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks);
if (NULL == stStream.pstPack)
{
SAMPLE_PRT("malloc stream pack failed!\n");
break;
}
/*******************************************************
step 2.3 : call mpi to get one-frame stream
*******************************************************/
stStream.u32PackCount = stStat.u32CurPacks;
s32Ret = HI_MPI_VENC_GetStream(i, &stStream, HI_TRUE);
if (HI_SUCCESS != s32Ret)
{
free(stStream.pstPack);
stStream.pstPack = NULL;
SAMPLE_PRT("HI_MPI_VENC_GetStream failed with %#x!\n", \
s32Ret);
break;
}
/*******************************************************
step 2.4 : save frame to file
*******************************************************/
HisiPutH264DataToBuffer(&stStream);
/*s32Ret = SAMPLE_COMM_VENC_SaveStream(enPayLoadType[i], pFile[i], &stStream);
if (HI_SUCCESS != s32Ret)
{
free(stStream.pstPack);
stStream.pstPack = NULL;
SAMPLE_PRT("save stream failed!\n");
break;
}*/
/*******************************************************
step 2.5 : release stream
*******************************************************/
s32Ret = HI_MPI_VENC_ReleaseStream(i, &stStream);
if (HI_SUCCESS != s32Ret)
{
free(stStream.pstPack);
stStream.pstPack = NULL;
break;
}
/*******************************************************
step 2.6 : free pack nodes
*******************************************************/
free(stStream.pstPack);
stStream.pstPack = NULL;
}
}
}
}
/*******************************************************
* step 3 : close save-file
*******************************************************/
for (i = 0; i < s32ChnTotal; i++)
{
fclose(pFile[i]);
}
return NULL;
}
如上,原始的應用程序是把編碼後的數據存入文件中,調用s32Ret = SAMPLE_COMM_VENC_SaveStream(enPayLoadType[i], pFile[i], &stStream);本示例中將該部分屏蔽,我們是要把編碼的數據以RTSP的協議發送出去。繼續分析HisiPutH264DataToBuffer。
/**************************************************************************************************
**把數據VENC_STREAM_S *pstStream裏的數據放到ringfifo[iput].buffer中
**************************************************************************************************/
HI_S32 HisiPutH264DataToBuffer(VENC_STREAM_S *pstStream)
{
HI_S32 i,j,x;
HI_S32 len=0,off=0,len2=2,uplen=0;
unsigned char *pstr;
int iframe=0;
for (i = 0; i < pstStream->u32PackCount; i++)
{
len+=pstStream->pstPack[i].u32Len;//pstStream->u32PackCount這些包的數據總長度
}
if(n<NMAX)
{
for (i = 0; i < pstStream->u32PackCount; i++)//把pstStream->u32PackCount這麼多包的數據都放到ringfifo[iput].buffer裏
{
memcpy(ringfifo[iput].buffer+off,pstStream->pstPack[i].pu8Addr,pstStream->pstPack[i].u32Len);
off+=pstStream->pstPack[i].u32Len;
}
ringfifo[iput].size= len;//pstStream->u32PackCount這麼多包的數據的總長度
iput = addring(iput); //input循環自加1
n++;
}
return HI_SUCCESS;
}
最後,EventLoop(s32MainFd)函數的分析將在下一篇文章進行解析,這部分功能是搭建RTSP服務器並偵聽RTSP的連接和處理暫停和播放等操作的處理。