当前位置:网站首页>webrtc 有关 SDP 部分的解析流程分析
webrtc 有关 SDP 部分的解析流程分析
2022-08-02 14:16:00 【freeabc】
----------------------------------------------------------------------------------------------------------------------------------------
一分钟快速搭建 rtmpd 服务器: https://blog.csdn.net/freeabc/article/details/102880984
软件下载地址: http://www.qiyicc.com/download/rtmpd.rar
github 地址:https://github.com/superconvert/smart_rtmpd
-----------------------------------------------------------------------------------------------------------------------------------------
webrtc 有关 SDP 部分的解析流程分析
在这个例子里,我们以 android 端为例进行说明整个流程,但整个流程都发生在 JNI 层,别的基本一样。
1.
./sdk/android/src/jni/pc/session_description.cc
std::unique_ptr<SessionDescriptionInterface> JavaToNativeSessionDescription(
JNIEnv* jni,
const JavaRef<jobject>& j_sdp) {
std::string std_type = JavaToStdString(
jni, Java_SessionDescription_getTypeInCanonicalForm(jni, j_sdp));
std::string std_description =
JavaToStdString(jni, Java_SessionDescription_getDescription(jni, j_sdp));
absl::optional<SdpType> sdp_type_maybe = SdpTypeFromString(std_type);
if (!sdp_type_maybe) {
RTC_LOG(LS_ERROR) << "Unexpected SDP type: " << std_type;
return nullptr;
}
// 需要进一步分析这个函数
return CreateSessionDescription(*sdp_type_maybe, std_description);
}
需要进一步分析这个函数 CreateSessionDescription
2.
./pc/jsep_session_description.cc
std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
SdpType type,
const std::string& sdp) {
return CreateSessionDescription(type, sdp, nullptr);
}
std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
SdpType type,
const std::string& sdp,
SdpParseError* error_out) {
auto jsep_desc = std::make_unique<JsepSessionDescription>(type);
if (type != SdpType::kRollback) {
// 需要进一步分析这个函数
if (!SdpDeserialize(sdp, jsep_desc.get(), error_out)) {
return nullptr;
}
}
return std::move(jsep_desc);
}
需要进一步分析这个函数 SdpDeserialize
3.
./pc/webrtc_sdp.cc
bool SdpDeserialize(const std::string& message,
JsepSessionDescription* jdesc,
SdpParseError* error) {
std::string session_id;
std::string session_version;
TransportDescription session_td("", "");
RtpHeaderExtensions session_extmaps;
rtc::SocketAddress session_connection_addr;
auto desc = std::make_unique<cricket::SessionDescription>();
size_t current_pos = 0;
// Session Description
if (!ParseSessionDescription(message, ¤t_pos, &session_id,
&session_version, &session_td, &session_extmaps,
&session_connection_addr, desc.get(), error)) {
return false;
}
// Media Description
std::vector<std::unique_ptr<JsepIceCandidate>> candidates;
if (!ParseMediaDescription(message, session_td, session_extmaps, ¤t_pos,
session_connection_addr, desc.get(), &candidates,
error)) {
return false;
}
jdesc->Initialize(std::move(desc), session_id, session_version);
for (const auto& candidate : candidates) {
jdesc->AddCandidate(candidate.get());
}
return true;
}
看上述代码,我们知道 SDP 解析分为这几个部分,会话层解析 ParseSessionDescription, 媒体层解析 ParseMediaDescription, 还有 Candidate 的解析(如果存在)
会话层解析
bool ParseSessionDescription(const std::string& message,
size_t* pos,
std::string* session_id,
std::string* session_version,
TransportDescription* session_td,
RtpHeaderExtensions* session_extmaps,
rtc::SocketAddress* connection_addr,
cricket::SessionDescription* desc,
SdpParseError* error) {
std::string line;
desc->set_msid_supported(false);
desc->set_extmap_allow_mixed(false);
// RFC 4566
// v= (protocol version)
if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
return ParseFailedExpectLine(message, *pos, kLineTypeVersion, std::string(),
error);
}
// RFC 4566
// o=<username> <sess-id> <sess-version> <nettype> <addrtype>
// <unicast-address>
if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
return ParseFailedExpectLine(message, *pos, kLineTypeOrigin, std::string(),
error);
}
std::vector<std::string> fields;
rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
const size_t expected_fields = 6;
if (fields.size() != expected_fields) {
return ParseFailedExpectFieldNum(line, expected_fields, error);
}
*session_id = fields[1];
*session_version = fields[2];
// RFC 4566
// s= (session name)
if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
std::string(), error);
}
// absl::optional lines
// Those are the optional lines, so shouldn't return false if not present.
// RFC 4566
// i=* (session information)
GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
// RFC 4566
// u=* (URI of description)
GetLineWithType(message, pos, &line, kLineTypeSessionUri);
// RFC 4566
// e=* (email address)
GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
// RFC 4566
// p=* (phone number)
GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
// RFC 4566
// c=* (connection information -- not required if included in
// all media)
if (GetLineWithType(message, pos, &line, kLineTypeConnection)) {
if (!ParseConnectionData(line, connection_addr, error)) {
return false;
}
}
// RFC 4566
// b=* (zero or more bandwidth information lines)
while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
// By pass zero or more b lines.
}
// RFC 4566
// One or more time descriptions ("t=" and "r=" lines; see below)
// t= (time the session is active)
// r=* (zero or more repeat times)
// Ensure there's at least one time description
if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
error);
}
while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
// By pass zero or more r lines.
}
// Go through the rest of the time descriptions
while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
// By pass zero or more r lines.
}
}
// RFC 4566
// z=* (time zone adjustments)
GetLineWithType(message, pos, &line, kLineTypeTimeZone);
// RFC 4566
// k=* (encryption key)
GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
// RFC 4566
// a=* (zero or more session attribute lines)
while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
if (HasAttribute(line, kAttributeGroup)) {
if (!ParseGroupAttribute(line, desc, error)) {
return false;
}
} else if (HasAttribute(line, kAttributeIceUfrag)) {
if (!GetValue(line, kAttributeIceUfrag, &(session_td->ice_ufrag),
error)) {
return false;
}
} else if (HasAttribute(line, kAttributeIcePwd)) {
if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
return false;
}
} else if (HasAttribute(line, kAttributeIceLite)) {
session_td->ice_mode = cricket::ICEMODE_LITE;
} else if (HasAttribute(line, kAttributeIceOption)) {
if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
return false;
}
} else if (HasAttribute(line, kAttributeFingerprint)) {
if (session_td->identity_fingerprint.get()) {
return ParseFailed(
line,
"Can't have multiple fingerprint attributes at the same level.",
error);
}
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
return false;
}
session_td->identity_fingerprint = std::move(fingerprint);
} else if (HasAttribute(line, kAttributeSetup)) {
if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
return false;
}
} else if (HasAttribute(line, kAttributeMsidSemantics)) {
std::string semantics;
if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
return false;
}
desc->set_msid_supported(
CaseInsensitiveFind(semantics, kMediaStreamSemantic));
} else if (HasAttribute(line, kAttributeExtmapAllowMixed)) {
desc->set_extmap_allow_mixed(true);
} else if (HasAttribute(line, kAttributeExtmap)) {
RtpExtension extmap;
if (!ParseExtmap(line, &extmap, error)) {
return false;
}
session_extmaps->push_back(extmap);
}
}
return true;
}
媒体层解析
bool ParseMediaDescription(
const std::string& message,
const TransportDescription& session_td,
const RtpHeaderExtensions& session_extmaps,
size_t* pos,
const rtc::SocketAddress& session_connection_addr,
cricket::SessionDescription* desc,
std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
SdpParseError* error) {
RTC_DCHECK(desc != NULL);
std::string line;
int mline_index = -1;
int msid_signaling = 0;
// Zero or more media descriptions
// RFC 4566
// m=<media> <port> <proto> <fmt>
while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
++mline_index;
std::vector<std::string> fields;
rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
const size_t expected_min_fields = 4;
if (fields.size() < expected_min_fields) {
return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
}
bool port_rejected = false;
// RFC 3264
// To reject an offered stream, the port number in the corresponding stream
// in the answer MUST be set to zero.
if (fields[1] == kMediaPortRejected) {
port_rejected = true;
}
int port = 0;
if (!rtc::FromString<int>(fields[1], &port) || !IsValidPort(port)) {
return ParseFailed(line, "The port number is invalid", error);
}
std::string protocol = fields[2];
// <fmt>
std::vector<int> payload_types;
if (cricket::IsRtpProtocol(protocol)) {
for (size_t j = 3; j < fields.size(); ++j) {
// TODO(wu): Remove when below bug is fixed.
// https://bugzilla.mozilla.org/show_bug.cgi?id=996329
if (fields[j].empty() && j == fields.size() - 1) {
continue;
}
int pl = 0;
if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
return false;
}
payload_types.push_back(pl);
}
}
// Make a temporary TransportDescription based on |session_td|.
// Some of this gets overwritten by ParseContent.
TransportDescription transport(
session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
session_td.ice_mode, session_td.connection_role,
session_td.identity_fingerprint.get());
std::unique_ptr<MediaContentDescription> content;
std::string content_name;
bool bundle_only = false;
int section_msid_signaling = 0;
if (HasAttribute(line, kMediaTypeVideo)) {
content = ParseContentDescription<VideoContentDescription>(
message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
payload_types, pos, &content_name, &bundle_only,
§ion_msid_signaling, &transport, candidates, error);
} else if (HasAttribute(line, kMediaTypeAudio)) {
content = ParseContentDescription<AudioContentDescription>(
message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
payload_types, pos, &content_name, &bundle_only,
§ion_msid_signaling, &transport, candidates, error);
} else if (HasAttribute(line, kMediaTypeData)) {
if (cricket::IsDtlsSctp(protocol)) {
// The draft-03 format is:
// m=application <port> DTLS/SCTP <sctp-port>...
// use_sctpmap should be false.
// The draft-26 format is:
// m=application <port> UDP/DTLS/SCTP webrtc-datachannel
// use_sctpmap should be false.
auto data_desc = std::make_unique<SctpDataContentDescription>();
// Default max message size is 64K
// according to draft-ietf-mmusic-sctp-sdp-26
data_desc->set_max_message_size(kDefaultSctpMaxMessageSize);
int p;
if (rtc::FromString(fields[3], &p)) {
data_desc->set_port(p);
} else if (fields[3] == kDefaultSctpmapProtocol) {
data_desc->set_use_sctpmap(false);
}
if (!ParseContent(message, cricket::MEDIA_TYPE_DATA, mline_index,
protocol, payload_types, pos, &content_name,
&bundle_only, §ion_msid_signaling,
data_desc.get(), &transport, candidates, error)) {
return false;
}
data_desc->set_protocol(protocol);
content = std::move(data_desc);
} else {
// RTP
std::unique_ptr<RtpDataContentDescription> data_desc =
ParseContentDescription<RtpDataContentDescription>(
message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
payload_types, pos, &content_name, &bundle_only,
§ion_msid_signaling, &transport, candidates, error);
content = std::move(data_desc);
}
} else {
RTC_LOG(LS_WARNING) << "Unsupported media type: " << line;
continue;
}
if (!content.get()) {
// ParseContentDescription returns NULL if failed.
return false;
}
msid_signaling |= section_msid_signaling;
bool content_rejected = false;
// A port of 0 is not interpreted as a rejected m= section when it's
// used along with a=bundle-only.
if (bundle_only) {
if (!port_rejected) {
// Usage of bundle-only with a nonzero port is unspecified. So just
// ignore bundle-only if we see this.
bundle_only = false;
RTC_LOG(LS_WARNING)
<< "a=bundle-only attribute observed with a nonzero "
"port; this usage is unspecified so the attribute is being "
"ignored.";
}
} else {
// If not using bundle-only, interpret port 0 in the normal way; the m=
// section is being rejected.
content_rejected = port_rejected;
}
if (cricket::IsRtpProtocol(protocol) && !content->as_sctp()) {
content->set_protocol(protocol);
// Set the extmap.
if (!session_extmaps.empty() &&
!content->rtp_header_extensions().empty()) {
return ParseFailed("",
"The a=extmap MUST be either all session level or "
"all media level.",
error);
}
for (size_t i = 0; i < session_extmaps.size(); ++i) {
content->AddRtpHeaderExtension(session_extmaps[i]);
}
} else if (content->as_sctp()) {
// Do nothing, it's OK
} else {
RTC_LOG(LS_WARNING) << "Parse failed with unknown protocol " << protocol;
return false;
}
// Use the session level connection address if the media level addresses are
// not specified.
rtc::SocketAddress address;
address = content->connection_address().IsNil()
? session_connection_addr
: content->connection_address();
address.SetPort(port);
content->set_connection_address(address);
desc->AddContent(content_name,
cricket::IsDtlsSctp(protocol) ? MediaProtocolType::kSctp
: MediaProtocolType::kRtp,
content_rejected, bundle_only, std::move(content));
// Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
desc->AddTransportInfo(TransportInfo(content_name, transport));
}
desc->set_msid_signaling(msid_signaling);
size_t end_of_message = message.size();
if (mline_index == -1 && *pos != end_of_message) {
ParseFailed(message, *pos, "Expects m line.", error);
return false;
}
return true;
}
我们继续分析一下 ParseContentDescription 函数 ./pc/webrtc_sdp.cc
template <class C>
static std::unique_ptr<C> ParseContentDescription(
const std::string& message,
const cricket::MediaType media_type,
int mline_index,
const std::string& protocol,
const std::vector<int>& payload_types,
size_t* pos,
std::string* content_name,
bool* bundle_only,
int* msid_signaling,
TransportDescription* transport,
std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
webrtc::SdpParseError* error) {
auto media_desc = std::make_unique<C>();
if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
pos, content_name, bundle_only, msid_signaling,
media_desc.get(), transport, candidates, error)) {
return nullptr;
}
// Sort the codecs according to the m-line fmt list.
std::unordered_map<int, int> payload_type_preferences;
// "size + 1" so that the lowest preference payload type has a preference of
// 1, which is greater than the default (0) for payload types not in the fmt
// list.
int preference = static_cast<int>(payload_types.size() + 1);
for (int pt : payload_types) {
payload_type_preferences[pt] = preference--;
}
std::vector<typename C::CodecType> codecs = media_desc->codecs();
absl::c_sort(
codecs, [&payload_type_preferences](const typename C::CodecType& a,
const typename C::CodecType& b) {
return payload_type_preferences[a.id] > payload_type_preferences[b.id];
});
media_desc->set_codecs(codecs);
return media_desc;
}
ParseContent 函数 ./pc/webrtc_sdp.cc
bool ParseContent(const std::string& message,
const cricket::MediaType media_type,
int mline_index,
const std::string& protocol,
const std::vector<int>& payload_types,
size_t* pos,
std::string* content_name,
bool* bundle_only,
int* msid_signaling,
MediaContentDescription* media_desc,
TransportDescription* transport,
std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
SdpParseError* error) {
RTC_DCHECK(media_desc != NULL);
RTC_DCHECK(content_name != NULL);
RTC_DCHECK(transport != NULL);
if (media_type == cricket::MEDIA_TYPE_AUDIO) {
MaybeCreateStaticPayloadAudioCodecs(payload_types, media_desc->as_audio());
}
// The media level "ice-ufrag" and "ice-pwd".
// The candidates before update the media level "ice-pwd" and "ice-ufrag".
Candidates candidates_orig;
std::string line;
std::string mline_id;
// Tracks created out of the ssrc attributes.
StreamParamsVec tracks;
SsrcInfoVec ssrc_infos;
SsrcGroupVec ssrc_groups;
std::string maxptime_as_string;
std::string ptime_as_string;
std::vector<std::string> stream_ids;
std::string track_id;
SdpSerializer deserializer;
std::vector<RidDescription> rids;
SimulcastDescription simulcast;
// Loop until the next m line
while (!IsLineType(message, kLineTypeMedia, *pos)) {
if (!GetLine(message, pos, &line)) {
if (*pos >= message.size()) {
break; // Done parsing
} else {
return ParseFailed(message, *pos, "Invalid SDP line.", error);
}
}
// RFC 4566
// b=* (zero or more bandwidth information lines)
if (IsLineType(line, kLineTypeSessionBandwidth)) {
std::string bandwidth;
if (HasAttribute(line, kApplicationSpecificMaximum)) {
if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
return false;
} else {
int b = 0;
if (!GetValueFromString(line, bandwidth, &b, error)) {
return false;
}
// TODO(deadbeef): Historically, applications may be setting a value
// of -1 to mean "unset any previously set bandwidth limit", even
// though ommitting the "b=AS" entirely will do just that. Once we've
// transitioned applications to doing the right thing, it would be
// better to treat this as a hard error instead of just ignoring it.
if (b == -1) {
RTC_LOG(LS_WARNING)
<< "Ignoring \"b=AS:-1\"; will be treated as \"no "
"bandwidth limit\".";
continue;
}
if (b < 0) {
return ParseFailed(line, "b=AS value can't be negative.", error);
}
// We should never use more than the default bandwidth for RTP-based
// data channels. Don't allow SDP to set the bandwidth, because
// that would give JS the opportunity to "break the Internet".
// See: https://code.google.com/p/chromium/issues/detail?id=280726
if (media_type == cricket::MEDIA_TYPE_DATA &&
cricket::IsRtpProtocol(protocol) &&
b > cricket::kDataMaxBandwidth / 1000) {
rtc::StringBuilder description;
description << "RTP-based data channels may not send more than "
<< cricket::kDataMaxBandwidth / 1000 << "kbps.";
return ParseFailed(line, description.str(), error);
}
// Prevent integer overflow.
b = std::min(b, INT_MAX / 1000);
media_desc->set_bandwidth(b * 1000);
}
}
continue;
}
// Parse the media level connection data.
if (IsLineType(line, kLineTypeConnection)) {
rtc::SocketAddress addr;
if (!ParseConnectionData(line, &addr, error)) {
return false;
}
media_desc->set_connection_address(addr);
continue;
}
if (!IsLineType(line, kLineTypeAttributes)) {
// TODO(deadbeef): Handle other lines if needed.
RTC_LOG(LS_INFO) << "Ignored line: " << line;
continue;
}
// Handle attributes common to SCTP and RTP.
if (HasAttribute(line, kAttributeMid)) {
// RFC 3388
// mid-attribute = "a=mid:" identification-tag
// identification-tag = token
// Use the mid identification-tag as the content name.
if (!GetValue(line, kAttributeMid, &mline_id, error)) {
return false;
}
*content_name = mline_id;
} else if (HasAttribute(line, kAttributeBundleOnly)) {
*bundle_only = true;
} else if (HasAttribute(line, kAttributeCandidate)) {
Candidate candidate;
if (!ParseCandidate(line, &candidate, error, false)) {
return false;
}
// ParseCandidate will parse non-standard ufrag and password attributes,
// since it's used for candidate trickling, but we only want to process
// the "a=ice-ufrag"/"a=ice-pwd" values in a session description, so
// strip them off at this point.
candidate.set_username(std::string());
candidate.set_password(std::string());
candidates_orig.push_back(candidate);
} else if (HasAttribute(line, kAttributeIceUfrag)) {
if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
return false;
}
} else if (HasAttribute(line, kAttributeIcePwd)) {
if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
return false;
}
} else if (HasAttribute(line, kAttributeIceOption)) {
if (!ParseIceOptions(line, &transport->transport_options, error)) {
return false;
}
} else if (HasAttribute(line, kOpaqueTransportParametersLine)) {
transport->opaque_parameters = cricket::OpaqueTransportParameters();
if (!ParseOpaqueTransportLine(
line, &transport->opaque_parameters->protocol,
&transport->opaque_parameters->parameters, error)) {
return false;
}
} else if (HasAttribute(line, kAltProtocolLine)) {
std::string alt_protocol;
if (!ParseAltProtocolLine(line, &alt_protocol, error)) {
return false;
}
media_desc->set_alt_protocol(alt_protocol);
} else if (HasAttribute(line, kAttributeFmtp)) {
if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
return false;
}
} else if (HasAttribute(line, kAttributeFingerprint)) {
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
return false;
}
transport->identity_fingerprint = std::move(fingerprint);
} else if (HasAttribute(line, kAttributeSetup)) {
if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
return false;
}
} else if (cricket::IsDtlsSctp(protocol) &&
HasAttribute(line, kAttributeSctpPort)) {
if (media_type != cricket::MEDIA_TYPE_DATA) {
return ParseFailed(
line, "sctp-port attribute found in non-data media description.",
error);
}
if (media_desc->as_sctp()->use_sctpmap()) {
return ParseFailed(
line, "sctp-port attribute can't be used with sctpmap.", error);
}
int sctp_port;
if (!ParseSctpPort(line, &sctp_port, error)) {
return false;
}
media_desc->as_sctp()->set_port(sctp_port);
} else if (cricket::IsDtlsSctp(protocol) &&
HasAttribute(line, kAttributeMaxMessageSize)) {
if (media_type != cricket::MEDIA_TYPE_DATA) {
return ParseFailed(
line,
"max-message-size attribute found in non-data media description.",
error);
}
int max_message_size;
if (!ParseSctpMaxMessageSize(line, &max_message_size, error)) {
return false;
}
media_desc->as_sctp()->set_max_message_size(max_message_size);
} else if (cricket::IsRtpProtocol(protocol)) {
//
// RTP specific attrubtes
//
if (HasAttribute(line, kAttributeRtcpMux)) {
media_desc->set_rtcp_mux(true);
} else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
media_desc->set_rtcp_reduced_size(true);
} else if (HasAttribute(line, kAttributeRtcpRemoteEstimate)) {
media_desc->set_remote_estimate(true);
} else if (HasAttribute(line, kAttributeSsrcGroup)) {
if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
return false;
}
} else if (HasAttribute(line, kAttributeSsrc)) {
if (!ParseSsrcAttribute(line, &ssrc_infos, msid_signaling, error)) {
return false;
}
} else if (HasAttribute(line, kAttributeCrypto)) {
if (!ParseCryptoAttribute(line, media_desc, error)) {
return false;
}
} else if (HasAttribute(line, kAttributeRtpmap)) {
if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
error)) {
return false;
}
} else if (HasAttribute(line, kCodecParamMaxPTime)) {
if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
return false;
}
} else if (HasAttribute(line, kAttributePacketization)) {
if (!ParsePacketizationAttribute(line, media_type, media_desc, error)) {
return false;
}
} else if (HasAttribute(line, kAttributeRtcpFb)) {
if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
return false;
}
} else if (HasAttribute(line, kCodecParamPTime)) {
if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
return false;
}
} else if (HasAttribute(line, kAttributeSendOnly)) {
media_desc->set_direction(RtpTransceiverDirection::kSendOnly);
} else if (HasAttribute(line, kAttributeRecvOnly)) {
media_desc->set_direction(RtpTransceiverDirection::kRecvOnly);
} else if (HasAttribute(line, kAttributeInactive)) {
media_desc->set_direction(RtpTransceiverDirection::kInactive);
} else if (HasAttribute(line, kAttributeSendRecv)) {
media_desc->set_direction(RtpTransceiverDirection::kSendRecv);
} else if (HasAttribute(line, kAttributeExtmapAllowMixed)) {
media_desc->set_extmap_allow_mixed_enum(
MediaContentDescription::kMedia);
} else if (HasAttribute(line, kAttributeExtmap)) {
RtpExtension extmap;
if (!ParseExtmap(line, &extmap, error)) {
return false;
}
media_desc->AddRtpHeaderExtension(extmap);
} else if (HasAttribute(line, kAttributeXGoogleFlag)) {
// Experimental attribute. Conference mode activates more aggressive
// AEC and NS settings.
// TODO(deadbeef): expose API to set these directly.
std::string flag_value;
if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
return false;
}
if (flag_value.compare(kValueConference) == 0)
media_desc->set_conference_mode(true);
} else if (HasAttribute(line, kAttributeMsid)) {
if (!ParseMsidAttribute(line, &stream_ids, &track_id, error)) {
return false;
}
*msid_signaling |= cricket::kMsidSignalingMediaSection;
} else if (HasAttribute(line, kAttributeRid)) {
const size_t kRidPrefixLength =
kLinePrefixLength + arraysize(kAttributeRid);
if (line.size() <= kRidPrefixLength) {
RTC_LOG(LS_INFO) << "Ignoring empty RID attribute: " << line;
continue;
}
RTCErrorOr<RidDescription> error_or_rid_description =
deserializer.DeserializeRidDescription(
line.substr(kRidPrefixLength));
// Malformed a=rid lines are discarded.
if (!error_or_rid_description.ok()) {
RTC_LOG(LS_INFO) << "Ignoring malformed RID line: '" << line
<< "'. Error: "
<< error_or_rid_description.error().message();
continue;
}
rids.push_back(error_or_rid_description.MoveValue());
} else if (HasAttribute(line, kAttributeSimulcast)) {
const size_t kSimulcastPrefixLength =
kLinePrefixLength + arraysize(kAttributeSimulcast);
if (line.size() <= kSimulcastPrefixLength) {
return ParseFailed(line, "Simulcast attribute is empty.", error);
}
if (!simulcast.empty()) {
return ParseFailed(line, "Multiple Simulcast attributes specified.",
error);
}
RTCErrorOr<SimulcastDescription> error_or_simulcast =
deserializer.DeserializeSimulcastDescription(
line.substr(kSimulcastPrefixLength));
if (!error_or_simulcast.ok()) {
return ParseFailed(line,
std::string("Malformed simulcast line: ") +
error_or_simulcast.error().message(),
error);
}
simulcast = error_or_simulcast.value();
} else {
// Unrecognized attribute in RTP protocol.
RTC_LOG(LS_INFO) << "Ignored line: " << line;
continue;
}
} else {
// Only parse lines that we are interested of.
RTC_LOG(LS_INFO) << "Ignored line: " << line;
continue;
}
}
// Remove duplicate or inconsistent rids.
RemoveInvalidRidDescriptions(payload_types, &rids);
// If simulcast is specifed, split the rids into send and receive.
// Rids that do not appear in simulcast attribute will be removed.
// If it is not specified, we assume that all rids are for send layers.
std::vector<RidDescription> send_rids;
std::vector<RidDescription> receive_rids;
if (!simulcast.empty()) {
// Verify that the rids in simulcast match rids in sdp.
RemoveInvalidRidsFromSimulcast(rids, &simulcast);
// Use simulcast description to figure out Send / Receive RIDs.
std::map<std::string, RidDescription> rid_map;
for (const RidDescription& rid : rids) {
rid_map[rid.rid] = rid;
}
for (const auto& layer : simulcast.send_layers().GetAllLayers()) {
auto iter = rid_map.find(layer.rid);
RTC_DCHECK(iter != rid_map.end());
send_rids.push_back(iter->second);
}
for (const auto& layer : simulcast.receive_layers().GetAllLayers()) {
auto iter = rid_map.find(layer.rid);
RTC_DCHECK(iter != rid_map.end());
receive_rids.push_back(iter->second);
}
media_desc->set_simulcast_description(simulcast);
} else {
send_rids = rids;
}
media_desc->set_receive_rids(receive_rids);
// Create tracks from the |ssrc_infos|.
// If the stream_id/track_id for all SSRCS are identical, one StreamParams
// will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
// the m= section.
if (!ssrc_infos.empty()) {
CreateTracksFromSsrcInfos(ssrc_infos, stream_ids, track_id, &tracks,
*msid_signaling);
} else if (media_type != cricket::MEDIA_TYPE_DATA &&
(*msid_signaling & cricket::kMsidSignalingMediaSection)) {
// If the stream_ids/track_id was signaled but SSRCs were unsignaled we
// still create a track. This isn't done for data media types because
// StreamParams aren't used for SCTP streams, and RTP data channels don't
// support unsignaled SSRCs.
CreateTrackWithNoSsrcs(stream_ids, track_id, send_rids, &tracks);
}
// Add the ssrc group to the track.
for (const SsrcGroup& ssrc_group : ssrc_groups) {
if (ssrc_group.ssrcs.empty()) {
continue;
}
uint32_t ssrc = ssrc_group.ssrcs.front();
for (StreamParams& track : tracks) {
if (track.has_ssrc(ssrc)) {
track.ssrc_groups.push_back(ssrc_group);
}
}
}
// Add the new tracks to the |media_desc|.
for (StreamParams& track : tracks) {
media_desc->AddStream(track);
}
if (media_type == cricket::MEDIA_TYPE_AUDIO) {
AudioContentDescription* audio_desc = media_desc->as_audio();
UpdateFromWildcardCodecs(audio_desc);
// Verify audio codec ensures that no audio codec has been populated with
// only fmtp.
if (!VerifyAudioCodecs(audio_desc)) {
return ParseFailed("Failed to parse audio codecs correctly.", error);
}
AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
}
if (media_type == cricket::MEDIA_TYPE_VIDEO) {
VideoContentDescription* video_desc = media_desc->as_video();
UpdateFromWildcardCodecs(video_desc);
// Verify video codec ensures that no video codec has been populated with
// only rtcp-fb.
if (!VerifyVideoCodecs(video_desc)) {
return ParseFailed("Failed to parse video codecs correctly.", error);
}
}
// RFC 5245
// Update the candidates with the media level "ice-pwd" and "ice-ufrag".
for (Candidate& candidate : candidates_orig) {
RTC_DCHECK(candidate.username().empty() ||
candidate.username() == transport->ice_ufrag);
candidate.set_username(transport->ice_ufrag);
RTC_DCHECK(candidate.password().empty());
candidate.set_password(transport->ice_pwd);
candidates->push_back(
std::make_unique<JsepIceCandidate>(mline_id, mline_index, candidate));
}
return true;
}
经过上述流程我们知道底层的 SDP 对象其实就是一个 JsepSessionDescription 对象,而 JsepSessionDescription 又包含这个对象 SessionDescription 作为属性,其实核心数据都在 SessionDescription
底层对象 PeerConnection 的属性
./pc/peer_connection.h
std::unique_ptr<SessionDescriptionInterface> current_local_description_
std::unique_ptr<SessionDescriptionInterface> pending_local_description_
std::unique_ptr<SessionDescriptionInterface> current_remote_description_
std::unique_ptr<SessionDescriptionInterface> pending_remote_description_
其实也就是 JsepSessionDescription 对象,有关 SDP 的具体数据就是 SessionDescription
我们所有的 SDP 信息都在 current_local_description_, current_remote_description_ 而它的 contents_ 包含了每一个 SDP 的 m 段,代表一路音频或视频的具体信息。
边栏推荐
猜你喜欢
【网络安全】学习笔记 --00
项目管理模块-项目权限功能开发
Server-Sent Events 一种轻量级的Push方式
Zabbix: PHP option“date.timezone” Fail
虚拟机使用的是此版本 VMware Workstation 不支持的硬件版本。模块“Upgrade”启动失败。未能启动虚拟机。
Evaluate multipath BBR congestion control on ns3
内存和硬盘、磁盘的区别
深入理解负载均衡
The relationship between base classes and derived classes [inheritance] / polymorphism and virtual functions / [inheritance and polymorphism] abstract classes and simple factories
GC垃圾回收ZGC
随机推荐
Priority table and Ascll table
mininet multihomed topology
创建系统还原点及恢复
【线程】线程创建 | 理解线程并发 (1)
代码细节带来的极致体验,ShardingSphere 5.1.0 性能提升密钥
华为单臂路由配置,实现不同vlan之间的通信
【软件测试】selenium自动化测试1
【数组】查表法(闰年)
图解MESI(缓存一致性协议)
【软件测试】自动化测试selenium3
【进程间通信】消息队列
HCIE学习记录——数通网络基础
JVM常量池详解
Oauth2.0 认证服务器搭建
The dynamic planning theory
H3C 交换机配置端口组、DHCP、DHCP中继、管理用户
mongodb连接本地服务失败的问题
从FAST TCP到POWERTCP
The use of a semaphore/interprocess communication 】 【 Shared memory
LAMP环境 源码编译安装(Apache 2.4.52 +mysql 8.0.28+php 8.1.3)