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:
committed by
Commit Bot
parent
a201204215
commit
d0b69a8c50
@ -272,14 +272,14 @@ bool VideoContentTypeExtension::Write(rtc::ArrayView<uint8_t> data,
|
||||
// 255 = Invalid. The whole timing frame extension should be ignored.
|
||||
//
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID | len=12| flags | encode start ms delta |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | encode finish ms delta | packetizer finish ms delta |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | pacer exit ms delta | network timestamp ms delta |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// 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 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | encode finish ms delta | packetizer finish ms delta |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | pacer exit ms delta | network 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.
|
||||
//
|
||||
// RTP header extension to carry HDR metadata.
|
||||
// Float values are upscaled by a static factor and transmitted as integers.
|
||||
// RTP header extension to carry color space information and optionally HDR
|
||||
// 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 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID | length=30 | Primaries | Transfer |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Matrix | Range | luminance_max |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | | luminance_min |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | mastering_metadata.primary_r.x and .y |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | mastering_metadata.primary_g.x and .y |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | mastering_metadata.primary_b.x and .y |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | mastering_metadata.white.x and .y |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | max_content_light_level | max_frame_average_light_level |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// 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 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Matrix | Range | luminance_max |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | | luminance_min |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | mastering_metadata.primary_r.x and .y |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | mastering_metadata.primary_g.x and .y |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | mastering_metadata.primary_b.x and .y |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | mastering_metadata.white.x and .y |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | 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 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID | length=4 | Primaries | Transfer |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Matrix | Range |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
||||
// 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 = 3 | Primaries | Transfer | Matrix |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Range |
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
|
||||
constexpr RTPExtensionType ColorSpaceExtension::kId;
|
||||
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,
|
||||
const ColorSpace& color_space) {
|
||||
RTC_DCHECK(data.size() >= ValueSize(color_space));
|
||||
RTC_DCHECK_EQ(data.size(), ValueSize(color_space));
|
||||
size_t offset = 0;
|
||||
// Write color space information.
|
||||
data.data()[offset++] = static_cast<uint8_t>(color_space.primaries());
|
||||
|
||||
@ -188,8 +188,8 @@ class ColorSpaceExtension {
|
||||
static constexpr RTPExtensionType kId = kRtpExtensionColorSpace;
|
||||
static constexpr uint8_t kValueSizeBytes = 30;
|
||||
static constexpr uint8_t kValueSizeBytesWithoutHdrMetadata = 4;
|
||||
// TODO(webrtc:8651): Change to a valid uri.
|
||||
static constexpr const char kUri[] = "rtp-colorspace-uri-placeholder";
|
||||
static constexpr const char kUri[] =
|
||||
"http://www.webrtc.org/experiments/rtp-hdrext/color-space";
|
||||
|
||||
static bool Parse(rtc::ArrayView<const uint8_t> data,
|
||||
ColorSpace* color_space);
|
||||
|
||||
@ -54,9 +54,17 @@ void BuildRedPayload(const RtpPacketToSend& media_packet,
|
||||
void AddRtpHeaderExtensions(const RTPVideoHeader& video_header,
|
||||
FrameType frame_type,
|
||||
bool set_video_rotation,
|
||||
bool set_color_space,
|
||||
bool first_packet,
|
||||
bool last_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)
|
||||
packet->SetExtension<VideoOrientation>(video_header.rotation);
|
||||
|
||||
@ -118,6 +126,28 @@ bool MinimizeDescriptor(const RTPVideoHeader& full, RTPVideoHeader* minimized) {
|
||||
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
|
||||
|
||||
RTPSenderVideo::RTPSenderVideo(Clock* clock,
|
||||
@ -131,6 +161,7 @@ RTPSenderVideo::RTPSenderVideo(Clock* clock,
|
||||
retransmission_settings_(kRetransmitBaseLayer |
|
||||
kConditionallyRetransmitHigherLayers),
|
||||
last_rotation_(kVideoRotation_0),
|
||||
transmit_color_space_next_frame_(false),
|
||||
red_payload_type_(-1),
|
||||
ulpfec_payload_type_(-1),
|
||||
flexfec_sender_(flexfec_sender),
|
||||
@ -361,6 +392,7 @@ bool RTPSenderVideo::SendVideo(enum VideoCodecType video_type,
|
||||
bool red_enabled;
|
||||
int32_t retransmission_settings;
|
||||
bool set_video_rotation;
|
||||
bool set_color_space = false;
|
||||
{
|
||||
rtc::CritScope cs(&crit_);
|
||||
// According to
|
||||
@ -380,6 +412,21 @@ bool RTPSenderVideo::SendVideo(enum VideoCodecType video_type,
|
||||
video_header->rotation != kVideoRotation_0;
|
||||
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.
|
||||
const FecProtectionParams& 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);
|
||||
// Simplest way to estimate how much extensions would occupy is to set them.
|
||||
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,
|
||||
/*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,
|
||||
/*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,
|
||||
/*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, first_packet->headers_size());
|
||||
|
||||
@ -138,6 +138,8 @@ class RTPSenderVideo {
|
||||
enum VideoCodecType video_type_;
|
||||
int32_t retransmission_settings_ 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.
|
||||
int red_payload_type_ RTC_GUARDED_BY(crit_);
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "absl/types/variant.h"
|
||||
#include "api/video/color_space.h"
|
||||
#include "api/video/video_codec_type.h"
|
||||
#include "api/video/video_content_type.h"
|
||||
#include "api/video/video_frame_marking.h"
|
||||
@ -63,6 +64,7 @@ struct RTPVideoHeader {
|
||||
PlayoutDelay playout_delay = {-1, -1};
|
||||
VideoSendTiming video_timing;
|
||||
FrameMarking frame_marking;
|
||||
absl::optional<ColorSpace> color_space;
|
||||
RTPVideoTypeHeader video_type_header;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user