讓WebRTC支援H264編解碼
https://blog.csdn.net/foruok/article/details/69525039
最近實驗了下如何讓WebRTC支援H264編碼,記錄下,供有需要的人蔘考。
說明一下,我是在 Ubuntu Server 14.04 下編譯的 WebRTC ,使用 native(C++) api 開發 WebRTC 應用。所以我的調整都是基於 native 程式碼。
最終的效果是瀏覽器可以用H264傳送視訊,也可以接收H264視訊。
注意,WebRTC 使用 OpenH264 來做 encoder (見 h264_encoder_impl.cc),使用 ffmpeg 來做 decoder (見 h264_decoder_impl.cc )。
程式碼版本
本文對應的程式碼是2017年2月8號的,可以使用 gclient revinfo -a來檢視具體版本,如下:
編譯選項調整
WebRTC可以支援H264,但在Linux下編譯時,預設沒有開啟。
rtc_use_h264,這個開關控制了是否使用 H264 (對應C++程式碼中的巨集 WEBRTC_USE_H264),在 webrtc/webrtc.gni 檔案中定義:
rtc_use_h264 = proprietary_codecs && !is_android && !is_ios
1
proprietary_codecs 在 build/config/features.gni 中定義:
proprietary_codecs = is_chrome_branded || is_chromecast
1
我在 Linux 下編譯,branded 預設是 Chromium ,所以,proprietary_codecs 預設就是 false 。
想來想去,只好通過 gn gen 時傳入 args 來調整比較方便,使用下面的命令來生成 ninja 構建檔案:
gn gen out/h264Debug --args="proprietary_codecs=true"
1
執行完畢後,可以使用下列命令驗證一下:
gn args out/h264Debug --list=proprietary_codecs
gn args out/h264Debug --list=rtc_use_h264
1
2
看到 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
1
因為我們為 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
修改為 1 即可。這樣 avcodec_register_all() 方法才會把 H264 decoder 註冊到系統中。
等下,實際上還有一部分非常重要的工作要做。因為 linux 下編譯 WebRtc ,預設生成的 ninja 構建檔案中,沒有 ffmpeg 的 h264 decoder 對應的原始碼,所以即便你開啟 FFMPEG_H264_DECODER 也不管用,必須得修改 third_party/ffmpeg/ffmpeg_generated.gni 檔案,找到包含 h264的那些條件,開啟即可。
注:因為我一開始編譯時沒有開啟 H264 支援,所以在修改了ffmpeg_generated.gni 檔案後,使用 gn gen 生成 ninja 構建檔案時,指定了一個新的目錄,然後把 ffmpeg 相關的 ninja 檔案(三個),拷貝到了原來的構建目錄中,然後使用 ninja ffmpeg 命令來編譯出 so 檔案。
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));
....
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
只要把 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];
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
最後那個 sort 呼叫,根據傳送端的 codec 順序重新調整了我們支援的解碼格式的順序。所以,我們在這裡也需要修改一下,把排序的部分去掉,或者針對 H264 去掉。
重新編譯
使用下列命令,可以編譯特定模組:
ninja pc (針對 mediasession.cc )
ninja media (針對 internalencoderfactory.cc 和 webrtcvideoengine2.cc )
ninja ffmpeg (針對 ffmpeg )
1
2
3
然後再編譯你自己的 native app 。
---------------------
作者:foruok
來源:CSDN
原文:https://blog.csdn.net/foruok/article/details/69525039
版權宣告:本文為博主原創文章,轉載請附上博文連結!