Add end-to-end test for ColorSpace information
Bug: webrtc:8651
Change-Id: Ib7f6162c8dd41868b7d7acca8a6292b2d911c520
Reviewed-on: https://webrtc-review.googlesource.com/c/113941
Commit-Queue: Johannes Kron <kron@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25992}
diff --git a/api/video/video_frame.h b/api/video/video_frame.h
index 2c5d081..cc61fee 100644
--- a/api/video/video_frame.h
+++ b/api/video/video_frame.h
@@ -118,6 +118,10 @@
const ColorSpace* color_space() const {
return color_space_ ? &*color_space_ : nullptr;
}
+ void set_color_space(ColorSpace* color_space) {
+ color_space_ =
+ color_space ? absl::make_optional(*color_space) : absl::nullopt;
+ }
// Get render time in milliseconds.
// TODO(nisse): Deprecated. Migrate all users to timestamp_us().
diff --git a/test/call_test.cc b/test/call_test.cc
index 6e25b85..5d2bbca 100644
--- a/test/call_test.cc
+++ b/test/call_test.cc
@@ -31,10 +31,6 @@
namespace webrtc {
namespace test {
-namespace {
-const int kVideoRotationRtpExtensionId = 4;
-}
-
CallTest::CallTest()
: clock_(Clock::GetRealTimeClock()),
send_event_log_(RtcEventLog::CreateNull()),
@@ -240,6 +236,7 @@
bitrate_allocator_factory_.get();
video_config->rtp.payload_name = "FAKE";
video_config->rtp.payload_type = kFakeVideoSendPayloadType;
+ video_config->rtp.extmap_allow_mixed = true;
video_config->rtp.extensions.push_back(
RtpExtension(RtpExtension::kTransportSequenceNumberUri,
kTransportSequenceNumberExtensionId));
@@ -255,8 +252,10 @@
for (size_t i = 0; i < num_video_streams; ++i)
video_config->rtp.ssrcs.push_back(kVideoSendSsrcs[num_used_ssrcs + i]);
- video_config->rtp.extensions.push_back(RtpExtension(
- RtpExtension::kVideoRotationUri, kVideoRotationRtpExtensionId));
+ video_config->rtp.extensions.push_back(
+ RtpExtension(RtpExtension::kVideoRotationUri, kVideoRotationExtensionId));
+ video_config->rtp.extensions.push_back(
+ RtpExtension(RtpExtension::kColorSpaceUri, kColorSpaceExtensionId));
}
void CallTest::CreateAudioAndFecSendConfigs(size_t num_audio_streams,
diff --git a/test/constants.cc b/test/constants.cc
index 12ae8e8..4f33d25 100644
--- a/test/constants.cc
+++ b/test/constants.cc
@@ -21,6 +21,7 @@
const int kVideoContentTypeExtensionId = 10;
const int kVideoTimingExtensionId = 11;
const int kGenericDescriptorExtensionId = 12;
+const int kColorSpaceExtensionId = 13;
} // namespace test
} // namespace webrtc
diff --git a/test/constants.h b/test/constants.h
index 3cee828..b1b87d6 100644
--- a/test/constants.h
+++ b/test/constants.h
@@ -19,5 +19,6 @@
extern const int kVideoContentTypeExtensionId;
extern const int kVideoTimingExtensionId;
extern const int kGenericDescriptorExtensionId;
+extern const int kColorSpaceExtensionId;
} // namespace test
} // namespace webrtc
diff --git a/test/frame_generator.h b/test/frame_generator.h
index a92c3b4..45cc9f0 100644
--- a/test/frame_generator.h
+++ b/test/frame_generator.h
@@ -50,6 +50,9 @@
virtual ~FrameGenerator() = default;
// Returns video frame that remains valid until next call.
+ // TODO(kron): Return rtc::scoped_refptr<VideoFrameBuffer> instead of
+ // VideoFrame* and populate the VideoFrame struct in FrameGeneratorCapturer
+ // using VideoFrame::Builder.
virtual VideoFrame* NextFrame() = 0;
// Change the capture resolution.
diff --git a/test/frame_generator_capturer.cc b/test/frame_generator_capturer.cc
index 8729187..6ca1ddb 100644
--- a/test/frame_generator_capturer.cc
+++ b/test/frame_generator_capturer.cc
@@ -147,6 +147,12 @@
fake_rotation_ = rotation;
}
+void FrameGeneratorCapturer::SetFakeColorSpace(
+ absl::optional<ColorSpace> color_space) {
+ rtc::CritScope cs(&lock_);
+ fake_color_space_ = color_space;
+}
+
bool FrameGeneratorCapturer::Init() {
// This check is added because frame_generator_ might be file based and should
// not crash because a file moved.
@@ -173,6 +179,9 @@
frame->set_timestamp_us(clock_->TimeInMicroseconds());
frame->set_ntp_time_ms(clock_->CurrentNtpInMilliseconds());
frame->set_rotation(fake_rotation_);
+ if (fake_color_space_) {
+ frame->set_color_space(&fake_color_space_.value());
+ }
if (first_frame_capture_time_ == -1) {
first_frame_capture_time_ = frame->ntp_time_ms();
}
diff --git a/test/frame_generator_capturer.h b/test/frame_generator_capturer.h
index 6b2ed69..a5e60a5 100644
--- a/test/frame_generator_capturer.h
+++ b/test/frame_generator_capturer.h
@@ -74,6 +74,7 @@
void ForceFrame();
void SetFakeRotation(VideoRotation rotation);
+ void SetFakeColorSpace(absl::optional<ColorSpace> color_space);
int64_t first_frame_capture_time() const { return first_frame_capture_time_; }
@@ -101,6 +102,7 @@
int target_capture_fps_ RTC_GUARDED_BY(&lock_);
absl::optional<int> wanted_fps_ RTC_GUARDED_BY(&lock_);
VideoRotation fake_rotation_ = kVideoRotation_0;
+ absl::optional<ColorSpace> fake_color_space_ RTC_GUARDED_BY(&lock_);
int64_t first_frame_capture_time_;
// Must be the last field, so it will be deconstructed first as tasks
diff --git a/video/end_to_end_tests/codec_tests.cc b/video/end_to_end_tests/codec_tests.cc
index 67ef611..4482a09 100644
--- a/video/end_to_end_tests/codec_tests.cc
+++ b/video/end_to_end_tests/codec_tests.cc
@@ -8,7 +8,10 @@
* be found in the AUTHORS file in the root of the source tree.
*/
+#include "absl/types/optional.h"
#include "api/test/video/function_video_encoder_factory.h"
+#include "api/video/color_space.h"
+#include "api/video/video_rotation.h"
#include "media/engine/internaldecoderfactory.h"
#include "media/engine/internalencoderfactory.h"
#include "modules/video_coding/codecs/h264/include/h264.h"
@@ -22,6 +25,35 @@
#include "test/gtest.h"
namespace webrtc {
+namespace {
+HdrMetadata CreateTestHdrMetadata() {
+ // Random but reasonable HDR metadata.
+ HdrMetadata hdr_metadata;
+ hdr_metadata.mastering_metadata.luminance_max = 2000.0;
+ hdr_metadata.mastering_metadata.luminance_min = 2.0001;
+ hdr_metadata.mastering_metadata.primary_r.x = 0.3003;
+ hdr_metadata.mastering_metadata.primary_r.y = 0.4004;
+ hdr_metadata.mastering_metadata.primary_g.x = 0.3201;
+ hdr_metadata.mastering_metadata.primary_g.y = 0.4604;
+ hdr_metadata.mastering_metadata.primary_b.x = 0.3409;
+ hdr_metadata.mastering_metadata.primary_b.y = 0.4907;
+ hdr_metadata.mastering_metadata.white_point.x = 0.4103;
+ hdr_metadata.mastering_metadata.white_point.y = 0.4806;
+ hdr_metadata.max_content_light_level = 2345;
+ hdr_metadata.max_frame_average_light_level = 1789;
+ return hdr_metadata;
+}
+
+ColorSpace CreateTestColorSpace(bool with_hdr_metadata) {
+ HdrMetadata hdr_metadata = CreateTestHdrMetadata();
+ return ColorSpace(
+ ColorSpace::PrimaryID::kBT709, ColorSpace::TransferID::kGAMMA22,
+ ColorSpace::MatrixID::kSMPTE2085, ColorSpace::RangeID::kFull,
+ ColorSpace::ChromaSiting::kCollocated,
+ ColorSpace::ChromaSiting::kCollocated,
+ with_hdr_metadata ? &hdr_metadata : nullptr);
+}
+} // namespace
class CodecEndToEndTest : public test::CallTest,
public testing::WithParamInterface<std::string> {
@@ -37,6 +69,7 @@
public:
CodecObserver(int no_frames_to_wait_for,
VideoRotation rotation_to_test,
+ absl::optional<ColorSpace> color_space_to_test,
const std::string& payload_name,
VideoEncoderFactory* encoder_factory,
VideoDecoderFactory* decoder_factory)
@@ -45,6 +78,7 @@
// https://bugs.webrtc.org/6830
no_frames_to_wait_for_(no_frames_to_wait_for),
expected_rotation_(rotation_to_test),
+ expected_color_space_(color_space_to_test),
payload_name_(payload_name),
encoder_factory_(encoder_factory),
decoder_factory_(decoder_factory),
@@ -75,6 +109,14 @@
void OnFrame(const VideoFrame& video_frame) override {
EXPECT_EQ(expected_rotation_, video_frame.rotation());
+ // Test only if explicit color space has been specified since otherwise the
+ // color space is codec dependent.
+ if (expected_color_space_) {
+ EXPECT_EQ(expected_color_space_,
+ video_frame.color_space()
+ ? absl::make_optional(*video_frame.color_space())
+ : absl::nullopt);
+ }
if (++frame_counter_ == no_frames_to_wait_for_)
observation_complete_.Set();
}
@@ -82,11 +124,13 @@
void OnFrameGeneratorCapturerCreated(
test::FrameGeneratorCapturer* frame_generator_capturer) override {
frame_generator_capturer->SetFakeRotation(expected_rotation_);
+ frame_generator_capturer->SetFakeColorSpace(expected_color_space_);
}
private:
int no_frames_to_wait_for_;
VideoRotation expected_rotation_;
+ absl::optional<ColorSpace> expected_color_space_;
std::string payload_name_;
VideoEncoderFactory* encoder_factory_;
VideoDecoderFactory* decoder_factory_;
@@ -103,8 +147,8 @@
[]() { return VP8Encoder::Create(); });
test::FunctionVideoDecoderFactory decoder_factory(
[]() { return VP8Decoder::Create(); });
- CodecObserver test(5, kVideoRotation_0, "VP8", &encoder_factory,
- &decoder_factory);
+ CodecObserver test(5, kVideoRotation_0, absl::nullopt, "VP8",
+ &encoder_factory, &decoder_factory);
RunBaseTest(&test);
}
@@ -113,8 +157,8 @@
[]() { return VP8Encoder::Create(); });
test::FunctionVideoDecoderFactory decoder_factory(
[]() { return VP8Decoder::Create(); });
- CodecObserver test(5, kVideoRotation_90, "VP8", &encoder_factory,
- &decoder_factory);
+ CodecObserver test(5, kVideoRotation_90, absl::nullopt, "VP8",
+ &encoder_factory, &decoder_factory);
RunBaseTest(&test);
}
@@ -124,8 +168,8 @@
[]() { return VP9Encoder::Create(); });
test::FunctionVideoDecoderFactory decoder_factory(
[]() { return VP9Decoder::Create(); });
- CodecObserver test(500, kVideoRotation_0, "VP9", &encoder_factory,
- &decoder_factory);
+ CodecObserver test(500, kVideoRotation_0, absl::nullopt, "VP9",
+ &encoder_factory, &decoder_factory);
RunBaseTest(&test);
}
@@ -134,8 +178,31 @@
[]() { return VP9Encoder::Create(); });
test::FunctionVideoDecoderFactory decoder_factory(
[]() { return VP9Decoder::Create(); });
- CodecObserver test(5, kVideoRotation_90, "VP9", &encoder_factory,
- &decoder_factory);
+ CodecObserver test(5, kVideoRotation_90, absl::nullopt, "VP9",
+ &encoder_factory, &decoder_factory);
+ RunBaseTest(&test);
+}
+
+TEST_P(CodecEndToEndTest, SendsAndReceivesVP9ExplicitColorSpace) {
+ test::FunctionVideoEncoderFactory encoder_factory(
+ []() { return VP9Encoder::Create(); });
+ test::FunctionVideoDecoderFactory decoder_factory(
+ []() { return VP9Decoder::Create(); });
+ CodecObserver test(5, kVideoRotation_90,
+ CreateTestColorSpace(/*with_hdr_metadata=*/false), "VP9",
+ &encoder_factory, &decoder_factory);
+ RunBaseTest(&test);
+}
+
+TEST_P(CodecEndToEndTest,
+ SendsAndReceivesVP9ExplicitColorSpaceWithHdrMetadata) {
+ test::FunctionVideoEncoderFactory encoder_factory(
+ []() { return VP9Encoder::Create(); });
+ test::FunctionVideoDecoderFactory decoder_factory(
+ []() { return VP9Decoder::Create(); });
+ CodecObserver test(5, kVideoRotation_90,
+ CreateTestColorSpace(/*with_hdr_metadata=*/true), "VP9",
+ &encoder_factory, &decoder_factory);
RunBaseTest(&test);
}
@@ -154,8 +221,8 @@
&internal_decoder_factory, SdpVideoFormat(cricket::kVp9CodecName));
});
- CodecObserver test(5, kVideoRotation_0, "multiplex", &encoder_factory,
- &decoder_factory);
+ CodecObserver test(5, kVideoRotation_0, absl::nullopt, "multiplex",
+ &encoder_factory, &decoder_factory);
RunBaseTest(&test);
}
@@ -172,8 +239,8 @@
return absl::make_unique<MultiplexDecoderAdapter>(
&internal_decoder_factory, SdpVideoFormat(cricket::kVp9CodecName));
});
- CodecObserver test(5, kVideoRotation_90, "multiplex", &encoder_factory,
- &decoder_factory);
+ CodecObserver test(5, kVideoRotation_90, absl::nullopt, "multiplex",
+ &encoder_factory, &decoder_factory);
RunBaseTest(&test);
}
@@ -200,8 +267,8 @@
[]() { return H264Encoder::Create(cricket::VideoCodec("H264")); });
test::FunctionVideoDecoderFactory decoder_factory(
[]() { return H264Decoder::Create(); });
- CodecObserver test(500, kVideoRotation_0, "H264", &encoder_factory,
- &decoder_factory);
+ CodecObserver test(500, kVideoRotation_0, absl::nullopt, "H264",
+ &encoder_factory, &decoder_factory);
RunBaseTest(&test);
}
@@ -210,8 +277,8 @@
[]() { return H264Encoder::Create(cricket::VideoCodec("H264")); });
test::FunctionVideoDecoderFactory decoder_factory(
[]() { return H264Decoder::Create(); });
- CodecObserver test(5, kVideoRotation_90, "H264", &encoder_factory,
- &decoder_factory);
+ CodecObserver test(5, kVideoRotation_90, absl::nullopt, "H264",
+ &encoder_factory, &decoder_factory);
RunBaseTest(&test);
}
@@ -222,8 +289,8 @@
[codec]() { return H264Encoder::Create(codec); });
test::FunctionVideoDecoderFactory decoder_factory(
[]() { return H264Decoder::Create(); });
- CodecObserver test(500, kVideoRotation_0, "H264", &encoder_factory,
- &decoder_factory);
+ CodecObserver test(500, kVideoRotation_0, absl::nullopt, "H264",
+ &encoder_factory, &decoder_factory);
RunBaseTest(&test);
}
@@ -234,8 +301,8 @@
[codec]() { return H264Encoder::Create(codec); });
test::FunctionVideoDecoderFactory decoder_factory(
[]() { return H264Decoder::Create(); });
- CodecObserver test(500, kVideoRotation_0, "H264", &encoder_factory,
- &decoder_factory);
+ CodecObserver test(500, kVideoRotation_0, absl::nullopt, "H264",
+ &encoder_factory, &decoder_factory);
RunBaseTest(&test);
}
#endif // defined(WEBRTC_USE_H264)