海思Hi3531A 如何实时观看摄像头VENC编码后的视频
要想看到摄像头图像实时VENC编码后的视频和画质,需要海思开启VI->VPSS->虚拟VO->VENC->视频队列->VDEC->VO这种工作模式先打开海思SDK的mpp/samole/comom/sample_comm_venc.c文件在第199行我们可以看到SAMPLE_COMM_VENC_SaveH264()这条函数,这条函数就是保存h264数据成为视频的
要想看到摄像头图像实时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 *****************/改为上面那样就行
然后编译运行成功显示出来
更多推荐
所有评论(0)