下表给出了上述几个参考模型的综合对比。
JM X264 T264
语言 C C、X86汇编 C
特点 官方测试代码,按照标准组织软件结构 注重实用,没有实现解码器 具有基C64X+DSP的版本
标准兼容性 完全兼容 没有完全实现 解码器不兼容
可移植性 很好 较低 很好
速度 非常慢 较快 很快
图像质量 较高 高 低
从上表可以看出,x264没有实现解码器;T264虽然可移植性、解码速度都很有优势,但是其解码器没有兼容性,只能解码T264编码器输出的码流,这必将影响其应用的广泛性。经过综合比较,本文最终选择基于JM10.1进行H.264转码器程序的实现,因为JM模型完全基于C语言实现,具有结构清晰、易于分析、移植方便等特点。
3.2 JM编码器分析
经过分析,JM编码器的主流程如图3-1和图3-2,函数Init_img()初始化Img参数,并给其中的就某些member分配内存,allocate memory for frame buffers用来放把重建帧进行1/4内插后的图像,函数init_global_buffers()为全局变量分配内存,函数Init_Motion_Search_Module()设置mv_search时候的参数,比如搜索范围。函数start_sequence()根据输出的模式,确定写nalu的方法,写sps,pps到输出文件中。在进行相应的初始工作之后循环调用encode_one_frame()函数,对原始视频序列的每一帧进行编码处理。
图3-1 JM编码主函数流程
图3-2 JM编码器主流程
从encode_one_frame()这个函数开始才开始真正进入编码阶段,在次之前都是做的一些准备工作,但是这个函数不是核心函数,这个函数的主要功能是通过调用其他的函数对一帧数据进行编码,每调用该函数一次,处理完一帧数据。函数frame_picture()是encode_one_frame()的主要函数,接着调用函数encode_one_slice()和encode_one_macroblock()分别对片数据和宏块编码。
3.3 JM解码器分析
经过分析,JM解码器的主流程如图3-3所示,在进行相应的初始工作之后循环调用Decode_one_frame()函数,对视频编码序列的每一帧进行解码处理。函数Decode_one_frame()的流程由图3-4给出,从中可以清晰的看出视频数据的分层处理结构,即对frame, slice, macroblock进行逐层解析。
图 3-3 JM解码器主流程图 3-4 Decode_one_frame()函数流程
如图3-4帧解码过程中,先通过read_new_slice()对码流中的片数据进行读取,然后进行片解码decode_slice()。接着判断该片是否解码完成,如果没有完成,则继续读循环取下一片数据,再解码。如果所有片解码完成,则对该图像进行去方块效应滤波。滤波是对图像中每一个宏块进行的,即DeblockMb()函数。该函数又包含获取边界强度GetStrength()和滤波过程EdgeLoop()。进行滤波之后,把已解码帧放入解码帧缓冲器(DPB),作为后续解码过程的参考帧。放入解码帧缓冲器的中的参考帧是按队列存放的。默认情况下,解码帧缓冲器最多存放16帧图像。当解码帧缓充器中的参考帧存放满而没有空间时,如有新的帧要进入则输出最早进入队列的帧,其它帧按顺序在队列中前移一位。
read_new_slice()流程:该过程读取编码片中的数据。首先,通过Al1ocNALU()为NAL单元分配一个大小为MAX_CODED_FRAME_SIZE的堆空间,用来存放己编码的帧。然后判断NAL单元类型,根据nal_unit_type的不同取值,进入不同的解码方式。接着是一些初始化,然后decode_poc()函数计算图像序列号POC(Picture Order Count),根据pic_order_cnt_type值的不同,会采用不同的算法来计算 POC。最后,执行函数FreeNALU(),释放分配的NAL单元堆空间,read_new_slice()过程结束。 H.264视频转码的设计与实现+文献综述(8):http://www.youerw.com/tongxin/lunwen_4076.html