jsvm代码理解

JSVM是 Scalable Video Coding (SVC) 的参考代码. 目前还在开发中. JSVM 提供了 Visual 
Studio & GCC 4.0 两种编译方案. 

JSVM Solution 中包含了多个 Project。 
分别是 
    ( 1 ) 库 
     H264AVCCommonLibStatic :  SVC的基本库  , 同时被编码和解码所使用。 
     H264AVCEncoderLibStatic : SVC的编码库 
     H264AVCDecoderLibStatic :  SVC的解码库 
     H264AVCVideoIOLibStatic :  这个库的功能是为 NAL的读写提供支持。 
    (2)工具 
     DownConvertStatic :  对视频进行重采样的工具 , 支持空域和时域的重采样。 
     H264AVCEncoderLibTestStatic :  AVC/SVC 编码器 
     H264AVCDecoderLibTestStatic :  AVC/SVC 解码器 
     BitStreamExtractorStatic :  可以用来从Global Scalable 的SVC流中取出Sub- 
bitstream. 
        QualityLevelAssignerStatic : 
        MCTFPreProcessor :  编码前的预处理工具 。 
       PSNRStatic :   PSNR 的计算工具 
       FixedQPEncoderStatic :   从名字看应该是固定QP的编码器。 
       SIPAnalyser :

JSVM 源码剖析(1) 
宏版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://volvet.blogbus.com/logs/25039893.html



(准备读读JSVM的代码, 写点体会)

JSVM的作者大概从没考虑过代码的可读性吧 . 跟H264的参考代码JM一样, JSVM的代码也是难以理解.

其中有不少古怪的宏,大概让人头痛吧. 不过细细看去, 还是有规律可循的.

本文讨论的宏定义于jsvm/h264extension/inc/macros.h



大部分宏的开头字母是R , 这次R的含义是 Return 

比如 RERR   ,  就是 Return 一个错误值  , 该值定义于ERR 类中的m_nERR.

不在开头位置的字母R , 此时的含义是该宏返回的值并非错误值 , 而是宏所输入的参数

retVal . 

比如 ROFR (exp, retVal)  的函数是  if ( !exp )  Return retVal

字母 O  ,  其含义是判断宏的输入参数 exp 的意思  F 的含义是 Fasle, T 的含义是 TRUE

比如  ROF(exp)  其含义就是   if( !exp )  ERRR

ROT(exp) 其含义为 if( exp ) ERRR

字母 S  , 一般位于宏的末尾 , 其含义是无 Assert 操作 . 

前面所说的宏 , 除了已经说明的含义外, 还有一个操作是 Assert( 0 ) 

但是 如果 宏的尾巴上加了S , 那这个宏不会 Assert( 0 ) 

好了 这下大部分宏的意思就可以看懂了 

RERR , 就是 ASSERT (0 ) ; Return  ERR值

ROF(exp)     就是 if ( !exp ) { ASSERT (0);  RERR }

ROFS(exp)   就是 if( !exp ) { RERR }

ROT(exp)  就是  if( exp ) { ASSERT(0);  RERR }

ROTS( exp ) 就是  if ( exp ) {  ERRR }

ROFR(exp,retVal)  就是 if( !exp ) { ASSERT(0) ; Return retVal; }

ROFRS(exp, retVal) 就是 if( !exp ) { Return retVal; }



其他大致如此  . 

另外还有几个重要的 , 就是  NOK , 就是要检查值是否等于 ERR 中定义的 m_nOK 

比如 RNOK(exp ) 就是  if( exp != OK ){ Assert(0); return exp;}

RNOKR(exp,retVal) 就是 if( exp != OK ) { Assert(0); return retVal; }

还有一个是 A , A 表示 Assert 

比如 AF  就是  Assert( False )

ANOK(exp) 就是  if( exp != OK ) { Assert(0) }

AOF( exp )  :   if( !exp )  { Assert(0) }

AOT( exp) :   if( exp ) {  Assert( 0 ) }

好了   那些难解的宏 大致就这些了 .  

Reference  

JSVM 9_13_1

JSVM源码剖析 2  解码测试
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://volvet.blogbus.com/logs/38366629.html




JSVM源码剖析 2   解码测试


测试程序的输入参数解析 封装于 class
DecoderParameter  中

DecoderParameter的主要作用是从命令行中解析输入参数
和打印帮助信息 

分别是函数 Init 和 xPrintUsage

主要就是记录下解码的es文件名和解码后输出的yuv文件名
配置 MaxPocDiff . 



JSVM的解码测试代码封装于 class
H264AVCDecoderTest 中. 

H264AVCDecoderTest  实现了读取es文件 解码, 保存yuv文件的功能
改变编译选项 似乎还可以做SVC - AVC的转码.   我还没试.

外部接口主要是 init 初始化  
               go   完成解码工作
               unint 
               destroy 析构的工作
               
关键的处理函数是 xProcessAccessUnit

    这个函数
       从ReadBitstreamIf 中获取数据 (BinData*) 
       然后用数据去初始化 NAL 对象, 知道获取完整
       图象
       
       然后调用CreateH264AVCDecoder::processNALUnit函数逐个对
       读入的NAL进行解码
       
       最后用xOutputPicBuffer 输出解码后的图象
       
关于输出图象缓存  还有一个class可以说下
就是 H264AVCDecoderTest::BufferParameters

这个东东是做为H264AVCDecoderTest的成员, 在得到图象分辨率之后
被初始化的.

JSVM是对输出图象做边界扩展的, 扩展的方式是 左右32象素, 上下 64象素

它的成员有

    UInt  m_uiLumaOffset;   //图象Y分量左上角象素在buffer的偏移
    UInt  m_uiCbOffset;     //图象Cb分量左上角象素的偏移
    UInt  m_uiCrOffset;     //图象Cr分量左上角象素的偏移
    UInt  m_uiLumaHeight;   //图象高度   
    UInt  m_uiLumaWidth;    //图象宽度
    UInt  m_uiLumaStride;   //图象跨度 = 宽度 + 32*2 
    UInt  m_uiBufferSize;   //Buffer的大小
    UInt  m_auiCropping[4]; //图象最终输出的时候裁剪用的

接下来说说JSVM编码器的一些主要函数 
Main函数:  ( H264AVCEncoderLibTest.cpp ) 

主要部分是H264AVCEncoderTest 类对象指针pcH264AVCEncoderTest所指的go ()函数,在 H264AVCEncoderTest .cpp有函数原型。 

Go函数 主要分为8部分:


1. 初始化 

2. 写参数信息 

3. 进入layer循环,输入必要的参数信息: 

for( uiLayer = 0; uiLayer < uiNumLayers; uiLayer++ ) 

4. 开始一个帧一个帧的编码: 

for( uiFrame = 0; uiFrame < uiMaxFrame; uiFrame++ ) 



    4-1 编码每一个帧时,layer循环 获取缓存存储图像,并且读入每个layer的帧信息: 

    for( uiLayer = 0; uiLayer < uiNumLayers; uiLayer++ ) 

    {  m_apcReadYuv[uiLayer]->readFrame(。。。。。) } 

    4-2 开始编码:m_pcH264AVCEncoder->process(。。。。。。) 

    4-3 写每一帧编码后的传输单元NAL unit,并且释放临时缓存; 

    4-4 写每一帧编码后的重建图像,并且释放临时缓存; 



5. 结束编码 

6. 写所有帧编码后的传输单元NAL unit,并且释放临时缓存 

7. 写所有帧编码后的重建图像,并且释放临时缓存 

8. 计算输出显示的参数信息,比如psnr值等等。

  

Process 函数,主要分为:  (PicEncoder.cpp)


===== fill lists ===== 

for( UInt uiLayer = 0; uiLayer <= uiHighestLayer; uiLayer++ ) 

判断编码模式,如果是AVC模式的话:(是否是AVC模式由编码配置文件encoder.cfg中的BaseLayerMode 项值决定) 

则运行函数:m_pcPicEncoder ->process //见下面PicEncoder::process函数 

否则: 

运行函数m_pcH264AVCEncoder->process //见下面H264AVCEncoder::process函数

  

PicEncoder::process函数: (PicEncoder.cpp)


1. 输入图片信息 

2. 编码图片头信息等 

3. 得到下一帧 

4. 初始化图像 

5. 编码 

6. 存储图像

  

H264AVCEncoder::process函数: (H264AVCEncoder.cpp)


1. 输入当前GOP信息 

2. 编码当前GOP (运行函数:xProcessGOP) 

3. 更新图像列表

  

xProcessGOP( apcPicBufferOutputList, apcPicBufferUnusedList )函数:(H264AVCEncoder.cpp)


1. 初始化GOP 

2. 在GOP范围内获取每一层的可获得的信息单元,并且编码: 

for( uiAUIndex = 0; uiAUIndex <= 64; uiAUIndex++ ) 



    for( uiLayer = 0; 

           uiLayer < m_pcCodingParameter->getNumberOfLayers(); uiLayer ++ ) 

    { m_apcMCTFEncoder[uiLayer]->process(。。。。) 。。。。} 



3. 更新图像缓存列表 

for( uiLayer = 0; uiLayer < m_pcCodingParameter->getNumberOfLayers(); uiLayer++ ) 



    //—– set output list —– 

    //—– update unused list —– 

    //—– reset lists —– 

}

MCTFEncoder::process函数: (GOPEncoder.cpp)


1. 初始化相关参数 

2. 更新更高层图像(我认为:在编码一个GOP,特别是使用FGS技术时,为了提高编码效率,编码帧有时是需要参考已编码帧的高质量重建图像的,那么就需要在编码之前,整理好已编码重建帧的高质量图像,即需要更新更高层图像) 

3. 编码此GOP内的anchor帧(即判断此帧是否是anchor帧,若是,则进入;若不是则进入4步骤),其中最主要的是xEncodeKeyPicture函数(后续介绍) 

4. 编码此GOP内的非anchor帧,其中最主要的是xEncodeNonKeyPicture函数(后续介绍) 

5. 结束GOP编码

在MCTFEncoder::xEncodeKeyPicture和MCTFEncoder::xEncodeNonKeyPicture函数中(GOPEncoder.cpp)比较主要的是xEncodeLowPassSignal、xEncodeHighPassSignal、xEncodeFGSLayer等函数。这些函数都是GOP层面的,至于其中的具体过程就不再一一的描述了。 

  

在jsvm里面,虽然编码过程很复杂(因为可伸缩的算法本身就比较复杂),但是最底层的MB结构还是和普通的单层编码结构一样的,序列-->GOP-->frame-->slice-->Mb。只是在编码的过程中分了很多种情况,首先编一个frame时,除了考虑它是哪种类型(I/P/B)的帧外,还要考虑它在时域中(即GOP内)的位置情况:看它是不是anchor帧(GOP内第一个anchor帧应该是I帧,那么不参考其他帧编码,而第二个anchor帧可以是P帧或I帧,若是P帧,则要参考本GOP内前面anchor帧的重建图像),若不是anchor帧,则参考帧也必须在此GOP以内,为的是防止误差传递(这个属于参考帧管理的内容);除了考虑时域外,还要同时考虑此帧是属于哪个空域层的,编码Mb时看是否需要做层间预测(此处说的层间预测包括-宏块划分方式层间预测、运动矢量层间预测和编码残差层间预测等);最后还要考虑质量可伸缩问题(即FGS,这块本人不了解,所以就不深入了)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值