//
// 关于创建 offer
//
void Conductor::ConnectToPeer(int peer_id) {
RTC_DCHECK(peer_id_ == -1);
RTC_DCHECK(peer_id != -1);
if (peer_connection_.get()) {
main_wnd_->MessageBox(
"Error", "We only support connecting to one peer at a time", true);
return;
}
if (InitializePeerConnection()) { 见上面的分析
peer_id_ = peer_id;
peer_connection_->CreateOffer( //
this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); 这里传递进去了 Conductor 的 this 指针
} else {
main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
}
}
void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
const RTCOfferAnswerOptions& options) {
RTC_DCHECK_RUN_ON(signaling_thread());
// Chain this operation. If asynchronous operations are pending on the chain,
// this operation will be queued to be invoked, otherwise the contents of the
// lambda will execute immediately.
operations_chain_->ChainOperation(
[this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), / weak_ptr_factory_ 的初始化是在 PeerConnection 的构造函数中 weak_ptr_factory_(this)
observer_refptr =
rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer), 这里的 observer 就是 Conductor 的 this 指针
options](std::function<void()> operations_chain_callback) { / 将 Conductor 的 this 指针包裹到 CreateSessionDescriptionObserver 类型的 observer_refptr 中
// Abort early if |this_weak_ptr| is no longer valid.
if (!this_weak_ptr) {
observer_refptr->OnFailure(
RTCError(RTCErrorType::INTERNAL_ERROR,
"CreateOffer failed because the session was shut down"));
operations_chain_callback();
return;
}
// The operation completes asynchronously when the wrapper is invoked.
rtc::scoped_refptr<CreateSessionDescriptionObserverOperationWrapper>
observer_wrapper(new rtc::RefCountedObject<
CreateSessionDescriptionObserverOperationWrapper>(
std::move(observer_refptr), /// 又将 observer_refptr 包裹进 CreateSessionDescriptionObserverOperationWrapper 类型的 observer_wrapper
std::move(operations_chain_callback)));
this_weak_ptr->DoCreateOffer(options, observer_wrapper); //
});
}
void PeerConnection::DoCreateOffer(
const RTCOfferAnswerOptions& options,
rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) { CreateSessionDescriptionObserverOperationWrapper 的父类是 CreateSessionDescriptionObserver
RTC_DCHECK_RUN_ON(signaling_thread());
TRACE_EVENT0("webrtc", "PeerConnection::DoCreateOffer");
if (!observer) {
RTC_LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
return;
}
if (IsClosed()) {
std::string error = "CreateOffer called when PeerConnection is closed.";
RTC_LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailure(
observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error)));
return;
}
// If a session error has occurred the PeerConnection is in a possibly
// inconsistent state so fail right away.
if (session_error() != SessionError::kNone) {
std::string error_message = GetSessionErrorMsg();
RTC_LOG(LS_ERROR) << "CreateOffer: " << error_message;
PostCreateSessionDescriptionFailure(
observer,
RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
return;
}
if (!ValidateOfferAnswerOptions(options)) {
std::string error = "CreateOffer called with invalid options.";
RTC_LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailure(
observer, RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error)));
return;
}
// Legacy handling for offer_to_receive_audio and offer_to_receive_video.
// Specified in WebRTC section 4.4.3.2 "Legacy configuration extensions".
if (IsUnifiedPlan()) {
RTCError error = HandleLegacyOfferOptions(options);
if (!error.ok()) {
PostCreateSessionDescriptionFailure(observer, std::move(error));
return;
}
}
cricket::MediaSessionOptions session_options;
GetOptionsForOffer(options, &session_options);
webrtc_session_desc_factory_->CreateOffer(observer, options, session_options); webrtc_session_desc_factory_ 在 PeerConnection::Initialize 中初始化
} 这里的 observer 就是包裹了多个参数的 CreateSessionDescriptionObserverOperationWrapper 观察器
void WebRtcSessionDescriptionFactory::CreateOffer(
CreateSessionDescriptionObserver* observer, CreateSessionDescriptionObserverOperationWrapper 的父类是 CreateSessionDescriptionObserver
const PeerConnectionInterface::RTCOfferAnswerOptions& options,
const cricket::MediaSessionOptions& session_options) {
std::string error = "CreateOffer";
if (certificate_request_state_ == CERTIFICATE_FAILED) {
error += kFailedDueToIdentityFailed;
RTC_LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailed(observer, error);
return;
}
if (!ValidMediaSessionOptions(session_options)) {
error += " called with invalid session options";
RTC_LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailed(observer, error);
return;
}
CreateSessionDescriptionRequest request(
CreateSessionDescriptionRequest::kOffer, observer, session_options); //将 CreateSessionDescriptionObserverOperationWrapper 类型的 observer 传递到 request 中
if (certificate_request_state_ == CERTIFICATE_WAITING) {
create_session_description_requests_.push(request); /
} else {
RTC_DCHECK(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
InternalCreateOffer(request); /
}
}
void WebRtcSessionDescriptionFactory::InternalCreateOffer(
CreateSessionDescriptionRequest request) {
if (pc_->local_description()) { / PeerConnection::local_description // 这里有些疑问,后面补充 ???????
// If the needs-ice-restart flag is set as described by JSEP, we should
// generate an offer with a new ufrag/password to trigger an ICE restart.
for (cricket::MediaDescriptionOptions& options :
request.options.media_description_options) {
if (pc_->NeedsIceRestart(options.mid)) {
options.transport_options.ice_restart = true;
}
}
}
std::unique_ptr<cricket::SessionDescription> desc =
session_desc_factory_.CreateOffer( // session_desc_factory_ 在 WebRtcSessionDescriptionFactory 构造函数中初始化
request.options, pc_->local_description() 就是 MediaSessionDescriptionFactory::CreateOffer
? pc_->local_description()->description()
: nullptr);
if (!desc) {
PostCreateSessionDescriptionFailed(request.observer,
"Failed to initialize the offer.");
return;
}
// RFC 3264
// When issuing an offer that modifies the session,
// the "o=" line of the new SDP MUST be identical to that in the
// previous SDP, except that the version in the origin field MUST
// increment by one from the previous SDP.
// Just increase the version number by one each time when a new offer
// is created regardless if it's identical to the previous one or not.
// The |session_version_| is a uint64_t, the wrap around should not happen.
RTC_DCHECK(session_version_ + 1 > session_version_);
auto offer = std::make_unique<JsepSessionDescription>( / 创建 SdpType::kOffer 类型的 JsepSessionDescription
SdpType::kOffer, std::move(desc), session_id_,
rtc::ToString(session_version_++));
if (pc_->local_description()) {
for (const cricket::MediaDescriptionOptions& options :
request.options.media_description_options) {
if (!options.transport_options.ice_restart) {
CopyCandidatesFromSessionDescription(pc_->local_description(),
options.mid, offer.get());
}
}
}
PostCreateSessionDescriptionSucceeded(request.observer, std::move(offer)); / request.observer 就是 CreateSessionDescriptionObserverOperationWrapper
}
const SessionDescriptionInterface* PeerConnection::local_description() const {
RTC_DCHECK_RUN_ON(signaling_thread());
return pending_local_description_ ? pending_local_description_.get()
: current_local_description_.get();
}
std::unique_ptr<SessionDescription> MediaSessionDescriptionFactory::CreateOffer(
const MediaSessionOptions& session_options,
const SessionDescription* current_description) const {
// Must have options for each existing section.
if (current_description) {
RTC_DCHECK_LE(current_description->contents().size(),
session_options.media_description_options.size());
}
IceCredentialsIterator ice_credentials(
session_options.pooled_ice_credentials);
std::vector<const ContentInfo*> current_active_contents;
if (current_description) {
current_active_contents =
GetActiveContents(*current_description, session_options);
}
StreamParamsVec current_streams =
GetCurrentStreamParams(current_active_contents);
AudioCodecs offer_audio_codecs;
VideoCodecs offer_video_codecs;
RtpDataCodecs offer_rtp_data_codecs;
GetCodecsForOffer(current_active_contents, &offer_audio_codecs,
&offer_video_codecs, &offer_rtp_data_codecs);
if (!session_options.vad_enabled) {
// If application doesn't want CN codecs in offer.
StripCNCodecs(&offer_audio_codecs);
}
AudioVideoRtpHeaderExtensions extensions_with_ids =
GetOfferedRtpHeaderExtensionsWithIds(
current_active_contents, session_options.offer_extmap_allow_mixed,
session_options.media_description_options);
auto offer = std::make_unique<SessionDescription>(); // 创建一个 offer
// Iterate through the media description options, matching with existing media
// descriptions in |current_description|.
size_t msection_index = 0;
for (const MediaDescriptionOptions& media_description_options :
session_options.media_description_options) {
const ContentInfo* current_content = nullptr;
if (current_description &&
msection_index < current_description->contents().size()) {
current_content = ¤t_description->contents()[msection_index];
// Media type must match unless this media section is being recycled.
RTC_DCHECK(current_content->name != media_description_options.mid ||
IsMediaContentOfType(current_content,
media_description_options.type));
}
switch (media_description_options.type) {
case MEDIA_TYPE_AUDIO:
if (!AddAudioContentForOffer(media_description_options, session_options,
current_content, current_description,
extensions_with_ids.audio,
offer_audio_codecs, ¤t_streams,
offer.get(), &ice_credentials)) {
return nullptr;
}
break;
case MEDIA_TYPE_VIDEO:
if (!AddVideoContentForOffer(media_description_options, session_options,
current_content, current_description,
extensions_with_ids.video,
offer_video_codecs, ¤t_streams,
offer.get(), &ice_credentials)) {
return nullptr;
}
break;
case MEDIA_TYPE_DATA:
if (!AddDataContentForOffer(media_description_options, session_options,
current_content, current_description,
offer_rtp_data_codecs, ¤t_streams,
offer.get(), &ice_credentials)) {
return nullptr;
}
break;
default:
RTC_NOTREACHED();
}
++msection_index;
}
// Bundle the contents together, if we've been asked to do so, and update any
// parameters that need to be tweaked for BUNDLE.
if (session_options.bundle_enabled) {
ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
for (const ContentInfo& content : offer->contents()) {
if (content.rejected) {
continue;
}
// TODO(deadbeef): There are conditions that make bundling two media
// descriptions together illegal. For example, they use the same payload
// type to represent different codecs, or same IDs for different header
// extensions. We need to detect this and not try to bundle those media
// descriptions together.
offer_bundle.AddContentName(content.name);
}
if (!offer_bundle.content_names().empty()) {
offer->AddGroup(offer_bundle);
if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
RTC_LOG(LS_ERROR)
<< "CreateOffer failed to UpdateTransportInfoForBundle.";
return nullptr;
}
if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
RTC_LOG(LS_ERROR)
<< "CreateOffer failed to UpdateCryptoParamsForBundle.";
return nullptr;
}
}
}
// The following determines how to signal MSIDs to ensure compatibility with
// older endpoints (in particular, older Plan B endpoints).
if (is_unified_plan_) {
// Be conservative and signal using both a=msid and a=ssrc lines. Unified
// Plan answerers will look at a=msid and Plan B answerers will look at the
// a=ssrc MSID line.
offer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
cricket::kMsidSignalingSsrcAttribute);
} else {
// Plan B always signals MSID using a=ssrc lines.
offer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
}
offer->set_extmap_allow_mixed(session_options.offer_extmap_allow_mixed);
return offer; 这里就是创建好的 offer
}
void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded(
CreateSessionDescriptionObserver* observer, CreateSessionDescriptionObserverOperationWrapper 的父类是 CreateSessionDescriptionObserver
std::unique_ptr<SessionDescriptionInterface> description) { 这里的 observer 就是 CreateSessionDescriptionObserverOperationWrapper 的指针
CreateSessionDescriptionMsg* msg =
new CreateSessionDescriptionMsg(observer, RTCError::OK()); ///这里的 observer 就是 CreateSessionDescriptionObserverOperationWrapper 的指针
msg->description = std::move(description); /// 重新包裹 SDP
signaling_thread_->Post(RTC_FROM_HERE, this, // 在信号线程进行发送,会在信号线程里面执行 WebRtcSessionDescriptionFactory::OnMessage
MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
}
void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) {
switch (msg->message_id) {
case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
CreateSessionDescriptionMsg* param =
static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
param->observer->OnSuccess(param->description.release()); / 就是 CreateSessionDescriptionObserverOperationWrapper::OnSucess
delete param; // 不过 OnSuccess 是在 CreateSessionDescriptionObserverOperationWrapper 的声明中定义的
break;
}
case MSG_CREATE_SESSIONDESCRIPTION_FAILED: {
CreateSessionDescriptionMsg* param =
static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
param->observer->OnFailure(std::move(param->error));
delete param;
break;
}
case MSG_USE_CONSTRUCTOR_CERTIFICATE: {
rtc::ScopedRefMessageData<rtc::RTCCertificate>* param =
static_cast<rtc::ScopedRefMessageData<rtc::RTCCertificate>*>(
msg->pdata);
RTC_LOG(LS_INFO) << "Using certificate supplied to the constructor.";
SetCertificate(param->data());
delete param;
break;
}
default:
RTC_NOTREACHED();
break;
}
}