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);
本文地址:https://blog.csdn.net/freeabc/article/details/110141938