PeerConnection::CreateAnswer
// Conductor 的继承关系 ///
class Conductor : public webrtc::PeerConnectionObserver,
public webrtc::CreateSessionDescriptionObserver,
public PeerConnectionClientObserver,
public MainWndCallback
void PeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer, observer 就是 Conductor 的指针
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 = 这里的 observer 就是 Conductor 的 this 指针
rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer), ///将 Conductor 的指针包裹到 CreateSessionDescriptionObserver 类型的 observer_refptr 中
options](std::function<void()> operations_chain_callback) {
// Abort early if |this_weak_ptr| is no longer valid.
if (!this_weak_ptr) {
observer_refptr->OnFailure(RTCError(
RTCErrorType::INTERNAL_ERROR,
"CreateAnswer 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->DoCreateAnswer(options, observer_wrapper); /// PeerConnection::DoCreateAnswer
});
}
void PeerConnection::DoCreateAnswer(
const RTCOfferAnswerOptions& options,
rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) { /// observer 就是 CreateSessionDescriptionObserverOperationWrapper
RTC_DCHECK_RUN_ON(signaling_thread());
TRACE_EVENT0("webrtc", "PeerConnection::DoCreateAnswer");
if (!observer) {
RTC_LOG(LS_ERROR) << "CreateAnswer - observer is NULL.";
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) << "CreateAnswer: " << error_message;
PostCreateSessionDescriptionFailure(
observer,
RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
return;
}
if (!(signaling_state_ == kHaveRemoteOffer ||
signaling_state_ == kHaveLocalPrAnswer)) {
std::string error =
"PeerConnection cannot create an answer in a state other than "
"have-remote-offer or have-local-pranswer.";
RTC_LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailure(
observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error)));
return;
}
// The remote description should be set if we're in the right state.
RTC_DCHECK(remote_description());
if (IsUnifiedPlan()) {
if (options.offer_to_receive_audio != RTCOfferAnswerOptions::kUndefined) {
RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_audio is not "
"supported with Unified Plan semantics. Use the "
"RtpTransceiver API instead.";
}
if (options.offer_to_receive_video != RTCOfferAnswerOptions::kUndefined) {
RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_video is not "
"supported with Unified Plan semantics. Use the "
"RtpTransceiver API instead.";
}
}
cricket::MediaSessionOptions session_options;
GetOptionsForAnswer(options, &session_options);
webrtc_session_desc_factory_->CreateAnswer(observer, session_options); // webrtc_session_desc_factory_ 就是 WebRtcSessionDescriptionFactory 具体见前面分析
} 所以这里就是 WebRtcSessionDescriptionFactory::CreateAnswer
void WebRtcSessionDescriptionFactory::CreateAnswer(
CreateSessionDescriptionObserver* observer,
const cricket::MediaSessionOptions& session_options) {
std::string error = "CreateAnswer";
if (certificate_request_state_ == CERTIFICATE_FAILED) {
error += kFailedDueToIdentityFailed;
RTC_LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailed(observer, error);
return;
}
if (!pc_->remote_description()) {
error += " can't be called before SetRemoteDescription.";
RTC_LOG(LS_ERROR) << error;
PostCreateSessionDescriptionFailed(observer, error);
return;
}
if (pc_->remote_description()->GetType() != SdpType::kOffer) {
error += " failed because remote_description is not an offer.";
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::kAnswer, observer, session_options); / 传递 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);
InternalCreateAnswer(request);
}
}
void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
CreateSessionDescriptionRequest request) {
if (pc_->remote_description()) {
for (cricket::MediaDescriptionOptions& options :
request.options.media_description_options) {
// According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
// an answer should also contain new ICE ufrag and password if an offer
// has been received with new ufrag and password.
options.transport_options.ice_restart =
pc_->IceRestartPending(options.mid);
// We should pass the current SSL role to the transport description
// factory, if there is already an existing ongoing session.
rtc::SSLRole ssl_role;
if (pc_->GetSslRole(options.mid, &ssl_role)) {
options.transport_options.prefer_passive_role =
(rtc::SSL_SERVER == ssl_role);
}
}
}
std::unique_ptr<cricket::SessionDescription> desc =
session_desc_factory_.CreateAnswer( // session_desc_factory_ 在 WebRtcSessionDescriptionFactory 构造函数中初始化
pc_->remote_description() ? pc_->remote_description()->description() / 就是 MediaSessionDescriptionFactory::CreateAnswer
: nullptr,
request.options,
pc_->local_description() ? pc_->local_description()->description()
: nullptr);
if (!desc) {
PostCreateSessionDescriptionFailed(request.observer,
"Failed to initialize the answer.");
return;
}
// RFC 3264
// If the answer is different from the offer in any way (different IP
// addresses, ports, etc.), the origin line MUST be different in the answer.
// In that case, the version number in the "o=" line of the answer is
// unrelated to the version number in the o line of the offer.
// Get a new version number by increasing the |session_version_answer_|.
// The |session_version_| is a uint64_t, the wrap around should not happen.
RTC_DCHECK(session_version_ + 1 > session_version_);
auto answer = std::make_unique<JsepSessionDescription>(
SdpType::kAnswer, std::move(desc), session_id_,
rtc::ToString(session_version_++)); /
if (pc_->local_description()) {
// Include all local ICE candidates in the SessionDescription unless
// the remote peer has requested an ICE restart.
for (const cricket::MediaDescriptionOptions& options :
request.options.media_description_options) {
if (!options.transport_options.ice_restart) {
CopyCandidatesFromSessionDescription(pc_->local_description(),
options.mid, answer.get());
}
}
}
PostCreateSessionDescriptionSucceeded(request.observer, std::move(answer)); / request.observer 就是 CreateSessionDescriptionObserverOperationWrapper
} PostCreateSessionDescriptionSucceeded 见下面分析
MediaSessionDescriptionFactory::CreateAnswer
std::unique_ptr<SessionDescription>
MediaSessionDescriptionFactory::CreateAnswer(
const SessionDescription* offer,
const MediaSessionOptions& session_options,
const SessionDescription* current_description) const {
if (!offer) {
return nullptr;
}
// Must have options for exactly as many sections as in the offer.
RTC_DCHECK_EQ(offer->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);
// Get list of all possible codecs that respects existing payload type
// mappings and uses a single payload type space.
//
// Note that these lists may be further filtered for each m= section; this
// step is done just to establish the payload type mappings shared by all
// sections.
AudioCodecs answer_audio_codecs;
VideoCodecs answer_video_codecs;
RtpDataCodecs answer_rtp_data_codecs;
GetCodecsForAnswer(current_active_contents, *offer, &answer_audio_codecs,
&answer_video_codecs, &answer_rtp_data_codecs);
if (!session_options.vad_enabled) {
// If application doesn't want CN codecs in answer.
StripCNCodecs(&answer_audio_codecs);
}
auto answer = std::make_unique<SessionDescription>();
// If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
// group in the answer with the appropriate content names.
const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
// Transport info shared by the bundle group.
std::unique_ptr<TransportInfo> bundle_transport;
answer->set_extmap_allow_mixed(offer->extmap_allow_mixed());
// 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* offer_content = &offer->contents()[msection_index];
// Media types and MIDs must match between the remote offer and the
// MediaDescriptionOptions.
RTC_DCHECK(
IsMediaContentOfType(offer_content, media_description_options.type));
RTC_DCHECK(media_description_options.mid == offer_content->name);
const ContentInfo* current_content = nullptr;
if (current_description &&
msection_index < current_description->contents().size()) {
current_content = ¤t_description->contents()[msection_index];
}
RtpHeaderExtensions header_extensions = RtpHeaderExtensionsFromCapabilities(
UnstoppedRtpHeaderExtensionCapabilities(
media_description_options.header_extensions));
switch (media_description_options.type) {
case MEDIA_TYPE_AUDIO:
if (!AddAudioContentForAnswer(
media_description_options, session_options, offer_content,
offer, current_content, current_description,
bundle_transport.get(), answer_audio_codecs, header_extensions,
¤t_streams, answer.get(), &ice_credentials)) {
return nullptr;
}
break;
case MEDIA_TYPE_VIDEO:
if (!AddVideoContentForAnswer(
media_description_options, session_options, offer_content,
offer, current_content, current_description,
bundle_transport.get(), answer_video_codecs, header_extensions,
¤t_streams, answer.get(), &ice_credentials)) {
return nullptr;
}
break;
case MEDIA_TYPE_DATA:
if (!AddDataContentForAnswer(
media_description_options, session_options, offer_content,
offer, current_content, current_description,
bundle_transport.get(), answer_rtp_data_codecs,
¤t_streams, answer.get(), &ice_credentials)) {
return nullptr;
}
break;
default:
RTC_NOTREACHED();
}
++msection_index;
// See if we can add the newly generated m= section to the BUNDLE group in
// the answer.
ContentInfo& added = answer->contents().back();
if (!added.rejected && session_options.bundle_enabled && offer_bundle &&
offer_bundle->HasContentName(added.name)) {
answer_bundle.AddContentName(added.name);
bundle_transport.reset(
new TransportInfo(*answer->GetTransportInfoByName(added.name)));
}
}
// If a BUNDLE group was offered, put a BUNDLE group in the answer even if
// it's empty. RFC5888 says:
//
// A SIP entity that receives an offer that contains an "a=group" line
// with semantics that are understood MUST return an answer that
// contains an "a=group" line with the same semantics.
if (offer_bundle) {
answer->AddGroup(answer_bundle);
}
if (answer_bundle.FirstContentName()) {
// Share the same ICE credentials and crypto params across all contents,
// as BUNDLE requires.
if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
RTC_LOG(LS_ERROR)
<< "CreateAnswer failed to UpdateTransportInfoForBundle.";
return NULL;
}
if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
RTC_LOG(LS_ERROR)
<< "CreateAnswer failed to UpdateCryptoParamsForBundle.";
return NULL;
}
}
// The following determines how to signal MSIDs to ensure compatibility with
// older endpoints (in particular, older Plan B endpoints).
if (is_unified_plan_) {
// Unified Plan needs to look at what the offer included to find the most
// compatible answer.
if (offer->msid_signaling() == 0) {
// We end up here in one of three cases:
// 1. An empty offer. We'll reply with an empty answer so it doesn't
// matter what we pick here.
// 2. A data channel only offer. We won't add any MSIDs to the answer so
// it also doesn't matter what we pick here.
// 3. Media that's either sendonly or inactive from the remote endpoint.
// We don't have any information to say whether the endpoint is Plan B
// or Unified Plan, so be conservative and send both.
answer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
cricket::kMsidSignalingSsrcAttribute);
} else if (offer->msid_signaling() ==
(cricket::kMsidSignalingMediaSection |
cricket::kMsidSignalingSsrcAttribute)) {
// If both a=msid and a=ssrc MSID signaling methods were used, we're
// probably talking to a Unified Plan endpoint so respond with just
// a=msid.
answer->set_msid_signaling(cricket::kMsidSignalingMediaSection);
} else {
// Otherwise, it's clear which method the offerer is using so repeat that
// back to them.
answer->set_msid_signaling(offer->msid_signaling());
}
} else {
// Plan B always signals MSID using a=ssrc lines.
answer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
}
return answer; /
}
/ 将创建好的 answer 发送到信令服务器,再由信令服务器转发到对端
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;
}
}
/// 下面的 OnSucess 就是在 CreateSessionDescriptionObserverOperationWrapper 声明中定义的 OnSucess
void OnSuccess(SessionDescriptionInterface* desc) override {
RTC_DCHECK(!was_called_);
#ifdef RTC_DCHECK_IS_ON
was_called_ = true;
#endif // RTC_DCHECK_IS_ON
// Completing the operation before invoking the observer allows the observer
// to execute SetLocalDescription() without delay.
operation_complete_callback_(); // 实际上就是 OperationsChain::CallbackHandle::OnOperationComplete()
observer_->OnSuccess(desc); /// 这里的 observer_ 就是包含了 Conductor 指针的 CreateSessionDescriptionObserver ,所以也就是调用 Conductor::OnSuccess
}
void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) {
peer_connection_->SetLocalDescription( 实际上就是 void PeerConnection::SetLocalDescription(SetSessionDescriptionObserver* observer,SessionDescriptionInterface* desc_ptr)
DummySetSessionDescriptionObserver::Create(), desc); / 这里会触发 dtls 的过程,具体参考下方
std::string sdp;
desc->ToString(&sdp);
// For loopback test. To save some connecting delay.
if (loopback_) {
// Replace message type from "offer" to "answer"
std::unique_ptr<webrtc::SessionDescriptionInterface> session_description =
webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp);
peer_connection_->SetRemoteDescription(
DummySetSessionDescriptionObserver::Create(),
session_description.release());
return;
}
Json::StyledWriter writer;
Json::Value jmessage;
jmessage[kSessionDescriptionTypeName] =
webrtc::SdpTypeToString(desc->GetType()); / 填写 sdp 类型,这里就是 answer (与前面Conductor::OnSuccess中的分析要区别开,上面是发送offer,而这里是接收到offer后,向对端回复answer)
jmessage[kSessionDescriptionSdpName] = sdp;
SendMessage(writer.write(jmessage)); 最后会通过 PeerConnectionClient::SendToPeer 发送出去,实际上就是通过 control_socket_
}