WebRTC SDP API 底层实现深度解析:从Java到Native的完整调用链
引言
在WebRTC的实际应用中,SDP (Session Description Protocol) 是实现端对端媒体协商的核心协议。本文将深入分析WebRTC Android SDK中四个最重要的SDP相关API的完整实现链路:
createOffer()- 创建SDP offercreateAnswer()- 创建SDP answersetLocalDescription()- 设置本地描述setRemoteDescription()- 设置远端描述
我们将从Java层API调用开始,逐层深入到JNI桥接层、C++ WebRTC核心实现,最终到达媒体协商和会话描述生成的底层逻辑。
第一层:Java API层 - 入口接口
1.1 PeerConnection.java 中的API定义
在 sdk/android/api/org/autelrtc/PeerConnection.java 中,我们可以看到这四个API的定义:
// 创建SDP Offer
public void createOffer(SdpObserver observer, MediaConstraints constraints) {
nativeCreateOffer(observer, constraints);
}
// 创建SDP Answer
public void createAnswer(SdpObserver observer, MediaConstraints constraints) {
nativeCreateAnswer(observer, constraints);
}
// 设置本地描述
public void setLocalDescription(SdpObserver observer, SessionDescription sdp) {
nativeSetLocalDescription(observer, sdp);
}
// 设置远端描述
public void setRemoteDescription(SdpObserver observer, SessionDescription sdp) {
nativeSetRemoteDescription(observer, sdp);
}
1.2 参数解析
SdpObserver: 异步回调观察者,用于接收SDP操作的成功或失败结果
onCreateSuccess()- SDP创建成功onCreateFailure()- SDP创建失败onSetSuccess()- SDP设置成功onSetFailure()- SDP设置失败
MediaConstraints: 媒体约束条件,影响SDP生成的参数
SessionDescription: 包含SDP类型(offer/answer)和SDP字符串内容
第二层:JNI桥接层 - Java与C++的桥梁
2.1 JNI方法实现
在 sdk/android/src/jni/pc/peer_connection.cc 中,我们可以看到JNI层的实现:
// CreateOffer的JNI实现
static void JNI_PeerConnection_CreateOffer(
JNIEnv* jni,
const jni_zero::JavaParamRef<jobject>& j_pc,
const jni_zero::JavaParamRef<jobject>& j_observer,
const jni_zero::JavaParamRef<jobject>& j_constraints) {
// 1. 转换Java MediaConstraints为C++ MediaConstraints
std::unique_ptr<MediaConstraints> constraints =
JavaToNativeMediaConstraints(jni, j_constraints);
// 2. 创建C++ SDP观察者包装器
auto observer = rtc::make_ref_counted<CreateSdpObserverJni>(
jni, j_observer, std::move(constraints));
// 3. 转换约束为RTCOfferAnswerOptions
PeerConnectionInterface::RTCOfferAnswerOptions options;
CopyConstraintsIntoOfferAnswerOptions(observer->constraints(), &options);
// 4. 调用C++ PeerConnection的CreateOffer方法
ExtractNativePC(jni, j_pc)->CreateOffer(observer.get(), options);
}
2.2 SDP观察者的桥接
CreateSdpObserverJni 类负责将C++回调转换为Java回调:
void CreateSdpObserverJni::OnSuccess(SessionDescriptionInterface* desc) {
JNIEnv* env = AttachCurrentThreadIfNeeded();
std::string sdp;
RTC_CHECK(desc->ToString(&sdp)) << "got so far: " << sdp;
// 将C++ SessionDescription转换为Java SessionDescription
Java_SdpObserver_onCreateSuccess(
env, j_observer_global_,
NativeToJavaSessionDescription(env, sdp, desc->type()));
delete desc; // 释放C++对象
}
void CreateSdpObserverJni::OnFailure(webrtc::RTCError error) {
JNIEnv* env = AttachCurrentThreadIfNeeded();
Java_SdpObserver_onCreateFailure(env, j_observer_global_,
NativeToJavaString(env, error.message()));
}
第三层:WebRTC C++核心层 - 协议逻辑核心
3.1 PeerConnection分发层
在 pc/peer_connection.cc 中,所有SDP相关操作都被委托给 SdpOfferAnswerHandler:
void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
const RTCOfferAnswerOptions& options) {
RTC_DCHECK_RUN_ON(signaling_thread());
sdp_handler_->CreateOffer(observer, options);
}
void PeerConnection::SetLocalDescription(
SetSessionDescriptionObserver* observer,
SessionDescriptionInterface* desc_ptr) {
RTC_DCHECK_RUN_ON(signaling_thread());
sdp_handler_->SetLocalDescription(observer, desc_ptr);
}
3.2 SdpOfferAnswerHandler - SDP处理核心
在 pc/sdp_offer_answer.cc 中,实现了完整的SDP处理逻辑:
3.2.1 CreateOffer实现详解
void SdpOfferAnswerHandler::DoCreateOffer(
const PeerConnectionInterface::RTCOfferAnswerOptions& options,
rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {
RTC_DCHECK_RUN_ON(signaling_thread());
TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoCreateOffer");
// 1. 验证观察者和PeerConnection状态
if (!observer) {
RTC_LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
return;
}
if (pc_->IsClosed()) {
// PeerConnection已关闭,返回错误
pc_->message_handler()->PostCreateSessionDescriptionFailure(
observer.get(),
RTCError(RTCErrorType::INVALID_STATE, "PeerConnection is closed"));
return;
}
// 2. 检查会话错误状态
if (session_error() != SessionError::kNone) {
std::string error_message = GetSessionErrorMsg();
pc_->message_handler()->PostCreateSessionDescriptionFailure(
observer.get(),
RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
return;
}
// 3. 验证Offer选项
if (!ValidateOfferAnswerOptions(options)) {
pc_->message_handler()->PostCreateSessionDescriptionFailure(
observer.get(),
RTCError(RTCErrorType::INVALID_PARAMETER, "Invalid options"));
return;
}
// 4. 处理Unified Plan的legacy选项
if (IsUnifiedPlan()) {
RTCError error = HandleLegacyOfferOptions(options);
if (!error.ok()) {
pc_->message_handler()->PostCreateSessionDescriptionFailure(
observer.get(), std::move(error));
return;
}
}
// 5. 构建媒体会话选项并创建Offer
cricket::MediaSessionOptions session_options;
GetOptionsForOffer(options, &session_options);
webrtc_session_desc_factory_->CreateOffer(observer.get(), options,
session_options);
}
3.2.2 SetLocalDescription实现详解
void SdpOfferAnswerHandler::DoSetLocalDescription(
std::unique_ptr<SessionDescriptionInterface> desc,
rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
RTC_DCHECK_RUN_ON(signaling_thread());
TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoSetLocalDescription");
// 1. 基础验证
if (!observer) {
RTC_LOG(LS_ERROR) << "SetLocalDescription - observer is NULL.";
return;
}
if (!desc) {
observer->OnSetLocalDescriptionComplete(
RTCError(RTCErrorType::INTERNAL_ERROR, "SessionDescription is NULL."));
return;
}
// 2. 检查会话状态
if (session_error() != SessionError::kNone) {
std::string error_message = GetSessionErrorMsg();
observer->OnSetLocalDescriptionComplete(
RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
return;
}
// 3. 处理Rollback操作
if (desc->GetType() == SdpType::kRollback) {
if (IsUnifiedPlan()) {
observer->OnSetLocalDescriptionComplete(Rollback(desc->GetType()));
} else {
observer->OnSetLocalDescriptionComplete(
RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
"Rollback not supported in Plan B"));
}
return;
}
// 4. 验证会话描述
std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid =
GetBundleGroupsByMid(desc->description());
RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL,
bundle_groups_by_mid);
if (!error.ok()) {
observer->OnSetLocalDescriptionComplete(std::move(error));
return;
}
// 5. 应用本地描述
error = ApplyLocalDescription(std::move(desc), bundle_groups_by_mid);
observer->OnSetLocalDescriptionComplete(std::move(error));
}
第四层:会话描述工厂 - SDP内容生成
4.1 WebRtcSessionDescriptionFactory
在 pc/webrtc_session_description_factory.cc 中,负责实际的SDP生成:
void WebRtcSessionDescriptionFactory::InternalCreateOffer(
CreateSessionDescriptionRequest request) {
// 1. 处理ICE重启
if (sdp_info_->local_description()) {
for (cricket::MediaDescriptionOptions& options :
request.options.media_description_options) {
if (sdp_info_->NeedsIceRestart(options.mid)) {
options.transport_options.ice_restart = true;
}
}
}
// 2. 调用媒体会话工厂创建Offer
auto result = session_desc_factory_.CreateOfferOrError(
request.options,
sdp_info_->local_description()
? sdp_info_->local_description()->description()
: nullptr);
if (!result.ok()) {
PostCreateSessionDescriptionFailed(request.observer.get(), result.error());
return;
}
std::unique_ptr<cricket::SessionDescription> desc = std::move(result.value());
// 3. 更新会话版本号(RFC 3264要求)
RTC_DCHECK(session_version_ + 1 > session_version_);
auto offer = std::make_unique<JsepSessionDescription>(
SdpType::kOffer, std::move(desc), session_id_,
rtc::ToString(session_version_++));
// 4. 复制现有ICE候选
if (sdp_info_->local_description()) {
for (const cricket::MediaDescriptionOptions& options :
request.options.media_description_options) {
if (!options.transport_options.ice_restart) {
CopyCandidatesFromSessionDescription(sdp_info_->local_description(),
options.mid, offer.get());
}
}
}
// 5. 返回创建成功的结果
PostCreateSessionDescriptionSucceeded(request.observer.get(),
std::move(offer));
}
4.2 MediaSessionDescriptionFactory - 媒体协商核心
在 pc/media_session.cc 中实现具体的媒体协商逻辑:
webrtc::RTCErrorOr<std::unique_ptr<SessionDescription>>
MediaSessionDescriptionFactory::CreateOfferOrError(
const MediaSessionOptions& session_options,
const SessionDescription* current_description) const {
// 1. 初始化ICE凭证迭代器
IceCredentialsIterator ice_credentials(
session_options.pooled_ice_credentials);
// 2. 获取当前活跃内容
std::vector<const ContentInfo*> current_active_contents;
if (current_description) {
current_active_contents =
GetActiveContents(*current_description, session_options);
}
// 3. 获取当前流参数
StreamParamsVec current_streams =
GetCurrentStreamParams(current_active_contents);
// 4. 获取所有可能的编解码器
Codecs audio_codecs;
Codecs video_codecs;
if (!GetCodecsForOffer(current_active_contents, &audio_codecs,
&video_codecs)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to get codecs for offer");
}
// 5. 创建会话描述
auto offer = std::make_unique<SessionDescription>();
// 6. 为每个媒体选项创建内容描述
for (const MediaDescriptionOptions& media_description_options :
session_options.media_description_options) {
// 创建音频或视频内容
auto error_or_content = CreateContentOffer(
media_description_options, session_options, audio_codecs,
video_codecs, current_streams, current_description,
&ice_credentials);
if (!error_or_content.ok()) {
return error_or_content.MoveError();
}
offer->AddContent(std::move(error_or_content.value()));
}
return offer;
}
第五层:协议状态管理 - 信令状态机
5.1 本地描述应用过程
RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
std::unique_ptr<SessionDescriptionInterface> desc,
const std::map<std::string, const cricket::ContentGroup*>&
bundle_groups_by_mid) {
RTC_DCHECK_RUN_ON(signaling_thread());
TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyLocalDescription");
// 1. 清除统计缓存
pc_->ClearStatsCache();
// 2. 保存旧的本地描述引用
const SessionDescriptionInterface* old_local_description =
local_description();
std::unique_ptr<SessionDescriptionInterface> replaced_local_description;
SdpType type = desc->GetType();
// 3. 根据SDP类型更新状态
if (type == SdpType::kAnswer) {
// Answer会使pending变为current
replaced_local_description = pending_local_description_
? std::move(pending_local_description_)
: std::move(current_local_description_);
current_local_description_ = std::move(desc);
pending_local_description_ = nullptr;
current_remote_description_ = std::move(pending_remote_description_);
} else {
// Offer设置为pending
replaced_local_description = std::move(pending_local_description_);
pending_local_description_ = std::move(desc);
}
// 4. 设置初始offer者标志
if (!initial_offerer_) {
initial_offerer_.emplace(type == SdpType::kOffer);
}
// 5. 确定caller/callee角色
if (!is_caller_) {
if (remote_description()) {
is_caller_ = false; // 远端描述先设置,本端是callee
} else {
is_caller_ = true; // 本端描述先设置,本端是caller
}
}
// 6. 推送传输描述到传输层
RTCError error = PushdownTransportDescription(cricket::CS_LOCAL, type);
if (!error.ok()) {
return error;
}
// 7. 更新收发器和数据通道(Unified Plan)
if (IsUnifiedPlan()) {
error = UpdateTransceiversAndDataChannels(
cricket::CS_LOCAL, *local_description()->description(),
old_local_description, bundle_groups_by_mid);
if (!error.ok()) {
return error;
}
}
// 8. 如果两端描述都已设置,创建媒体通道
if (remote_description()) {
error = CreateChannels(*local_description()->description());
if (!error.ok()) {
return error;
}
}
return RTCError::OK();
}
5.2 媒体通道创建
RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) {
TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateChannels");
RTC_DCHECK_RUN_ON(signaling_thread());
// 1. 创建音频通道
const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc);
if (voice && !voice->rejected &&
!rtp_manager()->GetAudioTransceiver()->internal()->channel()) {
auto error =
rtp_manager()->GetAudioTransceiver()->internal()->CreateChannel(
voice->name, pc_->call_ptr(), pc_->configuration()->media_config,
pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(),
video_options(), video_bitrate_allocator_factory_.get(),
[&](absl::string_view mid) {
RTC_DCHECK_RUN_ON(network_thread());
return transport_controller_n()->GetRtpTransport(mid);
});
if (!error.ok()) {
return error;
}
}
// 2. 创建视频通道
const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc);
if (video && !video->rejected &&
!rtp_manager()->GetVideoTransceiver()->internal()->channel()) {
auto error =
rtp_manager()->GetVideoTransceiver()->internal()->CreateChannel(
video->name, pc_->call_ptr(), pc_->configuration()->media_config,
pc_->SrtpRequired(), pc_->GetCryptoOptions(),
audio_options(), video_options(),
video_bitrate_allocator_factory_.get(),
[&](absl::string_view mid) {
RTC_DCHECK_RUN_ON(network_thread());
return transport_controller_n()->GetRtpTransport(mid);
});
if (!error.ok()) {
return error;
}
}
// 3. 创建数据通道传输
const cricket::ContentInfo* data = cricket::GetFirstDataContent(&desc);
if (data && !data->rejected &&
!pc_->CreateDataChannelTransport(data->name)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to create data channel transport.");
}
return RTCError::OK();
}
第六层:编解码器协商 - 媒体能力匹配
6.1 编解码器协商过程
在Answer创建过程中,需要根据Offer中的编解码器进行协商:
webrtc::RTCErrorOr<std::unique_ptr<SessionDescription>>
MediaSessionDescriptionFactory::CreateAnswerOrError(
const SessionDescription* offer,
const MediaSessionOptions& session_options,
const SessionDescription* current_description) const {
// 1. 验证输入参数
if (!offer) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, "Called without offer.");
}
// 2. 选择拥塞控制反馈格式
bool has_ack_ccfb = false;
if (transport_desc_factory_->trials().IsEnabled(
"WebRTC-RFC8888CongestionControlFeedback")) {
for (const auto& content : offer->contents()) {
if (content.media_description()->rtcp_fb_ack_ccfb()) {
has_ack_ccfb = true;
}
}
}
// 3. 获取Answer的编解码器列表
Codecs answer_audio_codecs;
Codecs answer_video_codecs;
GetCodecsForAnswer(current_active_contents, *offer,
&answer_audio_codecs, &answer_video_codecs);
auto answer = std::make_unique<SessionDescription>();
// 4. 为每个媒体段创建对应的Answer内容
size_t content_index = 0;
for (const ContentInfo& content : offer->contents()) {
// 根据Offer内容创建对应的Answer内容
auto error_or_content = CreateAnswerContent(
content, session_options.media_description_options[content_index],
session_options, answer_audio_codecs, answer_video_codecs,
current_streams, current_description, &ice_credentials);
if (!error_or_content.ok()) {
return error_or_content.MoveError();
}
answer->AddContent(std::move(error_or_content.value()));
++content_index;
}
return answer;
}
6.2 编解码器匹配和过滤
void MediaSessionDescriptionFactory::GetCodecsForAnswer(
const std::vector<const ContentInfo*>& current_active_contents,
const SessionDescription& remote_offer,
Codecs* answer_audio_codecs,
Codecs* answer_video_codecs) const {
// 获取所有支持的编解码器
*answer_audio_codecs = GetAudioCodecsForAnswer();
*answer_video_codecs = GetVideoCodecsForAnswer();
// 建立统一的payload type映射
UsedPayloadTypes used_payload_types;
for (const ContentInfo& content : remote_offer.contents()) {
if (IsMediaContent(&content)) {
const MediaContentDescription* media_desc =
content.media_description();
for (const Codec& codec : media_desc->codecs()) {
used_payload_types.FindAndSetIdUsed(codec.id);
}
}
}
// 为Answer编解码器分配payload type
for (Codec& codec : *answer_audio_codecs) {
if (!used_payload_types.FindAndSetIdUsed(codec.id)) {
// 如果当前ID已被占用,需要重新分配
codec.id = used_payload_types.FindAndSetIdUsed();
}
}
for (Codec& codec : *answer_video_codecs) {
if (!used_payload_types.FindAndSetIdUsed(codec.id)) {
codec.id = used_payload_types.FindAndSetIdUsed();
}
}
}
第七层:信令状态机 - 协商流程控制
7.1 信令状态转换
WebRTC的信令状态机控制整个SDP协商流程:
// 状态定义
enum SignalingState {
kStable, // 稳定状态
kHaveLocalOffer, // 已发送本地Offer
kHaveLocalPrAnswer, // 已发送本地临时Answer
kHaveRemoteOffer, // 已接收远端Offer
kHaveRemotePrAnswer, // 已接收远端临时Answer
kClosed // 已关闭
};
// 状态转换逻辑
void SdpOfferAnswerHandler::ChangeSignalingState(
PeerConnectionInterface::SignalingState signaling_state) {
RTC_DCHECK_RUN_ON(signaling_thread());
if (signaling_state_ == signaling_state) {
return;
}
RTC_LOG(LS_INFO) << "Session: " << pc_->session_id()
<< " Old state: " << GetSignalingStateString(signaling_state_)
<< " New state: " << GetSignalingStateString(signaling_state);
signaling_state_ = signaling_state;
pc_->Observer()->OnSignalingChange(signaling_state_);
}
7.2 协商需求检测
void SdpOfferAnswerHandler::UpdateNegotiationNeeded() {
RTC_DCHECK_RUN_ON(signaling_thread());
if (!IsUnifiedPlan()) {
return; // Plan B不支持自动协商检测
}
bool is_negotiation_needed = false;
// 1. 检查是否有未关联的收发器
for (const auto& transceiver : rtp_manager()->transceivers()->List()) {
if (!transceiver->internal()->mid().has_value() &&
!transceiver->stopped()) {
is_negotiation_needed = true;
break;
}
}
// 2. 检查是否有方向改变的收发器
if (!is_negotiation_needed && local_description() && remote_description()) {
for (const auto& transceiver : rtp_manager()->transceivers()->List()) {
if (transceiver->internal()->HasDirectionChanged()) {
is_negotiation_needed = true;
break;
}
}
}
// 3. 检查数据通道是否需要协商
if (!is_negotiation_needed) {
is_negotiation_needed = pc_->sctp_data_channels_n().size() > 0 &&
!cricket::GetFirstDataContent(local_description()->description());
}
if (is_negotiation_needed != is_negotiation_needed_) {
RTC_LOG(LS_INFO) << "Negotiation needed: " << is_negotiation_needed;
is_negotiation_needed_ = is_negotiation_needed;
}
}
性能优化和错误处理
异步处理机制
WebRTC使用消息处理器来确保操作在正确的线程上执行:
void SdpOfferAnswerHandler::PostCreateSessionDescriptionSucceeded(
CreateSessionDescriptionObserver* observer,
std::unique_ptr<SessionDescriptionInterface> description) {
pc_->message_handler()->PostTask(
[observer_refptr =
rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),
description = std::move(description)]() mutable {
observer_refptr->OnSuccess(description.release());
});
}
错误处理策略
void SdpOfferAnswerHandler::PostCreateSessionDescriptionFailed(
CreateSessionDescriptionObserver* observer,
RTCError error) {
RTC_DCHECK(observer);
std::string error_message = error.message();
RTC_LOG(LS_ERROR) << "Create SDP failed: " << error_message;
pc_->message_handler()->PostTask(
[observer_refptr =
rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),
error = std::move(error)]() mutable {
observer_refptr->OnFailure(std::move(error));
});
}
总结
通过深入分析这四个核心SDP API的实现,我们可以看到WebRTC的架构设计具有以下特点:
-
分层架构清晰:从Java API到JNI桥接,再到C++核心实现,每一层都有明确的职责分工。
-
异步处理模式:所有SDP操作都是异步的,通过回调机制返回结果,避免阻塞UI线程。
-
状态机管理:通过严格的信令状态机控制SDP协商流程,确保操作的正确性。
-
编解码器协商:复杂的媒体能力协商逻辑,支持多种音视频编解码器的匹配和选择。
-
错误处理完善:在每一层都有完善的错误检查和处理机制。
-
线程安全:使用线程检查宏确保操作在正确的线程上执行。
这种设计使得WebRTC能够在复杂的网络环境中稳定地进行实时音视频通信,是一个值得学习的工程实践典范。
理解这些底层实现细节,对于开发高质量的WebRTC应用和解决实际问题都有重要价值。
更多推荐



所有评论(0)