如何让webRtc支持H264?
编译选项调整
WebRTC能够支持H264,但在Linux下编译时,默认没有打开。
rtc_use_h264。这个开关控制了是否使用 H264 (相应C++代码中的宏 WEBRTC_USE_H264),在 webrtc/webrtc.gni 文件里定义:
rtc_use_h264 = proprietary_codecs && !is_android && !is_ios
proprietary_codecs 在 build/config/features.gni 中定义:
proprietary_codecs = is_chrome_branded || is_chromecast
我在 Linux 下编译,branded 默认是 Chromium ,所以,proprietary_codecs 默认就是 false 。
想来想去,仅仅好通过 gn gen 时传入 args 来调整比較方便,使用以下的命令来生成 ninja 构建文件:
gn gen out/h264Debug --args="proprietary_codecs=true"
运行完成后,能够使用下列命令验证一下:
gn args out/h264Debug --list=proprietary_codecs
gn args out/h264Debug --list=rtc_use_h264
看到 Current Value 为 true。就说明这个选项已经生效了。
打开 rtc_use_h264 。OpenH264 的编码支持就使能了。
WebRTC内部会使用 ffmpeg 来解码 H264 (见 h264_decoder_impl.cc ),与 ffmpeg 相关的另一个选项——rtc_initialize_ffmpeg。这个也得为 true ,否则 ffmpeg 的 avcodec 不会初始化,用不成。
rtc_initialize_ffmpeg 定义在 webrtc/webrtc.gni 中定义:
rtc_initialize_ffmpeg = !build_with_crhome
由于我们为 native 开发而编译。build_with_chrome 默觉得 false ,所以 rtc_initialize_ffmpeg 默觉得 true ,不用调整。
rtc_initialize_ffmpeg 开关相应一个 C++ 代码中的宏 WEBRTC_INITIALIZE_FFMPEG 。
要使用 ffmpeg 的 h264 decoder 功能。还须要改动一个宏: FFMPEG_H264_DECODER。
在 config.h 文件里,路径是 third_party/chromium/config/chromium/linux/x64。原来定义例如以下:
#define CONFIG_H264_DECODER 0
改动为 1 就可以。这样 avcodec_register_all() 方法才会把 H264 decoder 注冊到系统中。
等下,实际上另一部分很重要的工作要做。
由于 linux 下编译 WebRtc 。默认生成的 ninja 构建文件里。没有 ffmpeg 的 h264 decoder 相应的源代码,所以即便你打开 FFMPEG_H264_DECODER 也无用,必须得改动 third_party/ffmpeg/ffmpeg_generated.gni 文件,找到包括 h264的那些条件。打开就可以。
本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部↓↓
codec 的顺序调整
网页使用 WebRTC 发送 SDP ,进行协商时,默认的 codec 顺序是:
VP8
VP9
H264
在 C++ 代码里。会默认选择第一个来匹配(从PeerConnection::CreateAnswer/SetRemoteDescription两个方法跟进去,能够看到)。所以。我们要改动 C++ 代码,来改变这个选择逻辑。
WebRtcVideoChannel2(webrtcvideoengine2.cc)使用的 codec ,来自 InternalEncoderFactory类(internalencoderfactory.cc),无论是作为发送端还是接收端。编码格式都来自这里。
在InternalEncoderFactory的构造函数里,能够调整 codec 的顺序,默认代码例如以下:
supported_codecs_.push_back(cricket::VideoCodec(kVp8CodecName));
if (webrtc::VP9Encoder::IsSupported())
supported_codecs_.push_back(cricket::VideoCodec(kVp9CodecName));
if (webrtc::H264Encoder::IsSupported()) {
cricket::VideoCodec codec(kH264CodecName);
// TODO(magjed): Move setting these parameters into webrtc::H264Encoder
// instead.
codec.SetParam(kH264FmtpProfileLevelId,
kH264ProfileLevelConstrainedBaseline);
codec.SetParam(kH264FmtpLevelAsymmetryAllowed, "1");
supported_codecs_.push_back(std::move(codec));
}
supported_codecs_.push_back(cricket::VideoCodec(kRedCodecName));
supported_codecs_.push_back(cricket::VideoCodec(kUlpfecCodecName));
....
仅仅要把 H264 那个 codec 调整到前面就可以。
做了这个调整,Native app 作为发送视频的一端,在 SDP 协商时。 H264 的支持就会放在前面,另外一端假设支持 H264 解码,就会优先选择 H264 格式。两边就能以 H264 来交互视频流了。
浏览器作为发送视频的一端时。它发过来的视频格式顺序是 VP8、VP9、H264。Native C++代码中会依据这个顺序来调整本地的 codec 的顺序,代码在 mediasession.cc 中:
template <class C>
static void NegotiateCodecs(const std::vector<C>& local_codecs,
const std::vector<C>& offered_codecs,
std::vector<C>* negotiated_codecs) {
for (const C& ours : local_codecs) {
C theirs;
// Note that we intentionally only find one matching codec for each of our
// local codecs, in case the remote offer contains duplicate codecs.
if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
C negotiated = ours;
negotiated.IntersectFeedbackParams(theirs);
if (IsRtxCodec(negotiated)) {
const auto apt_it =
theirs.params.find(kCodecParamAssociatedPayloadType);
// FindMatchingCodec shouldn't return something with no apt value.
RTC_DCHECK(apt_it != theirs.params.end());
negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
}
if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
webrtc::H264::GenerateProfileLevelIdForAnswer(
ours.params, theirs.params, &negotiated.params);
}
negotiated.id = theirs.id;
negotiated.name = theirs.name;
negotiated_codecs->push_back(std::move(negotiated));
}
}
// RFC3264: Although the answerer MAY list the formats in their desired
// order of preference, it is RECOMMENDED that unless there is a
// specific reason, the answerer list formats in the same relative order
// they were present in the offer.
std::unordered_map<int, int> payload_type_preferences;
int preference = static_cast<int>(offered_codecs.size() + 1);
for (const C& codec : offered_codecs) {
payload_type_preferences[codec.id] = preference--;
}
std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
[&payload_type_preferences](const C& a, const C& b) {
return payload_type_preferences[a.id] >
payload_type_preferences[b.id];
});
}
最后那个 sort 调用,依据发送端的 codec 顺序又一次调整了我们支持的解码格式的顺序。
所以,我们在这里也须要改动一下,把排序的部分去掉。或者针对 H264 去掉。
又一次编译
使用下列命令。能够编译特定模块:
ninja pc (针对 mediasession.cc )
ninja media (针对 internalencoderfactory.cc 和 webrtcvideoengine2.cc )
ninja ffmpeg (针对 ffmpeg )
然后再编译你自己的 native app 。
为什么webRtc使用H264会黑屏?
WebRTC 自诞生之日起, 就代表了实时通信领域的最好的技术. 不过很长时间里, 它所支持的视频编码器只有VP8, 后来随着H265/VP9为代表的下一代视频编码器的诞生, WebRTC里出现了VP9 Codec. 而当前应用最广泛的H264 却一直不受待见. 一直到Cisco 宣布旗下的H264 Codec开源为OpenH264, 并且替所有OpenH264的使用者支付了H264的专利费, 以次为契机, 在IETF的WebRTC会议中, 把H264和VP8都列入了WebRTC所必需要支持的视频编码器。 接下来, Google终于在WebRTC中增加了对H264的支持, 在PC平台(Windows和MAC), 编码器是用OpenH264, 解码器是用FFMPEG, 在iOS平台上, 编码器和解码器既可以使用OpenH264和FFMPEG, 也可以用apple的VideoToolbox所支持的硬件编解码器. 在Android平台, 可以用 OpenH264, FFMPGE, 也可以用 MediaCodec. 这个对于广大需要H264的公司来说是一大福音. 在下载的WebRTC代码中做稍许配置, 就可以使用H264了.
WebRTC是以其出色的QoS而著称的, 其VP8和VP9 的视频在比较差的网络条件下都可以保持流畅, 而且其质量相对于当前的网络带宽, 也非常不错。 如果把Codec换成H264, 其质量是否能跟VP8/VP9相提并论呢?
揣着这个问题, 我们对H264的质量做了下评估, 虽然在限带宽和设置丢包的条件下, H264还比较流畅, 但是其出现卡顿的几率明显高于VP8/VP9, 时延也大于VP8/VP9, 有时候质量也会比较差, 出现明显的块状效应, 很容易判断这类块状效应是由编码质量损失造成的。 理论上, 一个好的QoS设计实现, 不应该跟Codec的类型有关, 而且公认的H264的RD性能是优于VP8的, 为什么在WebRTC中的H264质量要差一些呢?
带着这个问题,我们对WebRTC做了深入分析. WebRTC的QoS策略主要是码率评估(类似可用带宽评估), NACK重传, FEC(前向纠错), PLI 请求等, 这些控制都应该跟Codec类型无关呀。 但是真相却并非如此, 在WebRTC的实现中,如果Codec选择为H264的时候, FEC是被关闭的. VP8/VP9有支持时间分级, OpenH264虽然也支持时间分级, 但是在WebRTC中却不能打开.
关于FEC, Google的解释如下:
// Payload types without picture ID cannot determine that a stream is complete
// without retransmitting FEC, so using FEC + NACK for H.264 (for instance) is
// a waste of bandwidth since FEC packets still have to be transmitted. Note
// that this is not the case with FLEXFEC.
翻译成中文是: h264没有picture id, 所以无法判断流完整性, 所以使用FEC+NACK是浪费带宽了, 因为FEC 需要被冲传.
这个到底是不是真相呢?
答案是, 这个只是真相的一部分.
真正的真相是
1. 当H264的FEC 被启用后, 会导致重传包被丢弃。 接收端的JitterBuffer却一直傻傻等待, 从而导致长时间的等待
2. H264的FEC恢复隐藏着BUG, 容易引发流完整性判断出错导致解码失败, 引发视频卡顿.
3. 就是Google所说的原因, Picture ID 是VP8/VP9的概念, 用于标示视频帧的连续性, 而H264却只能依赖RTP序列号来判决连续。
H264不能启用时间分级的原因是, H264的rtp 组包协议里,无法包含视频参考层级关系的信息, 从而无法判决不同分层是否完整, 而VP8/VP9的rtp组包协议却包含着丰富的信息, 足以判断流完整性.
原文链接:如何让WebRTC支持H264? - 资料 - 我爱音视频网 - 构建全国最权威的音视频技术交流分享论坛
本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部↓↓