Add chroma siting to color space RTP extension

- Add chroma siting to color space RTP extension.
- Use 16 bits for max/min luminance.
- Change denominator of chromaticity and luminance.
- Add RTC_DCHECKs to protect against overflows.

Bug: webrtc:8651
Change-Id: If8b95bad6241381224eaba9c5bccce06a65a9195
Reviewed-on: https://webrtc-review.googlesource.com/c/113804
Commit-Queue: Johannes Kron <kron@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25990}
This commit is contained in:
Johannes Kron
2018-12-12 09:52:53 +01:00
committed by Commit Bot
parent 0697ce2a76
commit c13f4be5f4
4 changed files with 165 additions and 85 deletions

View File

@ -12,6 +12,7 @@
#include <string.h>
#include <cmath>
#include <limits>
#include "modules/rtp_rtcp/include/rtp_cvo.h"
#include "modules/rtp_rtcp/source/byte_io.h"
@ -444,30 +445,30 @@ bool FrameMarkingExtension::Write(rtc::ArrayView<uint8_t> data,
// 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 | length=30 | Primaries | Transfer |
// | ID | length=28 | primaries | transfer |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Matrix | Range | luminance_max |
// | matrix |range+chr.sit. | luminance_max |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | | luminance_min |
// | luminance_min | mastering_metadata.|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | mastering_metadata.primary_r.x and .y |
// |primary_r.x and .y | mastering_metadata.|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | mastering_metadata.primary_g.x and .y |
// |primary_g.x and .y | mastering_metadata.|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | mastering_metadata.primary_b.x and .y |
// |primary_b.x and .y | mastering_metadata.|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | mastering_metadata.white.x and .y |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | max_content_light_level | max_frame_average_light_level |
// |white.x and .y | max_content_light_level |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | max_frame_average_light_level |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | L = 3 | Primaries | Transfer | Matrix |
// | ID | L = 3 | primaries | transfer | matrix |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Range |
// |range+chr.sit. |
// +-+-+-+-+-+-+-+-+
constexpr RTPExtensionType ColorSpaceExtension::kId;
@ -483,13 +484,21 @@ bool ColorSpaceExtension::Parse(rtc::ArrayView<const uint8_t> data,
size_t offset = 0;
// Read color space information.
if (!color_space->set_primaries_from_uint8(data.data()[offset++]))
if (!color_space->set_primaries_from_uint8(data[offset++]))
return false;
if (!color_space->set_transfer_from_uint8(data.data()[offset++]))
if (!color_space->set_transfer_from_uint8(data[offset++]))
return false;
if (!color_space->set_matrix_from_uint8(data.data()[offset++]))
if (!color_space->set_matrix_from_uint8(data[offset++]))
return false;
if (!color_space->set_range_from_uint8(data.data()[offset++]))
uint8_t range_and_chroma_siting = data[offset++];
if (!color_space->set_range_from_uint8((range_and_chroma_siting >> 4) & 0x03))
return false;
if (!color_space->set_chroma_siting_horizontal_from_uint8(
(range_and_chroma_siting >> 2) & 0x03))
return false;
if (!color_space->set_chroma_siting_vertical_from_uint8(
range_and_chroma_siting & 0x03))
return false;
// Read HDR metadata if it exists, otherwise clear it.
@ -497,26 +506,9 @@ bool ColorSpaceExtension::Parse(rtc::ArrayView<const uint8_t> data,
color_space->set_hdr_metadata(nullptr);
} else {
HdrMetadata hdr_metadata;
offset += ParseLuminance(data.data() + offset,
&hdr_metadata.mastering_metadata.luminance_max,
kLuminanceMaxDenominator);
offset += ParseLuminance(data.data() + offset,
&hdr_metadata.mastering_metadata.luminance_min,
kLuminanceMinDenominator);
offset += ParseChromaticity(data.data() + offset,
&hdr_metadata.mastering_metadata.primary_r);
offset += ParseChromaticity(data.data() + offset,
&hdr_metadata.mastering_metadata.primary_g);
offset += ParseChromaticity(data.data() + offset,
&hdr_metadata.mastering_metadata.primary_b);
offset += ParseChromaticity(data.data() + offset,
&hdr_metadata.mastering_metadata.white_point);
hdr_metadata.max_content_light_level =
ByteReader<uint16_t>::ReadBigEndian(data.data() + offset);
offset += 2;
hdr_metadata.max_frame_average_light_level =
ByteReader<uint16_t>::ReadBigEndian(data.data() + offset);
offset += 2;
offset += ParseHdrMetadata(data.subview(offset), &hdr_metadata);
if (!hdr_metadata.Validate())
return false;
color_space->set_hdr_metadata(&hdr_metadata);
}
RTC_DCHECK_EQ(ValueSize(*color_space), offset);
@ -528,40 +520,67 @@ bool ColorSpaceExtension::Write(rtc::ArrayView<uint8_t> data,
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());
data.data()[offset++] = static_cast<uint8_t>(color_space.transfer());
data.data()[offset++] = static_cast<uint8_t>(color_space.matrix());
data.data()[offset++] = static_cast<uint8_t>(color_space.range());
data[offset++] = static_cast<uint8_t>(color_space.primaries());
data[offset++] = static_cast<uint8_t>(color_space.transfer());
data[offset++] = static_cast<uint8_t>(color_space.matrix());
data[offset++] = CombineRangeAndChromaSiting(
color_space.range(), color_space.chroma_siting_horizontal(),
color_space.chroma_siting_vertical());
// Write HDR metadata if it exists.
if (color_space.hdr_metadata()) {
const HdrMetadata& hdr_metadata = *color_space.hdr_metadata();
offset += WriteLuminance(data.data() + offset,
hdr_metadata.mastering_metadata.luminance_max,
kLuminanceMaxDenominator);
offset += WriteLuminance(data.data() + offset,
hdr_metadata.mastering_metadata.luminance_min,
kLuminanceMinDenominator);
offset += WriteChromaticity(data.data() + offset,
hdr_metadata.mastering_metadata.primary_r);
offset += WriteChromaticity(data.data() + offset,
hdr_metadata.mastering_metadata.primary_g);
offset += WriteChromaticity(data.data() + offset,
hdr_metadata.mastering_metadata.primary_b);
offset += WriteChromaticity(data.data() + offset,
hdr_metadata.mastering_metadata.white_point);
ByteWriter<uint16_t>::WriteBigEndian(data.data() + offset,
hdr_metadata.max_content_light_level);
offset += 2;
ByteWriter<uint16_t>::WriteBigEndian(
data.data() + offset, hdr_metadata.max_frame_average_light_level);
offset += 2;
offset +=
WriteHdrMetadata(data.subview(offset), *color_space.hdr_metadata());
}
RTC_DCHECK_EQ(ValueSize(color_space), offset);
return true;
}
// Combines range and chroma siting into one byte with the following bit layout:
// bits 0-1 Chroma siting vertical.
// 2-3 Chroma siting horizontal.
// 4-5 Range.
// 6-7 Unused.
uint8_t ColorSpaceExtension::CombineRangeAndChromaSiting(
ColorSpace::RangeID range,
ColorSpace::ChromaSiting chroma_siting_horizontal,
ColorSpace::ChromaSiting chroma_siting_vertical) {
RTC_DCHECK_LE(static_cast<uint8_t>(range), 3);
RTC_DCHECK_LE(static_cast<uint8_t>(chroma_siting_horizontal), 3);
RTC_DCHECK_LE(static_cast<uint8_t>(chroma_siting_vertical), 3);
return (static_cast<uint8_t>(range) << 4) |
(static_cast<uint8_t>(chroma_siting_horizontal) << 2) |
static_cast<uint8_t>(chroma_siting_vertical);
}
size_t ColorSpaceExtension::ParseHdrMetadata(rtc::ArrayView<const uint8_t> data,
HdrMetadata* hdr_metadata) {
RTC_DCHECK_EQ(data.size(),
kValueSizeBytes - kValueSizeBytesWithoutHdrMetadata);
size_t offset = 0;
offset += ParseLuminance(data.data() + offset,
&hdr_metadata->mastering_metadata.luminance_max,
kLuminanceMaxDenominator);
offset += ParseLuminance(data.data() + offset,
&hdr_metadata->mastering_metadata.luminance_min,
kLuminanceMinDenominator);
offset += ParseChromaticity(data.data() + offset,
&hdr_metadata->mastering_metadata.primary_r);
offset += ParseChromaticity(data.data() + offset,
&hdr_metadata->mastering_metadata.primary_g);
offset += ParseChromaticity(data.data() + offset,
&hdr_metadata->mastering_metadata.primary_b);
offset += ParseChromaticity(data.data() + offset,
&hdr_metadata->mastering_metadata.white_point);
hdr_metadata->max_content_light_level =
ByteReader<uint16_t>::ReadBigEndian(data.data() + offset);
offset += 2;
hdr_metadata->max_frame_average_light_level =
ByteReader<uint16_t>::ReadBigEndian(data.data() + offset);
offset += 2;
return offset;
}
size_t ColorSpaceExtension::ParseChromaticity(
const uint8_t* data,
HdrMasteringMetadata::Chromaticity* p) {
@ -576,16 +595,48 @@ size_t ColorSpaceExtension::ParseChromaticity(
size_t ColorSpaceExtension::ParseLuminance(const uint8_t* data,
float* f,
int denominator) {
uint32_t luminance_scaled = ByteReader<uint32_t, 3>::ReadBigEndian(data);
uint16_t luminance_scaled = ByteReader<uint16_t>::ReadBigEndian(data);
*f = static_cast<float>(luminance_scaled) / denominator;
return 3; // Return number of bytes read.
return 2; // Return number of bytes read.
}
size_t ColorSpaceExtension::WriteHdrMetadata(rtc::ArrayView<uint8_t> data,
const HdrMetadata& hdr_metadata) {
RTC_DCHECK_EQ(data.size(),
kValueSizeBytes - kValueSizeBytesWithoutHdrMetadata);
RTC_DCHECK(hdr_metadata.Validate());
size_t offset = 0;
offset += WriteLuminance(data.data() + offset,
hdr_metadata.mastering_metadata.luminance_max,
kLuminanceMaxDenominator);
offset += WriteLuminance(data.data() + offset,
hdr_metadata.mastering_metadata.luminance_min,
kLuminanceMinDenominator);
offset += WriteChromaticity(data.data() + offset,
hdr_metadata.mastering_metadata.primary_r);
offset += WriteChromaticity(data.data() + offset,
hdr_metadata.mastering_metadata.primary_g);
offset += WriteChromaticity(data.data() + offset,
hdr_metadata.mastering_metadata.primary_b);
offset += WriteChromaticity(data.data() + offset,
hdr_metadata.mastering_metadata.white_point);
ByteWriter<uint16_t>::WriteBigEndian(data.data() + offset,
hdr_metadata.max_content_light_level);
offset += 2;
ByteWriter<uint16_t>::WriteBigEndian(
data.data() + offset, hdr_metadata.max_frame_average_light_level);
offset += 2;
return offset;
}
size_t ColorSpaceExtension::WriteChromaticity(
uint8_t* data,
const HdrMasteringMetadata::Chromaticity& p) {
RTC_DCHECK_GE(p.x, 0.0f);
RTC_DCHECK_LE(p.x, 1.0f);
RTC_DCHECK_GE(p.y, 0.0f);
RTC_DCHECK_LE(p.y, 1.0f);
ByteWriter<uint16_t>::WriteBigEndian(
data, std::round(p.x * kChromaticityDenominator));
ByteWriter<uint16_t>::WriteBigEndian(
@ -597,8 +648,10 @@ size_t ColorSpaceExtension::WriteLuminance(uint8_t* data,
float f,
int denominator) {
RTC_DCHECK_GE(f, 0.0f);
ByteWriter<uint32_t, 3>::WriteBigEndian(data, std::round(f * denominator));
return 3; // Return number of bytes written.
float upscaled_value = f * denominator;
RTC_DCHECK_LE(upscaled_value, std::numeric_limits<uint16_t>::max());
ByteWriter<uint16_t>::WriteBigEndian(data, std::round(upscaled_value));
return 2; // Return number of bytes written.
}
bool BaseRtpStringExtension::Parse(rtc::ArrayView<const uint8_t> data,