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的连接和处理暂停和播放等操作的处理。

猜你喜欢

转载自blog.csdn.net/u014470361/article/details/89512512