hi3516a——RTSP播放H.264視頻流(1)

前言

由於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的連接和處理暫停和播放等操作的處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章