Add read support of RtpStreamId/RepairedRtpStreamId header extensions.
BUG=webrtc:7433
Review-Url: https://codereview.webrtc.org/2805023002
Cr-Commit-Position: refs/heads/master@{#17759}
diff --git a/webrtc/common_types.cc b/webrtc/common_types.cc
index 17bb265..b9c4737 100644
--- a/webrtc/common_types.cc
+++ b/webrtc/common_types.cc
@@ -10,8 +10,9 @@
#include "webrtc/common_types.h"
-#include <limits>
#include <string.h>
+#include <limits>
+#include <type_traits>
#include "webrtc/base/checks.h"
#include "webrtc/base/stringutils.h"
@@ -20,6 +21,20 @@
StreamDataCounters::StreamDataCounters() : first_packet_time_ms(-1) {}
+constexpr size_t StreamId::kMaxSize;
+
+void StreamId::Set(const char* data, size_t size) {
+ // If |data| contains \0, the stream id size might become less than |size|.
+ RTC_DCHECK_LE(size, kMaxSize);
+ memcpy(value_, data, size);
+ if (size < kMaxSize)
+ value_[size] = 0;
+}
+
+// StreamId is used as member of RTPHeader that is sometimes copied with memcpy
+// and thus assume trivial destructibility.
+static_assert(std::is_trivially_destructible<StreamId>::value, "");
+
RTPHeaderExtension::RTPHeaderExtension()
: hasTransmissionTimeOffset(false),
transmissionTimeOffset(0),
diff --git a/webrtc/common_types.h b/webrtc/common_types.h
index 7504201..06733be 100644
--- a/webrtc/common_types.h
+++ b/webrtc/common_types.h
@@ -20,6 +20,7 @@
#include "webrtc/api/video/video_content_type.h"
#include "webrtc/api/video/video_rotation.h"
+#include "webrtc/base/array_view.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/optional.h"
#include "webrtc/typedefs.h"
@@ -695,6 +696,42 @@
int max_ms;
};
+// Class to represent RtpStreamId which is a string.
+// Unlike std::string, it can be copied with memcpy and cleared with memset.
+// Empty value represent unset RtpStreamId.
+class StreamId {
+ public:
+ // Stream id is 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;
+
+ StreamId() { value_[0] = 0; }
+ explicit StreamId(rtc::ArrayView<const char> value) {
+ Set(value.data(), value.size());
+ }
+ StreamId(const StreamId&) = default;
+ StreamId& operator=(const StreamId&) = default;
+
+ bool empty() const { return value_[0] == 0; }
+ const char* data() const { return value_; }
+ size_t size() const { return strnlen(value_, kMaxSize); }
+
+ void Set(rtc::ArrayView<const uint8_t> value) {
+ Set(reinterpret_cast<const char*>(value.data()), value.size());
+ }
+ void Set(const char* data, size_t size);
+
+ friend bool operator==(const StreamId& lhs, const StreamId& rhs) {
+ return strncmp(lhs.value_, rhs.value_, kMaxSize) == 0;
+ }
+ friend bool operator!=(const StreamId& lhs, const StreamId& rhs) {
+ return !(lhs == rhs);
+ }
+
+ private:
+ char value_[kMaxSize];
+};
+
struct RTPHeaderExtension {
RTPHeaderExtension();
@@ -723,6 +760,12 @@
VideoContentType videoContentType;
PlayoutDelay playout_delay = {-1, -1};
+
+ // For identification of a stream when ssrc is not signaled. See
+ // https://tools.ietf.org/html/draft-ietf-avtext-rid-09
+ // TODO(danilchap): Update url from draft to release version.
+ StreamId stream_id;
+ StreamId repaired_stream_id;
};
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 56aa9bd..11bc943 100644
--- a/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h
+++ b/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h
@@ -77,6 +77,8 @@
kRtpExtensionTransportSequenceNumber,
kRtpExtensionPlayoutDelay,
kRtpExtensionVideoContentType,
+ kRtpExtensionRtpStreamId,
+ kRtpExtensionRepairedRtpStreamId,
kRtpExtensionNumberOfExtensions // Must be the last entity in the enum.
};
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc b/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc
index 1d39259..26762b0 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc
@@ -40,6 +40,8 @@
CreateExtensionInfo<TransportSequenceNumber>(),
CreateExtensionInfo<PlayoutDelayLimits>(),
CreateExtensionInfo<VideoContentTypeExtension>(),
+ CreateExtensionInfo<RtpStreamId>(),
+ CreateExtensionInfo<RepairedRtpStreamId>(),
};
// 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 8141f02..13f488b 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
@@ -244,4 +244,44 @@
return true;
}
+// RtpStreamId.
+constexpr RTPExtensionType RtpStreamId::kId;
+constexpr uint8_t RtpStreamId::kValueSizeBytes;
+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::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;
+}
+
+// RepairedRtpStreamId.
+constexpr RTPExtensionType RepairedRtpStreamId::kId;
+constexpr uint8_t RepairedRtpStreamId::kValueSizeBytes;
+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);
+}
+
+bool RepairedRtpStreamId::Parse(rtc::ArrayView<const uint8_t> data,
+ std::string* rsid) {
+ return RtpStreamId::Parse(data, rsid);
+}
+
} // 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 0d30848..c60f290 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
@@ -11,6 +11,7 @@
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_HEADER_EXTENSIONS_H_
#include <stdint.h>
+#include <string>
#include "webrtc/api/video/video_content_type.h"
#include "webrtc/api/video/video_rotation.h"
@@ -111,5 +112,31 @@
static bool Write(uint8_t* data, VideoContentType content_type);
};
+class RtpStreamId {
+ public:
+ static constexpr RTPExtensionType kId = kRtpExtensionRtpStreamId;
+ // TODO(danilchap): Implement write support of dynamic size extension that
+ // allows to remove the ValueSize constant.
+ static constexpr uint8_t kValueSizeBytes = 1;
+ static constexpr const char* kUri =
+ "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id";
+
+ static bool Parse(rtc::ArrayView<const uint8_t> data, StreamId* rid);
+ static bool Parse(rtc::ArrayView<const uint8_t> data, std::string* rid);
+};
+
+class RepairedRtpStreamId {
+ public:
+ static constexpr RTPExtensionType kId = kRtpExtensionRepairedRtpStreamId;
+ // TODO(danilchap): Implement write support of dynamic size extension that
+ // allows to remove the ValueSize constant.
+ static constexpr uint8_t kValueSizeBytes = 1;
+ 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* rid);
+ static bool Parse(rtc::ArrayView<const uint8_t> data, std::string* rid);
+};
+
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_HEADER_EXTENSIONS_H_
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_packet.cc b/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
index 2e87528..19e8984 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
@@ -172,6 +172,8 @@
header->extension.hasVideoContentType =
GetExtension<VideoContentTypeExtension>(
&header->extension.videoContentType);
+ GetExtension<RtpStreamId>(&header->extension.stream_id);
+ GetExtension<RepairedRtpStreamId>(&header->extension.repaired_stream_id);
}
size_t Packet::headers_size() const {
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc
index b85d98a..bbe4080 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc
@@ -364,6 +364,45 @@
EXPECT_EQ(time_offset, kTimeOffset);
}
+TEST(RtpPacketTest, ParseDynamicSizeExtension) {
+ // clang-format off
+ const uint8_t kPacket1[] = {
+ 0x90, kPayloadType, 0x00, kSeqNum,
+ 0x65, 0x43, 0x12, 0x78, // Timestamp.
+ 0x12, 0x34, 0x56, 0x78, // Ssrc.
+ 0xbe, 0xde, 0x00, 0x02, // Extensions block of size 2x32bit words.
+ 0x21, 'H', 'D', // Extension with id = 2, size = (1+1).
+ 0x12, 'r', 't', 'x', // Extension with id = 1, size = (2+1).
+ 0x00}; // Extension padding.
+ const uint8_t kPacket2[] = {
+ 0x90, kPayloadType, 0x00, kSeqNum,
+ 0x65, 0x43, 0x12, 0x78, // Timestamp.
+ 0x12, 0x34, 0x56, 0x79, // Ssrc.
+ 0xbe, 0xde, 0x00, 0x01, // Extensions block of size 1x32bit words.
+ 0x11, 'H', 'D', // Extension with id = 1, size = (1+1).
+ 0x00}; // Extension padding.
+ // clang-format on
+ RtpPacketReceived::ExtensionManager extensions;
+ extensions.Register<RtpStreamId>(1);
+ extensions.Register<RepairedRtpStreamId>(2);
+ RtpPacketReceived packet(&extensions);
+ ASSERT_TRUE(packet.Parse(kPacket1, sizeof(kPacket1)));
+
+ std::string rsid;
+ EXPECT_TRUE(packet.GetExtension<RtpStreamId>(&rsid));
+ EXPECT_EQ(rsid, "rtx");
+
+ std::string repaired_rsid;
+ EXPECT_TRUE(packet.GetExtension<RepairedRtpStreamId>(&repaired_rsid));
+ EXPECT_EQ(repaired_rsid, "HD");
+
+ // Parse another packet with RtpStreamId extension of different size.
+ ASSERT_TRUE(packet.Parse(kPacket2, sizeof(kPacket2)));
+ EXPECT_TRUE(packet.GetExtension<RtpStreamId>(&rsid));
+ EXPECT_EQ(rsid, "HD");
+ EXPECT_FALSE(packet.GetExtension<RepairedRtpStreamId>(&repaired_rsid));
+}
+
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 1c12c89..d003c0b 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
@@ -469,6 +469,15 @@
}
break;
}
+ case kRtpExtensionRtpStreamId: {
+ header->extension.stream_id.Set(rtc::MakeArrayView(ptr, len + 1));
+ break;
+ }
+ case kRtpExtensionRepairedRtpStreamId: {
+ header->extension.repaired_stream_id.Set(
+ rtc::MakeArrayView(ptr, len + 1));
+ break;
+ }
case kRtpExtensionNone:
case kRtpExtensionNumberOfExtensions: {
RTC_NOTREACHED() << "Invalid extension type: " << type;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc
index 80f22fc..dcaa242 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc
@@ -148,21 +148,23 @@
EXPECT_EQ(sizeof(kPacket), header.headerLength);
}
-TEST(RtpHeaderParser, ParseAll6Extensions) {
+TEST(RtpHeaderParser, ParseAll8Extensions) {
const uint8_t kAudioLevel = 0x5a;
// clang-format off
const uint8_t kPacket[] = {
0x90, kPayloadType, 0x00, kSeqNum,
0x65, 0x43, 0x12, 0x78, // kTimestamp.
0x12, 0x34, 0x56, 0x78, // kSsrc.
- 0xbe, 0xde, 0x00, 0x05, // Extension of size 5x32bit word.
+ 0xbe, 0xde, 0x00, 0x08, // Extension of size 8x32bit words.
0x40, 0x80|kAudioLevel, // AudioLevel.
0x22, 0x01, 0x56, 0xce, // TransmissionOffset.
0x62, 0x12, 0x34, 0x56, // AbsoluteSendTime.
0x81, 0xce, 0xab, // TransportSequenceNumber.
0xa0, 0x03, // VideoRotation.
0xb2, 0x12, 0x48, 0x76, // PlayoutDelayLimits.
- 0x00, // Padding to 32bit boundary.
+ 0xc2, 'r', 't', 'x', // RtpStreamId
+ 0xd5, 's', 't', 'r', 'e', 'a', 'm', // RepairedRtpStreamId
+ 0x00, 0x00, // Padding to 32bit boundary.
};
// clang-format on
ASSERT_EQ(sizeof(kPacket) % 4, 0u);
@@ -174,6 +176,8 @@
extensions.Register<TransportSequenceNumber>(8);
extensions.Register<VideoOrientation>(0xa);
extensions.Register<PlayoutDelayLimits>(0xb);
+ extensions.Register<RtpStreamId>(0xc);
+ extensions.Register<RepairedRtpStreamId>(0xd);
RtpUtility::RtpHeaderParser parser(kPacket, sizeof(kPacket));
RTPHeader header;
@@ -199,6 +203,33 @@
header.extension.playout_delay.min_ms);
EXPECT_EQ(0x876 * PlayoutDelayLimits::kGranularityMs,
header.extension.playout_delay.max_ms);
+ EXPECT_EQ(header.extension.stream_id, StreamId("rtx"));
+ EXPECT_EQ(header.extension.repaired_stream_id, StreamId("stream"));
+}
+
+TEST(RtpHeaderParser, ParseMalformedRsidExtensions) {
+ // clang-format off
+ const uint8_t kPacket[] = {
+ 0x90, kPayloadType, 0x00, kSeqNum,
+ 0x65, 0x43, 0x12, 0x78, // kTimestamp.
+ 0x12, 0x34, 0x56, 0x78, // kSsrc.
+ 0xbe, 0xde, 0x00, 0x03, // Extension of size 3x32bit words.
+ 0xc2, '\0', 't', 'x', // empty RtpStreamId
+ 0xd5, 's', 't', 'r', '\0', 'a', 'm', // RepairedRtpStreamId
+ 0x00, // Padding to 32bit boundary.
+ };
+ // clang-format on
+ ASSERT_EQ(sizeof(kPacket) % 4, 0u);
+
+ RtpHeaderExtensionMap extensions;
+ extensions.Register<RtpStreamId>(0xc);
+ extensions.Register<RepairedRtpStreamId>(0xd);
+ RtpUtility::RtpHeaderParser parser(kPacket, sizeof(kPacket));
+ RTPHeader header;
+
+ EXPECT_TRUE(parser.Parse(&header, &extensions));
+ EXPECT_TRUE(header.extension.stream_id.empty());
+ EXPECT_EQ(header.extension.repaired_stream_id, StreamId("str"));
}
TEST(RtpHeaderParser, ParseWithCsrcsExtensionAndPadding) {
diff --git a/webrtc/test/fuzzers/rtp_header_fuzzer.cc b/webrtc/test/fuzzers/rtp_header_fuzzer.cc
index 914a8fb..9e111a8 100644
--- a/webrtc/test/fuzzers/rtp_header_fuzzer.cc
+++ b/webrtc/test/fuzzers/rtp_header_fuzzer.cc
@@ -15,30 +15,31 @@
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
namespace webrtc {
-// We decide which header extensions to register by reading one byte
+// We decide which header extensions to register by reading two bytes
// from the beginning of |data| and interpreting it as a bitmask over
-// the RTPExtensionType enum. This assert ensures one byte is enough.
-static_assert(kRtpExtensionNumberOfExtensions <= 8,
+// the RTPExtensionType enum. This assert ensures two bytes are enough.
+static_assert(kRtpExtensionNumberOfExtensions <= 16,
"Insufficient bits read to configure all header extensions. Add "
"an extra byte and update the switches.");
void FuzzOneInput(const uint8_t* data, size_t size) {
- if (size <= 1)
+ if (size <= 2)
return;
// Don't use the configuration byte as part of the packet.
- std::bitset<8> extensionMask(data[0]);
- data++;
- size--;
+ std::bitset<16> extensionMask(*reinterpret_cast<const uint16_t*>(data));
+ data += 2;
+ size -= 2;
RtpPacketReceived::ExtensionManager extensions;
+ // Skip i = 0 since it maps to ExtensionNone and extension id = 0 is invalid.
for (int i = 0; i < kRtpExtensionNumberOfExtensions; i++) {
RTPExtensionType extension_type = static_cast<RTPExtensionType>(i);
if (extensionMask[i] && extension_type != kRtpExtensionNone) {
// Extensions are registered with an ID, which you signal to the
// peer so they know what to expect. This code only cares about
// parsing so the value of the ID isn't relevant; we use i.
- extensions.Register(extension_type, i);
+ extensions.RegisterByType(i, extension_type);
}
}
diff --git a/webrtc/test/fuzzers/rtp_packet_fuzzer.cc b/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
index 7cf65cf..64ab6ec 100644
--- a/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
+++ b/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
@@ -14,30 +14,31 @@
namespace webrtc {
-// We decide which header extensions to register by reading one byte
+// We decide which header extensions to register by reading two bytes
// from the beginning of |data| and interpreting it as a bitmask over
-// the RTPExtensionType enum. This assert ensures one byte is enough.
-static_assert(kRtpExtensionNumberOfExtensions <= 8,
+// the RTPExtensionType enum. This assert ensures two bytes are enough.
+static_assert(kRtpExtensionNumberOfExtensions <= 16,
"Insufficient bits read to configure all header extensions. Add "
"an extra byte and update the switches.");
void FuzzOneInput(const uint8_t* data, size_t size) {
- if (size <= 1)
+ if (size <= 2)
return;
- // Don't use the configuration byte as part of the packet.
- std::bitset<8> extensionMask(data[0]);
- data++;
- size--;
+ // Don't use the configuration bytes as part of the packet.
+ std::bitset<16> extensionMask(*reinterpret_cast<const uint16_t*>(data));
+ data += 2;
+ size -= 2;
RtpPacketReceived::ExtensionManager extensions;
- for (int i = 0; i < kRtpExtensionNumberOfExtensions; i++) {
+ // Skip i = 0 since it maps to ExtensionNone and extension id = 0 is invalid.
+ for (int i = 1; i < kRtpExtensionNumberOfExtensions; i++) {
RTPExtensionType extension_type = static_cast<RTPExtensionType>(i);
if (extensionMask[i] && extension_type != kRtpExtensionNone) {
// Extensions are registered with an ID, which you signal to the
// peer so they know what to expect. This code only cares about
// parsing so the value of the ID isn't relevant; we use i.
- extensions.Register(extension_type, i);
+ extensions.RegisterByType(i, extension_type);
}
}
@@ -89,6 +90,16 @@
VideoContentType content_type;
packet.GetExtension<VideoContentTypeExtension>(&content_type);
break;
+ case kRtpExtensionRtpStreamId: {
+ std::string rsid;
+ packet.GetExtension<RtpStreamId>(&rsid);
+ break;
+ }
+ case kRtpExtensionRepairedRtpStreamId: {
+ std::string rsid;
+ packet.GetExtension<RepairedRtpStreamId>(&rsid);
+ break;
+ }
}
}
}