使用MPP平台解码H264文件
在MPP平台下解码H264文件需要使用VDEC 模块,即视频解码模块。解码过程如下所示:
将H264码流文件传入解码模块后,会解码后生成的yuv文件。
1.主程序解析
1.1 初始化MPP平台
memset(&stContext.mSysConf, 0, sizeof(MPP_SYS_CONF_S));
stContext.mSysConf.nAlignWidth = 32;
AW_MPI_SYS_SetConf(&stContext.mSysConf);
AW_MPI_SYS_Init();
1.2 配置解码通道参数
memset(&stContext.mVDecAttr, 0, sizeof(stContext.mVDecAttr));
stContext.mVDecAttr.mType = stContext.mConfigPara.mType;
stContext.mVDecAttr.mOutputPixelFormat = MM_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
stContext.mVDecAttr.mBufSize = 2048*1024;
stContext.mVDecAttr.mPicWidth = 1920;
stContext.mVDecAttr.mPicHeight = 1088;
1.3 创建解码通道
ERRORTYPE ret;
BOOL bSuccessFlag = FALSE;
stContext.mVDecChn = 0;
while (stContext.mVDecChn < VDEC_MAX_CHN_NUM)
{
ret = AW_MPI_VDEC_CreateChn(stContext.mVDecChn, &stContext.mVDecAttr);
if(SUCCESS == ret)
{
bSuccessFlag = TRUE;
alogd("create VDec channel[%d] success!", stContext.mVDecChn);
break;
}
else if (ERR_VDEC_EXIST == ret)
{
alogd("VDec channel[%d] exist, find next!", stContext.mVDecChn);
stContext.mVDecChn++;
}
else
{
aloge("create VDec channel[%d] fail! ret[0x%x]!", stContext.mVDecChn, ret);
break;
}
}
if (FALSE == bSuccessFlag)
{
stContext.mVDecChn = MM_INVALID_CHN;
aloge("fatal error! create VDec channel fail!");
}
1.4 设置解码通道回调函数
MPPCallbackInfo cbInfo;
cbInfo.cookie = (void*)&stContext;
cbInfo.callback = (MPPCallbackFuncType)&SampleVDecCallbackWrapper;
AW_MPI_VDEC_RegisterCallback(stContext.mVDecChn, &cbInfo);
1.5 开启解码通道
AW_MPI_VDEC_StartRecvStream(stContext.mVDecChn);
1.6 初始化数据buf
char DstPath[128] = {0};
VDEC_STREAM_S nStreamInfo;
VIDEO_FRAME_INFO_S nFrameInfo;
memset(&nStreamInfo, 0, sizeof(nStreamInfo));
nStreamInfo.pAddr = malloc(4096*1024); // max size
if (NULL == nStreamInfo.pAddr)
{
aloge("fatal error! malloc nStreamInfo.pAddr fail! size=4MB");
goto _exit_1;
}
memset(nStreamInfo.pAddr, 0, 4096*1024);
1.7 打开h264和包长度
FILE *fp_bs = fopen(stContext.mConfigPara.mH264VbsPath, "rb");//打开 H.264 比特流
FILE *fp_sz = fopen(stContext.mConfigPara.mH264LenPath, "rb");//打开 包长度文件
if ((fp_bs==NULL) || (fp_sz==NULL))
{
aloge("fopen fail! BitStreamFile: %p, %s, LenFile: %p, %s",
fp_bs, stContext.mConfigPara.mH264VbsPath, fp_sz, stContext.mConfigPara.mH264LenPath);
goto _exit_1;
}
1.8 读取长度文件的内容到内存中
fseek(fp_sz, 0, SEEK_END);
int nLenFileSize = ftell(fp_sz);
fseek(fp_sz, 0, SEEK_SET);
char *pLenStr = malloc(nLenFileSize);
1.9 计算数据帧长度
memset(pLenStr, 0, nLenFileSize);
fread(pLenStr, 1, nLenFileSize, fp_sz);
char *endptr0 = pLenStr, *endptr1 = pLenStr + nLenFileSize;
1.10 打开输出文件
sprintf(DstPath, "%sh264.yuv", stContext.mConfigPara.mYuvFilePath);
stContext.mFpDstFile = fopen(DstPath, "wb");
if (stContext.mFpDstFile == NULL)
{
aloge("fatal error! Can't create file %s", DstPath);
exit(-1);
}
1.11 计算每帧的长度
int pkt_sz;
pkt_sz = strtol(endptr0, &endptr1, 10);
endptr0 += 8;//endptr1;
if (pkt_sz && yuvId <= 20)
{
alogd("yuvId=%d, pkt_sz=%d", yuvId++, pkt_sz);
}
else
{
alogw("pkt_sz=0 or yuvId big enough, exit loop!");
break;
}
1.12 读取视频帧
int rd_cnt = fread(nStreamInfo.pAddr, 1, pkt_sz, fp_bs);
if (rd_cnt != pkt_sz)
{
aloge("error happen! pkt_sz=%d, rd_cnt=%d", pkt_sz, rd_cnt);
break;
}
else
{
nStreamInfo.mLen = rd_cnt;
nStreamInfo.mbEndOfFrame = TRUE;
alogd("read vbs packet! rd_cnt=%d", rd_cnt);
}
1.13 发送视频帧至解码器
ret = AW_MPI_VDEC_SendStream(stContext.mVDecChn, &nStreamInfo, 100);//发送视频包到解码器
if(ret != SUCCESS)
{
alogw("send stream with 100ms timeout fail?! Maybe Vbs is full!");
goto __TryToSendStream;
}
1.14 保存解码后的数据
获取解码器生成的YUV文件,并将Y和UV保存输出文件中,保存后将原来的解码通道获取的图像帧释放。
if ((ret=AW_MPI_VDEC_GetImage(stContext.mVDecChn, &nFrameInfo, 1000)) == SUCCESS)//获取解码后的帧
{
//sprintf(DstPath, "%s%02d_%d_%d.yuv", stContext.mConfigPara.mYuvFilePath, yuvId++, nFrameInfo.VFrame.mWidth, nFrameInfo.VFrame.mHeight);
//open yuv file
//stContext.mFpDstFile = fopen(DstPath, "wb");
fwrite(nFrameInfo.VFrame.mpVirAddr[0], 1, nFrameInfo.VFrame.mWidth*nFrameInfo.VFrame.mHeight, stContext.mFpDstFile);//Y
fwrite(nFrameInfo.VFrame.mpVirAddr[1], 1, nFrameInfo.VFrame.mWidth*nFrameInfo.VFrame.mHeight/2, stContext.mFpDstFile);//UV
//fclose(stContext.mFpDstFile);
AW_MPI_VDEC_ReleaseImage(stContext.mVDecChn, &nFrameInfo);//释放解码图像帧
alogd("--------------get one yuv frame(%s)-----------------", DstPath);
waitFrmCnt = 0;
}
else if (ret == ERR_VDEC_NOBUF)
{
alogd("no valid buf! waitFrmCnt=%d", waitFrmCnt++);
if (waitFrmCnt<5)
goto __TryToGetFrame;
alogw("wait one frame for 5s, do not wait anymore! we send another stream!");
}
1.15 释放和关闭输出文件
free(pLenStr);
fclose(stContext.mFpDstFile);
1.16 停止并销毁解码通道
AW_MPI_VDEC_StopRecvStream(stContext.mVDecChn);
AW_MPI_VDEC_DestroyChn(stContext.mVDecChn);
1.17 退出MPP平台
AW_MPI_SYS_Exit();