Add parsing/serializing for MID RTP header extension.
This is the first in a series of CLs to add support for media
identification as part of unified plan SDP.
Bug: webrtc:4050
Change-Id: I0eb5639d240a9a1412c2b047a33d5112e4901f26
Reviewed-on: https://chromium-review.googlesource.com/576374
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Tommi <tommi@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#19111}diff --git a/webrtc/common_types.h b/webrtc/common_types.h
index 03d8ac92..43ae642 100644
--- a/webrtc/common_types.h
+++ b/webrtc/common_types.h
@@ -715,23 +715,26 @@
int max_ms;
};
-// Class to represent RtpStreamId which is a string.
+// Class to represent the value of RTP header extensions that are
+// variable-length strings (e.g., RtpStreamId and RtpMid).
// Unlike std::string, it can be copied with memcpy and cleared with memset.
-// Empty value represent unset RtpStreamId.
-class StreamId {
+//
+// Empty value represents unset header extension (use empty() to query).
+class StringRtpHeaderExtension {
public:
- // Stream id is limited to 16 bytes because it is the maximum length
- // that can be encoded with one-byte header extensions.
+ // String RTP header extensions are limited to 16 bytes because it is the
+ // maximum length that can be encoded with one-byte header extensions.
static constexpr size_t kMaxSize = 16;
static bool IsLegalName(rtc::ArrayView<const char> name);
- StreamId() { value_[0] = 0; }
- explicit StreamId(rtc::ArrayView<const char> value) {
+ StringRtpHeaderExtension() { value_[0] = 0; }
+ explicit StringRtpHeaderExtension(rtc::ArrayView<const char> value) {
Set(value.data(), value.size());
}
- StreamId(const StreamId&) = default;
- StreamId& operator=(const StreamId&) = default;
+ StringRtpHeaderExtension(const StringRtpHeaderExtension&) = default;
+ StringRtpHeaderExtension& operator=(const StringRtpHeaderExtension&) =
+ default;
bool empty() const { return value_[0] == 0; }
const char* data() const { return value_; }
@@ -742,10 +745,12 @@
}
void Set(const char* data, size_t size);
- friend bool operator==(const StreamId& lhs, const StreamId& rhs) {
+ friend bool operator==(const StringRtpHeaderExtension& lhs,
+ const StringRtpHeaderExtension& rhs) {
return strncmp(lhs.value_, rhs.value_, kMaxSize) == 0;
}
- friend bool operator!=(const StreamId& lhs, const StreamId& rhs) {
+ friend bool operator!=(const StringRtpHeaderExtension& lhs,
+ const StringRtpHeaderExtension& rhs) {
return !(lhs == rhs);
}
@@ -753,6 +758,12 @@
char value_[kMaxSize];
};
+// StreamId represents RtpStreamId which is a string.
+typedef StringRtpHeaderExtension StreamId;
+
+// Mid represents RtpMid which is a string.
+typedef StringRtpHeaderExtension Mid;
+
struct RTPHeaderExtension {
RTPHeaderExtension();
@@ -790,6 +801,10 @@
// TODO(danilchap): Update url from draft to release version.
StreamId stream_id;
StreamId repaired_stream_id;
+
+ // For identifying the media section used to interpret this RTP packet. See
+ // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-38
+ Mid mid;
};
struct RTPHeader {
diff --git a/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h b/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h
index 3bf93d8..185f6ba 100644
--- a/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h
+++ b/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h
@@ -80,6 +80,7 @@
kRtpExtensionVideoTiming,
kRtpExtensionRtpStreamId,
kRtpExtensionRepairedRtpStreamId,
+ kRtpExtensionMid,
kRtpExtensionNumberOfExtensions // Must be the last entity in the enum.
};
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extension_map.cc b/webrtc/modules/rtp_rtcp/source/rtp_header_extension_map.cc
index 18ee7e0..d65b11d 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_header_extension_map.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extension_map.cc
@@ -42,6 +42,7 @@
CreateExtensionInfo<VideoTimingExtension>(),
CreateExtensionInfo<RtpStreamId>(),
CreateExtensionInfo<RepairedRtpStreamId>(),
+ CreateExtensionInfo<RtpMid>(),
};
// Because of kRtpExtensionNone, NumberOfExtension is 1 bigger than the actual
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
index 3a4afef..8b40d04 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
@@ -312,72 +312,51 @@
return true;
}
-// RtpStreamId.
+bool BaseRtpStringExtension::Parse(rtc::ArrayView<const uint8_t> data,
+ StringRtpHeaderExtension* str) {
+ if (data.empty() || data[0] == 0) // Valid string extension can't be empty.
+ return false;
+ str->Set(data);
+ RTC_DCHECK(!str->empty());
+ return true;
+}
+
+bool BaseRtpStringExtension::Write(uint8_t* data,
+ const StringRtpHeaderExtension& str) {
+ RTC_DCHECK_GE(str.size(), 1);
+ RTC_DCHECK_LE(str.size(), StringRtpHeaderExtension::kMaxSize);
+ memcpy(data, str.data(), str.size());
+ return true;
+}
+
+bool BaseRtpStringExtension::Parse(rtc::ArrayView<const uint8_t> data,
+ std::string* str) {
+ if (data.empty() || data[0] == 0) // Valid string extension can't be empty.
+ return false;
+ const char* cstr = reinterpret_cast<const char*>(data.data());
+ // If there is a \0 character in the middle of the |data|, treat it as end
+ // of the string. Well-formed string extensions shouldn't contain it.
+ str->assign(cstr, strnlen(cstr, data.size()));
+ RTC_DCHECK(!str->empty());
+ return true;
+}
+
+bool BaseRtpStringExtension::Write(uint8_t* data, const std::string& str) {
+ RTC_DCHECK_GE(str.size(), 1);
+ RTC_DCHECK_LE(str.size(), StringRtpHeaderExtension::kMaxSize);
+ memcpy(data, str.data(), str.size());
+ return true;
+}
+
+// Constant declarations for string RTP header extension types.
+
constexpr RTPExtensionType RtpStreamId::kId;
constexpr const char* RtpStreamId::kUri;
-bool RtpStreamId::Parse(rtc::ArrayView<const uint8_t> data, StreamId* rsid) {
- if (data.empty() || data[0] == 0) // Valid rsid can't be empty.
- return false;
- rsid->Set(data);
- RTC_DCHECK(!rsid->empty());
- return true;
-}
-
-bool RtpStreamId::Write(uint8_t* data, const StreamId& rsid) {
- RTC_DCHECK_GE(rsid.size(), 1);
- RTC_DCHECK_LE(rsid.size(), StreamId::kMaxSize);
- memcpy(data, rsid.data(), rsid.size());
- return true;
-}
-
-bool RtpStreamId::Parse(rtc::ArrayView<const uint8_t> data, std::string* rsid) {
- if (data.empty() || data[0] == 0) // Valid rsid can't be empty.
- return false;
- const char* str = reinterpret_cast<const char*>(data.data());
- // If there is a \0 character in the middle of the |data|, treat it as end of
- // the string. Well-formed rsid shouldn't contain it.
- rsid->assign(str, strnlen(str, data.size()));
- RTC_DCHECK(!rsid->empty());
- return true;
-}
-
-bool RtpStreamId::Write(uint8_t* data, const std::string& rsid) {
- RTC_DCHECK_GE(rsid.size(), 1);
- RTC_DCHECK_LE(rsid.size(), StreamId::kMaxSize);
- memcpy(data, rsid.data(), rsid.size());
- return true;
-}
-
-// RepairedRtpStreamId.
constexpr RTPExtensionType RepairedRtpStreamId::kId;
constexpr const char* RepairedRtpStreamId::kUri;
-// RtpStreamId and RepairedRtpStreamId use the same format to store rsid.
-bool RepairedRtpStreamId::Parse(rtc::ArrayView<const uint8_t> data,
- StreamId* rsid) {
- return RtpStreamId::Parse(data, rsid);
-}
-
-size_t RepairedRtpStreamId::ValueSize(const StreamId& rsid) {
- return RtpStreamId::ValueSize(rsid);
-}
-
-bool RepairedRtpStreamId::Write(uint8_t* data, const StreamId& rsid) {
- return RtpStreamId::Write(data, rsid);
-}
-
-bool RepairedRtpStreamId::Parse(rtc::ArrayView<const uint8_t> data,
- std::string* rsid) {
- return RtpStreamId::Parse(data, rsid);
-}
-
-size_t RepairedRtpStreamId::ValueSize(const std::string& rsid) {
- return RtpStreamId::ValueSize(rsid);
-}
-
-bool RepairedRtpStreamId::Write(uint8_t* data, const std::string& rsid) {
- return RtpStreamId::Write(data, rsid);
-}
+constexpr RTPExtensionType RtpMid::kId;
+constexpr const char* RtpMid::kUri;
} // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
index 31d49b2..8471cfb 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
@@ -146,34 +146,40 @@
static bool Write(uint8_t* data, uint16_t time_delta_ms, uint8_t idx);
};
-class RtpStreamId {
+// Base extension class for RTP header extensions which are strings.
+// Subclasses must defined kId and kUri static constexpr members.
+class BaseRtpStringExtension {
+ public:
+ static bool Parse(rtc::ArrayView<const uint8_t> data,
+ StringRtpHeaderExtension* str);
+ static size_t ValueSize(const StringRtpHeaderExtension& str) {
+ return str.size();
+ }
+ static bool Write(uint8_t* data, const StringRtpHeaderExtension& str);
+
+ static bool Parse(rtc::ArrayView<const uint8_t> data, std::string* str);
+ static size_t ValueSize(const std::string& str) { return str.size(); }
+ static bool Write(uint8_t* data, const std::string& str);
+};
+
+class RtpStreamId : public BaseRtpStringExtension {
public:
static constexpr RTPExtensionType kId = kRtpExtensionRtpStreamId;
static constexpr const char* kUri =
"urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id";
-
- static bool Parse(rtc::ArrayView<const uint8_t> data, StreamId* rsid);
- static size_t ValueSize(const StreamId& rsid) { return rsid.size(); }
- static bool Write(uint8_t* data, const StreamId& rsid);
-
- static bool Parse(rtc::ArrayView<const uint8_t> data, std::string* rsid);
- static size_t ValueSize(const std::string& rsid) { return rsid.size(); }
- static bool Write(uint8_t* data, const std::string& rsid);
};
-class RepairedRtpStreamId {
+class RepairedRtpStreamId : public BaseRtpStringExtension {
public:
static constexpr RTPExtensionType kId = kRtpExtensionRepairedRtpStreamId;
static constexpr const char* kUri =
"urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id";
+};
- static bool Parse(rtc::ArrayView<const uint8_t> data, StreamId* rsid);
- static size_t ValueSize(const StreamId& rsid);
- static bool Write(uint8_t* data, const StreamId& rsid);
-
- static bool Parse(rtc::ArrayView<const uint8_t> data, std::string* rsid);
- static size_t ValueSize(const std::string& rsid);
- static bool Write(uint8_t* data, const std::string& rsid);
+class RtpMid : public BaseRtpStringExtension {
+ public:
+ static constexpr RTPExtensionType kId = kRtpExtensionMid;
+ static constexpr const char* kUri = "urn:ietf:params:rtp-hdrext:sdes:mid";
};
} // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_packet.cc b/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
index 78fa0d2..2c9d0aea 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
@@ -178,6 +178,7 @@
GetExtension<VideoTimingExtension>(&header->extension.video_timing);
GetExtension<RtpStreamId>(&header->extension.stream_id);
GetExtension<RepairedRtpStreamId>(&header->extension.repaired_stream_id);
+ GetExtension<RtpMid>(&header->extension.mid);
GetExtension<PlayoutDelayLimits>(&header->extension.playout_delay);
}
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc
index 10c94f5..1075d9e 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc
@@ -31,10 +31,12 @@
constexpr uint8_t kTransmissionOffsetExtensionId = 1;
constexpr uint8_t kAudioLevelExtensionId = 9;
constexpr uint8_t kRtpStreamIdExtensionId = 0xa;
+constexpr uint8_t kRtpMidExtensionId = 0xb;
constexpr int32_t kTimeOffset = 0x56ce;
constexpr bool kVoiceActive = true;
constexpr uint8_t kAudioLevel = 0x5a;
constexpr char kStreamId[] = "streamid";
+constexpr char kMid[] = "mid";
constexpr size_t kMaxPaddingSize = 224u;
// clang-format off
constexpr uint8_t kMinimumPacket[] = {
@@ -66,6 +68,13 @@
'e', 'a', 'm', 'i',
'd' , 0x00, 0x00, 0x00};
+constexpr uint8_t kPacketWithMid[] = {
+ 0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
+ 0x65, 0x43, 0x12, 0x78,
+ 0x12, 0x34, 0x56, 0x78,
+ 0xbe, 0xde, 0x00, 0x01,
+ 0xb2, 'm', 'i', 'd'};
+
constexpr uint32_t kCsrcs[] = {0x34567890, 0x32435465};
constexpr uint8_t kPayload[] = {'p', 'a', 'y', 'l', 'o', 'a', 'd'};
constexpr uint8_t kPacketPaddingSize = 8;
@@ -159,6 +168,22 @@
EXPECT_FALSE(packet.SetExtension<RtpStreamId>(kLongStreamId));
}
+TEST(RtpPacketTest, TryToCreateWithEmptyMid) {
+ RtpPacketToSend::ExtensionManager extensions;
+ extensions.Register<RtpMid>(kRtpMidExtensionId);
+ RtpPacketToSend packet(&extensions);
+ EXPECT_FALSE(packet.SetExtension<RtpMid>(""));
+}
+
+TEST(RtpPacketTest, TryToCreateWithLongMid) {
+ RtpPacketToSend::ExtensionManager extensions;
+ constexpr char kLongMid[] = "LoooooooooonogMid";
+ ASSERT_EQ(strlen(kLongMid), 17u);
+ extensions.Register<RtpMid>(kRtpMidExtensionId);
+ RtpPacketToSend packet(&extensions);
+ EXPECT_FALSE(packet.SetExtension<RtpMid>(kLongMid));
+}
+
TEST(RtpPacketTest, CreateWithExtensionsWithoutManager) {
RtpPacketToSend packet(nullptr);
packet.SetPayloadType(kPayloadType);
@@ -445,6 +470,17 @@
EXPECT_FALSE(packet.GetExtension<RepairedRtpStreamId>(&repaired_rsid));
}
+TEST(RtpPacketTest, ParseWithMid) {
+ RtpPacketReceived::ExtensionManager extensions;
+ extensions.Register<RtpMid>(kRtpMidExtensionId);
+ RtpPacketReceived packet(&extensions);
+ ASSERT_TRUE(packet.Parse(kPacketWithMid, sizeof(kPacketWithMid)));
+
+ std::string mid;
+ EXPECT_TRUE(packet.GetExtension<RtpMid>(&mid));
+ EXPECT_EQ(mid, kMid);
+}
+
TEST(RtpPacketTest, RawExtensionFunctionsAcceptZeroIdAndReturnFalse) {
RtpPacketReceived::ExtensionManager extensions;
RtpPacketReceived packet(&extensions);
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
index 8a8be10..9696ff4 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
@@ -484,6 +484,10 @@
rtc::MakeArrayView(ptr, len + 1));
break;
}
+ case kRtpExtensionMid: {
+ header->extension.mid.Set(rtc::MakeArrayView(ptr, len + 1));
+ break;
+ }
case kRtpExtensionNone:
case kRtpExtensionNumberOfExtensions: {
RTC_NOTREACHED() << "Invalid extension type: " << type;
diff --git a/webrtc/test/fuzzers/rtp_packet_fuzzer.cc b/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
index fc53216..3e33699 100644
--- a/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
+++ b/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
@@ -104,6 +104,11 @@
packet.GetExtension<RepairedRtpStreamId>(&rsid);
break;
}
+ case kRtpExtensionMid: {
+ std::string mid;
+ packet.GetExtension<RtpMid>(&mid);
+ break;
+ }
}
}
}