Send and receive color space information if available
Bug: webrtc:8651 Change-Id: I244647cb1ccbda66fce83ae925cf4273c5a6568b Reviewed-on: https://webrtc-review.googlesource.com/c/112383 Commit-Queue: Johannes Kron <kron@webrtc.org> Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Cr-Commit-Position: refs/heads/master@{#25884}
This commit is contained in:
parent
a201204215
commit
d0b69a8c50
@ -137,6 +137,10 @@ const char RtpExtension::kGenericFrameDescriptorUri[] =
|
|||||||
"http://www.webrtc.org/experiments/rtp-hdrext/generic-frame-descriptor-00";
|
"http://www.webrtc.org/experiments/rtp-hdrext/generic-frame-descriptor-00";
|
||||||
const int RtpExtension::kGenericFrameDescriptorDefaultId = 11;
|
const int RtpExtension::kGenericFrameDescriptorDefaultId = 11;
|
||||||
|
|
||||||
|
const char RtpExtension::kColorSpaceUri[] =
|
||||||
|
"http://www.webrtc.org/experiments/rtp-hdrext/color-space";
|
||||||
|
const int RtpExtension::kColorSpaceDefaultId = 12;
|
||||||
|
|
||||||
const char RtpExtension::kEncryptHeaderExtensionsUri[] =
|
const char RtpExtension::kEncryptHeaderExtensionsUri[] =
|
||||||
"urn:ietf:params:rtp-hdrext:encrypt";
|
"urn:ietf:params:rtp-hdrext:encrypt";
|
||||||
|
|
||||||
@ -162,7 +166,8 @@ bool RtpExtension::IsSupportedForVideo(const std::string& uri) {
|
|||||||
uri == webrtc::RtpExtension::kVideoTimingUri ||
|
uri == webrtc::RtpExtension::kVideoTimingUri ||
|
||||||
uri == webrtc::RtpExtension::kMidUri ||
|
uri == webrtc::RtpExtension::kMidUri ||
|
||||||
uri == webrtc::RtpExtension::kFrameMarkingUri ||
|
uri == webrtc::RtpExtension::kFrameMarkingUri ||
|
||||||
uri == webrtc::RtpExtension::kGenericFrameDescriptorUri;
|
uri == webrtc::RtpExtension::kGenericFrameDescriptorUri ||
|
||||||
|
uri == webrtc::RtpExtension::kColorSpaceUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpExtension::IsEncryptionSupported(const std::string& uri) {
|
bool RtpExtension::IsEncryptionSupported(const std::string& uri) {
|
||||||
|
|||||||
@ -309,6 +309,10 @@ struct RtpExtension {
|
|||||||
// https://tools.ietf.org/html/rfc6904
|
// https://tools.ietf.org/html/rfc6904
|
||||||
static const char kEncryptHeaderExtensionsUri[];
|
static const char kEncryptHeaderExtensionsUri[];
|
||||||
|
|
||||||
|
// Header extension for color space information.
|
||||||
|
static const char kColorSpaceUri[];
|
||||||
|
static const int kColorSpaceDefaultId;
|
||||||
|
|
||||||
// Inclusive min and max IDs for two-byte header extensions and one-byte
|
// Inclusive min and max IDs for two-byte header extensions and one-byte
|
||||||
// header extensions, per RFC8285 Section 4.2-4.3.
|
// header extensions, per RFC8285 Section 4.2-4.3.
|
||||||
static constexpr int kMinId = 1;
|
static constexpr int kMinId = 1;
|
||||||
|
|||||||
@ -121,12 +121,13 @@ class ColorSpace {
|
|||||||
MatrixID matrix,
|
MatrixID matrix,
|
||||||
RangeID range,
|
RangeID range,
|
||||||
const HdrMetadata* hdr_metadata);
|
const HdrMetadata* hdr_metadata);
|
||||||
bool operator==(const ColorSpace& other) const {
|
friend bool operator==(const ColorSpace& lhs, const ColorSpace& rhs) {
|
||||||
return primaries_ == other.primaries() && transfer_ == other.transfer() &&
|
return lhs.primaries_ == rhs.primaries_ && lhs.transfer_ == rhs.transfer_ &&
|
||||||
matrix_ == other.matrix() && range_ == other.range() &&
|
lhs.matrix_ == rhs.matrix_ && lhs.range_ == rhs.range_ &&
|
||||||
((hdr_metadata_.has_value() && other.hdr_metadata() &&
|
lhs.hdr_metadata_ == rhs.hdr_metadata_;
|
||||||
*hdr_metadata_ == *other.hdr_metadata()) ||
|
}
|
||||||
(!hdr_metadata_.has_value() && other.hdr_metadata() == nullptr));
|
friend bool operator!=(const ColorSpace& lhs, const ColorSpace& rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimaryID primaries() const;
|
PrimaryID primaries() const;
|
||||||
|
|||||||
@ -156,7 +156,9 @@ RTPVideoHeader RtpPayloadParams::GetRtpVideoHeader(
|
|||||||
rtp_video_header.playout_delay = image.playout_delay_;
|
rtp_video_header.playout_delay = image.playout_delay_;
|
||||||
rtp_video_header.width = image._encodedWidth;
|
rtp_video_header.width = image._encodedWidth;
|
||||||
rtp_video_header.height = image._encodedHeight;
|
rtp_video_header.height = image._encodedHeight;
|
||||||
|
rtp_video_header.color_space = image.ColorSpace()
|
||||||
|
? absl::make_optional(*image.ColorSpace())
|
||||||
|
: absl::nullopt;
|
||||||
SetVideoTiming(image, &rtp_video_header.video_timing);
|
SetVideoTiming(image, &rtp_video_header.video_timing);
|
||||||
|
|
||||||
const bool is_keyframe = image._frameType == kVideoFrameKey;
|
const bool is_keyframe = image._frameType == kVideoFrameKey;
|
||||||
|
|||||||
@ -106,6 +106,7 @@ TEST(RtpPayloadParamsTest, InfoMappedToRtpVideoHeader_Vp9) {
|
|||||||
EXPECT_EQ(kVideoRotation_90, header.rotation);
|
EXPECT_EQ(kVideoRotation_90, header.rotation);
|
||||||
EXPECT_EQ(VideoContentType::SCREENSHARE, header.content_type);
|
EXPECT_EQ(VideoContentType::SCREENSHARE, header.content_type);
|
||||||
EXPECT_EQ(kVideoCodecVP9, header.codec);
|
EXPECT_EQ(kVideoCodecVP9, header.codec);
|
||||||
|
EXPECT_FALSE(header.color_space);
|
||||||
const auto& vp9_header =
|
const auto& vp9_header =
|
||||||
absl::get<RTPVideoHeaderVP9>(header.video_type_header);
|
absl::get<RTPVideoHeaderVP9>(header.video_type_header);
|
||||||
EXPECT_EQ(kPictureId + 1, vp9_header.picture_id);
|
EXPECT_EQ(kPictureId + 1, vp9_header.picture_id);
|
||||||
@ -122,11 +123,16 @@ TEST(RtpPayloadParamsTest, InfoMappedToRtpVideoHeader_Vp9) {
|
|||||||
codec_info.codecSpecific.VP9.end_of_picture = true;
|
codec_info.codecSpecific.VP9.end_of_picture = true;
|
||||||
|
|
||||||
encoded_image.SetSpatialIndex(1);
|
encoded_image.SetSpatialIndex(1);
|
||||||
|
ColorSpace color_space(
|
||||||
|
ColorSpace::PrimaryID::kSMPTE170M, ColorSpace::TransferID::kSMPTE170M,
|
||||||
|
ColorSpace::MatrixID::kSMPTE170M, ColorSpace::RangeID::kFull);
|
||||||
|
encoded_image.SetColorSpace(&color_space);
|
||||||
header = params.GetRtpVideoHeader(encoded_image, &codec_info, kDontCare);
|
header = params.GetRtpVideoHeader(encoded_image, &codec_info, kDontCare);
|
||||||
|
|
||||||
EXPECT_EQ(kVideoRotation_90, header.rotation);
|
EXPECT_EQ(kVideoRotation_90, header.rotation);
|
||||||
EXPECT_EQ(VideoContentType::SCREENSHARE, header.content_type);
|
EXPECT_EQ(VideoContentType::SCREENSHARE, header.content_type);
|
||||||
EXPECT_EQ(kVideoCodecVP9, header.codec);
|
EXPECT_EQ(kVideoCodecVP9, header.codec);
|
||||||
|
EXPECT_EQ(absl::make_optional(color_space), header.color_space);
|
||||||
EXPECT_EQ(kPictureId + 1, vp9_header.picture_id);
|
EXPECT_EQ(kPictureId + 1, vp9_header.picture_id);
|
||||||
EXPECT_EQ(kTl0PicIdx, vp9_header.tl0_pic_idx);
|
EXPECT_EQ(kTl0PicIdx, vp9_header.tl0_pic_idx);
|
||||||
EXPECT_EQ(vp9_header.temporal_idx, codec_info.codecSpecific.VP9.temporal_idx);
|
EXPECT_EQ(vp9_header.temporal_idx, codec_info.codecSpecific.VP9.temporal_idx);
|
||||||
|
|||||||
@ -504,6 +504,9 @@ RtpCapabilities WebRtcVideoEngine::GetCapabilities() const {
|
|||||||
capabilities.header_extensions.push_back(
|
capabilities.header_extensions.push_back(
|
||||||
webrtc::RtpExtension(webrtc::RtpExtension::kFrameMarkingUri,
|
webrtc::RtpExtension(webrtc::RtpExtension::kFrameMarkingUri,
|
||||||
webrtc::RtpExtension::kFrameMarkingDefaultId));
|
webrtc::RtpExtension::kFrameMarkingDefaultId));
|
||||||
|
capabilities.header_extensions.push_back(
|
||||||
|
webrtc::RtpExtension(webrtc::RtpExtension::kColorSpaceUri,
|
||||||
|
webrtc::RtpExtension::kColorSpaceDefaultId));
|
||||||
if (webrtc::field_trial::IsEnabled("WebRTC-GenericDescriptorAdvertised")) {
|
if (webrtc::field_trial::IsEnabled("WebRTC-GenericDescriptorAdvertised")) {
|
||||||
capabilities.header_extensions.push_back(webrtc::RtpExtension(
|
capabilities.header_extensions.push_back(webrtc::RtpExtension(
|
||||||
webrtc::RtpExtension::kGenericFrameDescriptorUri,
|
webrtc::RtpExtension::kGenericFrameDescriptorUri,
|
||||||
|
|||||||
@ -277,50 +277,42 @@ TEST_F(WebRtcVideoEngineTest, DefaultRtxCodecHasAssociatedPayloadTypeSet) {
|
|||||||
|
|
||||||
TEST_F(WebRtcVideoEngineTest, SupportsTimestampOffsetHeaderExtension) {
|
TEST_F(WebRtcVideoEngineTest, SupportsTimestampOffsetHeaderExtension) {
|
||||||
RtpCapabilities capabilities = engine_.GetCapabilities();
|
RtpCapabilities capabilities = engine_.GetCapabilities();
|
||||||
ASSERT_FALSE(capabilities.header_extensions.empty());
|
EXPECT_THAT(
|
||||||
for (const RtpExtension& extension : capabilities.header_extensions) {
|
capabilities.header_extensions,
|
||||||
if (extension.uri == RtpExtension::kTimestampOffsetUri) {
|
testing::Contains(RtpExtension(RtpExtension::kTimestampOffsetUri,
|
||||||
EXPECT_EQ(RtpExtension::kTimestampOffsetDefaultId, extension.id);
|
RtpExtension::kTimestampOffsetDefaultId)));
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FAIL() << "Timestamp offset extension not in header-extension list.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebRtcVideoEngineTest, SupportsAbsoluteSenderTimeHeaderExtension) {
|
TEST_F(WebRtcVideoEngineTest, SupportsAbsoluteSenderTimeHeaderExtension) {
|
||||||
RtpCapabilities capabilities = engine_.GetCapabilities();
|
RtpCapabilities capabilities = engine_.GetCapabilities();
|
||||||
ASSERT_FALSE(capabilities.header_extensions.empty());
|
EXPECT_THAT(
|
||||||
for (const RtpExtension& extension : capabilities.header_extensions) {
|
capabilities.header_extensions,
|
||||||
if (extension.uri == RtpExtension::kAbsSendTimeUri) {
|
testing::Contains(RtpExtension(RtpExtension::kAbsSendTimeUri,
|
||||||
EXPECT_EQ(RtpExtension::kAbsSendTimeDefaultId, extension.id);
|
RtpExtension::kAbsSendTimeDefaultId)));
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FAIL() << "Absolute Sender Time extension not in header-extension list.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebRtcVideoEngineTest, SupportsTransportSequenceNumberHeaderExtension) {
|
TEST_F(WebRtcVideoEngineTest, SupportsTransportSequenceNumberHeaderExtension) {
|
||||||
RtpCapabilities capabilities = engine_.GetCapabilities();
|
RtpCapabilities capabilities = engine_.GetCapabilities();
|
||||||
ASSERT_FALSE(capabilities.header_extensions.empty());
|
EXPECT_THAT(capabilities.header_extensions,
|
||||||
for (const RtpExtension& extension : capabilities.header_extensions) {
|
testing::Contains(RtpExtension(
|
||||||
if (extension.uri == RtpExtension::kTransportSequenceNumberUri) {
|
RtpExtension::kTransportSequenceNumberUri,
|
||||||
EXPECT_EQ(RtpExtension::kTransportSequenceNumberDefaultId, extension.id);
|
RtpExtension::kTransportSequenceNumberDefaultId)));
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FAIL() << "Transport sequence number extension not in header-extension list.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebRtcVideoEngineTest, SupportsVideoRotationHeaderExtension) {
|
TEST_F(WebRtcVideoEngineTest, SupportsVideoRotationHeaderExtension) {
|
||||||
RtpCapabilities capabilities = engine_.GetCapabilities();
|
RtpCapabilities capabilities = engine_.GetCapabilities();
|
||||||
ASSERT_FALSE(capabilities.header_extensions.empty());
|
EXPECT_THAT(
|
||||||
for (const RtpExtension& extension : capabilities.header_extensions) {
|
capabilities.header_extensions,
|
||||||
if (extension.uri == RtpExtension::kVideoRotationUri) {
|
testing::Contains(RtpExtension(RtpExtension::kVideoRotationUri,
|
||||||
EXPECT_EQ(RtpExtension::kVideoRotationDefaultId, extension.id);
|
RtpExtension::kVideoRotationDefaultId)));
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
}
|
TEST_F(WebRtcVideoEngineTest, SupportsColorSpaceHeaderExtension) {
|
||||||
FAIL() << "Video Rotation extension not in header-extension list.";
|
RtpCapabilities capabilities = engine_.GetCapabilities();
|
||||||
|
EXPECT_THAT(
|
||||||
|
capabilities.header_extensions,
|
||||||
|
testing::Contains(RtpExtension(RtpExtension::kColorSpaceUri,
|
||||||
|
RtpExtension::kColorSpaceDefaultId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebRtcVideoEngineTestWithGenericDescriptor
|
class WebRtcVideoEngineTestWithGenericDescriptor
|
||||||
|
|||||||
@ -272,14 +272,14 @@ bool VideoContentTypeExtension::Write(rtc::ArrayView<uint8_t> data,
|
|||||||
// 255 = Invalid. The whole timing frame extension should be ignored.
|
// 255 = Invalid. The whole timing frame extension should be ignored.
|
||||||
//
|
//
|
||||||
// 0 1 2 3
|
// 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 2
|
// 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 | len=12| flags | encode start ms delta |
|
// | ID | len=12| flags | encode start ms delta |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | encode finish ms delta | packetizer finish ms delta |
|
// | encode finish ms delta | packetizer finish ms delta |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | pacer exit ms delta | network timestamp ms delta |
|
// | pacer exit ms delta | network timestamp ms delta |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | network2 timestamp ms delta |
|
// | network2 timestamp ms delta |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
@ -436,38 +436,39 @@ bool FrameMarkingExtension::Write(rtc::ArrayView<uint8_t> data,
|
|||||||
|
|
||||||
// Color space including HDR metadata as an optional field.
|
// Color space including HDR metadata as an optional field.
|
||||||
//
|
//
|
||||||
// RTP header extension to carry HDR metadata.
|
// RTP header extension to carry color space information and optionally HDR
|
||||||
// Float values are upscaled by a static factor and transmitted as integers.
|
// metadata. The float values in the HDR metadata struct are upscaled by a
|
||||||
|
// static factor and transmitted as unsigned integers.
|
||||||
//
|
//
|
||||||
// Data layout with HDR metadata
|
// Data layout of color space with HDR metadata (two-byte RTP header extension)
|
||||||
// 0 1 2 3
|
// 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 2
|
// 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 | length=30 | Primaries | Transfer |
|
// | ID | length=30 | Primaries | Transfer |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | Matrix | Range | luminance_max |
|
// | Matrix | Range | luminance_max |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | | luminance_min |
|
// | | luminance_min |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | mastering_metadata.primary_r.x and .y |
|
// | mastering_metadata.primary_r.x and .y |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | mastering_metadata.primary_g.x and .y |
|
// | mastering_metadata.primary_g.x and .y |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | mastering_metadata.primary_b.x and .y |
|
// | mastering_metadata.primary_b.x and .y |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | mastering_metadata.white.x and .y |
|
// | mastering_metadata.white.x and .y |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | max_content_light_level | max_frame_average_light_level |
|
// | max_content_light_level | max_frame_average_light_level |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
//
|
//
|
||||||
// Data layout without HDR metadata
|
// Data layout of color space w/o HDR metadata (one-byte RTP header extension)
|
||||||
// 0 1 2 3
|
// 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 2
|
// 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 | length=4 | Primaries | Transfer |
|
// | ID | L = 3 | Primaries | Transfer | Matrix |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | Matrix | Range |
|
// | Range |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
constexpr RTPExtensionType ColorSpaceExtension::kId;
|
constexpr RTPExtensionType ColorSpaceExtension::kId;
|
||||||
constexpr uint8_t ColorSpaceExtension::kValueSizeBytes;
|
constexpr uint8_t ColorSpaceExtension::kValueSizeBytes;
|
||||||
@ -524,7 +525,7 @@ bool ColorSpaceExtension::Parse(rtc::ArrayView<const uint8_t> data,
|
|||||||
|
|
||||||
bool ColorSpaceExtension::Write(rtc::ArrayView<uint8_t> data,
|
bool ColorSpaceExtension::Write(rtc::ArrayView<uint8_t> data,
|
||||||
const ColorSpace& color_space) {
|
const ColorSpace& color_space) {
|
||||||
RTC_DCHECK(data.size() >= ValueSize(color_space));
|
RTC_DCHECK_EQ(data.size(), ValueSize(color_space));
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
// Write color space information.
|
// Write color space information.
|
||||||
data.data()[offset++] = static_cast<uint8_t>(color_space.primaries());
|
data.data()[offset++] = static_cast<uint8_t>(color_space.primaries());
|
||||||
|
|||||||
@ -188,8 +188,8 @@ class ColorSpaceExtension {
|
|||||||
static constexpr RTPExtensionType kId = kRtpExtensionColorSpace;
|
static constexpr RTPExtensionType kId = kRtpExtensionColorSpace;
|
||||||
static constexpr uint8_t kValueSizeBytes = 30;
|
static constexpr uint8_t kValueSizeBytes = 30;
|
||||||
static constexpr uint8_t kValueSizeBytesWithoutHdrMetadata = 4;
|
static constexpr uint8_t kValueSizeBytesWithoutHdrMetadata = 4;
|
||||||
// TODO(webrtc:8651): Change to a valid uri.
|
static constexpr const char kUri[] =
|
||||||
static constexpr const char kUri[] = "rtp-colorspace-uri-placeholder";
|
"http://www.webrtc.org/experiments/rtp-hdrext/color-space";
|
||||||
|
|
||||||
static bool Parse(rtc::ArrayView<const uint8_t> data,
|
static bool Parse(rtc::ArrayView<const uint8_t> data,
|
||||||
ColorSpace* color_space);
|
ColorSpace* color_space);
|
||||||
|
|||||||
@ -54,9 +54,17 @@ void BuildRedPayload(const RtpPacketToSend& media_packet,
|
|||||||
void AddRtpHeaderExtensions(const RTPVideoHeader& video_header,
|
void AddRtpHeaderExtensions(const RTPVideoHeader& video_header,
|
||||||
FrameType frame_type,
|
FrameType frame_type,
|
||||||
bool set_video_rotation,
|
bool set_video_rotation,
|
||||||
|
bool set_color_space,
|
||||||
bool first_packet,
|
bool first_packet,
|
||||||
bool last_packet,
|
bool last_packet,
|
||||||
RtpPacketToSend* packet) {
|
RtpPacketToSend* packet) {
|
||||||
|
// Color space requires two-byte header extensions if HDR metadata is
|
||||||
|
// included. Therefore, it's best to add this extension first so that the
|
||||||
|
// other extensions in the same packet are written as two-byte headers at
|
||||||
|
// once.
|
||||||
|
if (last_packet && set_color_space && video_header.color_space)
|
||||||
|
packet->SetExtension<ColorSpaceExtension>(video_header.color_space.value());
|
||||||
|
|
||||||
if (last_packet && set_video_rotation)
|
if (last_packet && set_video_rotation)
|
||||||
packet->SetExtension<VideoOrientation>(video_header.rotation);
|
packet->SetExtension<VideoOrientation>(video_header.rotation);
|
||||||
|
|
||||||
@ -118,6 +126,28 @@ bool MinimizeDescriptor(const RTPVideoHeader& full, RTPVideoHeader* minimized) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBaseLayer(const RTPVideoHeader& video_header) {
|
||||||
|
switch (video_header.codec) {
|
||||||
|
case kVideoCodecVP8: {
|
||||||
|
const auto& vp8 =
|
||||||
|
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
|
||||||
|
return (vp8.temporalIdx == 0 || vp8.temporalIdx == kNoTemporalIdx);
|
||||||
|
}
|
||||||
|
case kVideoCodecVP9: {
|
||||||
|
const auto& vp9 =
|
||||||
|
absl::get<RTPVideoHeaderVP9>(video_header.video_type_header);
|
||||||
|
return (vp9.temporal_idx == 0 || vp9.temporal_idx == kNoTemporalIdx);
|
||||||
|
}
|
||||||
|
case kVideoCodecH264:
|
||||||
|
// TODO(kron): Implement logic for H264 once WebRTC supports temporal
|
||||||
|
// layers for H264.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
RTPSenderVideo::RTPSenderVideo(Clock* clock,
|
RTPSenderVideo::RTPSenderVideo(Clock* clock,
|
||||||
@ -131,6 +161,7 @@ RTPSenderVideo::RTPSenderVideo(Clock* clock,
|
|||||||
retransmission_settings_(kRetransmitBaseLayer |
|
retransmission_settings_(kRetransmitBaseLayer |
|
||||||
kConditionallyRetransmitHigherLayers),
|
kConditionallyRetransmitHigherLayers),
|
||||||
last_rotation_(kVideoRotation_0),
|
last_rotation_(kVideoRotation_0),
|
||||||
|
transmit_color_space_next_frame_(false),
|
||||||
red_payload_type_(-1),
|
red_payload_type_(-1),
|
||||||
ulpfec_payload_type_(-1),
|
ulpfec_payload_type_(-1),
|
||||||
flexfec_sender_(flexfec_sender),
|
flexfec_sender_(flexfec_sender),
|
||||||
@ -361,6 +392,7 @@ bool RTPSenderVideo::SendVideo(enum VideoCodecType video_type,
|
|||||||
bool red_enabled;
|
bool red_enabled;
|
||||||
int32_t retransmission_settings;
|
int32_t retransmission_settings;
|
||||||
bool set_video_rotation;
|
bool set_video_rotation;
|
||||||
|
bool set_color_space = false;
|
||||||
{
|
{
|
||||||
rtc::CritScope cs(&crit_);
|
rtc::CritScope cs(&crit_);
|
||||||
// According to
|
// According to
|
||||||
@ -380,6 +412,21 @@ bool RTPSenderVideo::SendVideo(enum VideoCodecType video_type,
|
|||||||
video_header->rotation != kVideoRotation_0;
|
video_header->rotation != kVideoRotation_0;
|
||||||
last_rotation_ = video_header->rotation;
|
last_rotation_ = video_header->rotation;
|
||||||
|
|
||||||
|
// Send color space when changed or if the frame is a key frame. Keep
|
||||||
|
// sending color space information until the first base layer frame to
|
||||||
|
// guarantee that the information is retrieved by the receiver.
|
||||||
|
if (video_header->color_space != last_color_space_) {
|
||||||
|
last_color_space_ = video_header->color_space;
|
||||||
|
set_color_space = true;
|
||||||
|
transmit_color_space_next_frame_ = !IsBaseLayer(*video_header);
|
||||||
|
} else {
|
||||||
|
set_color_space =
|
||||||
|
frame_type == kVideoFrameKey || transmit_color_space_next_frame_;
|
||||||
|
transmit_color_space_next_frame_ = transmit_color_space_next_frame_
|
||||||
|
? !IsBaseLayer(*video_header)
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
// FEC settings.
|
// FEC settings.
|
||||||
const FecProtectionParams& fec_params =
|
const FecProtectionParams& fec_params =
|
||||||
frame_type == kVideoFrameKey ? key_fec_params_ : delta_fec_params_;
|
frame_type == kVideoFrameKey ? key_fec_params_ : delta_fec_params_;
|
||||||
@ -410,13 +457,17 @@ bool RTPSenderVideo::SendVideo(enum VideoCodecType video_type,
|
|||||||
auto last_packet = absl::make_unique<RtpPacketToSend>(*single_packet);
|
auto last_packet = absl::make_unique<RtpPacketToSend>(*single_packet);
|
||||||
// Simplest way to estimate how much extensions would occupy is to set them.
|
// Simplest way to estimate how much extensions would occupy is to set them.
|
||||||
AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation,
|
AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation,
|
||||||
/*first=*/true, /*last=*/true, single_packet.get());
|
set_color_space, /*first=*/true, /*last=*/true,
|
||||||
|
single_packet.get());
|
||||||
AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation,
|
AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation,
|
||||||
/*first=*/true, /*last=*/false, first_packet.get());
|
set_color_space, /*first=*/true, /*last=*/false,
|
||||||
|
first_packet.get());
|
||||||
AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation,
|
AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation,
|
||||||
/*first=*/false, /*last=*/false, middle_packet.get());
|
set_color_space, /*first=*/false, /*last=*/false,
|
||||||
|
middle_packet.get());
|
||||||
AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation,
|
AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation,
|
||||||
/*first=*/false, /*last=*/true, last_packet.get());
|
set_color_space, /*first=*/false, /*last=*/true,
|
||||||
|
last_packet.get());
|
||||||
|
|
||||||
RTC_DCHECK_GT(packet_capacity, single_packet->headers_size());
|
RTC_DCHECK_GT(packet_capacity, single_packet->headers_size());
|
||||||
RTC_DCHECK_GT(packet_capacity, first_packet->headers_size());
|
RTC_DCHECK_GT(packet_capacity, first_packet->headers_size());
|
||||||
|
|||||||
@ -138,6 +138,8 @@ class RTPSenderVideo {
|
|||||||
enum VideoCodecType video_type_;
|
enum VideoCodecType video_type_;
|
||||||
int32_t retransmission_settings_ RTC_GUARDED_BY(crit_);
|
int32_t retransmission_settings_ RTC_GUARDED_BY(crit_);
|
||||||
VideoRotation last_rotation_ RTC_GUARDED_BY(crit_);
|
VideoRotation last_rotation_ RTC_GUARDED_BY(crit_);
|
||||||
|
absl::optional<ColorSpace> last_color_space_ RTC_GUARDED_BY(crit_);
|
||||||
|
bool transmit_color_space_next_frame_ RTC_GUARDED_BY(crit_);
|
||||||
|
|
||||||
// RED/ULPFEC.
|
// RED/ULPFEC.
|
||||||
int red_payload_type_ RTC_GUARDED_BY(crit_);
|
int red_payload_type_ RTC_GUARDED_BY(crit_);
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
#include "absl/container/inlined_vector.h"
|
#include "absl/container/inlined_vector.h"
|
||||||
#include "absl/types/optional.h"
|
#include "absl/types/optional.h"
|
||||||
#include "absl/types/variant.h"
|
#include "absl/types/variant.h"
|
||||||
|
#include "api/video/color_space.h"
|
||||||
#include "api/video/video_codec_type.h"
|
#include "api/video/video_codec_type.h"
|
||||||
#include "api/video/video_content_type.h"
|
#include "api/video/video_content_type.h"
|
||||||
#include "api/video/video_frame_marking.h"
|
#include "api/video/video_frame_marking.h"
|
||||||
@ -63,6 +64,7 @@ struct RTPVideoHeader {
|
|||||||
PlayoutDelay playout_delay = {-1, -1};
|
PlayoutDelay playout_delay = {-1, -1};
|
||||||
VideoSendTiming video_timing;
|
VideoSendTiming video_timing;
|
||||||
FrameMarking frame_marking;
|
FrameMarking frame_marking;
|
||||||
|
absl::optional<ColorSpace> color_space;
|
||||||
RTPVideoTypeHeader video_type_header;
|
RTPVideoTypeHeader video_type_header;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -72,6 +72,9 @@ RtpFrameObject::RtpFrameObject(PacketBuffer* packet_buffer,
|
|||||||
// frame (I-frame or IDR frame in H.264 (AVC), or an IRAP picture in H.265
|
// frame (I-frame or IDR frame in H.264 (AVC), or an IRAP picture in H.265
|
||||||
// (HEVC)).
|
// (HEVC)).
|
||||||
rotation_ = last_packet->video_header.rotation;
|
rotation_ = last_packet->video_header.rotation;
|
||||||
|
SetColorSpace(last_packet->video_header.color_space
|
||||||
|
? &last_packet->video_header.color_space.value()
|
||||||
|
: nullptr);
|
||||||
_rotation_set = true;
|
_rotation_set = true;
|
||||||
content_type_ = last_packet->video_header.content_type;
|
content_type_ = last_packet->video_header.content_type;
|
||||||
if (last_packet->video_header.video_timing.flags !=
|
if (last_packet->video_header.video_timing.flags !=
|
||||||
|
|||||||
@ -505,7 +505,17 @@ void RtpVideoStreamReceiver::ReceivePacket(const RtpPacketReceived& packet) {
|
|||||||
&webrtc_rtp_header.video_header().video_timing);
|
&webrtc_rtp_header.video_header().video_timing);
|
||||||
packet.GetExtension<PlayoutDelayLimits>(
|
packet.GetExtension<PlayoutDelayLimits>(
|
||||||
&webrtc_rtp_header.video_header().playout_delay);
|
&webrtc_rtp_header.video_header().playout_delay);
|
||||||
|
webrtc_rtp_header.video_header().color_space =
|
||||||
|
packet.GetExtension<ColorSpaceExtension>();
|
||||||
|
if (webrtc_rtp_header.video_header().color_space ||
|
||||||
|
webrtc_rtp_header.frameType == kVideoFrameKey) {
|
||||||
|
// Store color space since it's only transmitted when changed or for key
|
||||||
|
// frames. Color space will be cleared if a key frame is transmitted without
|
||||||
|
// color space information.
|
||||||
|
last_color_space_ = webrtc_rtp_header.video_header().color_space;
|
||||||
|
} else if (last_color_space_) {
|
||||||
|
webrtc_rtp_header.video_header().color_space = last_color_space_;
|
||||||
|
}
|
||||||
absl::optional<RtpGenericFrameDescriptor> generic_descriptor_wire;
|
absl::optional<RtpGenericFrameDescriptor> generic_descriptor_wire;
|
||||||
generic_descriptor_wire.emplace();
|
generic_descriptor_wire.emplace();
|
||||||
if (packet.GetExtension<RtpGenericFrameDescriptorExtension>(
|
if (packet.GetExtension<RtpGenericFrameDescriptorExtension>(
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
#include "absl/types/optional.h"
|
#include "absl/types/optional.h"
|
||||||
|
|
||||||
#include "api/crypto/framedecryptorinterface.h"
|
#include "api/crypto/framedecryptorinterface.h"
|
||||||
|
#include "api/video/color_space.h"
|
||||||
#include "api/video_codecs/video_codec.h"
|
#include "api/video_codecs/video_codec.h"
|
||||||
#include "call/rtp_packet_sink_interface.h"
|
#include "call/rtp_packet_sink_interface.h"
|
||||||
#include "call/syncable.h"
|
#include "call/syncable.h"
|
||||||
@ -222,6 +223,7 @@ class RtpVideoStreamReceiver : public RecoveredPacketReceiver,
|
|||||||
// rtp_reference_finder if they are decryptable.
|
// rtp_reference_finder if they are decryptable.
|
||||||
std::unique_ptr<BufferedFrameDecryptor> buffered_frame_decryptor_
|
std::unique_ptr<BufferedFrameDecryptor> buffered_frame_decryptor_
|
||||||
RTC_PT_GUARDED_BY(network_tc_);
|
RTC_PT_GUARDED_BY(network_tc_);
|
||||||
|
absl::optional<ColorSpace> last_color_space_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user