欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

海思3531 一如何实时观看摄像头VENC编码后的视频

程序员文章站 2024-03-24 21:30:04
...

要想看到摄像头图像实时VENC编码后的视频和画质,需要海思开启VI->VPSS->虚拟VO->VENC->视频队列->VDEC->VO 

这种工作模式

先打开海思SDK的mpp/samole/comom/sample_comm_venc.c文件

在第199行我们可以看到SAMPLE_COMM_VENC_SaveH264()这条函数,这条函数就是保存h264数据成为视频的。

海思3531 一如何实时观看摄像头VENC编码后的视频

现在将这个条函数修改如下图

海思3531 一如何实时观看摄像头VENC编码后的视频

然后去执行,打印如下图

海思3531 一如何实时观看摄像头VENC编码后的视频

如图打印所示,我们可以得出结论

pstStream->u32PacketCount是代表这次有多少个h264的NAL,pstStream->pstPack[i]->pu8Addr[0] 是数据开始存储的地址,pstStream->pstPack[i].u32Len[0] 代表这次数据有多长,当pstStream->pstPack[i].u32Len[1]>0的时候代表这段NAL还有一个地址空间的数据是在pstStream->pstPack[i]->pu8Addr[1]里,如下图注释:

海思3531 一如何实时观看摄像头VENC编码后的视频


我现在将这个函数再改为下面这样,只为将分开IDR帧数据连接起来入队列,如果是P帧也入队列。

HI_S32 SAMPLE_COMM_VENC_SaveH264(FILE* fpH264File, VENC_STREAM_S *pstStream)
{
    HI_U32 i;
    int data_len=0,buf_offset;
    unsigned char *buf=NULL;
    for(i=0;i<pstStream->u32PackCount;i++)   //计算长度
    {
        data_len+=pstStream->pstPack[i].u32Len[0];
        if(pstStream->pstPack[i].u32Len[1]>0)
        {
            data_len+=pstStream->pstPack[i].u32Len[1];
        }
    }

    buf=malloc(data_len);//根据长度分配空间
    if(NULL==buf)
    {
      printf("molloc buf error");
      return -1;
    }
    buf_offset=0;
    for(i=0;i<pstStream->u32PackCount;i++)//将数据装入buf
    {
        memcpy(buf+buf_offset,pstStream->pstPack[i].pu8Addr[0],pstStream->pstPack[i].u32Len[0]);
        buf_offset+=pstStream->pstPack[i].u32Len[0];
        if(pstStream->pstPack[i].u32Len[1]>0)
        {
            memcpy(buf+buf_offset,pstStream->pstPack[i].pu8Addr[1],pstStream->pstPack[i].u32Len[1]);
            buf_offset+=pstStream->pstPack[i].u32Len[i];
        }
    }
    inQueue(buf,buf_offset,0);//数据如队列
    return HI_SUCCESS;
}

数据入队列之后我们主要是看他是如何读取h264数据的显示出来主要程序是在mpp/sample/vdec/sample_vdec.c

void* SAMPLE_VDEC_SendStream(void* p)
{
    VDEC_STREAM_S stStream;
    SAMPLE_VDEC_SENDPARAM_S *pstSendParam;
    char sFileName[50], sFilePostfix[20];
    FILE* fp = NULL;
    HI_S32 s32BlockMode = HI_IO_BLOCK;
    HI_U8 *pu8Buf;
    HI_U64 u64pts;
    HI_S32 s32IntervalTime = 40000;
    HI_S32 i, s32Ret, len, start;
    HI_S32 s32UsedBytes, s32ReadLen;
    HI_BOOL bFindStart, bFindEnd;

    start = 0;
    u64pts= 0;
    s32UsedBytes = 0;
    pstSendParam = (SAMPLE_VDEC_SENDPARAM_S *)p;

    /******************* open the stream file *****************/
    SAMPLE_COMM_SYS_Payload2FilePostfix(pstSendParam->enPayload, sFilePostfix);
    sprintf(sFileName, "stream_chn0%s", sFilePostfix);
     fp = fopen(sFileName, "r");
    if (HI_NULL == fp)
    {
        SAMPLE_PRT("can't open file %s in send stream thread:%d\n", sFileName,pstSendParam->VdChn);
        return (HI_VOID *)(HI_FAILURE);
    }
    printf("open file [%s] ok in send stream thread:%d!\n", sFileName,pstSendParam->VdChn);


    /******************* malloc the  stream buffer in user space *****************/
    if(pstSendParam->s32MinBufSize!=0)
    {
        pu8Buf=malloc(pstSendParam->s32MinBufSize);
        if(pu8Buf==NULL)
        {
            SAMPLE_PRT("can't alloc %d in send stream thread:%d\n",pstSendParam->s32MinBufSize,pstSendParam->VdChn);
            fclose(fp);
            return (HI_VOID *)(HI_FAILURE);
        }
    }
    else
    {
        SAMPLE_PRT("none buffer to operate in send stream thread:%d\n",pstSendParam->VdChn);
        return (HI_VOID *)(HI_FAILURE);
    }

    while (pstSendParam->bRun)
    {
        fseek(fp, s32UsedBytes, SEEK_SET);
        s32ReadLen = fread(pu8Buf, 1, pstSendParam->s32MinBufSize, fp);
        if (s32ReadLen<=0)
        {
             printf("file end.\n");
             break;
        }

        /******************* cutting the stream for frame *****************/
        if( (pstSendParam->enVideoMode==VIDEO_MODE_FRAME) && (pstSendParam->enPayload== PT_H264) )
        {
            bFindStart = HI_FALSE;
            bFindEnd   = HI_FALSE;
            for (i=0; i<s32ReadLen-5; i++)
            {
                if (  pu8Buf[i  ] == 0 && pu8Buf[i+1] == 0 && pu8Buf[i+2] == 1 &&
                    ((pu8Buf[i+3]&0x1F) == 0x5 || (pu8Buf[i+3]&0x1F) == 0x1) &&
                    ((pu8Buf[i+4]&0x80) == 0x80)
                   )
                {
                    bFindStart = HI_TRUE;
                    i += 4;
                    break;
                }
            }
            for (; i<s32ReadLen-5; i++)
            {
                if (  pu8Buf[i  ] == 0 && pu8Buf[i+1] == 0 && pu8Buf[i+2] == 1 &&
                    ((pu8Buf[i+3]&0x1F) == 0x5 || (pu8Buf[i+3]&0x1F) == 0x1) &&
                    ((pu8Buf[i+4]&0x80) == 0x80)
                   )
                {
                    bFindEnd = HI_TRUE;
                    break;
                }
            }
            s32ReadLen = i;
            if (bFindStart == HI_FALSE)
            {
                SAMPLE_PRT("can not find start code in send stream thread:%d\n",pstSendParam->VdChn);
            }
            else if (bFindEnd == HI_FALSE)
            {
                s32ReadLen = i+5;
            }
        }
        else if( (pstSendParam->enPayload== PT_JPEG) || (pstSendParam->enPayload == PT_MJPEG) )
        {
            bFindStart = HI_FALSE;
            bFindEnd   = HI_FALSE;
            for (i=0; i<s32ReadLen-2; i++)
            {
                if (pu8Buf[i] == 0xFF && pu8Buf[i+1] == 0xD8)
                {
                    start = i;
                    bFindStart = HI_TRUE;
                    i = i + 2;
                    break;
                }
            }
            for (; i<s32ReadLen-4; i++)
            {
                if ((pu8Buf[i] == 0xFF) && (pu8Buf[i+1]& 0xF0) == 0xE0)
                {
                     len = (pu8Buf[i+2]<<8) + pu8Buf[i+3];
                     i += 1 + len;
                }
                else
                {
                    break;
                }
            }
            for (; i<s32ReadLen-2; i++)
            {
                if (pu8Buf[i] == 0xFF && pu8Buf[i+1] == 0xD8)

            s32ReadLen = i;
            if (bFindStart == HI_FALSE)
            {
                printf("\033[0;31mALERT!!!,can not find start code in send stream thread:%d!!!\033[0;39m\n",
                pstSendParam->VdChn);
            }
            else if (bFindEnd == HI_FALSE)
            {
                s32ReadLen = i+2;
            }
        }
         stStream.u64PTS  = u64pts;
         stStream.pu8Addr = pu8Buf + start;
         stStream.u32Len  = s32ReadLen;
        if(pstSendParam->enVideoMode==VIDEO_MODE_FRAME)
        {
            u64pts+=40000;
        }
        /******************* send stream *****************/
        if (s32BlockMode == HI_IO_BLOCK)
        {
            s32Ret=HI_MPI_VDEC_SendStream(pstSendParam->VdChn, &stStream, HI_IO_BLOCK);
        }
        else if (s32BlockMode == HI_IO_NOBLOCK)
        {
            s32Ret=HI_MPI_VDEC_SendStream(pstSendParam->VdChn, &stStream, HI_IO_NOBLOCK);
        }
        else
        {
            s32Ret=HI_MPI_VDEC_SendStream_TimeOut(pstSendParam->VdChn, &stStream, 8000);
        }

        if (HI_SUCCESS == s32Ret)
        {
            s32UsedBytes = s32UsedBytes +s32ReadLen + start;
        }
        else
        {
            if (s32BlockMode != HI_IO_BLOCK)
            {
                SAMPLE_PRT("failret:%x\n",s32Ret);
            }
            usleep(s32IntervalTime);
        }
        usleep(20000);

    }
        printf("send steam thread %d return ...\n", pstSendParam->VdChn);
        fflush(stdout);
        if (pu8Buf != HI_NULL)
        {
        free(pu8Buf);
        }       
        fclose(fp);
      return (HI_VOID *)HI_SUCCESS
}
主要是这一条是显示函数,其实它是获取h264文件里的IDR帧和P帧然后在再送去显示
因为我们入队列已经是IDR帧和P帧,所以我们直接将数据出队列提交给它就行,主要改动如下
...
  while (pstSendParam->bRun)
    {
        while(isEmptyQueue());
        a=(struct data_infornation *)outQueue();
        memcpy(pu8Buf,a->buf,a->len);
        s32ReadLen=a->len;
        free(a->buf);
        free(a);
        /******************* cutting the stream for frame *****************/
        if( (pstSendParam->enVideoMode==VIDEO_MODE_FRAME) && (pstSendParam->enPayload== PT_H264) )
.....
在while(pstSendParam->bRun) 到/******************* cutting the stream for frame *****************/改为上面那样就行
然后编译运行成功显示出来