- 前言
- WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory
- RTCCertificateGenerator::GenerateCertificateAsync
- SdpOfferAnswerHandler::CreateAnswer
- SdpOfferAnswerHandler::DoCreateAnswer
- WebRtcSessionDescriptionFactory::CreateAnswer
- WebRtcCertificateGeneratorCallback::OnSuccess
- WebRtcSessionDescriptionFactory::SetCertificate
- WebRtcSessionDescriptionFactory::InternalCreateAnswer
- MediaSessionDescriptionFactory::CreateAnswer
- MediaSessionDescriptionFactory::AddAudioContentForAnswer
前言
注意这个需要对端发起呼叫,调试的这端作为被呼叫方。
SetCertificate里面是个不断循环的,直到有answer请求的时候,才会继续往下走。
最终生成的就是jsepSessionDescription对象,有了这个信息再转为sdp,传回给呼叫方。呼叫方收到后调用SetRemoteDescription就完成了媒体协商。
PeerConnection::Create
PeerConnection::Initialize
SdpOfferAnswerHandler::Create
SdpOfferAnswerHandler::Initialize
-》
std::makeunique
signaling_thread(), channel_manager(), this, pc
pc->dtlsenabled(), std::move(dependencies.cert_generator),
certificate, &ssrc_generator,
this {
transport_controller()->SetLocalCertificate(certificate);
})
WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory
WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(rtc::Thread* signaling_thread,cricket::ChannelManager* channel_manager,const SdpStateProvider* sdp_info,const std::string& session_id,bool dtls_enabled,std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,const rtc::scoped_refptr<rtc::RTCCertificate>& certificate,UniqueRandomIdGenerator* ssrc_generator,std::function<void(const rtc::scoped_refptr<rtc::RTCCertificate>&)>on_certificate_ready): signaling_thread_(signaling_thread),session_desc_factory_(channel_manager,&transport_desc_factory_,ssrc_generator),// RFC 4566 suggested a Network Time Protocol (NTP) format timestamp// as the session id and session version. To simplify, it should be fine// to just use a random number as session id and start version from// |kInitSessionVersion|.session_version_(kInitSessionVersion),cert_generator_(dtls_enabled ? std::move(cert_generator) : nullptr),sdp_info_(sdp_info),session_id_(session_id),certificate_request_state_(CERTIFICATE_NOT_NEEDED),on_certificate_ready_(on_certificate_ready) {RTC_DCHECK(signaling_thread_);if (!dtls_enabled) {SetSdesPolicy(cricket::SEC_REQUIRED);RTC_LOG(LS_VERBOSE) << "DTLS-SRTP disabled.";return;}// SRTP-SDES is disabled if DTLS is on.SetSdesPolicy(cricket::SEC_DISABLED);if (certificate) {// Use |certificate|.certificate_request_state_ = CERTIFICATE_WAITING;RTC_LOG(LS_VERBOSE) << "DTLS-SRTP enabled; has certificate parameter.";// We already have a certificate but we wait to do |SetIdentity|; if we do// it in the constructor then the caller has not had a chance to connect to// |SignalCertificateReady|.signaling_thread_->Post(RTC_FROM_HERE, this, MSG_USE_CONSTRUCTOR_CERTIFICATE,new rtc::ScopedRefMessageData<rtc::RTCCertificate>(certificate));} else { //创建证书// Generate certificate.certificate_request_state_ = CERTIFICATE_WAITING; //设置状态//创建回调绑定rtc::scoped_refptr<WebRtcCertificateGeneratorCallback> callback(new rtc::RefCountedObject<WebRtcCertificateGeneratorCallback>());callback->SignalRequestFailed.connect(this, &WebRtcSessionDescriptionFactory::OnCertificateRequestFailed);callback->SignalCertificateReady.connect(this, &WebRtcSessionDescriptionFactory::SetCertificate);rtc::KeyParams key_params = rtc::KeyParams();RTC_LOG(LS_VERBOSE)<< "DTLS-SRTP enabled; sending DTLS identity request (key type: "<< key_params.type() << ").";// 异步创建证书// Request certificate. This happens asynchronously, so that the caller gets// a chance to connect to |SignalCertificateReady|.cert_generator_->GenerateCertificateAsync(key_params, absl::nullopt,callback);}}
cert_generator_->GenerateCertificateAsync(key_params, absl::nullopt,<br /> callback);
RTCCertificateGenerator::GenerateCertificateAsync
void RTCCertificateGenerator::GenerateCertificateAsync(const KeyParams& key_params,const absl::optional<uint64_t>& expires_ms,const scoped_refptr<RTCCertificateGeneratorCallback>& callback) {RTC_DCHECK(signaling_thread_->IsCurrent());RTC_DCHECK(callback);// Create a new |RTCCertificateGenerationTask| for this generation request. It// is reference counted and referenced by the message data, ensuring it lives// until the task has completed (independent of |RTCCertificateGenerator|).worker_thread_->PostTask(RTC_FROM_HERE, [key_params, expires_ms,signaling_thread = signaling_thread_,cb = callback]() {scoped_refptr<RTCCertificate> certificate =RTCCertificateGenerator::GenerateCertificate(key_params, expires_ms);signaling_thread->PostTask(RTC_FROM_HERE, [cert = std::move(certificate), cb = std::move(cb)]() {cert ? cb->OnSuccess(cert) : cb->OnFailure();});});}
异步去创建证书,里面主要是workerthread->PostTask,向工作线程发送创建任务
SdpOfferAnswerHandler::CreateAnswer

void SdpOfferAnswerHandler::CreateAnswer(CreateSessionDescriptionObserver* observer,const PeerConnectionInterface::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(),observer_refptr =rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),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),std::move(operations_chain_callback)));this_weak_ptr->DoCreateAnswer(options, observer_wrapper);});}
SdpOfferAnswerHandler::DoCreateAnswer
void SdpOfferAnswerHandler::DoCreateAnswer(const PeerConnectionInterface::RTCOfferAnswerOptions& options,rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {RTC_DCHECK_RUN_ON(signaling_thread());TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::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;pc_->message_handler()->PostCreateSessionDescriptionFailure(observer,RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));return;}if (!(signaling_state_ == PeerConnectionInterface::kHaveRemoteOffer ||signaling_state_ == PeerConnectionInterface::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;pc_->message_handler()->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 !=PeerConnectionInterface::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 !=PeerConnectionInterface::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);}
里面也是主要是后面的 webrtcsession_desc_factory->CreateAnswer(observer, session_options);
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 (!sdp_info_->remote_description()) {error += " can't be called before SetRemoteDescription.";RTC_LOG(LS_ERROR) << error;PostCreateSessionDescriptionFailed(observer, error);return;}if (sdp_info_->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);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);}}

请求完成后,就调用 WebRtcCertificateGeneratorCallback::OnSuccess
WebRtcCertificateGeneratorCallback::OnSuccess

这时候就会进来WebRtcSessionDescriptionFactory::SetCertificate
WebRtcSessionDescriptionFactory::SetCertificate

void WebRtcSessionDescriptionFactory::SetCertificate(const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {RTC_DCHECK(certificate);RTC_LOG(LS_VERBOSE) << "Setting new certificate.";certificate_request_state_ = CERTIFICATE_SUCCEEDED;//设置信号on_certificate_ready_(certificate);transport_desc_factory_.set_certificate(certificate);transport_desc_factory_.set_secure(cricket::SEC_ENABLED);while (!create_session_description_requests_.empty()) {if (create_session_description_requests_.front().type ==CreateSessionDescriptionRequest::kOffer) {InternalCreateOffer(create_session_description_requests_.front());} else {InternalCreateAnswer(create_session_description_requests_.front());}create_session_description_requests_.pop();}}
这里现在就会进去到WebRtcSessionDescriptionFactory::InternalCreateAnswer
注意该函数SetCertificate会一直执行,直到有个answer请求才会真正的去创建一个answer。
WebRtcSessionDescriptionFactory::InternalCreateAnswer
void WebRtcSessionDescriptionFactory::InternalCreateAnswer(CreateSessionDescriptionRequest request) {if (sdp_info_->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 =sdp_info_->IceRestartPending(options.mid);// We should pass the current DTLS role to the transport description// factory, if there is already an existing ongoing session.absl::optional<rtc::SSLRole> dtls_role =sdp_info_->GetDtlsRole(options.mid);if (dtls_role) {options.transport_options.prefer_passive_role =(rtc::SSL_SERVER == *dtls_role);}}}std::unique_ptr<cricket::SessionDescription> desc =session_desc_factory_.CreateAnswer(sdp_info_->remote_description()? sdp_info_->remote_description()->description(): nullptr,request.options,sdp_info_->local_description()? sdp_info_->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 (sdp_info_->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(sdp_info_->local_description(),options.mid, answer.get());}}}PostCreateSessionDescriptionSucceeded(request.observer, std::move(answer));}
主要是
auto answer = std::makeunique
SdpType::kAnswer, std::move(desc), session_id
rtc::ToString(sessionversion++));
最终我们要获得就是这个answer。
前面调用了
std::uniqueptr
session_desc_factory
sdpinfo->remotedescription()
? sdp_info->remotedescription()->description()
: nullptr,
request.options,
sdp_info->localdescription()
? sdp_info->local_description()->description()
: nullptr);
MediaSessionDescriptionFactory::CreateAnswer
std::unique_ptr<SessionDescription>MediaSessionDescriptionFactory::CreateAnswer(const SessionDescription* offer,const MediaSessionOptions& session_options,const SessionDescription* current_description) const {******// 创建了answer,后面的内容都是往里面填东西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(**}break;case MEDIA_TYPE_VIDEO:if (!AddVideoContentForAnswer(**}break;case MEDIA_TYPE_DATA:if (!AddDataContentForAnswer(**}break;case MEDIA_TYPE_UNSUPPORTED:if (!AddUnsupportedContentForAnswer(**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)));}}***return answer;}

这里以调用音频的AddAudioContentForAnswer为例子
MediaSessionDescriptionFactory::AddAudioContentForAnswer
// |audio_codecs| = set of all possible codecs that can be used, with correct// payload type mappings//// |supported_audio_codecs| = set of codecs that are supported for the direction// of this m= section//// acd->codecs() = set of previously negotiated codecs for this m= section//// The payload types should come from audio_codecs, but the order should come// from acd->codecs() and then supported_codecs, to ensure that re-offers don't// change existing codec priority, and that new codecs are added with the right// priority.bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(const MediaDescriptionOptions& media_description_options,const MediaSessionOptions& session_options,const ContentInfo* offer_content,const SessionDescription* offer_description,const ContentInfo* current_content,const SessionDescription* current_description,const TransportInfo* bundle_transport,const AudioCodecs& audio_codecs,const RtpHeaderExtensions& default_audio_rtp_header_extensions,StreamParamsVec* current_streams,SessionDescription* answer,IceCredentialsIterator* ice_credentials) const {RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));const AudioContentDescription* offer_audio_description =offer_content->media_description()->as_audio();// 创建音频的CreateTransportAnswerstd::unique_ptr<TransportDescription> audio_transport = CreateTransportAnswer(media_description_options.mid, offer_description,media_description_options.transport_options, current_description,bundle_transport != nullptr, ice_credentials);if (!audio_transport) {return false;}// Pick codecs based on the requested communications direction in the offer// and the selected direction in the answer.// Note these will be filtered one final time in CreateMediaContentAnswer.auto wants_rtd = media_description_options.direction;auto offer_rtd = offer_audio_description->direction();auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);AudioCodecs supported_audio_codecs =GetAudioCodecsForAnswer(offer_rtd, answer_rtd);AudioCodecs filtered_codecs;// 过滤音频编码器if (!media_description_options.codec_preferences.empty()) {filtered_codecs = MatchCodecPreference(media_description_options.codec_preferences, supported_audio_codecs);} else {// Add the codecs from current content if it exists and is not rejected nor// recycled.if (current_content && !current_content->rejected &¤t_content->name == media_description_options.mid) {RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));const AudioContentDescription* acd =current_content->media_description()->as_audio();for (const AudioCodec& codec : acd->codecs()) {if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,nullptr)) {filtered_codecs.push_back(codec);}}}// Add other supported audio codecs.for (const AudioCodec& codec : supported_audio_codecs) {if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,codec, nullptr) &&!FindMatchingCodec<AudioCodec>(supported_audio_codecs,filtered_codecs, codec, nullptr)) {// We should use the local codec with local parameters and the codec id// would be correctly mapped in |NegotiateCodecs|.filtered_codecs.push_back(codec);}}}bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&session_options.bundle_enabled;auto audio_answer = std::make_unique<AudioContentDescription>();// Do not require or create SDES cryptos if DTLS is used.cricket::SecurePolicy sdes_policy =audio_transport->secure() ? cricket::SEC_DISABLED : secure();if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs,media_description_options, session_options,ssrc_generator_, current_streams,audio_answer.get())) {return false;}// 构建音频的MediaContentif (!CreateMediaContentAnswer(offer_audio_description, media_description_options, session_options,sdes_policy, GetCryptos(current_content),filtered_rtp_header_extensions(default_audio_rtp_header_extensions),ssrc_generator_, enable_encrypted_rtp_header_extensions_,current_streams, bundle_enabled, audio_answer.get())) {return false; // Fails the session setup.}bool secure = bundle_transport ? bundle_transport->description.secure(): audio_transport->secure();bool rejected = media_description_options.stopped ||offer_content->rejected ||!IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,audio_answer->protocol(), secure);if (!AddTransportAnswer(media_description_options.mid,*(audio_transport.get()), answer)) {return false;}if (rejected) {RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid<< "' being rejected in answer.";}answer->AddContent(media_description_options.mid, offer_content->type,rejected, std::move(audio_answer));return true;}
首先调用CreateTransportAnswer,然后过滤获得音频编解码器filtered_codecs,并传入到SetCodecsInAnswer。然后调用CreateMediaContentAnswer构建音频的MediaContent。
调用AddContent传入到音频audio_answer,然后返回。
