Frame marking RTP header extension (PART 1: implement extension)
Bug: webrtc:7765
Change-Id: I23896d121afd6be4bce5ff4deaf736149efebcdb
Reviewed-on: https://webrtc-review.googlesource.com/85200
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24695}
diff --git a/modules/rtp_rtcp/source/rtp_header_extension_map.cc b/modules/rtp_rtcp/source/rtp_header_extension_map.cc
index 9fa66c0..23ccd0e 100644
--- a/modules/rtp_rtcp/source/rtp_header_extension_map.cc
+++ b/modules/rtp_rtcp/source/rtp_header_extension_map.cc
@@ -38,6 +38,7 @@
CreateExtensionInfo<PlayoutDelayLimits>(),
CreateExtensionInfo<VideoContentTypeExtension>(),
CreateExtensionInfo<VideoTimingExtension>(),
+ CreateExtensionInfo<FrameMarkingExtension>(),
CreateExtensionInfo<RtpStreamId>(),
CreateExtensionInfo<RepairedRtpStreamId>(),
CreateExtensionInfo<RtpMid>(),
diff --git a/modules/rtp_rtcp/source/rtp_header_extensions.cc b/modules/rtp_rtcp/source/rtp_header_extensions.cc
index 2dba4d7..e9e4d50 100644
--- a/modules/rtp_rtcp/source/rtp_header_extensions.cc
+++ b/modules/rtp_rtcp/source/rtp_header_extensions.cc
@@ -350,6 +350,86 @@
return true;
}
+// Frame Marking.
+//
+// Meta-information about an RTP stream outside the encrypted media payload,
+// useful for an RTP switch to do codec-agnostic selective forwarding
+// without decrypting the payload.
+//
+// For non-scalable streams:
+// 0 1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | ID | L = 0 |S|E|I|D|0 0 0 0|
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// For scalable streams:
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | ID | L = 2 |S|E|I|D|B| TID | LID | TL0PICIDX |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+constexpr RTPExtensionType FrameMarkingExtension::kId;
+constexpr const char FrameMarkingExtension::kUri[];
+
+bool FrameMarkingExtension::IsScalable(uint8_t temporal_id, uint8_t layer_id) {
+ return temporal_id != kNoTemporalIdx || layer_id != kNoSpatialIdx;
+}
+
+bool FrameMarkingExtension::Parse(rtc::ArrayView<const uint8_t> data,
+ FrameMarking* frame_marking) {
+ RTC_DCHECK(frame_marking);
+
+ if (data.size() != 1 && data.size() != 3)
+ return false;
+
+ frame_marking->start_of_frame = (data[0] & 0x80) != 0;
+ frame_marking->end_of_frame = (data[0] & 0x40) != 0;
+ frame_marking->independent_frame = (data[0] & 0x20) != 0;
+ frame_marking->discardable_frame = (data[0] & 0x10) != 0;
+
+ if (data.size() == 3) {
+ frame_marking->base_layer_sync = (data[0] & 0x08) != 0;
+ frame_marking->temporal_id = data[0] & 0x7;
+ frame_marking->layer_id = data[1];
+ frame_marking->tl0_pic_idx = data[2];
+ } else {
+ // non-scalable
+ frame_marking->base_layer_sync = false;
+ frame_marking->temporal_id = kNoTemporalIdx;
+ frame_marking->layer_id = kNoSpatialIdx;
+ frame_marking->tl0_pic_idx = 0;
+ }
+ return true;
+}
+
+size_t FrameMarkingExtension::ValueSize(const FrameMarking& frame_marking) {
+ if (IsScalable(frame_marking.temporal_id, frame_marking.layer_id))
+ return 3;
+ else
+ return 1;
+}
+
+bool FrameMarkingExtension::Write(rtc::ArrayView<uint8_t> data,
+ const FrameMarking& frame_marking) {
+ RTC_DCHECK_GE(data.size(), 1);
+ RTC_CHECK_LE(frame_marking.temporal_id, 0x07);
+ data[0] = frame_marking.start_of_frame ? 0x80 : 0x00;
+ data[0] |= frame_marking.end_of_frame ? 0x40 : 0x00;
+ data[0] |= frame_marking.independent_frame ? 0x20 : 0x00;
+ data[0] |= frame_marking.discardable_frame ? 0x10 : 0x00;
+
+ if (IsScalable(frame_marking.temporal_id, frame_marking.layer_id)) {
+ RTC_DCHECK_EQ(data.size(), 3);
+ data[0] |= frame_marking.base_layer_sync ? 0x08 : 0x00;
+ data[0] |= frame_marking.temporal_id & 0x07;
+ data[1] = frame_marking.layer_id;
+ data[2] = frame_marking.tl0_pic_idx;
+ }
+ return true;
+}
+
bool BaseRtpStringExtension::Parse(rtc::ArrayView<const uint8_t> data,
StringRtpHeaderExtension* str) {
if (data.empty() || data[0] == 0) // Valid string extension can't be empty.
diff --git a/modules/rtp_rtcp/source/rtp_header_extensions.h b/modules/rtp_rtcp/source/rtp_header_extensions.h
index 4e1afc1..84e9831 100644
--- a/modules/rtp_rtcp/source/rtp_header_extensions.h
+++ b/modules/rtp_rtcp/source/rtp_header_extensions.h
@@ -153,6 +153,22 @@
uint8_t idx);
};
+class FrameMarkingExtension {
+ public:
+ static constexpr RTPExtensionType kId = kRtpExtensionFrameMarking;
+ static constexpr const char kUri[] =
+ "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07";
+
+ static bool Parse(rtc::ArrayView<const uint8_t> data,
+ FrameMarking* frame_marking);
+ static size_t ValueSize(const FrameMarking& frame_marking);
+ static bool Write(rtc::ArrayView<uint8_t> data,
+ const FrameMarking& frame_marking);
+
+ private:
+ static bool IsScalable(uint8_t temporal_id, uint8_t layer_id);
+};
+
// Base extension class for RTP header extensions which are strings.
// Subclasses must defined kId and kUri static constexpr members.
class BaseRtpStringExtension {
diff --git a/modules/rtp_rtcp/source/rtp_packet_received.cc b/modules/rtp_rtcp/source/rtp_packet_received.cc
index 6acc9b5..c8deb99 100644
--- a/modules/rtp_rtcp/source/rtp_packet_received.cc
+++ b/modules/rtp_rtcp/source/rtp_packet_received.cc
@@ -61,6 +61,8 @@
&header->extension.videoContentType);
header->extension.has_video_timing =
GetExtension<VideoTimingExtension>(&header->extension.video_timing);
+ header->extension.has_frame_marking =
+ GetExtension<FrameMarkingExtension>(&header->extension.frame_marking);
GetExtension<RtpStreamId>(&header->extension.stream_id);
GetExtension<RepairedRtpStreamId>(&header->extension.repaired_stream_id);
GetExtension<RtpMid>(&header->extension.mid);
diff --git a/modules/rtp_rtcp/source/rtp_receiver_video.cc b/modules/rtp_rtcp/source/rtp_receiver_video.cc
index 1101bec..3c9b9e5 100644
--- a/modules/rtp_rtcp/source/rtp_receiver_video.cc
+++ b/modules/rtp_rtcp/source/rtp_receiver_video.cc
@@ -74,6 +74,7 @@
rtp_header->video_header().rotation = kVideoRotation_0;
rtp_header->video_header().content_type = VideoContentType::UNSPECIFIED;
rtp_header->video_header().video_timing.flags = VideoSendTiming::kInvalid;
+ rtp_header->video_header().frame_marking.temporal_id = kNoTemporalIdx;
// Retrieve the video rotation information.
if (rtp_header->header.extension.hasVideoRotation) {
@@ -94,6 +95,11 @@
rtp_header->video_header().playout_delay =
rtp_header->header.extension.playout_delay;
+ if (rtp_header->header.extension.has_frame_marking) {
+ rtp_header->video_header().frame_marking =
+ rtp_header->header.extension.frame_marking;
+ }
+
return data_callback_->OnReceivedPayloadData(parsed_payload.payload,
parsed_payload.payload_length,
rtp_header) == 0
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index fe8dbf3..20ae519 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -50,6 +50,8 @@
return kRtpExtensionVideoContentType;
if (extension == RtpExtension::kVideoTimingUri)
return kRtpExtensionVideoTiming;
+ if (extension == RtpExtension::kFrameMarkingUri)
+ return kRtpExtensionFrameMarking;
if (extension == RtpExtension::kMidUri)
return kRtpExtensionMid;
RTC_NOTREACHED() << "Looking up unsupported RTP extension.";
diff --git a/modules/rtp_rtcp/source/rtp_utility.cc b/modules/rtp_rtcp/source/rtp_utility.cc
index d150ce2..5f5b3dc 100644
--- a/modules/rtp_rtcp/source/rtp_utility.cc
+++ b/modules/rtp_rtcp/source/rtp_utility.cc
@@ -236,6 +236,10 @@
header->extension.has_video_timing = false;
header->extension.video_timing = {0u, 0u, 0u, 0u, 0u, 0u, false};
+ header->extension.has_frame_marking = false;
+ header->extension.frame_marking = {false, false, false, false, false,
+ kNoTemporalIdx, 0, 0};
+
if (X) {
/* RTP header extension, RFC 3550.
0 1 2 3
@@ -454,6 +458,15 @@
&header->extension.video_timing);
break;
}
+ case kRtpExtensionFrameMarking: {
+ if (!FrameMarkingExtension::Parse(rtc::MakeArrayView(ptr, len + 1),
+ &header->extension.frame_marking)) {
+ RTC_LOG(LS_WARNING) << "Incorrect frame marking len: " << len;
+ return;
+ }
+ header->extension.has_frame_marking = true;
+ break;
+ }
case kRtpExtensionRtpStreamId: {
header->extension.stream_id.Set(rtc::MakeArrayView(ptr, len + 1));
break;
diff --git a/modules/rtp_rtcp/source/rtp_video_header.h b/modules/rtp_rtcp/source/rtp_video_header.h
index 6c7a150..68b4884 100644
--- a/modules/rtp_rtcp/source/rtp_video_header.h
+++ b/modules/rtp_rtcp/source/rtp_video_header.h
@@ -13,6 +13,7 @@
#include "absl/container/inlined_vector.h"
#include "absl/types/variant.h"
#include "api/video/video_content_type.h"
+#include "api/video/video_frame_marking.h"
#include "api/video/video_rotation.h"
#include "api/video/video_timing.h"
#include "common_types.h" // NOLINT(build/include)
@@ -56,6 +57,7 @@
PlayoutDelay playout_delay;
VideoSendTiming video_timing;
+ FrameMarking frame_marking;
RTPVideoTypeHeader video_type_header;
};