—————————————————————————————————————————————-

一分钟快速搭建 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