JM代码分析(三
printf颜色打印
\033[显示方式;前景色;背景色m
显示方式: 0(默认值)、1(高亮)、22(非粗体)、4(下划线)、24(非下划线)、5(闪烁)、25(非闪烁)、7(反显)、27(非反显)
前景色: 30(黑色)、31(红色)、32(绿色)、 33(黄色)、34(蓝色)、35(洋红)、36(青色)、37(白色)
背景色: 40(黑色)、41(红色)、42(绿色)、 43(黄色)、44(蓝色)、45(洋红)、46(青色)、47(白色)
\033[0m 默认
\033[1;32;40m 绿色
033[1;31;40m 红色
printf( “\033[1;31;40m 输出红色字符 \033[0m” )
//红色高亮
printf("\033[1;31m tmp_test===============%s %d %s======= \033[0m \n",__FILE__,__LINE__,__FUNCTION__);
//黄色高亮
printf("\033[1;33m tmp_test===============%s %d %s======= \033[0m \n",__FILE__,__LINE__,__FUNCTION__);
JM编码过程
encode_one_frame
perform_encode_field 场编码
perform_encode_frame 帧编码
frame_picture
code_a_picture
code_a_plane
while循环中编码
encode_one_slice
ReportI 输出I帧信息
Annex B & RTP输出格式
字节流格式(Annex B)和RTP格式流浅析
AnnexB(附录B)格式:NALU数据+开始前缀(00000001或000001),针对H.320电话会议。
RTP格式:NALU数据+20个字节的类似的并不符合RTP协议的RTP头。针对IP网络的RTP打包方式。为原始的NAL打包格式,就是开始的若干字节(1,2,4字节)是NAL的长度,而不是start_code,此时必须借助某个全局的数据来获得编码器的profile,level,PPS,SPS等信息才可以解码。
H.264协议只规定了字节流格式,没有规定 RTP 格式。可能也是因为这个原因,JM 的 RTP 格式没有被用到任何场合场合中,成为了摆设。
一共有两种起始码:3字节的0x000001和4字节的0x00000001 3字节的0x000001只有一种场合下使用,就是一个完整的帧被编为多个slice的时候,包含这些slice的nalu使用3字节起始码。其余场合都是4字节的。
H.264 的两种码流格式:
GetAnnexbNALU 处理字节流格式的码流
GetRTPNALU 处理 RTP 格式码流
字节流格式的码流主要用于存储,例如制作 DVD(当然现在的 DVD 还不是用 H.264)
RTP 格式码流主要用于网络传送,例如在线看电影
率失真优化 RDO
率失真优化(Rate–distortion optimization,简称RDO)是一种提升视频压缩性能的最优化方法。其原理是对视频的有损(画面质量)与比特率(编码所需的数据量)同时进行最优化,以求达到一个最佳的平衡点。虽然此算法一开始是在视频压缩的编码器中被使用,但也可以用于各种多媒体编码包含视频、视频、音频等等,只要编码时会同时考虑到质量及文件大小皆可使用。
率失真优化会较大部分其他区块比对的衡量方法来得慢,譬如绝对值差和(SAD)和转换后绝对值差和(SATD)。因此率失真优化通常只在动作补偿的最后一个步骤才会被使用,像是在H.264/AVC中最后需要对各种分区模式下决定的时候。
YUV格式
YUV420格式的理解:
在YUV420中,一个像素点对应一个Y,一个2X2的小方块对应一个U和V。对于所有YUV420图像,它们的Y值排列是完全相同的,因为只有Y的图像就是灰度图像。YUV420sp与YUV420p的数据格式它们的UV排列在原理上是完全不同的。420p它是先把U存放完后,再存放V,也就是说UV它们是连续的。而420sp它是UV、UV这样交替存放的。(见下图)
有了上面的理论,我就可以准确的计算出一个YUV420在内存中存放的大小。
width * hight =Y(总和)
U = Y / 4
V = Y / 4
所以YUV420 数据在内存中的长度是 width * hight * 3 / 2,
假设一个分辨率为8X4的YUV图像,它们的格式如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k6WppvFu-1672565231066)(null)]
另附:
宏块类型的介绍 B_Skip、P_Skip、CBP
1 CBP表示残差的编码状态,CBP一共6bit,低4位表示4个亮度8x8块,第4位表示U,第五位表示V,如果相应的位为"1", 表示此块有残差系数,反之没有残差,此宏块没有被编码.
2 direct 是帧间宏块的一种预测模式,而不是宏块类型,而 Skip 是帧间宏块的一种类型,而不是预测模式。skip 类型宏块采用的是direct 预测模式。
3 B_Skip类型宏块: 无像素残差,无运动矢量残差(MVD)。解码时,通过Direct预测模式(时间或空间)计算出前、后向MV后,直接利用前、后向MV得到像素预测值。像素重构值=像素预测值
P_Skip类型宏块: 也就是COPY宏块。无像素残差,无运动矢量残差(MVD)。直接利用预测MV得到像素预测值。像素重构值=像素预测值 (不使用Direct预测模式)
B_Direct_1616类型宏块: 有像素残差,无运动矢量残差(MVD)。解码时,通过Direct预测模式(时间或空间)计算出前、后向MV后,利用前、后向MV得到像素预测值。然后,像素重构值=像素预测值+像素残差解码值 .
Skip类型的两种宏块,都是无残差、无MVD
B_Direct分为对1616和88,都是有残差、无MVD
B_Skip 类型宏块(大小为 1616)
B_Direct_88 类型块(大小为 88)
B_Direct 类型宏块(大小为 16*16)都采用的是 Direct 预测模式
对于mb_type为P8*8的16*16宏块,里面可能存在B_Direct模式的8*8子快。跟B_Direct_16*16类型宏块一样有像素残差,无运动矢量残差(MVD)。 区别是对于B_Direct_16*16,其4个8*8子块都采用direct模式;但对于B_Direct_8*8,其所属的16*16宏块中剩余的8*8块却不一定要采用direct模式。
4 如果满足以下三个条件则将宏块按 Skip 类型进行编码:
(1)最佳模式选择为Inter16×16;
(2)MC得到的最终运动矢量等于预测运动矢量,即运动矢量的残差为0;
(3)变换系数均被量化为0。
P帧中,有3种基本的宏块:I宏块,skip形式的P宏块和非skip形式的P宏块
帧间预测过程
普通的(非skip)帧间预测, 先得到MVPRED, 然后搜索得到最佳匹配块. (判断最佳的方法…是可能使用MAD作为判断标准…) 然后把模式, 运动残差(MVD), CBP, 和像素残差一起编码传输 (或者还有其他一些flags)
JM:从264流中读取MVD过程
1、码流中读取MVD
readMBMotionVectors 熵编码时读取帧间宏块的运动矢量残差,调用过程如下:
#0 readMBMotionVectors (currSE=0xbfffdde8, dP=0x81ab8c8, currMB=0x814f434, list=0, step_h0=2, step_v0=2, offset=1) at src/macroblock.c:431
#1 0x08093fc1 in read_motion_info_from_NAL_p_slice (currMB=0x814f434) at src/macroblock.c:1269
#2 0x0809c71e in read_P8x8_macroblock (currMB=0x814f434, dP=0x81ab8c8, currSE=0xbfffde98) at src/mb_read.c:1123
#3 0x0809d42a in read_one_macroblock_p_slice_cavlc (currMB=0x814f434) at src/mb_read.c:1532
#4 0x0806554f in decode_one_slice (currSlice=0x81a68d8) at src/image.c:2540
#5 0x08060e94 in decode_slice (currSlice=0x81a68d8, current_header=2) at src/image.c:748
#6 0x0806169d in decode_one_frame (pDecoder=0x810d170) at src/image.c:943
#7 0x08087376 in DecodeOneFrame (ppDecPicList=0xbfffdfe4) at src/ldecod.c:1254
#8 0x08054fd3 in main (argc=1, argv=0xbffff174) at src/decoder_test.c:282
nalu->buf 存放着EBSP起始的第一个位
2、写MVD到码流中
writeMotionVector8x8
#0 writeMotionVector8x8 (currMB=0x8238378, i0=0, j0=0, i1=2, j1=2, refframe=0, list_idx=0, mv_mode=4, bipred_me=0,
Extraction=0) at src/macroblock.c:3087
#1 0x0812b88a in rdcost_for_8x8blocks (currMB=0x8238378, dataTr=0x8c55df0, cnt_nonz=0xbfffea60, cbp_blk=0xbfffeac0,
lambda=2505, block=0, mode=4, part=0xbfffeabb, min_rdcost=356116) at src/rdopt.c:830
#2 0x080fb713 in submacroblock_mode_decision_b_slice (currMB=0x8238378, enc_mb=0xbfffecc8, dataTr=0x8c55df0,
cofACtr=0x8c4e730, block=0, cost=0xbfffec48) at src/mode_decision_P8x8.c:519
#3 0x080c0f5d in encode_one_macroblock_high (currMB=0x8238378) at src/md_high.c:207
#4 0x0814da9b in encode_one_slice (p_Vid=0x81d5020, SliceGroupId=0, TotalCodedMBs=0) at src/slice.c:515
#5 0x0806cfe5 in code_a_plane (p_Vid=0x81d5020, p_Inp=0x81e9c88) at src/image.c:219
#6 0x0806d32e in code_a_picture (p_Vid=0x81d5020, pic=0x836ac80) at src/image.c:306
#7 0x08070e2c in frame_picture (p_Vid=0x81d5020, frame=0x836ac80, imgData=0x81d50ec, rd_pass=0) at src/image.c:1664
#8 0x0806eaa1 in perform_encode_frame (p_Vid=0x81d5020) at src/image.c:818
#9 0x0807010b in encode_one_frame (p_Vid=0x81d5020, p_Inp=0x81e9c88) at src/image.c:1300
#10 0x0808e43b in encode_sequence (p_Vid=0x81d5020, p_Inp=0x81e9c88) at src/lencod.c:1106
#11 0x0808c504 in main (argc=1, argv=0xbffff164) at src/lencod.c:392
对码流的分析
1 首先给出一张264码流的二进制文件,如下:
0x00 00 00 01 or 0x00 00 01是NALU的前缀编码,在get_annex_b_NALU
中将其识别,并去掉,而将一个NALU给nalu->buf
从后面的67~80是NALU单元,(代表着一个SPS),而NALU中的第一个字节0x67,是NALU的head,其后才是一个RBSP数据.
2 现在研究一个PPS
PPS的码流数据为: {0xcb, 0xe3, 0xcb, 0x22, 0xc0} 不包括0x68的NALU head
先语法元素用掉了33位(p_Dec->UsedBits存储了一个NALU语法元素的位数),那么0xcb, 0xe3, 0xcb, 0x22, + 1 = 33bit 而后面的100 0000是RBSP trailing bits (第一位是1,后跟0,直到补齐一个字节)
H264码流分析
NAL+VCL
NAL全称Network Abstract Layer, 即网络抽象层。
在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL)。其中,前者负责有效表示视频数据的内容,而后者则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。因此我们平时的每帧数据就是一个NAL单元(SPS与PPS除外)。
一个frame是可以分割成多个Slice来编码的,而一个Slice编码之后被打包进一个NAL单元,另外NAL单元中也存放着SPS/PPS/SEI等信息.
Slice是片的意思,264中把图像分成一帧(frame)或两场(field),而帧又可以分成一个或几个片(Slilce);片由宏块(MB)组成。宏块是编码处理的基本单元。
SODB:最原始的编码数据,没有任何附加数据
RBSP:在 SODB 的基础上加了rbsp_stop_ont_bit(bit 值为 1)并用 0 按字节补位对齐
EBSP:在 RBSP 的基础上增加了防止伪起始码字节(0X03)
对于RBSP NALU的介绍和理解:
H.264中NALU、RBSP、SODB的关系
JM8.6中NALU(此处指非VCL式的NALU,如SPS和PPS)是如何写进码流的?
JM8.6中NALU(此处指VCL式的NALU)是如何写进码流的?
通过使用h264bitstream-0.1.9分析了H264的码流的NAL数据(未对V),以下是一部分输出的NAL数据。
/************************************************
* SPS
* **********************************************/
!! Found NAL at offset 4 (0x0004), size 23 (0x0017)
XX 00 00 00 01 67 64 00 0B AC D9 42 C4 E8 40 00 00
==================== NAL ====================
forbidden_zero_bit : 0
nal_ref_idc : 3
nal_unit_type : 7 ( Sequence parameter set )
======= SPS =======
profile_idc : 100
constraint_set0_flag : 0
constraint_set1_flag : 0
constraint_set2_flag : 0
constraint_set3_flag : 0
constraint_set4_flag : 0
constraint_set5_flag : 0
reserved_zero_2bits : 0
level_idc : 11
seq_parameter_set_id : 0
chroma_format_idc : 1
residual_colour_transform_flag : 0
bit_depth_luma_minus8 : 0
bit_depth_chroma_minus8 : 0
qpprime_y_zero_transform_bypass_flag : 0
seq_scaling_matrix_present_flag : 0
log2_max_frame_num_minus4 : 0
pic_order_cnt_type : 0
log2_max_pic_order_cnt_lsb_minus4 : 2
delta_pic_order_always_zero_flag : 0
offset_for_non_ref_pic : 0
offset_for_top_to_bottom_field : 0
num_ref_frames_in_pic_order_cnt_cycle : 0
num_ref_frames : 4
gaps_in_frame_num_value_allowed_flag : 0
pic_width_in_mbs_minus1 : 10
pic_height_in_map_units_minus1 : 8
frame_mbs_only_flag : 1
mb_adaptive_frame_field_flag : 0
direct_8x8_inference_flag : 1
frame_cropping_flag : 0
frame_crop_left_offset : 0
frame_crop_right_offset : 0
frame_crop_top_offset : 0
frame_crop_bottom_offset : 0
vui_parameters_present_flag : 1
=== VUI ===
aspect_ratio_info_present_flag : 0
aspect_ratio_idc : 0
sar_width : 0
sar_height : 0
overscan_info_present_flag : 0
overscan_appropriate_flag : 0
video_signal_type_present_flag : 0
video_format : 0
video_full_range_flag : 0
colour_description_present_flag : 0
colour_primaries : 0
transfer_characteristics : 0
matrix_coefficients : 0
chroma_loc_info_present_flag : 0
chroma_sample_loc_type_top_field : 0
chroma_sample_loc_type_bottom_field : 0
timing_info_present_flag : 1
num_units_in_tick : 1
time_scale : 50
fixed_frame_rate_flag : 1
nal_hrd_parameters_present_flag : 0
vcl_hrd_parameters_present_flag : 0
low_delay_hrd_flag : 0
pic_struct_present_flag : 0
bitstream_restriction_flag : 1
motion_vectors_over_pic_boundaries_flag : 1
max_bytes_per_pic_denom : 0
max_bits_per_mb_denom : 0
log2_max_mv_length_horizontal : 9
log2_max_mv_length_vertical : 9
num_reorder_frames : 2
max_dec_frame_buffering : 4
=== HRD ===
cpb_cnt_minus1 : 0
bit_rate_scale : 0
cpb_size_scale : 0
bit_rate_value_minus1[0] : 0
cpb_size_value_minus1[0] : 0
cbr_flag[0] : 0
initial_cpb_removal_delay_length_minus1 : 0
cpb_removal_delay_length_minus1 : 0
dpb_output_delay_length_minus1 : 0
time_offset_length : 0
/************************************************
* PPS
* **********************************************/
!! Found NAL at offset 31 (0x001F), size 6 (0x0006)
XX 00 00 00 01 68 CB E3 CB 22 C0
==================== NAL ====================
forbidden_zero_bit : 0
nal_ref_idc : 3
nal_unit_type : 8 ( Picture parameter set )
======= PPS =======
pic_parameter_set_id : 0
seq_parameter_set_id : 0
entropy_coding_mode_flag : 0
pic_order_present_flag : 0
num_slice_groups_minus1 : 0
slice_group_map_type : 0
num_ref_idx_l0_active_minus1 : 2
num_ref_idx_l1_active_minus1 : 0
weighted_pred_flag : 1
weighted_bipred_idc : 2
pic_init_qp_minus26 : -3
pic_init_qs_minus26 : 0
chroma_qp_index_offset : -2
deblocking_filter_control_present_flag : 1
constrained_intra_pred_flag : 0
redundant_pic_cnt_present_flag : 0
transform_8x8_mode_flag : 0
pic_scaling_matrix_present_flag : 0
second_chroma_qp_index_offset : 0
/************************************************
* SEI
* **********************************************/
!! Found NAL at offset 40 (0x0028), size 672 (0x02A0)
XX C0 00 00 01 06 05 FF FF 9C DC 45 E9 BD E6 D9 48
==================== NAL ====================
forbidden_zero_bit : 0
nal_ref_idc : 0
nal_unit_type : 6 ( Supplemental enhancement information (SEI) )
======= SEI =======
=== User data unregistered ===
payloadType : 5
payloadSize : 666
payload :
DC 45 E9 BD E6 D9 48 B7 96 2C D8 20 D9 23 EE EF
........
61 71 3D 31 3A 31 2E 30 30 00
/************************************************
* 帧数据1
* **********************************************/
!! Found NAL at offset 715 (0x02CB), size 5142 (0x1416)
XX 80 00 00 01 65 88 84 01 7B A7 CD 83 87 14 00 04
==================== NAL ====================
forbidden_zero_bit : 0
nal_ref_idc : 3
nal_unit_type : 5 ( Coded slice of an IDR picture )
======= Slice Header =======
first_mb_in_slice : 0
slice_type : 7 ( I slice only )
pic_parameter_set_id : 0
frame_num : 0
field_pic_flag : 0
bottom_field_flag : 0
idr_pic_id : 0
pic_order_cnt_lsb : 0
delta_pic_order_cnt_bottom : 0
redundant_pic_cnt : 0
direct_spatial_mv_pred_flag : 0
num_ref_idx_active_override_flag : 0
num_ref_idx_l0_active_minus1 : 0
num_ref_idx_l1_active_minus1 : 0
cabac_init_idc : 0
slice_qp_delta : 1
sp_for_switch_flag : 0
slice_qs_delta : 0
disable_deblocking_filter_idc : 0
slice_alpha_c0_offset_div2 : 0
slice_beta_offset_div2 : 0
slice_group_change_cycle : 0
=== Prediction Weight Table ===
luma_log2_weight_denom : 0
chroma_log2_weight_denom : 0
=== Ref Pic List Reordering ===
ref_pic_list_reordering_flag_l0 : 0
ref_pic_list_reordering_flag_l1 : 0
=== Decoded Ref Pic Marking ===
no_output_of_prior_pics_flag : 0
long_term_reference_flag : 0
adaptive_ref_pic_marking_mode_flag : 0
/************************************************
* 帧数据2
* **********************************************/
!! Found NAL at offset 5861 (0x16E5), size 2213 (0x08A5)
XX 00 00 00 01 41 9A 22 6C 13 E2 83 E1 44 23 ED EA
从中可以看到码流由SPS+PPS+SEI+帧数据构成…
而用HEX打开该H264的视频中可看到16进制的信息。
1、SPS信息
2、PPS信息
3、SEI增强信息
4、帧数据