diff --git a/RTMP/1-yuv.md b/RTMP/1-yuv.md index e0e2cbb..bc09c7c 100644 --- a/RTMP/1-yuv.md +++ b/RTMP/1-yuv.md @@ -28,14 +28,14 @@ - RGB转YUV: -> Y = 0.299R + 0.587G + 0.114B -> U = 0.564(B - Y) +> Y = 0.299R + 0.587G + 0.114B
+> U = 0.564(B - Y)
> V = 0.713(R - Y) - YUV转RGB: -> R = Y + 1.402V -> B = Y + 1.772U +> R = Y + 1.402V
+> B = Y + 1.772U
> G = Y - 0.344U - 0.714V #### YUV取样格式 diff --git a/RTMP/3-h264.md b/RTMP/3-h264.md index 1ffedfe..1fe2031 100644 --- a/RTMP/3-h264.md +++ b/RTMP/3-h264.md @@ -1,4 +1,4 @@ -#视频编码之H.264(3) +# 视频编码之H.264(3) ### 简单说说编码 @@ -27,7 +27,7 @@ - 4.P帧:有了I帧作为基础,P就有参考的对象。跟前面I帧的变化非常少(相似度70%之内)。 -- 5.GOF:按上面说录像人在发呆场景时,将一组数据相差较小的图片进行编码,这就有了GOF,即:`group of picture`,即一组图片,也叫一个场景的一组图片。是从一个I帧开始到下一个I帧前的一组数据。接着编码就是这样的一个个GOF的轮回。 +- 5.GOP:按上面说录像人在发呆场景时,将一组数据相差较小的图片进行编码,这就有了GOP,即:`group of picture`,即一组图片,也叫一个场景的一组图片。是从一个I帧开始到下一个I帧前的一组数据。接着编码就是这样的一个个GOP的轮回。 如果不追求其中的细节,把他看成一个"黑盒"的话,编码成了: @@ -102,7 +102,7 @@ SPS全称 `Sequence parameter set`(序列参数集),当`nal_unit_type`=7时, 上面中的主要参数的含义: -####profile_idc +#### profile_idc - **profile_idc** 档次(H.264编码标准有几个档次,等级越高视频越清晰): @@ -117,7 +117,7 @@ SPS全称 `Sequence parameter set`(序列参数集),当`nal_unit_type`=7时, | *144* | High 4:4:4 (FRExt) | -####level_idc +#### level_idc - **level_idc** 最大支持码流范围: @@ -141,11 +141,11 @@ SPS全称 `Sequence parameter set`(序列参数集),当`nal_unit_type`=7时, | *50* | 5 (Supports 3672x1536 format. Frame coding only. 150994944 samples/sec) | | *51* | 5.1 (Supports 4096x2304 format. Frame coding only. 251658240 samples/sec) | -####seq_parameter_set_id +#### seq_parameter_set_id - **seq_parameter_set_id** 标识符,本序列的id号,会被PPS引用。 -####chroma_format_idc +#### chroma_format_idc - **chroma_format_idc** 与亮度取样对应的色度取样 @@ -161,14 +161,14 @@ SPS全称 `Sequence parameter set`(序列参数集),当`nal_unit_type`=7时, |3 | 4:4:4 |1 | 1 | -####num_ref_frames +#### num_ref_frames 规定了可能在视频序列中任何图像帧间预测的解码过程中用到的短期参考帧和长期参考 帧、互补参考场对以及不成对的参考场的最大数量。num_ref_frames 字段也决定了 8.2.5.3 节规定的滑动窗口操作 的大小。num_ref_frames 的取值范围应该在 0 到 MaxDpbSize (参见 A.3.1 或 A.3.2 节的定义)范围内,包括 0 和 MaxDpbSize 。 -####pic_width_in_mbs_minus1 +#### pic_width_in_mbs_minus1 - **pic_width_in_mbs_minus1** 加1是指以宏块为单元的每个解码图像的宽度。 > 本句法元素加 1 后指明图像宽度,以宏块为单位:PicWidthInMbs = pic_width_in_mbs_minus1 + 1 @@ -198,7 +198,7 @@ MaxDpbSize 。 > frame_height = 16 × (pic_height_in_map_units_minus1 + 1); > ``` -####手撕SPS +#### 手撕SPS 下面为从一个只放h.264视频编码文件的一段(SPS): @@ -208,37 +208,50 @@ ue(v)和se(v)的计算公式见 [H.264描述符](./h264-descriptor.md) 。 ```shell # 00000001 6764001E ACD940A0 2FF96100 00030001 00000300 320F162D 96 + 00000001 #起始码 #NAL单元头---0x67 0110 0111 -------------- 0... .... # forbidden_zero_bit -->u(1) .11. .... # nal_ref_idc -->u(2) -->HIGHEST ...0 0111 # nal_unit_type -->u(5) -->SPS -64 # profile_idc=103 -->u(8) + +0x64 # profile_idc=100 -->u(8) + #---0x00 0000 0000 -------------- 0... .... #constraint_set0_flag .0.. .... #constraint_set1_flag ..0. .... #constraint_set2_flag ...0 .... #constraint_set3_flag .... 0000 #reserved_zero_4bits + 1E # level_idc -->u(8) --> 30 + #-----------0xAC 1010 1100 -------------- 1... .... # seq_parameter_set_id --> ue(v) --> 0 -.010 .... # log2_max_frame_num_minus4 --> ue(v) --> 1 -.... 1... # pic_order_cnt_type --> ue(v) -->1 执行else if( pic_order_cnt_type == 1 ) -.... .1..#delta_pic_order_always_zero_flag -->u(1) -#----------0xACD9 (1010 11)00 1101 1001 ====== 括号里面的bit上面已使用 -.... ..00 110. .... #offset_for_non_ref_pic -->se(v)->codeNum=5->value=3 -.... .... ...1 .... #offset_for_top_to_bottom_field -->se(v)->codeNum=0->value=0 -.... .... .... 1... #num_ref_frames_in_pic_order_cnt_cycle -->ue(v)->0 +.010 .... # chroma_format_idc --> ue(v) --> 1 +.... 1... # bit_depth_luma_minus8 --> ue(v) --> 0 +.... .1.. # bit_depth_chroma_minus8 -->ue(v) --> 0 +.... ..0. # qpprime_y_zero_transform_bypass_flag -->u(1) --> 0 +.... ...0 # seq_scaling_matrix_present_flag -->u(1) --> 0 + +#----------0xD9 1101 1001 -------------- +1. .... # log2_max_frame_num_minus4 -->ue(v)->0 +.1.. .... # pic_order_present_flag -->ue(v)->0 +..01 1... # log2_max_pic_order_cnt_lsb_minus4 -->ue(v)->2 +.... .001 # 下一部分使用 + #----------0xD940 (1101 1)001 0100 0000 ====== 括号里面的bit上面已使用 -.... .001 01.. .... #num_ref_frames -->ue(v)->4 -.... .... ..0. .... #gaps_in_frame_num_value_allowed_flag -->u(1)->0 +.... .001 01.. .... # max_num_ref_frames -->ue(v)->4 +.... .... ..0. .... # gaps_in_frame_num_value_allowed_flag -->u(1)->0 + #----------0x40A0 (010)0 0000 1010 0000 ====== 括号里面的bit上面已使用 ...0 0000 1010 00.. #pic_width_in_mbs_minus1 -->ue(v)->32-1+8=39 --> 视频宽 = (宏块 + 1) X 16 = (39 + 1)X16 = 640 + #----------0xA02F (1010 00)00 0010 1111 ====== 括号里面的bit上面已使用 .... ..00 0010 111. #pic_height_in_map_units_minus1 -->ue(v)->16-1+7=22--> 视频高 = (宏块 + 1) X 16 = (22 + 1)X16 = 368 …………就到这里了,偷个懒,有兴趣大家自己分析下去,哈哈 +2025.5.12 补充:这里有一份 SPS 和 PPS 的完整手撕,可以参考:https://blog.qer.im/writings/parse-nalu-sps-pps ``` diff --git a/RTMP/h264-descriptor.md b/RTMP/h264-descriptor.md index 52e102c..1a2ee1d 100644 --- a/RTMP/h264-descriptor.md +++ b/RTMP/h264-descriptor.md @@ -7,7 +7,7 @@ H264/AVC文档中存在着大量元素描述符ue(v),me(v),se(v),te(v)等 ###浅谈哥伦布编码 在说哥伦布编码之前首先先谈谈什么是定长编码?什么是可变长度编码? -####定长编码 +#### 定长编码 即固定长度的编码,如:对汉字进行编码时,每个汉字都是2两个字节。 @@ -15,7 +15,7 @@ H264/AVC文档中存在着大量元素描述符ue(v),me(v),se(v),te(v)等 坏处是会浪费很多不必要的内存,比如用1个字节能存储的值,却必须要2个字节,浪费了一个字节。 -####可变长编码 +#### 可变长编码 长度不固定的编码,随内容大小而改变编码长度。 @@ -24,7 +24,7 @@ H264/AVC文档中存在着大量元素描述符ue(v),me(v),se(v),te(v)等 但是因为内容不固定,所以得在内存中是如何知道这些内容的长度呢?所以一般都是在内容前面加上内容的长度。 -####为何H264中采用哥伦布编码 +#### 为何H264中采用哥伦布编码 在H264编码中,有大量的短数据(在255以内),如:宏块大小,各种标志位等。为了减少码流的长度,把这些短数据使用可变长度编码,而在众多可变长度编码的算法中,哥伦布编码尤其合适H264编码。因为数据的值小,在其二进制的值上+1再补长度个数的0即可,这样就可以把每个bit用到,不会产生多余的浪费。 而为何只是用于短数据呢?因为多少个0代码数据长度,也就是说数据大的,就会产生很多很多0,会造成更大的浪费。具体看下面原理。 @@ -34,7 +34,7 @@ H264/AVC文档中存在着大量元素描述符ue(v),me(v),se(v),te(v)等 无符号整数指数哥伦布码编码的语法元素,左位在先。计算公式: -codeNum = $$2^{leadingZeroBits}$$ − 1 + read_bits( leadingZeroBits ) +$$ codeNum = 2^{leadingZeroBits} − 1 + read_bits( leadingZeroBits ) $$ - **codeNum:** 占的位数(bit数量) - **leadingZeroBits:** 遇到第一个1前面0的个数;如:0010 1011,leadingZeroBits的值为2; @@ -42,8 +42,8 @@ codeNum = $$2^{leadingZeroBits}$$ − 1 + read_bits( leadingZeroBits ) 举两个栗子: -(1)0001 1001 =>leadingZeroBits== 000. ....= 3,read_bits( 3 )==.... 100.=4,所以:codeNum=$$2^3$$-1+4=13 -(2)0000 0101 0000 0000 => leadingZeroBits=5,read_bits( 5 ) = .... ..01 000. ....=8,codeNum=$$2^5$$-1+8=39 +(1)0001 1001 => leadingZeroBits==000. ....=3,read_bits(3)==.... 100.=4,所以:$$codeNum=2^3-1+4=13$$ +(2)0000 0101 0000 0000 => leadingZeroBits=5,read_bits(5)=.... ..01 000. ....=8,$$codeNum=2^5-1+8=39$$ ### se(v)