WebRTC 编解码

作者:追风剑情 发布于:2024-1-29 15:31 分类:Unity3d

一个未压缩的视频数据有多大?我们举个例子计算一下。

  • 分辨率为1920像素 x 1080像素的真彩色视频,每帧数据是 1920x1080x4=8294400字节。
  • 如果该视频的帧率是 30Hz,则每秒产生的数据量是 8294400x30=248832000字 节,约为 249MB。

在今天的网络环境下,这么大的数据量是不可能进行实时传输的,我们需要对视频进行有效压缩,这是对视频(或音频) 进行编解码的主要目的。

WebRTC 规范对音视频编码有强制要求,对于视频,要求所有兼容 WebRTC 的浏览器必须支持 VP8 和 AVC/H.264 视频编码,当然除了这两种编码格式之外,浏览器也可以选择支持其他编码格式;对于音频,要求必须支持 Opus 和 G.711 PCM 编码格式。

当前主流浏览器对 WebRTC 编码格式的支持情况如下表所示。

WebRTC 编码格式
编码格式 媒体类型 兼容浏览器 说明
VP8 视频 Chrome、Edge、Firefox、Safari(12.1+) WebRTC 规范
H.264 视频 Chrome(52+)、Edge、 Firefox、 Safari WebRTC 规范
VP9 视频 Chrome(48+)、Firefox
AV1 视频 Chrome(70+)、Firefox(67+)
Opus 音频 Chrome、 Firefox、Safari、 Edge WebRTC 规范
G.711 PCM 音频 Chrome、Firefox、Safari WebRTC 规范

AV1专门为 Web 技术设计,拥有比 VP9 更高的压缩率,但由于 AV1 技术尚未成熟目前不具备产品化条件,我们就不做过多介绍了。

1.视频编码 VP8/VP9

VP8 (Video Processor 8)由 On2 公司开发,谷歌收购 On2 后,将 VP8 开源。从压缩率和视频质量方面看,VP8 与 H264 很相近。

VP9 (Video Processor 9)是谷歌在 VP8 的基础上研发的优化版,拥有比 VP8 更好的性能和视频质量,与 H.265 更相近。

VP8/VP9都是开源且免费的技术,没有版权相关的问题(H.265 有版权问题),可以放心使用。

通常将视频格式WebM与VP8/VP9结合使用。注意,因为目前Safari浏览器不支持VP9也不支持WebM,所以如果要使用VP9,需要考虑为 Safari提供其他编码方案。

2.视频编码 H.264

H.264是在MPEG-4技术的基础上建立起来的编码格式,又名为AVC(Advanced VideoCoding),目前被广泛应用于各种流媒体。

H.264 的使用非常灵活,通过更改配置可以胜任不同场景,如配置 Constrained BaselineProfile(CBP)使用了较低的带宽,适合用于视频会议和移动网络;Main Profile 适用于标准的视频内容;而 High Profile 则适用于高清蓝光 DVD 视频。

H.264 配置表
配置 16进制编码值
Constrained Baseline Profile (CBP) 42
Baseline Profile (BP) 42
Main Profile (MP) 4D
High Profile (HiP) 64

WebRTC规范要求必须支持 Constrained Baseline(CB) 配置,以便应用于低延时的视频会议场景。SDP 在参数 profile-level-id 指定 H.264 配置(profile)和级别(level),形式如下。

profile-level-id=42e01f

其中42 对应着 Constrained BaselineProfile 配置。

参数 packetization-mode 用于指明封包模式,取值说明如下。

  • 取值为 0: 一个 RTP 包里只包含一个 NALU 包,不分片。
  • 取值为 1: 一个 RTP 包可以包含多个 NALU 包,可分片。
  • 取值为 2: 一个 RTP 包可以包含多个 NALU 包,且允许乱序,可分片。

注意,WebRTC 两端的编码格式和相应的配置参数都需要保持一致,否则建立连接过程会失败。

3.音频编码 Opus/G.711

Opus是WebRTC主要使用的音频编码格式,具备很好的灵活性和可扩展性,可用于语音、音乐播放等复杂的音频场景。

Opus支持多种压缩算法,甚至可以在同一个音频内应用多个压缩算法,编码器可灵活设置码率、带宽、算法压缩等参数。

Opus完全开源,支持码率范围为6~510kbps,支持最多255个音频通道,最大采样率 48kHz,延时范围为5~66.5ms,可用于 MP4/WebM/MPEG-TS 封装格式。

G.711 PCM 是一种实现简单,兼容性好的音频编码格式,支持码率64kbps,支持采样率 8kHz,通常用于向下兼容。

4.编码格式选择

推荐在应用中采用WebRTC规范支持的编码格式,如果采用非推荐的编码格式,需要认真考虑回退方案。

不同的编码格式在浏览器兼容性、占用带宽、能耗等方面存在明显差异,我们可以根据应用程序的特点灵活选择。

(1)音频
即使是在网络环境不好的情况下,使用Opus的窄带模式仍然可以保证较好的通话质量。如果希望应用程序能提供较好的兼容性,可以考虑使用G.711编码格式提供较好的通话质量。

(2)视频
选择视频编码通常需要考虑如下因素。

  ① 许可条款
  VP8/VP9 是完全免费的,但是H.264可能会有潜在的专利费用,不过对于大多数Web开发者来讲,目前无须担心费用问题,H.264的专利所有人表示只会向编解码软件收费。

  ② 耗能
  尤其是在iOS和iPadOS平台,支持硬件编码的H.264更加省电。出于兼容性考虑Safari12.1以后的版本支持 VP8,但是很遗憾,没有提供硬件编码支持。

  ③ 性能
  从终端用户角度来看,VP8与H.264的性能差不多,可以同等应用到WebRTC应用中。

  ④ 使用复杂度
  因为H.264涉及较多的配置和参数,所以使用复杂度较高,而VP8则相对简单很多。

5.编码能力 RTCRtpCodecCapability

WebRTC 使用 RTCRtpCodecCapability 描述当前平台支持的编码格式。RTCRtpCodecCapability 的定义如下面的代码清单所示。

//RTCRtpCodecCapabiity的定义
dictionary RTCRtpCodecCapability {
	required DOMString mimeType;
	required unsigned long clockRate;
	unsigned short channels;
	DOMString sdpFmtpLine;
};  

RTCRtpCodecCapability 属性说明
属性 说明
mimeType MIME媒体类型
clockRate 时钟频率
channels 最大通道数
sdpFmtpLine SDP与该编码格式对应的a=fmtp行信息

6.编码格式 RTCRtpCodecParameters

WebRTC 使用 RTCRtpCodecParameters 描述当前使用的编码格式。RTCRtpCodecParameters 的定义如下面的代码清单所示。

//RTCRtpCodecParameters的定义
dictionary RTCRtpCodecParameters {
	required octet payloadType;
	required DOMstring mimeType;
	required unsigned long clockRate;
	unsigned short channels;
	DOMString sdpFmtpLine;
};  

RTCRtpCodecParameters 属性说明
属性 说明
payloadType RTP载荷类型,用于标识该编码格式。我们在RTP协议相关内容里介绍过载荷类型
mimeType MIME 媒体类型
clockRate 时钟频率
channels 最大通道数
sdpFmtpLine SDP与该编码格式对应的a=fmtp行信息

7.编码参数 RTCRtpEncodingParameters

WebRTC 使用 RTCRtpEncodingParameters 描述编码格式使用到的编码参数。RTCRtpEncodingParameters 的定义如下面的代码清单所示。

//RTCRtpEncodingParameters的定义
dictionary RTCRtpEncodingParameters : RTCRtpCodingParameters {
	boolean active = true;
	unsigned long maxBitrate;
	double scaleResolutionDownBy;
};
dictionary RTCRtpCodingParameters {
	DOMString rid;
};  

RTCRtpEncodingParameters 属性说明
属性 说明
active 编码是否处于活跃状态,true表示活跃,false表示不活跃
maxBitrate 媒体流的最大码率,单位是 bps
scaleResolutionDownBy 指示将视频内容发送给对等端之前,如何缩减视频尺寸。比如,该值取2,则高和宽都除以2,结果只发送原尺寸的1/4;该值取1,则尺寸保持不变,默认值为1
rid RTP id,rid 头扩展

8.获取平台支持的编码格式

由于不同平台支持的编码格式不同,所以在为WebRTC应用编码之前,要先获取当前平台支持的编码格式列表。

使用静态方法 RTCRtpSender.getCapabilities() 或者 RTCRtpReceiver.getCapabilities() 可以获得当前平台支持的编码格式列表,方法的参数是媒体类型,如下面的代码清单所示。

codecList = RCRtpSender.getCapabilities("video" ).codecs;  

codecList 是一个 RTCRtpCodecCapability 对象数组,每个对象描述一个编码格式。 在Chrome83中执行上述方法,codecList的结果如下面的代码清单所示。

//RTCRtpSender.getCapabilities()输出
0:{clockRate:90000,mimeType:"video/VP8"}
1:{clockRate:90000,mimeType:"video/rtx"}
2:{clockRate:90000,mimeType:"video/VP9",sdpFmtpLine:"profile-id=0"}
3:(clockRate: 90000,mimeType:"video/vp9",sdpFmtpLine: "profile-id=2"}
4:(clockRate: 90000,mimeType:"video/H264",sdpFmtpLine: "level-asymmetry-
allowed=l;packetization-mode=l;profile-level-id=42001f"}
5:{clockRate:90000,mimeType:"video/H264",sdpFmtpLine:"level-asymmetry-
allowed=l;packetization-mode=0;profile-level-id=42001f"}
6:{clockRate:90000,mimeType:"video/H264",sdpFmtpLine:"level-asymmetry-
allowed=l;packetization-mode=l;profile-level-id=42e01f" }
7:(clockRate: 90000,mimeType:"video/H264",sdpFmtpLine:"level-asymmetry-
allowed=l;packetization-mode=0;profile-level-id=42e01f"}
8:(clockRate: 90000,mimeType:"video/H264",sdpFmtpLine:"level-asymmetry-
allowed=1;packetization-mode=l;profile-level-id=4d0032"}
9:(clockRate: 90000,mimeType:"video/H264", sdprmtprine: "level-asymmetry-
allowed=1;packetization-mode=l;profile-level-id-640032"}
10:{clockRate:90000,mimeType:"video/red"}
11:(clockRate:90000, mimerype: "video/ulpfea"}  

在输出结果中,"video/rtx"是重传入口,"video/red"是冗余编码入口,"video/ulpfec"是错误重定向入口,它们都不是可用的编码格式。

9.设置编码格式

获取当前平台支持的编码格式列表后,如何指定编码格式呢?这时候只须调整一下列表顺序,把我们想要的编码格式放在最前面,然后再告诉WebRTC就可以了。

下面的代码清单针对每个媒体轨道应用编码格式mimeType,该示例展示了如何获取当前浏览器的视频编码能力,并将与mimeType匹配的编码能力优先级调整为最高(数组最前面),然后调用 setCodecPreferences 进行设置。

//指定编码格式示例
function changeVideoCodec(mimeType) {
	const transceivers = pc.getTransceivers();
	transceivers.forEach(transceiver => {
		const kind = transceiver.sender.track.kind;
		let sendCodecs = RTCRtpSender.getCapabilities(kind).codecs;
		let recvCodecs = RTCRtpReceiver.getCapabilities(kind).codecs;
		if (kind === "video") {
			sendCodecs = preferCodec(sendCodecs,mimeType);
			recvCodecs = preferCodec(recvCodecs,mimeType);
			transceiver.setCodecPreferences([...sendCodecs, ...recvCodecs]);
		}
	});
	pc.onnegotiationneeded();
}
function preferCodec(codecs, mimeType) {
	let otherCodecs = [];
	let sortedCodecs = [];
	codecs.forEach(codec => {
		if (codec.mimeType === mimeType) {
			sortedCodecs.push(codec);
		} else {
			otherCodecs.push(codec);
		}
	});
	
	return sortedCodecs.concat(otherCodecs);
}  

10.查询当前使用的编码

我们给 WebRTC 提供了包含多个编码格式的数组,并按照优先级做了排序,WebRTC 从最高优先级开始,遍历这个数组,逐一尝试应用编码格式,如果不支持,则尝试下一个。

这时就产生了一个问题,WebRTC最终使用了哪个编码格式呢?要回答这个问题就要使用 getParameters() 方法进行査询,如下面的代码清单所示。

//查询当前使用的编码格式
const senders = peerConnection.getSenders();
senders.forEach((sender) => {
	if(sender.track.kind === "video") {
		codecList = sender.getParameters().codecs;
		return;
	}
});

上面的代码清单中的codecList是一个包含 RTCRtpCodecParameters 的数组,代表了当前使用的编码格式。

标签: Unity3d

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号