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}
This commit is contained in:
1
AUTHORS
1
AUTHORS
@ -90,6 +90,7 @@ Temasys Communications <*@temasys.io>
|
||||
The Chromium Authors <*@chromium.org>
|
||||
The WebRTC Authors <*@webrtc.org>
|
||||
Videxio AS <*@videxio.com>
|
||||
Vidyo, Inc. <*@vidyo.com>
|
||||
Vonage Holdings Corp. <*@vonage.com>
|
||||
Wire Swiss GmbH <*@wire.com>
|
||||
Miguel Paris <mparisdiaz@gmail.com>
|
||||
|
@ -34,7 +34,9 @@ RTPHeaderExtension::RTPHeaderExtension()
|
||||
videoRotation(kVideoRotation_0),
|
||||
hasVideoContentType(false),
|
||||
videoContentType(VideoContentType::UNSPECIFIED),
|
||||
has_video_timing(false) {}
|
||||
has_video_timing(false),
|
||||
has_frame_marking(false),
|
||||
frame_marking({false, false, false, false, false, 0xFF, 0, 0}) {}
|
||||
|
||||
RTPHeaderExtension::RTPHeaderExtension(const RTPHeaderExtension& other) =
|
||||
default;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.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"
|
||||
|
||||
@ -116,6 +117,9 @@ struct RTPHeaderExtension {
|
||||
bool has_video_timing;
|
||||
VideoSendTiming video_timing;
|
||||
|
||||
bool has_frame_marking;
|
||||
FrameMarking frame_marking;
|
||||
|
||||
PlayoutDelay playout_delay = {-1, -1};
|
||||
|
||||
// For identification of a stream when ssrc is not signaled. See
|
||||
|
@ -129,6 +129,10 @@ const int RtpExtension::kVideoTimingDefaultId = 8;
|
||||
const char RtpExtension::kMidUri[] = "urn:ietf:params:rtp-hdrext:sdes:mid";
|
||||
const int RtpExtension::kMidDefaultId = 9;
|
||||
|
||||
const char RtpExtension::kFrameMarkingUri[] =
|
||||
"http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07";
|
||||
const int RtpExtension::kFrameMarkingDefaultId = 10;
|
||||
|
||||
const char RtpExtension::kEncryptHeaderExtensionsUri[] =
|
||||
"urn:ietf:params:rtp-hdrext:encrypt";
|
||||
|
||||
@ -149,7 +153,8 @@ bool RtpExtension::IsSupportedForVideo(const std::string& uri) {
|
||||
uri == webrtc::RtpExtension::kPlayoutDelayUri ||
|
||||
uri == webrtc::RtpExtension::kVideoContentTypeUri ||
|
||||
uri == webrtc::RtpExtension::kVideoTimingUri ||
|
||||
uri == webrtc::RtpExtension::kMidUri;
|
||||
uri == webrtc::RtpExtension::kMidUri ||
|
||||
uri == webrtc::RtpExtension::kFrameMarkingUri;
|
||||
}
|
||||
|
||||
bool RtpExtension::IsEncryptionSupported(const std::string& uri) {
|
||||
|
@ -282,6 +282,10 @@ struct RtpExtension {
|
||||
static const char kVideoTimingUri[];
|
||||
static const int kVideoTimingDefaultId;
|
||||
|
||||
// Header extension for video frame marking.
|
||||
static const char kFrameMarkingUri[];
|
||||
static const int kFrameMarkingDefaultId;
|
||||
|
||||
// Header extension for transport sequence number, see url for details:
|
||||
// http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions
|
||||
static const char kTransportSequenceNumberUri[];
|
||||
|
@ -19,6 +19,7 @@ rtc_source_set("video_frame") {
|
||||
"video_frame.h",
|
||||
"video_frame_buffer.cc",
|
||||
"video_frame_buffer.h",
|
||||
"video_frame_marking.h",
|
||||
"video_rotation.h",
|
||||
"video_sink_interface.h",
|
||||
"video_source_interface.cc",
|
||||
|
29
api/video/video_frame_marking.h
Normal file
29
api/video/video_frame_marking.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_VIDEO_VIDEO_FRAME_MARKING_H_
|
||||
#define API_VIDEO_VIDEO_FRAME_MARKING_H_
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
struct FrameMarking {
|
||||
bool start_of_frame;
|
||||
bool end_of_frame;
|
||||
bool independent_frame;
|
||||
bool discardable_frame;
|
||||
bool base_layer_sync;
|
||||
uint8_t temporal_id;
|
||||
uint8_t layer_id;
|
||||
uint8_t tl0_pic_idx;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_VIDEO_VIDEO_FRAME_MARKING_H_
|
@ -157,6 +157,8 @@ webrtc::RtpHeaderExtensionMap GetDefaultHeaderExtensionMap() {
|
||||
webrtc::RtpExtension::kVideoContentTypeDefaultId);
|
||||
default_map.Register<webrtc::VideoTimingExtension>(
|
||||
webrtc::RtpExtension::kVideoTimingDefaultId);
|
||||
default_map.Register<webrtc::FrameMarkingExtension>(
|
||||
webrtc::RtpExtension::kFrameMarkingDefaultId);
|
||||
default_map.Register<webrtc::TransportSequenceNumber>(
|
||||
webrtc::RtpExtension::kTransportSequenceNumberDefaultId);
|
||||
default_map.Register<webrtc::PlayoutDelayLimits>(
|
||||
|
@ -228,6 +228,8 @@ webrtc::RtpHeaderExtensionMap GetDefaultHeaderExtensionMap() {
|
||||
webrtc::RtpExtension::kVideoContentTypeDefaultId);
|
||||
default_map.Register<VideoTimingExtension>(
|
||||
webrtc::RtpExtension::kVideoTimingDefaultId);
|
||||
default_map.Register<FrameMarkingExtension>(
|
||||
webrtc::RtpExtension::kFrameMarkingDefaultId);
|
||||
default_map.Register<TransportSequenceNumber>(
|
||||
webrtc::RtpExtension::kTransportSequenceNumberDefaultId);
|
||||
default_map.Register<PlayoutDelayLimits>(
|
||||
|
@ -509,6 +509,9 @@ RtpCapabilities WebRtcVideoEngine::GetCapabilities() const {
|
||||
capabilities.header_extensions.push_back(
|
||||
webrtc::RtpExtension(webrtc::RtpExtension::kVideoTimingUri,
|
||||
webrtc::RtpExtension::kVideoTimingDefaultId));
|
||||
capabilities.header_extensions.push_back(
|
||||
webrtc::RtpExtension(webrtc::RtpExtension::kFrameMarkingUri,
|
||||
webrtc::RtpExtension::kFrameMarkingDefaultId));
|
||||
// TODO(bugs.webrtc.org/4050): Add MID header extension as capability once MID
|
||||
// demuxing is completed.
|
||||
// capabilities.header_extensions.push_back(webrtc::RtpExtension(
|
||||
|
@ -103,6 +103,7 @@ enum RTPExtensionType {
|
||||
kRtpExtensionPlayoutDelay,
|
||||
kRtpExtensionVideoContentType,
|
||||
kRtpExtensionVideoTiming,
|
||||
kRtpExtensionFrameMarking,
|
||||
kRtpExtensionRtpStreamId,
|
||||
kRtpExtensionRepairedRtpStreamId,
|
||||
kRtpExtensionMid,
|
||||
|
@ -38,6 +38,7 @@ constexpr ExtensionInfo kExtensions[] = {
|
||||
CreateExtensionInfo<PlayoutDelayLimits>(),
|
||||
CreateExtensionInfo<VideoContentTypeExtension>(),
|
||||
CreateExtensionInfo<VideoTimingExtension>(),
|
||||
CreateExtensionInfo<FrameMarkingExtension>(),
|
||||
CreateExtensionInfo<RtpStreamId>(),
|
||||
CreateExtensionInfo<RepairedRtpStreamId>(),
|
||||
CreateExtensionInfo<RtpMid>(),
|
||||
|
@ -350,6 +350,86 @@ bool VideoTimingExtension::Write(rtc::ArrayView<uint8_t> data,
|
||||
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.
|
||||
|
@ -153,6 +153,22 @@ class VideoTimingExtension {
|
||||
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 {
|
||||
|
@ -61,6 +61,8 @@ void RtpPacketReceived::GetHeader(RTPHeader* header) const {
|
||||
&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);
|
||||
|
@ -74,6 +74,7 @@ int32_t RTPReceiverVideo::ParseRtpPacket(WebRtcRTPHeader* rtp_header,
|
||||
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 @@ int32_t RTPReceiverVideo::ParseRtpPacket(WebRtcRTPHeader* rtp_header,
|
||||
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
|
||||
|
@ -50,6 +50,8 @@ RTPExtensionType StringToRtpExtensionType(const std::string& extension) {
|
||||
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.";
|
||||
|
@ -236,6 +236,10 @@ bool RtpHeaderParser::Parse(
|
||||
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 @@ void RtpHeaderParser::ParseOneByteExtensionHeader(
|
||||
&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;
|
||||
|
@ -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 @@ struct RTPVideoHeader {
|
||||
|
||||
PlayoutDelay playout_delay;
|
||||
VideoSendTiming video_timing;
|
||||
FrameMarking frame_marking;
|
||||
RTPVideoTypeHeader video_type_header;
|
||||
};
|
||||
|
||||
|
@ -159,5 +159,13 @@ absl::optional<RTPVideoHeader> RtpFrameObject::GetRtpVideoHeader() const {
|
||||
return packet->video_header;
|
||||
}
|
||||
|
||||
absl::optional<FrameMarking> RtpFrameObject::GetFrameMarking() const {
|
||||
rtc::CritScope lock(&packet_buffer_->crit_);
|
||||
VCMPacket* packet = packet_buffer_->GetPacket(first_seq_num_);
|
||||
if (!packet)
|
||||
return absl::nullopt;
|
||||
return packet->video_header.frame_marking;
|
||||
}
|
||||
|
||||
} // namespace video_coding
|
||||
} // namespace webrtc
|
||||
|
@ -41,6 +41,7 @@ class RtpFrameObject : public EncodedFrame {
|
||||
int64_t RenderTime() const override;
|
||||
bool delayed_by_retransmission() const override;
|
||||
absl::optional<RTPVideoHeader> GetRtpVideoHeader() const;
|
||||
absl::optional<FrameMarking> GetFrameMarking() const;
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<PacketBuffer> packet_buffer_;
|
||||
|
@ -95,6 +95,10 @@ void FuzzOneInput(const uint8_t* data, size_t size) {
|
||||
VideoSendTiming timing;
|
||||
packet.GetExtension<VideoTimingExtension>(&timing);
|
||||
break;
|
||||
case kRtpExtensionFrameMarking:
|
||||
FrameMarking frame_marking;
|
||||
packet.GetExtension<FrameMarkingExtension>(&frame_marking);
|
||||
break;
|
||||
case kRtpExtensionRtpStreamId: {
|
||||
std::string rsid;
|
||||
packet.GetExtension<RtpStreamId>(&rsid);
|
||||
|
Reference in New Issue
Block a user