webrtc 中怎麼根據 SDP 建立或關聯底層的 socket 物件?
----------------------------------------------------------------------------------------------------------------------------------------
一分鐘快速搭建 rtmpd 伺服器: https://blog.csdn.net/freeabc/article/details/102880984
軟體下載地址: http://www.qiyicc.com/download/rtmpd.rar
github 地址:https://github.com/superconvert/smart_rtmpd
-----------------------------------------------------------------------------------------------------------------------------------------
webrtc 中怎麼根據 SDP 建立或關聯底層的 socket 物件?
我們知道上層需要兩個介面設定 SDP 的相關資訊,體現在底層就是下面兩個介面:
./pc/peer_connection.cc
1. void PeerConnection::SetLocalDescription(SetSessionDescriptionObserver* observer, SessionDescriptionInterface* desc_ptr)
2. void PeerConnection::SetRemoteDescription(SetSessionDescriptionObserver* observer, SessionDescriptionInterface* desc_ptr)
這兩個介面最終都會呼叫,具體呼叫流程,我就不闡述了,參見我的其它部落格
transport_controller_->SetLocalDescription(type, sdesc->description())
transport_controller_->SetRemoteDescription(type, sdesc->description());
我們知道 PeerConnection 的屬性 transport_controller_ 就是所有 transport 的控制器,其實就是一個 JsepTransportController,我們分別跟蹤
./pc/jsep_transport_controller.cc
RTCError JsepTransportController::SetLocalDescription(SdpType type, const cricket::SessionDescription* description)
RTCError JsepTransportController::SetRemoteDescription(SdpType type, const cricket::SessionDescription* description)
這兩個介面最終呼叫
./pc/jsep_transport_controller.cc
RTCError JsepTransportController::ApplyDescription_n(bool local, SdpType type, const cricket::SessionDescription* description)
for (const cricket::ContentInfo& content_info : description->contents()) {
// Don't create transports for rejected m-lines and bundled m-lines."
if (content_info.rejected ||
(IsBundled(content_info.name) && content_info.name != *bundled_mid())) {
continue;
}
error = MaybeCreateJsepTransport(local, content_info, *description);
if (!error.ok()) {
return error;
}
}
其實每個 content 就是 SDP 裡面的一個 m 媒體段,具體參考部落格 https://blog.csdn.net/freeabc/article/details/109784860
通過介面 MaybeCreateJsepTransport 建立 transport 物件
RTCError JsepTransportController::MaybeCreateJsepTransport(
bool local,
const cricket::ContentInfo& content_info,
const cricket::SessionDescription& description) {
RTC_DCHECK(network_thread_->IsCurrent());
cricket::JsepTransport* transport = GetJsepTransportByName(content_info.name);
if (transport) {
return RTCError::OK();
}
const cricket::MediaContentDescription* content_desc =
content_info.media_description();
if (certificate_ && !content_desc->cryptos().empty()) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"SDES and DTLS-SRTP cannot be enabled at the same time.");
}
rtc::scoped_refptr<webrtc::IceTransportInterface> ice =
CreateIceTransport(content_info.name, /*rtcp=*/false);
RTC_DCHECK(ice);
std::unique_ptr<DatagramTransportInterface> datagram_transport =
MaybeCreateDatagramTransport(content_info, description, local);
if (datagram_transport) {
datagram_transport->Connect(ice->internal());
}
std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
CreateDtlsTransport(content_info, ice->internal(), nullptr);
std::unique_ptr<cricket::DtlsTransportInternal> rtcp_dtls_transport;
std::unique_ptr<RtpTransport> unencrypted_rtp_transport;
std::unique_ptr<SrtpTransport> sdes_transport;
std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport;
std::unique_ptr<RtpTransportInternal> datagram_rtp_transport;
rtc::scoped_refptr<webrtc::IceTransportInterface> rtcp_ice;
if (config_.rtcp_mux_policy !=
PeerConnectionInterface::kRtcpMuxPolicyRequire &&
content_info.type == cricket::MediaProtocolType::kRtp) {
RTC_DCHECK(datagram_transport == nullptr);
rtcp_ice = CreateIceTransport(content_info.name, /*rtcp=*/true);
rtcp_dtls_transport =
CreateDtlsTransport(content_info, rtcp_ice->internal(),
/*datagram_transport=*/nullptr);
}
// Only create a datagram RTP transport if the datagram transport should be
// used for RTP.
if (datagram_transport && config_.use_datagram_transport) {
// TODO(sukhanov): We use unencrypted RTP transport over DatagramTransport,
// because MediaTransport encrypts. In the future we may want to
// implement our own version of RtpTransport over MediaTransport, because
// it will give us more control over things like:
// - Fusing
// - Rtp header compression
// - Handling Rtcp feedback.
RTC_LOG(LS_INFO) << "Creating UnencryptedRtpTransport, because datagram "
"transport is used.";
RTC_DCHECK(!rtcp_dtls_transport);
datagram_rtp_transport = std::make_unique<DatagramRtpTransport>(
content_info.media_description()->rtp_header_extensions(),
ice->internal(), datagram_transport.get());
}
// used for RTP.
if (datagram_transport && config_.use_datagram_transport) {
// TODO(sukhanov): We use unencrypted RTP transport over DatagramTransport,
// because MediaTransport encrypts. In the future we may want to
// implement our own version of RtpTransport over MediaTransport, because
// it will give us more control over things like:
// - Fusing
// - Rtp header compression
// - Handling Rtcp feedback.
RTC_LOG(LS_INFO) << "Creating UnencryptedRtpTransport, because datagram "
"transport is used.";
RTC_DCHECK(!rtcp_dtls_transport);
datagram_rtp_transport = std::make_unique<DatagramRtpTransport>(
content_info.media_description()->rtp_header_extensions(),
ice->internal(), datagram_transport.get());
}
if (config_.disable_encryption) {
RTC_LOG(LS_INFO)
<< "Creating UnencryptedRtpTransport, becayse encryption is disabled.";
unencrypted_rtp_transport = CreateUnencryptedRtpTransport(
content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
} else if (!content_desc->cryptos().empty()) {
sdes_transport = CreateSdesTransport(
content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
RTC_LOG(LS_INFO) << "Creating SdesTransport.";
} else {
RTC_LOG(LS_INFO) << "Creating DtlsSrtpTransport.";
dtls_srtp_transport = CreateDtlsSrtpTransport(
content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
}
std::unique_ptr<cricket::SctpTransportInternal> sctp_transport;
if (config_.sctp_factory) {
sctp_transport =
config_.sctp_factory->CreateSctpTransport(rtp_dtls_transport.get());
}
DataChannelTransportInterface* data_channel_transport = nullptr;
if (config_.use_datagram_transport_for_data_channels) {
data_channel_transport = datagram_transport.get();
}
std::unique_ptr<cricket::JsepTransport> jsep_transport =
std::make_unique<cricket::JsepTransport>(
content_info.name, certificate_, std::move(ice), std::move(rtcp_ice),
std::move(unencrypted_rtp_transport), std::move(sdes_transport),
std::move(dtls_srtp_transport), std::move(datagram_rtp_transport),
std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport),
std::move(sctp_transport), std::move(datagram_transport),
data_channel_transport);
jsep_transport->rtp_transport()->SignalRtcpPacketReceived.connect(
this, &JsepTransportController::OnRtcpPacketReceived_n);
jsep_transport->SignalRtcpMuxActive.connect(
this, &JsepTransportController::UpdateAggregateStates_n);
jsep_transport->SignalDataChannelTransportNegotiated.connect(
this, &JsepTransportController::OnDataChannelTransportNegotiated_n);
SetTransportForMid(content_info.name, jsep_transport.get());
jsep_transports_by_name_[content_info.name] = std::move(jsep_transport);
UpdateAggregateStates_n();
return RTCError::OK();
}
這個函式根據 SDP 的內容建立各種各樣的 transport ,這裡我們看到熟悉的介面 CreateDtlsSrtpTransport ,這個建立了 webrtc::DtlsSrtpTransport 物件,webrtc::DtlsSrtpTransport 物件下面的兩個成員物件非常關鍵
// Owned by the TransportController.
cricket::DtlsTransportInternal* rtp_dtls_transport_ = nullptr;
cricket::DtlsTransportInternal* rtcp_dtls_transport_ = nullptr;
我們先分析一下 cricket::DtlsTransportInternal* rtp_dtls_transport_ 物件是由下面的介面建立的,我們看到有個 ice 介面傳遞進去!!!!
std::unique_ptr<cricket::DtlsTransportInternal>
JsepTransportController::CreateDtlsTransport(
const cricket::ContentInfo& content_info,
cricket::IceTransportInternal* ice,
DatagramTransportInterface* datagram_transport) {
RTC_DCHECK(network_thread_->IsCurrent());
std::unique_ptr<cricket::DtlsTransportInternal> dtls;
if (datagram_transport) {
RTC_DCHECK(config_.use_datagram_transport ||
config_.use_datagram_transport_for_data_channels);
} else if (config_.dtls_transport_factory) {
dtls = config_.dtls_transport_factory->CreateDtlsTransport(
ice, config_.crypto_options);
} else {
// 這個類的定義參見檔案 ./p2p/base/dtls_transport.h
dtls = std::make_unique<cricket::DtlsTransport>(ice, config_.crypto_options,
config_.event_log);
}
RTC_DCHECK(dtls);
dtls->SetSslMaxProtocolVersion(config_.ssl_max_version);
dtls->ice_transport()->SetIceRole(ice_role_);
dtls->ice_transport()->SetIceTiebreaker(ice_tiebreaker_);
dtls->ice_transport()->SetIceConfig(ice_config_);
if (certificate_) {
bool set_cert_success = dtls->SetLocalCertificate(certificate_);
RTC_DCHECK(set_cert_success);
}
// Connect to signals offered by the DTLS and ICE transport.
dtls->SignalWritableState.connect(
this, &JsepTransportController::OnTransportWritableState_n);
dtls->SignalReceivingState.connect(
this, &JsepTransportController::OnTransportReceivingState_n);
dtls->SignalDtlsHandshakeError.connect(
this, &JsepTransportController::OnDtlsHandshakeError);
dtls->ice_transport()->SignalGatheringState.connect(
this, &JsepTransportController::OnTransportGatheringState_n);
dtls->ice_transport()->SignalCandidateGathered.connect(
this, &JsepTransportController::OnTransportCandidateGathered_n);
dtls->ice_transport()->SignalCandidateError.connect(
this, &JsepTransportController::OnTransportCandidateError_n);
dtls->ice_transport()->SignalCandidatesRemoved.connect(
this, &JsepTransportController::OnTransportCandidatesRemoved_n);
dtls->ice_transport()->SignalRoleConflict.connect(
this, &JsepTransportController::OnTransportRoleConflict_n);
dtls->ice_transport()->SignalStateChanged.connect(
this, &JsepTransportController::OnTransportStateChanged_n);
dtls->ice_transport()->SignalIceTransportStateChanged.connect(
this, &JsepTransportController::OnTransportStateChanged_n);
dtls->ice_transport()->SignalCandidatePairChanged.connect(
this, &JsepTransportController::OnTransportCandidatePairChanged_n);
return dtls;
}
ICE 介面的建立程式碼
RTCError JsepTransportController::MaybeCreateJsepTransport(
bool local,
const cricket::ContentInfo& content_info,
const cricket::SessionDescription& description)
// 我們看到這裡會根據 SDP 裡 m 段落的 name 建立 ice
rtc::scoped_refptr<webrtc::IceTransportInterface> ice = CreateIceTransport(content_info.name, /*rtcp=*/false);
rtc::scoped_refptr<webrtc::IceTransportInterface>
JsepTransportController::CreateIceTransport(const std::string& transport_name,
bool rtcp) {
int component = rtcp ? cricket::ICE_CANDIDATE_COMPONENT_RTCP
: cricket::ICE_CANDIDATE_COMPONENT_RTP;
IceTransportInit init;
init.set_port_allocator(port_allocator_);
init.set_async_resolver_factory(async_resolver_factory_);
init.set_event_log(config_.event_log);
return config_.ice_transport_factory->CreateIceTransport(
transport_name, component, std::move(init));
}
//----------------------------------------------------------
// ./p2p/base/default_ice_transport_factory.cc
//----------------------------------------------------------
rtc::scoped_refptr<IceTransportInterface>
DefaultIceTransportFactory::CreateIceTransport(
const std::string& transport_name,
int component,
IceTransportInit init) {
BasicIceControllerFactory factory;
return new rtc::RefCountedObject<DefaultIceTransport>(
std::make_unique<cricket::P2PTransportChannel>(
transport_name, component, init.port_allocator(),
init.async_resolver_factory(), init.event_log(), &factory));
}
由上述我們可以看到 JsepTransportController 的成員物件 port_allocator_ 是個很重要的東東!這個物件是 JsepTransportController 初始化時候傳遞過來的,就是這個物件 cricket::BasicPortAllocator 這個的具體流程 請參考部落格
https://blog.csdn.net/freeabc/article/details/106000923
這樣每個 transport 就具有收發網路資料的能力了。而 transport 會通過下面的介面繫結到每個 RtpTransceiver 裡面,
transceiver->internal()->sender_internal()->set_transport(dtls_transport);
transceiver->internal()->receiver_internal()->set_transport(dtls_transport);
Good luck!
- WebRTC 中有關 Media Stream & Track & Channel 之間的關係
- smart_rtmpd 推流和拉流那些事
- 為什麼 RTP 的視訊的取樣率是 90kHz ?
- CDN的加速原理是什麼?
- smart_rtmpd 的 NAT 對映方式使用說明
- Mediasoup 雜談(待完善)
- C 獲取當前日期精確到毫秒的幾種方法
- webrtc 中怎麼根據 SDP 建立或關聯底層的 socket 物件?
- webrtc 資料接收流程圖解
- webrtc 有關 SDP 部分的解析流程分析
- smart rtmpd 推流 url 和拉流 url
- Windows 版本的 Webrtc 的編譯 ( 基於聲網映象 )