前言

image.png

image.png

代码分析

Conductor::OnSuccess

  1. void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) {
  2. peer_connection_->SetLocalDescription(
  3. DummySetSessionDescriptionObserver::Create(), desc);
  4. std::string sdp;
  5. desc->ToString(&sdp);
  6. ***
  7. }

JsepSessionDescription::ToString

  1. bool JsepSessionDescription::ToString(std::string* out) const {
  2. if (!description_ || !out) {
  3. return false;
  4. }
  5. *out = SdpSerialize(*this);
  6. return !out->empty();
  7. }

SdpSerialize

  1. std::string SdpSerialize(const JsepSessionDescription& jdesc) {
  2. const cricket::SessionDescription* desc = jdesc.description();
  3. if (!desc) {
  4. return "";
  5. }
  6. std::string message;
  7. // Session Description.
  8. AddLine(kSessionVersion, &message);
  9. // Session Origin
  10. // RFC 4566
  11. // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
  12. // <unicast-address>
  13. rtc::StringBuilder os;
  14. InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
  15. const std::string& session_id =
  16. jdesc.session_id().empty() ? kSessionOriginSessionId : jdesc.session_id();
  17. const std::string& session_version = jdesc.session_version().empty()
  18. ? kSessionOriginSessionVersion
  19. : jdesc.session_version();
  20. os << " " << session_id << " " << session_version << " "
  21. << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
  22. << kSessionOriginAddress;
  23. AddLine(os.str(), &message);
  24. AddLine(kSessionName, &message);
  25. // Time Description.
  26. AddLine(kTimeDescription, &message);
  27. // Group
  28. if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
  29. std::string group_line = kAttrGroup;
  30. const cricket::ContentGroup* group =
  31. desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
  32. RTC_DCHECK(group != NULL);
  33. for (const std::string& content_name : group->content_names()) {
  34. group_line.append(" ");
  35. group_line.append(content_name);
  36. }
  37. AddLine(group_line, &message);
  38. }
  39. // Mixed one- and two-byte header extension.
  40. if (desc->extmap_allow_mixed()) {
  41. InitAttrLine(kAttributeExtmapAllowMixed, &os);
  42. AddLine(os.str(), &message);
  43. }
  44. // MediaStream semantics
  45. InitAttrLine(kAttributeMsidSemantics, &os);
  46. os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
  47. std::set<std::string> media_stream_ids;
  48. const ContentInfo* audio_content = GetFirstAudioContent(desc);
  49. if (audio_content)
  50. GetMediaStreamIds(audio_content, &media_stream_ids);
  51. const ContentInfo* video_content = GetFirstVideoContent(desc);
  52. if (video_content)
  53. GetMediaStreamIds(video_content, &media_stream_ids);
  54. for (const std::string& id : media_stream_ids) {
  55. os << " " << id;
  56. }
  57. AddLine(os.str(), &message);
  58. // a=ice-lite
  59. //
  60. // TODO(deadbeef): It's weird that we need to iterate TransportInfos for
  61. // this, when it's a session-level attribute. It really should be moved to a
  62. // session-level structure like SessionDescription.
  63. for (const cricket::TransportInfo& transport : desc->transport_infos()) {
  64. if (transport.description.ice_mode == cricket::ICEMODE_LITE) {
  65. InitAttrLine(kAttributeIceLite, &os);
  66. AddLine(os.str(), &message);
  67. break;
  68. }
  69. }
  70. // Preserve the order of the media contents.
  71. // 重点***
  72. int mline_index = -1;
  73. for (const ContentInfo& content : desc->contents()) {
  74. std::vector<Candidate> candidates;
  75. GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
  76. // 构建每一个媒体行和解释
  77. BuildMediaDescription(&content, desc->GetTransportInfoByName(content.name),
  78. content.media_description()->type(), candidates,
  79. desc->msid_signaling(), &message);
  80. }
  81. return message;
  82. }

AddLine的作用是将信息添加到message中,可以算是sdp文本中的一行。
InitLine 一行信息的初始化

BuildMediaDescription

  1. void BuildMediaDescription(const ContentInfo* content_info,
  2. const TransportInfo* transport_info,
  3. const cricket::MediaType media_type,
  4. const std::vector<Candidate>& candidates,
  5. int msid_signaling,
  6. std::string* message) {
  7. RTC_DCHECK(message != NULL);
  8. if (content_info == NULL || message == NULL) {
  9. return;
  10. }
  11. // 创建一个buffer,最后将这个buffer输出到message中。
  12. rtc::StringBuilder os;
  13. // 取出media_desc,然后下面根据类型来做相应的处理
  14. const MediaContentDescription* media_desc = content_info->media_description();
  15. RTC_DCHECK(media_desc);
  16. // RFC 4566
  17. // m=<media> <port> <proto> <fmt>
  18. // fmt is a list of payload type numbers that MAY be used in the session.
  19. std::string type;
  20. std::string fmt;
  21. if (media_type == cricket::MEDIA_TYPE_VIDEO) { //视频类型
  22. type = kMediaTypeVideo;
  23. const VideoContentDescription* video_desc = media_desc->as_video();
  24. for (const cricket::VideoCodec& codec : video_desc->codecs()) {
  25. fmt.append(" ");
  26. fmt.append(rtc::ToString(codec.id));
  27. }
  28. } else if (media_type == cricket::MEDIA_TYPE_AUDIO) { // 音频类型
  29. type = kMediaTypeAudio;
  30. const AudioContentDescription* audio_desc = media_desc->as_audio();
  31. for (const cricket::AudioCodec& codec : audio_desc->codecs()) {
  32. fmt.append(" ");
  33. fmt.append(rtc::ToString(codec.id));
  34. }
  35. } else if (media_type == cricket::MEDIA_TYPE_DATA) { // 数据类型
  36. type = kMediaTypeData;
  37. const cricket::SctpDataContentDescription* sctp_data_desc =
  38. media_desc->as_sctp();
  39. if (sctp_data_desc) {
  40. fmt.append(" ");
  41. if (sctp_data_desc->use_sctpmap()) {
  42. fmt.append(rtc::ToString(sctp_data_desc->port()));
  43. } else {
  44. fmt.append(kDefaultSctpmapProtocol);
  45. }
  46. } else {
  47. const RtpDataContentDescription* rtp_data_desc =
  48. media_desc->as_rtp_data();
  49. for (const cricket::RtpDataCodec& codec : rtp_data_desc->codecs()) {
  50. fmt.append(" ");
  51. fmt.append(rtc::ToString(codec.id));
  52. }
  53. }
  54. } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) { // 不支持的类型
  55. const UnsupportedContentDescription* unsupported_desc =
  56. media_desc->as_unsupported();
  57. type = unsupported_desc->media_type();
  58. } else {
  59. RTC_NOTREACHED();
  60. }
  61. // The fmt must never be empty. If no codecs are found, set the fmt attribute
  62. // to 0.
  63. if (fmt.empty()) {
  64. fmt = " 0";
  65. }
  66. // The port number in the m line will be updated later when associated with
  67. // the candidates.
  68. //
  69. // A port value of 0 indicates that the m= section is rejected.
  70. // RFC 3264
  71. // To reject an offered stream, the port number in the corresponding stream in
  72. // the answer MUST be set to zero.
  73. //
  74. // However, the BUNDLE draft adds a new meaning to port zero, when used along
  75. // with a=bundle-only.
  76. std::string port = kDummyPort; //定义端口,默认为9
  77. if (content_info->rejected || content_info->bundle_only) {
  78. port = kMediaPortRejected;
  79. } else if (!media_desc->connection_address().IsNil()) {
  80. port = rtc::ToString(media_desc->connection_address().port());
  81. }
  82. // 获取指纹对象
  83. rtc::SSLFingerprint* fp =
  84. (transport_info) ? transport_info->description.identity_fingerprint.get()
  85. : NULL;
  86. // Add the m and c lines.
  87. // 添加m行和c行
  88. InitLine(kLineTypeMedia, type, &os);
  89. os << " " << port << " " << media_desc->protocol() << fmt;
  90. AddLine(os.str(), message);
  91. InitLine(kLineTypeConnection, kConnectionNettype, &os);
  92. if (media_desc->connection_address().IsNil()) {
  93. os << " " << kConnectionIpv4Addrtype << " " << kDummyAddress;
  94. } else if (media_desc->connection_address().family() == AF_INET) {
  95. os << " " << kConnectionIpv4Addrtype << " "
  96. << media_desc->connection_address().ipaddr().ToString();
  97. } else if (media_desc->connection_address().family() == AF_INET6) {
  98. os << " " << kConnectionIpv6Addrtype << " "
  99. << media_desc->connection_address().ipaddr().ToString();
  100. } else {
  101. os << " " << kConnectionIpv4Addrtype << " " << kDummyAddress;
  102. }
  103. AddLine(os.str(), message);
  104. // 添加带宽的行内容
  105. // RFC 4566
  106. // b=AS:<bandwidth> or
  107. // b=TIAS:<bandwidth>
  108. int bandwidth = media_desc->bandwidth();
  109. std::string bandwidth_type = media_desc->bandwidth_type();
  110. if (bandwidth_type == kApplicationSpecificBandwidth && bandwidth >= 1000) {
  111. InitLine(kLineTypeSessionBandwidth, bandwidth_type, &os);
  112. bandwidth /= 1000;
  113. os << kSdpDelimiterColon << bandwidth;
  114. AddLine(os.str(), message);
  115. } else if (bandwidth_type == kTransportSpecificBandwidth && bandwidth > 0) {
  116. InitLine(kLineTypeSessionBandwidth, bandwidth_type, &os);
  117. os << kSdpDelimiterColon << bandwidth;
  118. AddLine(os.str(), message);
  119. }
  120. // Add the a=bundle-only line.
  121. if (content_info->bundle_only) {
  122. InitAttrLine(kAttributeBundleOnly, &os);
  123. AddLine(os.str(), message);
  124. }
  125. // Add the a=rtcp line.
  126. if (cricket::IsRtpProtocol(media_desc->protocol())) {
  127. std::string rtcp_line = GetRtcpLine(candidates);
  128. if (!rtcp_line.empty()) {
  129. AddLine(rtcp_line, message);
  130. }
  131. }
  132. // 老的方式,现在已经不使用了。用的是读取配置方式
  133. // Build the a=candidate lines. We don't include ufrag and pwd in the
  134. // candidates in the SDP to avoid redundancy.
  135. BuildCandidate(candidates, false, message);
  136. // Use the transport_info to build the media level ice-ufrag and ice-pwd.
  137. if (transport_info) {
  138. // RFC 5245
  139. // ice-pwd-att = "ice-pwd" ":" password
  140. // ice-ufrag-att = "ice-ufrag" ":" ufrag
  141. // ice-ufrag
  142. if (!transport_info->description.ice_ufrag.empty()) {
  143. InitAttrLine(kAttributeIceUfrag, &os);
  144. os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
  145. AddLine(os.str(), message);
  146. }
  147. // ice-pwd
  148. if (!transport_info->description.ice_pwd.empty()) {
  149. InitAttrLine(kAttributeIcePwd, &os);
  150. os << kSdpDelimiterColon << transport_info->description.ice_pwd;
  151. AddLine(os.str(), message);
  152. }
  153. // draft-petithuguenin-mmusic-ice-attributes-level-03
  154. BuildIceOptions(transport_info->description.transport_options, message);
  155. // RFC 4572
  156. // fingerprint-attribute =
  157. // "fingerprint" ":" hash-func SP fingerprint
  158. if (fp) {
  159. // Insert the fingerprint attribute.
  160. InitAttrLine(kAttributeFingerprint, &os);
  161. os << kSdpDelimiterColon << fp->algorithm << kSdpDelimiterSpace
  162. << fp->GetRfc4572Fingerprint();
  163. AddLine(os.str(), message);
  164. // Inserting setup attribute.
  165. if (transport_info->description.connection_role !=
  166. cricket::CONNECTIONROLE_NONE) {
  167. // Making sure we are not using "passive" mode.
  168. cricket::ConnectionRole role =
  169. transport_info->description.connection_role;
  170. std::string dtls_role_str;
  171. const bool success =
  172. cricket::ConnectionRoleToString(role, &dtls_role_str);
  173. RTC_DCHECK(success);
  174. InitAttrLine(kAttributeSetup, &os);
  175. os << kSdpDelimiterColon << dtls_role_str;
  176. AddLine(os.str(), message);
  177. }
  178. }
  179. }
  180. // RFC 3388
  181. // mid-attribute = "a=mid:" identification-tag
  182. // identification-tag = token
  183. // Use the content name as the mid identification-tag.
  184. InitAttrLine(kAttributeMid, &os);
  185. os << kSdpDelimiterColon << content_info->name;
  186. AddLine(os.str(), message);
  187. if (cricket::IsDtlsSctp(media_desc->protocol())) {
  188. const cricket::SctpDataContentDescription* data_desc =
  189. media_desc->as_sctp();
  190. BuildSctpContentAttributes(message, data_desc);
  191. } else if (cricket::IsRtpProtocol(media_desc->protocol())) {
  192. BuildRtpContentAttributes(media_desc, media_type, msid_signaling, message);
  193. }
  194. }

BuildSctpContentAttributes
BuildRtpContentAttributes

BuildRtpContentAttributes

  1. void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
  2. const cricket::MediaType media_type,
  3. int msid_signaling,
  4. std::string* message) {
  5. SdpSerializer serializer;
  6. rtc::StringBuilder os;
  7. // RFC 8285
  8. // a=extmap-allow-mixed
  9. // The attribute MUST be either on session level or media level. We support
  10. // responding on both levels, however, we don't respond on media level if it's
  11. // set on session level.
  12. if (media_desc->extmap_allow_mixed_enum() ==
  13. MediaContentDescription::kMedia) {
  14. InitAttrLine(kAttributeExtmapAllowMixed, &os);
  15. AddLine(os.str(), message);
  16. }
  17. // RFC 8285
  18. // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
  19. // The definitions MUST be either all session level or all media level. This
  20. // implementation uses all media level.
  21. for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
  22. const RtpExtension& extension = media_desc->rtp_header_extensions()[i];
  23. InitAttrLine(kAttributeExtmap, &os);
  24. os << kSdpDelimiterColon << extension.id;
  25. if (extension.encrypt) {
  26. os << kSdpDelimiterSpace << RtpExtension::kEncryptHeaderExtensionsUri;
  27. }
  28. os << kSdpDelimiterSpace << extension.uri;
  29. AddLine(os.str(), message);
  30. }
  31. // RFC 3264
  32. // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
  33. switch (media_desc->direction()) {
  34. // Special case that for sdp purposes should be treated same as inactive.
  35. case RtpTransceiverDirection::kStopped:
  36. case RtpTransceiverDirection::kInactive:
  37. InitAttrLine(kAttributeInactive, &os);
  38. break;
  39. case RtpTransceiverDirection::kSendOnly:
  40. InitAttrLine(kAttributeSendOnly, &os);
  41. break;
  42. case RtpTransceiverDirection::kRecvOnly:
  43. InitAttrLine(kAttributeRecvOnly, &os);
  44. break;
  45. case RtpTransceiverDirection::kSendRecv:
  46. InitAttrLine(kAttributeSendRecv, &os);
  47. break;
  48. default:
  49. RTC_NOTREACHED();
  50. InitAttrLine(kAttributeSendRecv, &os);
  51. break;
  52. }
  53. // 默认是sendrecv方式
  54. AddLine(os.str(), message);
  55. // Specified in https://datatracker.ietf.org/doc/draft-ietf-mmusic-msid/16/
  56. // a=msid:<msid-id> <msid-appdata>
  57. // The msid-id is a 1*64 token char representing the media stream id, and the
  58. // msid-appdata is a 1*64 token char representing the track id. There is a
  59. // line for every media stream, with a special msid-id value of "-"
  60. // representing no streams. The value of "msid-appdata" MUST be identical for
  61. // all lines.
  62. if (msid_signaling & cricket::kMsidSignalingMediaSection) {
  63. const StreamParamsVec& streams = media_desc->streams();
  64. if (streams.size() == 1u) {
  65. const StreamParams& track = streams[0];
  66. std::vector<std::string> stream_ids = track.stream_ids();
  67. if (stream_ids.empty()) {
  68. stream_ids.push_back(kNoStreamMsid);
  69. }
  70. for (const std::string& stream_id : stream_ids) {
  71. InitAttrLine(kAttributeMsid, &os);
  72. os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track.id;
  73. AddLine(os.str(), message);
  74. }
  75. } else if (streams.size() > 1u) {
  76. RTC_LOG(LS_WARNING)
  77. << "Trying to serialize Unified Plan SDP with more than "
  78. "one track in a media section. Omitting 'a=msid'.";
  79. }
  80. }
  81. // RFC 5761
  82. // a=rtcp-mux
  83. if (media_desc->rtcp_mux()) {
  84. InitAttrLine(kAttributeRtcpMux, &os);
  85. AddLine(os.str(), message);
  86. }
  87. // RFC 5506
  88. // a=rtcp-rsize
  89. if (media_desc->rtcp_reduced_size()) {
  90. InitAttrLine(kAttributeRtcpReducedSize, &os);
  91. AddLine(os.str(), message);
  92. }
  93. if (media_desc->conference_mode()) {
  94. InitAttrLine(kAttributeXGoogleFlag, &os);
  95. os << kSdpDelimiterColon << kValueConference;
  96. AddLine(os.str(), message);
  97. }
  98. if (media_desc->remote_estimate()) {
  99. InitAttrLine(kAttributeRtcpRemoteEstimate, &os);
  100. AddLine(os.str(), message);
  101. }
  102. // RFC 4568
  103. // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
  104. for (const CryptoParams& crypto_params : media_desc->cryptos()) {
  105. InitAttrLine(kAttributeCrypto, &os);
  106. os << kSdpDelimiterColon << crypto_params.tag << " "
  107. << crypto_params.cipher_suite << " " << crypto_params.key_params;
  108. if (!crypto_params.session_params.empty()) {
  109. os << " " << crypto_params.session_params;
  110. }
  111. AddLine(os.str(), message);
  112. }
  113. // RFC 4566
  114. // a=rtpmap:<payload type> <encoding name>/<clock rate>
  115. // [/<encodingparameters>]
  116. BuildRtpMap(media_desc, media_type, message);
  117. for (const StreamParams& track : media_desc->streams()) {
  118. // Build the ssrc-group lines.
  119. for (const SsrcGroup& ssrc_group : track.ssrc_groups) {
  120. // RFC 5576
  121. // a=ssrc-group:<semantics> <ssrc-id> ...
  122. if (ssrc_group.ssrcs.empty()) {
  123. continue;
  124. }
  125. InitAttrLine(kAttributeSsrcGroup, &os);
  126. os << kSdpDelimiterColon << ssrc_group.semantics;
  127. for (uint32_t ssrc : ssrc_group.ssrcs) {
  128. os << kSdpDelimiterSpace << rtc::ToString(ssrc);
  129. }
  130. AddLine(os.str(), message);
  131. }
  132. // Build the ssrc lines for each ssrc.
  133. for (uint32_t ssrc : track.ssrcs) {
  134. // RFC 5576
  135. // a=ssrc:<ssrc-id> cname:<value>
  136. AddSsrcLine(ssrc, kSsrcAttributeCname, track.cname, message);
  137. if (msid_signaling & cricket::kMsidSignalingSsrcAttribute) {
  138. // draft-alvestrand-mmusic-msid-00
  139. // a=ssrc:<ssrc-id> msid:identifier [appdata]
  140. // The appdata consists of the "id" attribute of a MediaStreamTrack,
  141. // which corresponds to the "id" attribute of StreamParams.
  142. // Since a=ssrc msid signaling is used in Plan B SDP semantics, and
  143. // multiple stream ids are not supported for Plan B, we are only adding
  144. // a line for the first media stream id here.
  145. const std::string& track_stream_id = track.first_stream_id();
  146. // We use a special msid-id value of "-" to represent no streams,
  147. // for Unified Plan compatibility. Plan B will always have a
  148. // track_stream_id.
  149. const std::string& stream_id =
  150. track_stream_id.empty() ? kNoStreamMsid : track_stream_id;
  151. InitAttrLine(kAttributeSsrc, &os);
  152. os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
  153. << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
  154. << kSdpDelimiterSpace << track.id;
  155. AddLine(os.str(), message);
  156. // TODO(ronghuawu): Remove below code which is for backward
  157. // compatibility.
  158. // draft-alvestrand-rtcweb-mid-01
  159. // a=ssrc:<ssrc-id> mslabel:<value>
  160. // The label isn't yet defined.
  161. // a=ssrc:<ssrc-id> label:<value>
  162. AddSsrcLine(ssrc, kSsrcAttributeMslabel, stream_id, message);
  163. AddSsrcLine(ssrc, kSSrcAttributeLabel, track.id, message);
  164. }
  165. }
  166. // Build the rid lines for each layer of the track
  167. for (const RidDescription& rid_description : track.rids()) {
  168. InitAttrLine(kAttributeRid, &os);
  169. os << kSdpDelimiterColon
  170. << serializer.SerializeRidDescription(rid_description);
  171. AddLine(os.str(), message);
  172. }
  173. }
  174. for (const RidDescription& rid_description : media_desc->receive_rids()) {
  175. InitAttrLine(kAttributeRid, &os);
  176. os << kSdpDelimiterColon
  177. << serializer.SerializeRidDescription(rid_description);
  178. AddLine(os.str(), message);
  179. }
  180. // Simulcast (a=simulcast)
  181. // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
  182. if (media_desc->HasSimulcast()) {
  183. const auto& simulcast = media_desc->simulcast_description();
  184. InitAttrLine(kAttributeSimulcast, &os);
  185. os << kSdpDelimiterColon
  186. << serializer.SerializeSimulcastDescription(simulcast);
  187. AddLine(os.str(), message);
  188. }
  189. }

image.png
默认是sendrecv。