前面已经分析了wrap和hantro,但是二者是如何结合的,wrap是如何封装hantro的,提供了哪些接口,封装了哪些细节还不太清楚,此文来探究下。这里还是只关注解码。
imx VPU解码分析1-wrap-CSDN博客
imx VPU解码分析2-hantro_huntenganw的博客-CSDN博客
imx VPU解码分析3-wrap的示例-CSDN博客
上述文章已经分析了vpu_wrapper.h文件,这是wrap库对外的接口。vpu_wrapper_hantro.c这个文件就是封装的hantro接口,包含了hantro解码需要用到的api。
这里分析下vpu_wrapper_hantro.c,看看具体如何操作的。
首先看看头文件的包含:
#include "codec.h"
#include "codec_h264.h"
#include "codec_jpeg.h"
#include "codec_mpeg4.h"
#include "codec_vc1.h"
#include "codec_rv.h"
#include "codec_mpeg2.h"
#include "codec_vp6.h"
#include "codec_avs.h"
#include "codec_vp8.h"
#include "codec_webp.h"
#include "codec_hevc.h"
#include "codec_vp9.h"
这些头文件在hantro/openmax_il/source/decoder下,即包含了这些相应的解码方式。
然后是定义了几个内部结构体:
typedef enum
{
VPU_DEC_STATE_OPEN=0,
VPU_DEC_STATE_INITOK,
VPU_DEC_STATE_REGFRMOK,
VPU_DEC_STATE_DEC,
VPU_DEC_STATE_STARTFRAMEOK,
VPU_DEC_STATE_OUTOK,
VPU_DEC_STATE_EOS,
VPU_DEC_STATE_CORRUPT
}VpuDecState;
这个结构体是具体的解码状态。可以对比VpuDecBufRetCode看看。
typedef struct
{
/* open parameters */
VpuCodStd CodecFormat;
const void *pdwl;
/* hantro decoder */
CODEC_PROTOTYPE *codec;
OMX_VIDEO_PARAM_CONFIGTYPE config;
/* decode parameters */
int iframeSearchEnable;
int skipFrameMode;
int skipFrameNum;
int inputType; /*normal, kick, drain(EOS)*/
int streamBufDelaySize; /*unit: bytes. used in stream mode: valid data size should reach the threshold before decoding*/
int initDataCountThd;
VpuDecErrInfo nLastErrorInfo; /*it record the last error info*/
/*resolution for some special formats, such as package VC1 header,...*/
int picWidth;
int picHeight;
/* init info */
VpuDecInitInfo initInfo;
/* out frame info */
VpuDecOutFrameInfo frameInfo;
/*used to store extended frame info*/
VpuFrameExtInfo frmExtInfo;
/* frame buffer management */
int frameNum;
VpuFrameBuffer frameBuf[VPU_MAX_FRAME_INDEX]; /*buffer node*/
int frameBufState[VPU_MAX_FRAME_INDEX]; /*record frame state for clearing display frame(if user forgot to clear them)*/
/* bitstream buffer pointer info */
unsigned char* pBsBufVirtStart;
unsigned char* pBsBufPhyStart;
unsigned char* pBsBufPhyEnd;
int nBsBufLen;
int nBsBufOffset;
/* state */
VpuDecState state;
/*management of consumed bytes: used to sync with frame boundary*/
int nDecFrameRptEnabled; /*1:support frame reported; 0: not support*/
int nAccumulatedConsumedStufferBytes;/*stuffer size between frames: if it <0, indicate that some frames are contained in config data*/
int nAccumulatedConsumedFrmBytes; /*frame size: >=0*/
int nAccumulatedConsumedBytes; /*it should match with the input data size == nAccumulatedConsumedStufferBytes+nAccumulatedConsumedFrmBytes*/
VpuFrameBuffer* pLastDecodedFrm; /*the nearest decoded frame*/
int nAdditionalSeqBytes; /*seq header inserted by wrapper itself , or config data */
int nAdditionalFrmHeaderBytes; /*frame header inserted by wrapper itself */
unsigned int nLastFrameEndPosPhy; /*point to the previous frame tail: used to compute the stuff data length between frames*/
int nDecResolutionChangeEnabled; /*1: support resolution change notification; 0: not support*/
int nPrivateSeqHeaderInserted;
int nIsAvcc; /*for H.264/HEVC format*/
int nNalSizeLen;
int nNalNum; /*added for nal_size_length = 1 or 2*/
bool eosing;
bool ringbuffer;
bool config_tile;
int nFrameSize;
int nOutFrameCount;
int total_frames;
long long total_time;
int slice_info_num;
RvDecSliceInfo slice_info[128];
int frame_size;
bool bSecureMode;
bool bConsumeInputLater;
int nSecureBufferAllocSize;
}VpuDecObj;
这个结构体就是此文件中最重要的了,基本汇聚了所有信息,统统打包。
typedef struct
{
VpuDecObj obj;
}VpuDecHandleInternal;
这个结构体就把VpuDecObj再次包装下,在下面的函数中,既用了VpuDecObj,又用了VpuDecHandleInternal,暂时不明白为啥这么做。
下面看看有哪些关键函数。
先看看static修饰的函数:
//将数据复制到解码器
static void VpuPutInBuf(VpuDecObj* pObj, unsigned char *pIn, unsigned int len, bool useRingBuffer);
//解码器对不同格式进行解析,通过pInData->pVirAddr == (unsigned char *)0x01 && pInData->nSize //== 0来判断数据读完,这里读完解码器中还有几帧数据待解
static VpuDecRetCode VPU_DecProcessInBuf(VpuDecObj* pObj, VpuBufferNode* pInData);
//查找解码帧
static int VpuSearchFrameIndex(VpuDecObj* pObj, unsigned char *pInPhysY);
//获取解码帧
static VpuDecRetCode VPU_DecGetFrame(VpuDecObj* pObj, int* pOutBufRetCode);
//解码指针的处理,结构返回值
static VpuDecRetCode VPU_DecDecode(VpuDecObj* pObj, int* pOutBufRetCode);
下面是对外的函数,所有函数的返回值都是VpuDecRetCode :
//加载解码器
VpuDecRetCode VPU_DecLoad()
//申请内存
VpuDecRetCode VPU_DecQueryMem(VpuMemInfo* pOutMemInfo)
//打开解码器,进行各种内存、指针、DWL的初始化,创建相应格式的解码器
VpuDecRetCode VPU_DecOpen(VpuDecHandle *pOutHandle, VpuDecOpenParam * pInParam,VpuMemInfo* pInMemInfo)
//查看支持的解码VpuDecCapability值
VpuDecRetCode VPU_DecGetCapability(VpuDecHandle InHandle,VpuDecCapability eInCapability, int* pOutCapbility)
//关闭解码支持
VpuDecRetCode VPU_DecDisCapability(VpuDecHandle InHandle,VpuDecCapability eInCapability)
//解码模式设置,参看VpuDecConfig结构体
VpuDecRetCode VPU_DecConfig(VpuDecHandle InHandle, VpuDecConfig InDecConf, void* pInParam)
//主解码函数
VpuDecRetCode VPU_DecDecodeBuf(VpuDecHandle InHandle, VpuBufferNode* pInData,
int* pOutBufRetCode)
//获取初始化参数
VpuDecRetCode VPU_DecGetInitialInfo(VpuDecHandle InHandle, VpuDecInitInfo * pOutInitInfo)
//更新注册帧信息
VpuDecRetCode VPU_DecRegisterFrameBuffer(VpuDecHandle InHandle,VpuFrameBuffer *pInFrameBufArray, int nNum)
//获取解码帧
VpuDecRetCode VPU_DecGetOutputFrame(VpuDecHandle InHandle, VpuDecOutFrameInfo * pOutFrameInfo)
//获取解码帧信息
VpuDecRetCode VPU_DecGetConsumedFrameInfo(VpuDecHandle InHandle,VpuDecFrameLengthInfo* pOutFrameLengthInfo)
//释放帧缓冲区
VpuDecRetCode VPU_DecOutFrameDisplayed(VpuDecHandle InHandle, VpuFrameBuffer* pInFrameBuf)
//释放解码器
VpuDecRetCode VPU_DecFlushAll(VpuDecHandle InHandle)
//关闭解码器
VpuDecRetCode VPU_DecClose(VpuDecHandle InHandle)
//卸载解码器
VpuDecRetCode VPU_DecUnLoad()
//reset解码器
VpuDecRetCode VPU_DecReset(VpuDecHandle InHandle)
//获取内存
VpuDecRetCode VPU_DecGetMem(VpuMemDesc* pInOutMem)
//释放内存
VpuDecRetCode VPU_DecFreeMem(VpuMemDesc* pInMem)
具体的解码过程在前文已经介绍,这里挑几个重点的函数探究下内部过程。
VPU_DecOpen函数打开对应的编译器,对应前文讲的codec几个头文件,支持下面的解码器:
HantroHwDecOmx_decoder_create_h264
HantroHwDecOmx_decoder_create_mpeg2
HantroHwDecOmx_decoder_create_mpeg4
HantroHwDecOmx_decoder_create_vc1
HantroHwDecOmx_decoder_create_jpeg
HantroHwDecOmx_decoder_create_webp
HantroHwDecOmx_decoder_create_avs,
HantroHwDecOmx_decoder_create_vp6
HantroHwDecOmx_decoder_create_vp8
HantroHwDecOmx_decoder_create_hevc
HantroHwDecOmx_decoder_create_vp9
VPU_DecDecodeBuf函数核心解码函数,如下示意图显示了其内部调用关系:
这样基本就清晰了,可以看到最终调用了内层的decode函数来执行具体的解码过程,这个函数在codec.h中定义如下。
// Decode n bytes of data given in the stream buffer object.
// On return consumed should indicate how many bytes were consumed from the buffer.
//
// The function should return one of following:
//
// CODEC_NEED_MORE - nothing happened, codec needs more data.
// CODEC_HAS_INFO - headers were parsed and information about stream is ready.
// CODEC_HAS_FRAME - codec has one or more headers ready
// less than zero - one of the enumerated error values
//
// Parameters:
//
// CODEC_PROTOTYPE - this codec instance
// STREAM_BUFFER - data to be decoded
// OMX_U32 - pointer to an integer that should on return indicate how many bytes were used from the input buffer
// FRAME - where to store any frames that are ready immediately
CODEC_STATE(*decode)(CODEC_PROTOTYPE *, STREAM_BUFFER *, OMX_U32 *, FRAME *);
DecGetOutputFrame函数就是获取解码帧数据了。VPU_DEC_OUTPUT_DIS这个信号就可以取数据了。取完数据,使用DecOutFrameDisplayed函数释放掉。
这样分析一遍wrap和hantro的联系就基本清晰了。