sdp_message.cc 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "remoting/protocol/sdp_message.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "base/notreached.h"
  8. #include "base/strings/string_piece.h"
  9. #include "base/strings/string_split.h"
  10. #include "base/strings/string_util.h"
  11. namespace remoting {
  12. namespace protocol {
  13. SdpMessage::SdpMessage(const std::string& sdp) {
  14. sdp_lines_ = base::SplitString(
  15. sdp, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  16. for (const auto& line : sdp_lines_) {
  17. if (base::StartsWith(line, "m=audio", base::CompareCase::SENSITIVE))
  18. has_audio_ = true;
  19. if (base::StartsWith(line, "m=video", base::CompareCase::SENSITIVE))
  20. has_video_ = true;
  21. }
  22. }
  23. SdpMessage::~SdpMessage() = default;
  24. std::string SdpMessage::ToString() const {
  25. return base::JoinString(sdp_lines_, "\r\n") + "\r\n";
  26. }
  27. std::string SdpMessage::NormalizedForSignature() const {
  28. return base::JoinString(sdp_lines_, "\n") + "\n";
  29. }
  30. bool SdpMessage::AddCodecParameter(const std::string& codec,
  31. const std::string& parameters_to_add) {
  32. std::vector<std::pair<int, std::string>> payload_types = FindCodec(codec);
  33. if (payload_types.empty()) {
  34. return false;
  35. }
  36. for (size_t i = 0; i < payload_types.size(); i++) {
  37. sdp_lines_.insert(
  38. sdp_lines_.begin() + payload_types[i].first + i + 1,
  39. "a=fmtp:" + payload_types[i].second + ' ' + parameters_to_add);
  40. }
  41. return true;
  42. }
  43. bool SdpMessage::PreferVideoCodec(const std::string& codec) {
  44. if (!has_video_) {
  45. return false;
  46. }
  47. std::vector<std::pair<int, std::string>> payload_types = FindCodec(codec);
  48. if (payload_types.empty()) {
  49. return false;
  50. }
  51. for (size_t i = 0; i < sdp_lines_.size(); i++) {
  52. if (!base::StartsWith(sdp_lines_[i],
  53. "m=video",
  54. base::CompareCase::SENSITIVE)) {
  55. continue;
  56. }
  57. // A valid SDP contains only one "m=video" line. So instead of continue, if
  58. // this line is invalid, we should return false immediately.
  59. std::vector<base::StringPiece> fields = base::SplitStringPiece(
  60. sdp_lines_[i], " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  61. // The first three fields are "m=video", port and proto.
  62. static constexpr int kSkipFields = 3;
  63. if (fields.size() <= kSkipFields) {
  64. return false;
  65. }
  66. const auto first_codec_pos = fields.begin() + kSkipFields;
  67. for (const auto& payload : payload_types) {
  68. auto pos = std::find(first_codec_pos,
  69. fields.end(),
  70. base::StringPiece(payload.second));
  71. // The codec has not been found in codec list.
  72. if (pos == fields.end()) {
  73. continue;
  74. }
  75. std::rotate(first_codec_pos, pos, pos + 1);
  76. }
  77. sdp_lines_[i] = base::JoinString(fields, " ");
  78. return true;
  79. }
  80. // If has_video_ is true (tested at the very beginning of the function), we
  81. // should always return within the for-loop above.
  82. NOTREACHED();
  83. return false;
  84. }
  85. std::vector<std::pair<int, std::string>> SdpMessage::FindCodec(
  86. const std::string& codec) const {
  87. const std::string kRtpMapPrefix = "a=rtpmap:";
  88. std::vector<std::pair<int, std::string>> results;
  89. for (size_t i = 0; i < sdp_lines_.size(); ++i) {
  90. const auto& line = sdp_lines_[i];
  91. if (!base::StartsWith(line, kRtpMapPrefix, base::CompareCase::SENSITIVE)) {
  92. continue;
  93. }
  94. size_t space_pos = line.find(' ');
  95. if (space_pos == std::string::npos) {
  96. continue;
  97. }
  98. if (line.substr(space_pos + 1, codec.size()) == codec &&
  99. line[space_pos + 1 + codec.size()] == '/') {
  100. std::string payload_type =
  101. line.substr(kRtpMapPrefix.size(), space_pos - kRtpMapPrefix.size());
  102. results.push_back(std::make_pair(i, std::move(payload_type)));
  103. }
  104. }
  105. return results;
  106. }
  107. } // namespace protocol
  108. } // namespace remoting