ps:sdk v1.19
简单概念,
1. fourcc,代表四字符代码 (four character code), 它是一个32位的标示符,其实就是typedef unsigned int FOURCC;是一种独立标示视频数据流格式的四字符代码。通过四个字符经过一定的运算得到对应的码来选择对应的解码器。
执行流程,
1. Init过程,因为Sample是命令行输参,大部分初始化参数是未指定的,构造的时候初始化为0了。介绍一下初始化中比较重要的结构体,
typedef struct {
mfxIMPL Implementation; // sdk的实现方式
mfxVersion Version; // 库版本
mfxU16 ExternalThreads;// 线程模式,0表示内部线程,1-外部线程
union {
struct {
mfxExtBuffer **ExtParam;
mfxU16 NumExtParam; // 额外的配置数量
};
mfxU16 reserved2[5];
};
mfxU16 GPUCopy; // 使能GPU拷贝
mfxU16 reserved[21];
} mfxInitParam;
typedef struct {
mfxExtBuffer Header;
mfxU16 NumThread; // 线程的数量
mfxI32 SchedulingType; // 线程调度策略
mfxI32 Priority; //所有线程的优先级
mfxU16 reserved[55];
} mfxExtThreadsParam;
typedef struct {
mfxU32 BufferId; // buffer内容的ID, ExtendedBufferID列举了完整的列表
mfxU32 BufferSz; // buffer的size
} mfxExtBuffer;
typedef union {
struct {
mfxU16 Minor; // 次版本
mfxU16 Major; // 主版本
};
mfxU32 Version;
} mfxVersion;
解码session的初始化,m_mfxSession.InitEx(initPar);
ps:使用sdk之前,必须初始化session,sdk维护了一个编解码器或者vpp的执行。
根据已填充的mfxInitParam。
Sample中session.init前已填充的有意义的值有,
typedef struct {
mfxIMPL Implementation = soft/hard/auto;
mfxVersion Version; // 库版本
mfxU16 ExternalThreads;// 线程模式,0表示内部线程,1-外部线程
<del>union {
struct {
mfxExtBuffer **ExtParam;
mfxU16 NumExtParam; // 额外的配置数量
};
mfxU16 reserved2[5];
};</del>
mfxU16 GPUCopy = 0; // 使用了0默认
mfxU16 reserved[21];
} mfxInitParam;
几乎没填什么初始值。
session init成功后,可以m_mfxSession.QueryVersion(&version);
m_mfxSession.QueryIMPL(&m_impl);获得加载库的版本和库的实现。接着,会将cmd参数的一些选项与获取的版本做对比,判断是否支持。
m_pmfxDEC = new MFXVideoDECODE(m_mfxSession)紧接着,根据创建的session获取了解码器(MFXVideoDECODE)的对象。并对待解码的视频表现参数赋值,m_mfxVideoParams.mfx.CodecId = pParams->videoType。若待解码的码流非MFX_CODEC_CAPTURE,初始化一个mfxBitStream。该结构如下,
typedef struct {
union {
struct {
mfxEncryptedData *EncryptedData; // 保留,为0
mfxExtBuffer **ExtParam; // 额外配置,不管
mfxU16 NumExtParam; // 额外结构的数量,不管
};
mfxU32 reserved[6]; // 保留
};
mfxI64 DecodeTimeStamp; // 另辟blog学习
mfxU64 TimeStamp;
mfxU8 *Data; // 码流数据
mfxU32 DataOffset; // 码流数据中的偏移
mfxU32 DataLength; // 真实码流的字节数
mfxU32 MaxLength; // 分配码流buffer的字节数
mfxU16 PicStruct; // 输出图像的类型
mfxU16 FrameType; // 输出帧的类型
mfxU16 DataFlag; // buffer的额外标识,见BitstreamDataFlag
mfxU16 reserved2;
} mfxBitstream;
enum {
MFX_BITSTREAM_COMPLETE_FRAME = 0X0001, // buffer包含了完整的帧或场,意味着解码器可以不等待下一帧开始就进行该buffer的解码,有效的减少了延时
MFX_BITSTREAM_EOS = 0X0002 // buffer包含了流的结尾,意味着app不需要发送额外的数据给解码器
};
该结构就是一个存储压缩后的码流数据用的buffer。Sample中的初始化很简单,
delete []data;
pBitstream->Data = new mfxU8[nSize];
pBitstream->MaxLength = nSize;
接着是插件相关,因为初始时全清0,所以是MSDK_PLUGINGUID_NULL,然后根据对应的实现和初始化的参数(impl,MSDK_VDECODE,videotype),通过msdkGetPluginUID获取pluginGuid。
接着,是对解码器参数的设置InitMfxParams(pParams)。如果说之前的操作是申请资源,那这里就是开始初始化赋值了。
拿h264来说,该函数执行直接进入,m_pmfxDEC->DecodeHeader(&m_mfxBS, &m_mfxVideoParams),追踪到下一层发现底层有三个形参m_session以及上面的两个。执行成功后,m_bVppIsUsed = IsVppRequired(pParams);根据pParams的相关值看是否使用Vpp。该函数内的判断涉及了一个比较重要的结构体。
typedef struct {
mfxU32 AllocId;
mfxU32 reserved[2];
mfxU16 reserved3;
mfxI16 AsyncDepth; // 指定异步操作的数量在出执行结果之前(我的理解,流水线步骤的多少,一个需要均衡的值)
union {
mfxInfoMFX mfx; // dec,enc,transcode相关配置参数
mfxInfoVPP vpp; // video processing相关配置参数
};
mfxU16 Protected; // 指定保护机制,该成员为保留参数,为0
mfxU16 IOPattern; // 指定了SDK的内存访问形式,详细见IOPattern枚举 ps:enc必须指定input,dec必须指定output,vpp i/o都需要指定
mfxExtBuffer** ExtParam;
mfxU16 NumExtParam; // 额外配置的结构数量
mfxU16 reserved2;
} mfxVideoParam;
该结构体包含了编解码转码视频处理参数的配置,总之就是针对整个codec的配置。
Vpp较少涉及,主要介绍mfxInfoMFX,
typedef struct {
mfxU32 reserved[7];
mfxU16 LowPower;
mfxU16 BRCaramMultiplier;
mfxFrameInfo FrameInfo;
mfxU32 CodecId;
mfxU16 CodecProfile;
mfxU16 CodecLevel;
mfxU16 NumThread;
union {
...
};
} mfxInfoMFX;
该结构指定了dec, enc, transcoding的配置,这些参数中任意的0值,都表示该参数配置没有被显示指定。IsVppRequired中涉及到该结构中的mfxFrameInfo结构,
typedef struct {
mfxU32 reserced[4];
mfxU16 reserved4;
mfxU16 BitDepthLuma; // 描述亮度的位数,不是所有的x和sdk都支持该值,具体可Query查询是否支持
mfxU16 BitDepthChroma; // 描述色度的位数,同上
mfxU16 Shift;
mfxFrameId FrameId;
mfxU32 FourCC; // 色彩fourcc(nv12,rgb...)
union {
struct { /* Frame parameters */
/* 宽高即视频帧的像素,必须是16的倍数,对于逐行帧序列,高位32的倍数 */
mfxU16 Width;
mfxU16 Height;
/* 帧的显示区域,指定了videoparam中的显示宽高 */
mfxU16 CropX;
mfxU16 CropY;
mfxU16 CropW;
mfxU16 CropH;
};
struct { /* Buffer parameters (for plain formats like P8) */
mfxU64 BufferSize; // frame buffer的字节数,仅适用于plain格式(p8),w, h, crop这种情况下是无效的
mfxU32 reserved5;
};
};
/* 帧率公式ExtN/ExtD,如果是编码,必须指定帧率,若为解码,帧率可能是未指定的(ExtN,ExtD都为0),此时帧率默认30fps */
mfxU32 FrameRateExtN; // 分子
mfxU32 FrameRateExtD; // 分母
mfxU16 reserved3;
/* 指定了sample的长宽比,如果都为0,采用默认,否则参考规范和公式,对于264比例无限制 */
mfxU16 AspectRatioW;
mfxU16 AspectRatioH;
mfxU16 PicStruct;
mfxU16 ChromaFormat; // 颜色采样的方式(420,422...),0为不指定
mfxU16 reserved2;
} mfxFrameInfo;
顾名思义,描述了视频帧的属性。初始情况下IsVppRequired也没有执行什么,直接return false了。
接下来直接执行bitstream里的帧率判断了。因为ExtN和ExtD都为0,所以分别设30,1,帧率默认30。比例为1:1。因为不设多视角,接着numViews = 1。Vpp是不使用的,m_mfxVideoParams.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY(输出到系统内存)。
至此初始化完毕。
2. 解码过程,RunDecoding(),先看其中涉及的重要的结构体。
typedef struct {
mfxU32 reserved[4];
mfxFrameInfo Info; // 描述帧的属性
mfxFrameData Data; // 帧的真实数据
} mfxFrameSurface1;
typedef struct {
union {
mfxExtBuffer **ExtParam;
mfxU64 reserved2;
};
mfxU16 NumExtParam;
mfxU16 reserved[9];
mfxU16 MemType;
mfxU16 PitchHigh; // 帧中两个连续行的起始字节数之差
mfxU64 TimeStamp;
mfxU32 FrameOrder;
mfxU16 Locked; // app的计数标识,若>0,app会锁住帧或场对(2),此时不能移动修改和删除
union {
mfxU16 Pitch;
mfxU16 PitchLow;
};
/* color planes */
...
mfxU8 *A;
mfxMemId MemId;
/* Additional Flags */
mfxU16 Corrupted;
mfxU16 DataFlag;
} mfxFrameData;
该结构体定义了未压缩的帧面信息和数据buffer。帧面在帧或者互补场成对像素中多达四个色彩通道。