前言

createOffer之前,必须生成证书。。

代码分析

  1. Conductor::ConnectToPeer
  2. ->
  3. if (InitializePeerConnection()) {
  4. peer_id_ = peer_id;
  5. peer_connection_->CreateOffer(
  6. this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
  7. } else {
  8. main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
  9. }
  10. ->
  11. PeerConnection::CreateOffer
  12. ->
  13. SdpOfferAnswerHandler::CreateOffer(
  14. CreateSessionDescriptionObserver* observer,
  15. const PeerConnectionInterface::RTCOfferAnswerOptions& options)
  16. ->
  17. SdpOfferAnswerHandler::DoCreateOffer
  18. ->
  19. WebRtcSessionDescriptionFactory::CreateOffer
  20. ->
  21. WebRtcSessionDescriptionFactory::InternalCreateOffer

SdpOfferAnswerHandler::DoCreateOffer

  1. void SdpOfferAnswerHandler::DoCreateOffer(
  2. const PeerConnectionInterface::RTCOfferAnswerOptions& options,
  3. rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {
  4. ******
  5. 获取会话选项参数,并创建offer
  6. cricket::MediaSessionOptions session_options;
  7. GetOptionsForOffer(options, &session_options);
  8. webrtc_session_desc_factory_->CreateOffer(observer, options, session_options);
  9. }

image.png

WebRtcSessionDescriptionFactory::CreateOffer

  1. void WebRtcSessionDescriptionFactory::CreateOffer(
  2. CreateSessionDescriptionObserver* observer,
  3. const PeerConnectionInterface::RTCOfferAnswerOptions& options,
  4. const cricket::MediaSessionOptions& session_options) {
  5. ******
  6. // 创建申请,然后设置observer回调
  7. CreateSessionDescriptionRequest request(
  8. CreateSessionDescriptionRequest::kOffer, observer, session_options);
  9. if (certificate_request_state_ == CERTIFICATE_WAITING) {
  10. create_session_description_requests_.push(request);
  11. } else {
  12. RTC_DCHECK(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
  13. certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
  14. InternalCreateOffer(request);
  15. }
  16. }

image.png

工作线程创建好证书后,就会调用WebRtcCertificateGeneratorCallback::OnSuccess ,后续给信令线程继续操作

WebRtcCertificateGeneratorCallback::OnSuccess

  1. void WebRtcCertificateGeneratorCallback::OnSuccess(
  2. const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
  3. SignalCertificateReady(certificate);
  4. }

image.png
后面是在void RTCCertificateGenerator::GenerateCertificateAsync调用的OnSuccess

  1. void RTCCertificateGenerator::GenerateCertificateAsync(
  2. const KeyParams& key_params,
  3. const absl::optional<uint64_t>& expires_ms,
  4. const scoped_refptr<RTCCertificateGeneratorCallback>& callback) {
  5. RTC_DCHECK(signaling_thread_->IsCurrent());
  6. RTC_DCHECK(callback);
  7. // Create a new |RTCCertificateGenerationTask| for this generation request. It
  8. // is reference counted and referenced by the message data, ensuring it lives
  9. // until the task has completed (independent of |RTCCertificateGenerator|).
  10. worker_thread_->PostTask(RTC_FROM_HERE, [key_params, expires_ms,
  11. signaling_thread = signaling_thread_,
  12. cb = callback]() {
  13. scoped_refptr<RTCCertificate> certificate =
  14. RTCCertificateGenerator::GenerateCertificate(key_params, expires_ms);
  15. signaling_thread->PostTask(
  16. RTC_FROM_HERE, [cert = std::move(certificate), cb = std::move(cb)]() {
  17. cert ? cb->OnSuccess(cert) : cb->OnFailure();
  18. });
  19. });

WebRtcCertificateGeneratorCallback::OnSuccess函数里面的SignalCertificateReady(certificate);函数实际上就是调用WebRtcSessionDescriptionFactory::SetCertificate 。

WebRtcSessionDescriptionFactory::SetCertificate

  1. void WebRtcSessionDescriptionFactory::SetCertificate(
  2. const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
  3. RTC_DCHECK(certificate);
  4. RTC_LOG(LS_VERBOSE) << "Setting new certificate.";
  5. certificate_request_state_ = CERTIFICATE_SUCCEEDED;
  6. on_certificate_ready_(certificate);
  7. transport_desc_factory_.set_certificate(certificate);
  8. transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
  9. // 判断请求是否为空
  10. while (!create_session_description_requests_.empty()) {
  11. if (create_session_description_requests_.front().type ==
  12. CreateSessionDescriptionRequest::kOffer) {
  13. InternalCreateOffer(create_session_description_requests_.front());
  14. } else {
  15. InternalCreateAnswer(create_session_description_requests_.front());
  16. }
  17. create_session_description_requests_.pop();
  18. }
  19. }

image.png

当前创建的是offer,所以进去InternalCreateOffer

WebRtcSessionDescriptionFactory::InternalCreateOffer

  1. void WebRtcSessionDescriptionFactory::InternalCreateOffer(
  2. CreateSessionDescriptionRequest request) {
  3. // 如果是本地
  4. if (sdp_info_->local_description()) {
  5. // If the needs-ice-restart flag is set as described by JSEP, we should
  6. // generate an offer with a new ufrag/password to trigger an ICE restart.
  7. for (cricket::MediaDescriptionOptions& options :
  8. request.options.media_description_options) {
  9. if (sdp_info_->NeedsIceRestart(options.mid)) {
  10. options.transport_options.ice_restart = true;
  11. }
  12. }
  13. }
  14. // 创建offer
  15. std::unique_ptr<cricket::SessionDescription> desc =
  16. session_desc_factory_.CreateOffer(
  17. request.options, sdp_info_->local_description()
  18. ? sdp_info_->local_description()->description()
  19. : nullptr);
  20. if (!desc) {
  21. PostCreateSessionDescriptionFailed(request.observer,
  22. "Failed to initialize the offer.");
  23. return;
  24. }

MediaSessionDescriptionFactory::CreateOffer

  1. std::unique_ptr<SessionDescription> MediaSessionDescriptionFactory::CreateOffer(
  2. *****
  3. AudioCodecs offer_audio_codecs;
  4. VideoCodecs offer_video_codecs;
  5. RtpDataCodecs offer_rtp_data_codecs;
  6. // 获取编码信息
  7. GetCodecsForOffer(
  8. current_active_contents, &offer_audio_codecs, &offer_video_codecs,
  9. session_options.data_channel_type == DataChannelType::DCT_SCTP
  10. ? nullptr
  11. : &offer_rtp_data_codecs);
  12. if (!session_options.vad_enabled) {
  13. // If application doesn't want CN codecs in offer.
  14. StripCNCodecs(&offer_audio_codecs);
  15. }
  16. //获取rtp扩展头
  17. AudioVideoRtpHeaderExtensions extensions_with_ids =
  18. GetOfferedRtpHeaderExtensionsWithIds(
  19. current_active_contents, session_options.offer_extmap_allow_mixed,
  20. session_options.media_description_options);
  21. auto offer = std::make_unique<SessionDescription>();
  22. // Iterate through the media description options, matching with existing media
  23. // descriptions in |current_description|.
  24. size_t msection_index = 0;
  25. // 循环处理属性选项
  26. for (const MediaDescriptionOptions& media_description_options :
  27. session_options.media_description_options) {
  28. const ContentInfo* current_content = nullptr;
  29. if (current_description &&
  30. msection_index < current_description->contents().size()) {
  31. current_content = &current_description->contents()[msection_index];
  32. // Media type must match unless this media section is being recycled.
  33. RTC_DCHECK(current_content->name != media_description_options.mid ||
  34. IsMediaContentOfType(current_content,
  35. media_description_options.type));
  36. }
  37. switch (media_description_options.type) {
  38. case MEDIA_TYPE_AUDIO:
  39. // 添加音频媒体数据
  40. if (!AddAudioContentForOffer(media_description_options, session_options,
  41. current_content, current_description,
  42. extensions_with_ids.audio,
  43. offer_audio_codecs, &current_streams,
  44. offer.get(), &ice_credentials)) {
  45. ***
  46. break;
  47. case MEDIA_TYPE_VIDEO:
  48. if (!AddVideoContentForOffer(media_description_options, session_options,
  49. current_content, current_description,
  50. extensions_with_ids.video,
  51. offer_video_codecs, &current_streams,
  52. offer.get(), &ice_credentials)) {
  53. ***
  54. case MEDIA_TYPE_DATA:
  55. if (!AddDataContentForOffer(media_description_options, session_options,
  56. current_content, current_description,
  57. offer_rtp_data_codecs, &current_streams,
  58. offer.get(), &ice_credentials)) {
  59. ***
  60. case MEDIA_TYPE_UNSUPPORTED:
  61. if (!AddUnsupportedContentForOffer(
  62. media_description_options, session_options, current_content,
  63. current_description, offer.get(), &ice_credentials)) {
  64. ***
  65. }
  66. ++msection_index;
  67. }
  68. // Bundle the contents together, if we've been asked to do so, and update any
  69. // parameters that need to be tweaked for BUNDLE.
  70. if (session_options.bundle_enabled) {
  71. ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
  72. for (const ContentInfo& content : offer->contents()) {
  73. if (content.rejected) {
  74. continue;
  75. }
  76. // TODO(deadbeef): There are conditions that make bundling two media
  77. // descriptions together illegal. For example, they use the same payload
  78. // type to represent different codecs, or same IDs for different header
  79. // extensions. We need to detect this and not try to bundle those media
  80. // descriptions together.
  81. offer_bundle.AddContentName(content.name);
  82. }
  83. if (!offer_bundle.content_names().empty()) {
  84. offer->AddGroup(offer_bundle);
  85. if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
  86. RTC_LOG(LS_ERROR)
  87. << "CreateOffer failed to UpdateTransportInfoForBundle.";
  88. return nullptr;
  89. }
  90. if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
  91. RTC_LOG(LS_ERROR)
  92. << "CreateOffer failed to UpdateCryptoParamsForBundle.";
  93. return nullptr;
  94. }
  95. }
  96. }
  97. // The following determines how to signal MSIDs to ensure compatibility with
  98. // older endpoints (in particular, older Plan B endpoints).
  99. if (is_unified_plan_) {
  100. // Be conservative and signal using both a=msid and a=ssrc lines. Unified
  101. // Plan answerers will look at a=msid and Plan B answerers will look at the
  102. // a=ssrc MSID line.
  103. offer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
  104. cricket::kMsidSignalingSsrcAttribute);
  105. } else {
  106. // Plan B always signals MSID using a=ssrc lines.
  107. offer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
  108. }
  109. offer->set_extmap_allow_mixed(session_options.offer_extmap_allow_mixed);
  110. return offer;
  111. }

image.png
以音频为例

AddAudioContentForOffer

  1. bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
  2. const MediaDescriptionOptions& media_description_options,
  3. const MediaSessionOptions& session_options,
  4. const ContentInfo* current_content,
  5. const SessionDescription* current_description,
  6. const RtpHeaderExtensions& audio_rtp_extensions,
  7. const AudioCodecs& audio_codecs,
  8. StreamParamsVec* current_streams,
  9. SessionDescription* desc,
  10. IceCredentialsIterator* ice_credentials) const {
  11. *****
  12. if (!CreateMediaContentOffer(media_description_options, session_options,
  13. filtered_codecs, sdes_policy,
  14. GetCryptos(current_content), crypto_suites,
  15. audio_rtp_extensions, ssrc_generator_,
  16. current_streams, audio.get())) {
  17. return false;
  18. }
  19. // 设置媒体协议
  20. bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
  21. SetMediaProtocol(secure_transport, audio.get());
  22. audio->set_direction(media_description_options.direction);
  23. // 添加内容
  24. desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
  25. media_description_options.stopped, std::move(audio));
  26. if (!AddTransportOffer(media_description_options.mid,
  27. media_description_options.transport_options,
  28. current_description, desc, ice_credentials)) {
  29. return false;
  30. }
  31. return true;
  32. }

CreateMediaContentOffer

  1. template <class C>
  2. static bool CreateMediaContentOffer(
  3. const MediaDescriptionOptions& media_description_options,
  4. const MediaSessionOptions& session_options,
  5. const std::vector<C>& codecs,
  6. const SecurePolicy& secure_policy,
  7. const CryptoParamsVec* current_cryptos,
  8. const std::vector<std::string>& crypto_suites,
  9. const RtpHeaderExtensions& rtp_extensions,
  10. UniqueRandomIdGenerator* ssrc_generator,
  11. StreamParamsVec* current_streams,
  12. MediaContentDescriptionImpl<C>* offer) {
  13. offer->AddCodecs(codecs);
  14. if (!AddStreamParams(media_description_options.sender_options,
  15. session_options.rtcp_cname, ssrc_generator,
  16. current_streams, offer)) {
  17. return false;
  18. }
  19. return CreateContentOffer(media_description_options, session_options,
  20. secure_policy, current_cryptos, crypto_suites,
  21. rtp_extensions, ssrc_generator, current_streams,
  22. offer);
  23. }

image.png

AddStreamParams-》
CreateStreamParamsForNewSenderWithSsrcs 每一路流ssrc的创建都调用它

CreateContentOffer

  1. // Create a media content to be offered for the given |sender_options|,
  2. // according to the given options.rtcp_mux, session_options.is_muc, codecs,
  3. // secure_transport, crypto, and current_streams. If we don't currently have
  4. // crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
  5. // created (according to crypto_suites). The created content is added to the
  6. // offer.
  7. static bool CreateContentOffer(
  8. const MediaDescriptionOptions& media_description_options,
  9. const MediaSessionOptions& session_options,
  10. const SecurePolicy& secure_policy,
  11. const CryptoParamsVec* current_cryptos,
  12. const std::vector<std::string>& crypto_suites,
  13. const RtpHeaderExtensions& rtp_extensions,
  14. UniqueRandomIdGenerator* ssrc_generator,
  15. StreamParamsVec* current_streams,
  16. MediaContentDescription* offer) {
  17. offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
  18. if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
  19. offer->set_rtcp_reduced_size(true);
  20. }
  21. // Build the vector of header extensions with directions for this
  22. // media_description's options.
  23. RtpHeaderExtensions extensions;
  24. for (auto extension_with_id : rtp_extensions) {
  25. for (const auto& extension : media_description_options.header_extensions) {
  26. if (extension_with_id.uri == extension.uri) {
  27. // TODO(crbug.com/1051821): Configure the extension direction from
  28. // the information in the media_description_options extension
  29. // capability.
  30. extensions.push_back(extension_with_id);
  31. }
  32. }
  33. }
  34. offer->set_rtp_header_extensions(extensions);
  35. AddSimulcastToMediaDescription(media_description_options, offer);
  36. // sdes 安全策略,我们用的是dtls,暂时不会进来这里
  37. if (secure_policy != SEC_DISABLED) {
  38. if (current_cryptos) {
  39. AddMediaCryptos(*current_cryptos, offer);
  40. }
  41. if (offer->cryptos().empty()) {
  42. if (!CreateMediaCryptos(crypto_suites, offer)) {
  43. return false;
  44. }
  45. }
  46. }
  47. if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
  48. return false;
  49. }
  50. return true;
  51. }

offer->set_rtp_header_extensions(extensions); 设置拓展头
image.png

当该函数返回时,已经创建好offer了,可以查看
image.png
打开第一个标签的sendstreams 属性,可以看到是音频标签。
image.png
还可以看codecs
image.png

SetMediaProtocol

  1. static void SetMediaProtocol(bool secure_transport,
  2. MediaContentDescription* desc) {
  3. if (!desc->cryptos().empty()) //sdes协议
  4. desc->set_protocol(kMediaProtocolSavpf);
  5. else if (secure_transport) //dtls协议
  6. desc->set_protocol(kMediaProtocolDtlsSavpf);
  7. else
  8. desc->set_protocol(kMediaProtocolAvpf);
  9. }