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

这种工作模式

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

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

/******************************************************************************
* funciton : save H264 stream
******************************************************************************/
HI_S32 SAMPLE_COMM_VENC_SaveH264(FILE* fpH264File, VENC_STREAM_S *pstStream)
{
    HI_S32 i;

    for (i = 0; i < pstStream->u32PackCount; i++)
    {
        fwrite(pstStream->pstPack[i].pu8Addr+pstStream->pstPack[i].u32Offset,
               pstStream->pstPack[i].u32Len-pstStream->pstPack[i].u32Offset, 1, fpH264File);

        fflush(fpH264File);
    }
    

    return HI_SUCCESS;
}

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

HI_S32 SAMPLE_COMM_VENC_SaveH264(FILE* fpH264File, VENC_STREAM_S *pstStream)
{
    unsigned char *h264_buf;
    int i ;

    for (i = 0; i < pstStream->u32PackCount; i++)
    {
        h264_buf = pstStream->pstPack[i].pu8Addr[0];
        printf("%x %x %x %x\n",h264[0],h264[1],h264[2],h264[3],h264[4]);
        if(pstStream->pstPack[i].u32Len[1]>0)
        {

        }
    }
   printf("**********************\n");
    return HI_SUCCESS;
}

然后去执行,打印如下图

在这里插入图片描述
如图打印所示,我们可以得出结论

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]里,如下图注释:

HI_S32 SAMPLE_COMM_VENC_SaveH264(FILE* fpH264File, VENC_STREAM_S *pstStream)
{
    unsigned char *h264_buf;
    int i ;

    for (i = 0; i < pstStream->u32PackCount; i++)  //NAL个数,当是IDR帧的时候 patPacket->u32PackCout是4,如果是P帧的时候则是1
    {
        h264_buf = pstStream->pstPack[i].pu8Addr[0];  //NAL存储的地址
        //IDR帧是0  0  0  1  67
        //       0  0  0  1  68
        //       0  0  0  1  6
        //       0  0  0  1  65

        //P帧是0  0  0  1  61

        printf("%x %x %x %x\n",h264[0],h264[1],h264[2],h264[3],h264[4]);
        
        if(pstStream->pstPack[i].u32Len[1]>0) //如果大于0代表这个NAL的地址不是连续的,
                                             //剩下的地址在pstStream->pstPack[i].pu8Addr[1],
                                            //长度为pstStream->pstPack[i].u32Len[1]。
        {

        }
    }
   printf("**********************\n");
    return HI_SUCCESS;
}

我现在将这个函数再改为下面这样,只为将分开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 *****************/改为上面那样就行
然后编译运行成功显示出来

参考文章:
《ffmpeg 提取mp4里的h264流和pcm流》

《海思3531下的mp4视频播放器》

ffmpeg pcm编码aac》

《ffmpeg aac解码pcm》

《ffmpeg解码mp4视频数据保存成图片》

《centos下安装ffmpeg加上fdk-aac的支持》

参考原文连接:

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐