音视频入门:H264、H265概念总结

文章目录

      • 首先,串流传输协议使用rtsp,
      • 其次,什么是H.264与H.265
        • H.264和H.265对比
      • 视频编码的背景
        • 视频质量
      • 视频编码的意义
      • H.264 视频 RTP 负载格式 video/avc
          • H264打包成rtp数据包有三种方式
        • 1.h264的组成结构/组成单元/网络抽象层单元类型 (NALU)
          • 1、NAL、Slice与frame简介及相互关联
          • 2、NAL nal_unit_type
        • #3、NAL nal_unit_type参数集
        • 3. SDP 参数
      • 播放H265视频流
        • 1. H265 Nal Unit Header 简单介绍:
        • 2.RTP 打包格式.
          • 1 单个Nal单元打包:
          • 2 Nal单元分片打包:
      • SDP,解码参数vps sps pps
      • 注意:vps sps pps的值并不是字符串
      • 264 265中I帧的判断

最近一个月在做一个串流的项目,使用rtsp传输经过H264编码或者H265编码的视频流,在安卓客户端使用MediaCodec硬解码,渲染播放出画面,由于之前接触的编解码知识非常有限,几乎没有研究过这一块的内容,所以,走了不少弯路,这里总结一下,给自己备忘,也希望可以帮助到其他人。

首先,串流传输协议使用rtsp,

rtsp相对于rtmp推送的优势是可以用udp传输音视频数据,udp传输可以避免延时累积,当然rtsp也可以用tcp传输rtp包, 另外rtsp协议本身就支持h265,不像rtmp还需要用一个非官方扩展去支持. 实际测试可以看出延时非常低,就几百毫秒,这个延时可以满足大多数低延时场景需求. 另外rtsp协议本身就是为实时流传输设计的,能更好的兼容现有的监控系统等. rtsp推送更适合互联网远程监控等项目.
说到传输,必不可少的就离不开抓包,wireshark进行抓包分析,有很多的门道,强大的抓包工具,可以分析每一个连接。里面的一大串看不懂的东西都是可以通过这个工具进行转换的,转换成看得懂的字符串。
rtsp具体的,后续单独写一篇文章。这里着重总结一下264/265的一些基础。

其次,什么是H.264与H.265

H.264与H.265都是视频压缩格式,由于视频本身的码流太大,所以需要经过压缩然后再通过网络进行传输。H.264是目前比较主流的压缩算法,像视频会议设备一般都采用这个编码格式。基础的H.264可以支持在1M带宽下传输720P30帧/秒的图像;H.264 HIGH PROFILE支持在512K带宽下传输720P30帧/秒的图像。

H.265是比较新的压缩算法,可以更一步提高压缩比,随着我们现在生活中出现的视频格式越来越大(比如现在基本都是1080P甚至4K的显示器,4K片源将来也会越来越多),就需要像H.265这样的新压缩算法,提高效率、节约带宽或存储空间。H.265支持在384K带宽下传输720P30帧/秒。

现在5G到来了,2K,4K分辨率的视频传输在带宽上可以给到更大的支持了,所以H265逐渐广泛使用起来,其实已经诞生出了新一代压缩算法H266,只是由于新技术以及高昂的专利费还没有推广开来,腾讯似乎参与了其中的设计。

H.264和H.265对比

1、版本

H.265是新的编码协议,也即是H.264的升级版。H.265标准保留H.264原来的某些技术,同时对一些相关的技术加以改进。新技术使用先进的技术用以改善码流、编码质量、延时和算法复杂度之间的关系,达到最优化设置;

2、降码率

比起H.264/AVC,H.265/HEVC提供了更多不同的工具来降低码率,以编码单位来说,H.264中每个宏块(macroblock/MB)大小都是固定的16x16像素,而H.265的编码单位可以选择从最小的8x8到最大的64x64;

3、新技术使用先进的技术用以改善码流、编码质量、延时和算法复杂度之间的关系,达到最优化设置;

4、采用了块的四叉树划分结构

H.265相比H.264最主要的改变是采用了块的四叉树划分结构,采用了从64x64~8x8像素的自适应块划分,并基于这种块划分结构采用一系列自适应的预测和变换等编码技术;

5、算法优化

H264由于算法优化,可以低于1Mbps的速度实现标清数字图像传送;H265则可以实现利用1~2Mbps的传输速度传送720P(分辨率1280*720)普通高清音视频传送;

6、同样的画质和同样的码率,H.265比H2.64 占用的存储空间要少理论50%;

7、占用的存储空间缩小

比起H.264/AVC,H.265/HEVC提供了更多不同的工具来降低码率,以编码单位来说,H.264中每个宏块(macroblock/MB)大小都是固定的16x16像素,而H.265的编码单位可以选择从最小的8x8到最大的64x64。那么,在相同的图象质量下,相比于H.264,通过H.265编码的视频大小将减少大约39-44%;
  
H.264与H.265

H.264与H.265都是视频压缩格式,由于视频本身的码流太大,所以需要经过压缩然后再通过网络进行传输。H.264上目前比较主流的压缩算法,像视频会议设备一般都采用这个编码格式。基础的H.264可以支持在1M带宽下传输720P30帧/秒的图像;H.264 HIGH PROFILE支持在512K带宽下传输720P30帧/秒的图像。

H.265是比较新的压缩算法,可以更一步提高压缩比,随着我们现在生活中出现的视频格式越来越大(比如现在基本都是1080P甚至4K的显示器,4K片源将来也会越来越多),就需要像H.265这样的新压缩算法,提高效率、节约带宽或存储空间。H.265支持在384K带宽下传输720P30帧/秒。

视频编码的背景

伴随着用户对高清视频需求量的增加,视频多媒体的视频数据量也越来越大。如果不经过压缩,这些视频将很难应用于实际的存储和传输。视频压缩解码技术可以有效地去除视频数据中的冗余信息,实现视频数据在互联网中的快速传输和离线存储。因此,视频压缩解码技术是视频应用中的一项关键性技术。

在过去的几十年中,一系列视频编码标准被推出并广泛应用。现有的视频压缩标准就有很多种,包括国际标准化组织(International Organization for Standardization, ISO)/国际电工技术委员会(International Electrotechnical Commission, IEC)制定的MPEG-1、MPEG-2、MPEG-4标准;国际电信联盟电信标准化部门(International Telecommunication Union-Telecom, ITU-T)制定的H.261、H.263等。

2003年3月,ITU-T和ISO/IEC 正式公布了H.264/MPEG-4 AVC视频压缩标准。H.264作为目前应用最为广泛的视频编码标准,在提高编码效率和灵活性方面取得了巨大成功,使得数字视频有效地应用在各种各样的网络类型和工程领域。为了在关键技术上不受国外牵制、节约专利费用支出,中国制定了AVS系列标准,可以提供与H.264/AVC相当的编码效率。

近年来随着用户要求的不断提升,高清(1920x1080)和超高清(3840x2160)视频的应用越来越广泛。相比于标清视频,高清视频分辨率更大更清晰,但是相应的数据量也随之增加。在存储空间和网络带宽有限的情况下,现有的视频压缩技术已经不能满足现实的应用需求。为了解决高清及超高清视频急剧增长的数据率给网络传输和数据存储带来的冲击,ITU-T和ISO/IEC联合制定了具有更高压缩效率的新一代视频压缩标准HEVC(High Efficiency Video Coding)。

HEVC基于传统的混合视频编码框架,采用了更多的技术创新,包括灵活的块划分、更精细的帧内预测、新加入的Merge模式、Tile划分、自适应样点补偿等。灵活的块划分对编码性能提升最大,块划分包括编码单元(CU)、预测单元(PU)和变换单元(TU)。这些技术使得HEVC编码性能比H.264/AVC提高了一倍。但是,这些技术也使得HEVC编码器的复杂度大大增加,不利于HEVC编码器的实时应用和推广。

至此,明白,H.264/H.265是一套视频压缩算法,是一套标准组织制定的视频压缩标准、规范。

视频质量

标清:480x800
普通高清:720x1280 720P
高清:1920x1080 1080P
2K:2048*1024
超高清 4K:3840x2160

视频编码的意义

在实际应用场合,传输带宽通常都是有限的,如何在有限的带宽下尽可能保证视频的质量,即码率控制,是视频编码技术需要解决的一个重要问题。对于以高清、超清视频作为主要编码对象的HEVC标准来说,如何在有限的带宽资源下合理分配码率,使视频质量达到最佳,也就成为了编码时需要考虑的重要问题。

H.264 视频 RTP 负载格式 video/avc

H264就是通过打包到rtp协议的数据部分发送出去的。

H264打包成rtp数据包有三种方式

单一封包模式
组合封包模式
分片模式

要想弄明白这三种打包方式,必须先弄清楚h264的组成结构,或者叫组成单元。

H264数据流最基本的结构单元叫nalu单元。每一帧画面拥有一个或多个nalu单元,每个nalu单元以start code进行分离。

nalu header占一个字节,它又分了三个部分:F,NRI,TYPE。
H.264码流第一个 NALU 是 SPS(序列参数集Sequence Parameter Set)
H.264码流第二个 NALU 是 PPS(图像参数集Picture Parameter Set)
H.264码流第三个 NALU 是 IDR(即时解码器刷新)

1.h264的组成结构/组成单元/网络抽象层单元类型 (NALU)

NALU 头由一个字节组成, 它的语法如下:

  +---------------+
  |0|1|2|3|4|5|6|7|
  +-+-+-+-+-+-+-+-+
  |F|NRI|  Type   |
  +---------------+

F: 1 个比特. 一般为0
forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.
NRI: 2 个比特.
nal_ref_idc. 取 00 ~ 11, 指示nalu单元的重要性,, 如 00 的 NALU 解码器可以丢弃它而不影响图像的回放. 不过一般情况下不太关心这个属性.
Type: 5 个比特.
nal_unit_type. 这个 NALU 单元的类型.

TYPE类型简述如下:
0 没有定义
1-23 NAL单元 单个 NAL 单元包.
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义

特别注意的是7是SPS,8为PPS,发送SDP协议包时需进行base64编码 25,26,27,29这四种类型基本不会出现。

1、NAL、Slice与frame简介及相互关联

NAL指网络提取层,里面放一些与网络相关的信息。
Slice是片的意思,264中把图像分成一帧(frame)或两场(field),而帧又可以分成一个或几个片(Slilce);片由宏块(MB)组成。宏块是编码处理的基本单元。
一个frame是可以分割成多个Slice来编码的,而一个Slice在编码之后被打包进一个NAL单元。每个slice中的数据,在帧内预测只用到自己slice的数据, 与其他slice 数据没有依赖关系。NAL 是用来将编码的数据进行打包的,每一个slice 数据可以放在NAL 包中。不过NAL单元除了容纳Slice编码的码流外,还可以容纳其他数据,比如序列参数集SPS。

I frame 是自己独立编码,不依赖于其他frame 数据。
P frame 依赖 I frame 数据。
B frame 依赖 I frame, P frame 或其他 B frame 数据。

2、NAL nal_unit_type

NALnal_unit_type中有1(非IDR图像的编码条带)、2(编码条带数据分割块A)、3(编码条带数据分割块B)、4(编码条带数据分割块C)、5(IDR图像的编码条带)五种类型 。
Slice有三种编码模式:I_slice、P_slice、B_slice 。
NALnal_unit_type 里的五种类型,代表接下来数据是表示啥信息的和具体如何分块。I_slice、P_slice、B_slice 表示I类型的片、P类型的片,B类型的片。其中I_slice为帧内预测模式编码;P_slice为单向预测编码或帧内模式;B_slice 中为双向预测或帧内模式。

#3、NAL nal_unit_type参数集

NALnal_unit_type中的帧分为:序列参数集(SPS)、图像参数集(PPS)、增强信息(SEI)不属于帧的概念,表示后面的数据信息为序列参数集(SPS)、图像参数集(PPS)、增强信息(SEI)。

2.H264的RTP打包

  1. 单一 NAL 单元模式
    即一个 RTP 包仅由一个完整的 NALU 组成. 这种情况下 RTP NAL 头类型字段和原始的 H.264的 NALU 头类型字段是一样的.
  2. 组合封包模式
    即可能是由多个 NAL 单元组成一个 RTP 包. 分别有4种组合方式: STAP-A, STAP-B, MTAP16, MTAP24. 那么这里的类型值分别是 24, 25, 26 以及 27.
  3. 分片封包模式
    用于把一个 NALU 单元封装成多个 RTP 包. 存在两种类型 FU-A 和 FU-B. 类型值分别是 28 和 29.

单一 NAL 单元模式
对于 NALU 单元长度小于MTU长度(通常是1500,live555定义的是2400)一般采用单一 NAL 单元模式.
对于一个原始的 H.264 NALU 单元常由 [Start Code] [NALU Header] [NALU Payload] 三部分组成, 其中 Start Code 用于标示这是一个NALU 单元的开始, 必须是 “00 00 00 01” 或 “00 00 01”, NALU 头仅一个字节, 其后都是 NALU 单元内容. 打包时去除 “00 00 01” 或 “00 00 00 01” 的开始码, 把其他数据封包的 RTP 包即可.
组合封包模式
nalu单元实在太小,多个nalu长度和都小于MTU长度, 也就是,当 NALU 的长度特别小时, 可以把几个 NALU 单元封在一个 RTP 包中.
分片封包模式Fragmentation Units (FUs).
而当 NALU 的长度超过 MTU 时, 就必须对 NALU 单元进行分片封包. 也称为 Fragmentation Units (FUs).

以上封包模式是按nalu长度来分的,同时也完全符合nalu单元中TYPE类型来分。
TYPE1-23 单一封包,24组合封包, 28分片封包。

3. SDP 参数

如何在 SDP 中表示一个 H.264 流:
. “m=” 行中的媒体名必须是 “video”
. “a=rtpmap” 行中的编码名称必须是 “H264”.
. “a=rtpmap” 行中的时钟频率必须是 90000.
. 其他参数都包括在 “a=fmtp” 行中.

  m=video 49170 RTP/AVP 98
  a=rtpmap:98 H264/90000
  a=fmtp:98 profile-level-id=42A01E; sprop-parameter-sets=Z0IACpZTBYmI,aMljiA==

packetization-mode 主要是定义包的模式,单一 NALU单元模式(0);非交错(non-interleaved)封包模式(1);交错(interleaved)封包模式(2)
使用RTP传输H264的时候,需要用到sdp协议描述,其中有两项:Sequence Parameter Sets (SPS) 和Picture Parameter Set (PPS)需要用到,那么这两项从哪里获取呢?答案是从H264码流中获取.在H264码流中,都是以"0x00 0x00 0x01"或者"0x00 0x00 0x00 0x01"为开始码的,找到开始码之后,使用开始码之后的第一个字节的低5位判断是否为7(sps)或者8(pps), 即data[4] & 0x1f == 7 || data[4] & 0x1f == 8.然后对获取的nal去掉开始码之后进行base64编码,得到的信息就可以用于sdp.
sps和pps需要用逗号分隔开来.
看到上面的 a = fmtp: 98 … 一行了吧。因为 h264 播放时候的 pps, sps 都是一样的,所以在 sdp 信息中返回了 sps, pps 之后在每一帧的信息中就没有再需要了。在客户端解码的时候需要用 pps, sps 来初始化解码器,

H.264码流第一个 NALU 是 SPS(序列参数集Sequence Parameter Set)
H.264码流第二个 NALU 是 PPS(图像参数集Picture Parameter Set)
H.264码流第三个 NALU 是 IDR(即时解码器刷新)
SDP中的H.264的SPS和PPS串,包含了初始化H.264解码器所需要的信息参数,包括编码所用的profile,level,图像的宽和高,deblock滤波器等。
由于SDP中的SPS和PPS都是BASE64编码形式的,不容易理解,需要使用工具软件可以对SDP中的SPS和PPS进行解析。
音视频入门:H264、H265概念总结_第1张图片

播放H265视频流

对rtsp来说,要播放h265只要正确解析sdp和rtp包即可.
h265和h264有很多相似之处,都有sps和pps,用00 00 00 01进行nal 单元分隔.
1、H265一个图像序列的组成:VPS+SPS+PPS+SEI+一个I帧+若干个P帧。VPS、SPS、PPS、SEI、一个I帧、一个P帧都可以称为一个NALU。
2、H265的NALU结构:开始码+NALU头+NALU数据
(1)、开始码大小为四个字节,是一个固定值00 00 00 01(十六进制),标识一个NALU的开始。
(2)、NALU头大小为两个字节,共16位,第1位值为0,第2-7位为NALU的type位(共6位),标识当前NALU的类型,第8-15位值为0,第16位值为1。
(3)、NALU数据为编码器编出来的图像信息或图像数据。
3、六种类型的NALU
(1)、VPS(视频参数集):NALU头值为0x40 01(十六进制),NALU头type位值为32(十进制)。
(2)、SPS(序列参数集):NALU头值为0x42 01(十六进制),NALU头type位值为33(十进制)。
(3)、PPS(图像参数集):NALU头值为0x44 01(十六进制),NALU头type位值为34(十进制)。
(4)、SEI(补充增强信息):NALU头值为0x4e 01(十六进制),NALU头type位值为39(十进制)。
(5)、I帧:NALU头值为0x26 01(十六进制),NALU头type位值为19(十进制)。
(6)、P帧:NALU头值为0x02 01(十六进制),NALU头type位值为1(十进制)。

1. H265 Nal Unit Header 简单介绍:

h265和h264有很多相似之处,都有sps和pps,用00 00 00 01进行nal 单元分隔.

H264的Nal Unit头是一个字节,265变成两个字节:
音视频入门:H264、H265概念总结_第2张图片265NALU头
264NALU头音视频入门:H264、H265概念总结_第3张图片
F: 1 bit. forbidden_zero_bit. 265要求是0,是1的话指示语法违规等.
Type: 6 bits. Nal类型. vps是32, sps是33, pps是34, 前缀sei是39. IDR是19和20.
LayerId: 6 bits. nuh_layer_id. 现在是0,将来可能扩展用.
TID: 3 bits. nuh_temporal_id_plus1. TemporalId 是TID-1.

2.RTP 打包格式.

实际中其实就用到两种格式,一种是一个nal单元打包到一个rtp包中。一种是nal单元比较大,分片打包在多个rtp中.

1 单个Nal单元打包:

音视频入门:H264、H265概念总结_第4张图片
PayloadHdr 把 NAL单元头填入就好.

2 Nal单元分片打包:

音视频入门:H264、H265概念总结_第5张图片

PayloadHdr还是拷贝NAL单元头,但是要把Type换成49. FU header 就一个字节,格式如下:
音视频入门:H264、H265概念总结_第6张图片

S:为1表示第一个分片。 E:为1表示表示最后一个分片。FuType就是实际的Nal type类型。

SDP,解码参数vps sps pps

取流后需要简单封装数据,h264需提取sps 和 pps 给解码器使用,最好是将sps pps I帧 封装在一起送给解码器。
按照如下格式00 00 00 01+ sps+00 00 00 01 +pps+00 00 00 01 + i帧+ 00 00 00 01 +p帧,Sps 和 pps 只需传入一次就行(后续无变动)。
h265需提取vps,sps,pps,按照如下格式00 00 00 01+vps+00 00 00 01+sps+00 00 00 01+pps+00 00 00 01+i帧+00 00 00 01 + p帧。
Vps sps pps 均是rtspclient 发送describe请求后,rtspserver回复的sdp中带的。

注意:vps sps pps的值并不是字符串

public MediaFormat videoSetByteBuffer(String name, byte data[], int len) {
     

		Log.d(TAG,"videoSetByteBuffer function:"+videoFormat.getString(MediaFormat.KEY_MIME));
		String mime=videoFormat.getString(MediaFormat.KEY_MIME);
		if(videoFormat != null) {
     
			ByteBuffer bb = ByteBuffer.wrap(data, 0, len);
			videoFormat.setByteBuffer(name, bb);
		}

		return videoFormat;
}

H264中sps为csd-0,pps为csd-1;

 	videoSetByteBuffer(rtspParam->jnienv, "csd-0", config_h264_sps, config_h264_spslen);
 	videoSetByteBuffer(rtspParam->jnienv, "csd-1", config_h264_pps, config_h264_ppslen);

H265中vps、sps、pps都在csd-0中按顺序拼接。
注意,按顺序拼接,vps-sps-pps。
1,他们并不是字符串不能使用字符串拼接功能,我在这里踩坑踩了两天。
2,他们使用char 表示数据结构,但是只是表示一个8bit的数据结构,并不是表示这是一个字符串

	int var_len = 0;
	unsigned char *vps_sps_pps = NULL;
	var_len=config_h265_vpslen+config_h265_spslen+config_h265_ppslen;
    vps_sps_pps=(unsigned char *)malloc(var_len);
    memset(vps_sps_pps, 0, var_len);

    memcpy(vps_sps_pps, config_h265_vps, config_h265_vpslen);
    memcpy((vps_sps_pps+config_h265_vpslen), \
            config_h265_sps, config_h265_spslen);
    memcpy((vps_sps_pps+config_h265_vpslen+config_h265_spslen), \
            config_h265_pps, config_h265_ppslen);

    videoSetByteBuffer(rtspParam->jnienv, "csd-0", vps_sps_pps, var_len);

    free(vps_sps_pps);

音视频入门:H264、H265概念总结_第7张图片

264 265中I帧的判断

264中I帧用如下方式判断

(fReceiveBuffer[0] & 0x1F) == 5

265中i帧用如下方式判断

((fReceiveBuffer[0] & 0x7E) >> 1) == 19

参考:
RTP Payload Format for HEVC RFC7798 API http://pike.lysator.liu.se/docs/ietf/rfc/77/rfc7798.xml
MediaCodec API
https://developer.android.google.cn/reference/android/media/MediaCodec?hl=en
Android MediaCodec编码后解析出H264/H265中vps sps pps帧
https://blog.csdn.net/zhichi202/article/details/105920432
MediaCodec
https://www.cnblogs.com/roger-yu/p/5635494.html
mediacodec配置h265解码
https://blog.csdn.net/HTJOY1202/article/details/80776980

你可能感兴趣的:(音视频开发,音视频入门,H.264,H.265,H264,H265)