- 前言
- 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();
// 创建音频的CreateTransportAnswer
std::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 &&
current_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;
}
// 构建音频的MediaContent
if (!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,然后返回。