diff --git a/api/test/simulcast_test_fixture.h b/api/test/simulcast_test_fixture.h index e7eab24134..5270d13306 100644 --- a/api/test/simulcast_test_fixture.h +++ b/api/test/simulcast_test_fixture.h @@ -33,6 +33,7 @@ class SimulcastTestFixture { virtual void TestSpatioTemporalLayers333PatternEncoder() = 0; virtual void TestSpatioTemporalLayers321PatternEncoder() = 0; virtual void TestStrideEncodeDecode() = 0; + virtual void TestDecodeWidthHeightSet() = 0; }; } // namespace test diff --git a/media/engine/simulcast_encoder_adapter_unittest.cc b/media/engine/simulcast_encoder_adapter_unittest.cc index 5b7d185b8c..bd39595f0c 100644 --- a/media/engine/simulcast_encoder_adapter_unittest.cc +++ b/media/engine/simulcast_encoder_adapter_unittest.cc @@ -144,6 +144,12 @@ TEST(SimulcastEncoderAdapterSimulcastTest, fixture->TestSpatioTemporalLayers321PatternEncoder(); } +TEST(SimulcastEncoderAdapterSimulcastTest, TestDecodeWidthHeightSet) { + InternalEncoderFactory internal_encoder_factory; + auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); + fixture->TestDecodeWidthHeightSet(); +} + class MockVideoEncoder; class MockVideoEncoderFactory : public VideoEncoderFactory { diff --git a/modules/video_coding/utility/simulcast_test_fixture_impl.cc b/modules/video_coding/utility/simulcast_test_fixture_impl.cc index 25735931a2..edc15b72de 100644 --- a/modules/video_coding/utility/simulcast_test_fixture_impl.cc +++ b/modules/video_coding/utility/simulcast_test_fixture_impl.cc @@ -18,6 +18,7 @@ #include "api/video/encoded_image.h" #include "api/video_codecs/sdp_video_format.h" #include "common_video/libyuv/include/webrtc_libyuv.h" +#include "modules/video_coding/include/mock/mock_video_codec_interface.h" #include "modules/video_coding/include/video_coding_defines.h" #include "rtc_base/checks.h" #include "test/gtest.h" @@ -819,5 +820,67 @@ void SimulcastTestFixtureImpl::TestStrideEncodeDecode() { EXPECT_EQ(2, decoder_callback.DecodedFrames()); } +void SimulcastTestFixtureImpl::TestDecodeWidthHeightSet() { + MockEncodedImageCallback encoder_callback; + MockDecodedImageCallback decoder_callback; + + EncodedImage encoded_frame[3]; + SetRates(kMaxBitrates[2], 30); // To get all three streams. + encoder_->RegisterEncodeCompleteCallback(&encoder_callback); + decoder_->RegisterDecodeCompleteCallback(&decoder_callback); + + EXPECT_CALL(encoder_callback, OnEncodedImage(_, _, _)) + .Times(3) + .WillRepeatedly( + testing::Invoke([&](const EncodedImage& encoded_image, + const CodecSpecificInfo* codec_specific_info, + const RTPFragmentationHeader* fragmentation) { + EXPECT_EQ(encoded_image._frameType, kVideoFrameKey); + + size_t index = encoded_image.SpatialIndex().value_or(0); + encoded_frame[index]._buffer = new uint8_t[encoded_image._size]; + encoded_frame[index]._size = encoded_image._size; + encoded_frame[index]._length = encoded_image._length; + encoded_frame[index]._frameType = encoded_image._frameType; + encoded_frame[index]._completeFrame = encoded_image._completeFrame; + memcpy(encoded_frame[index]._buffer, encoded_image._buffer, + encoded_image._length); + return EncodedImageCallback::Result( + EncodedImageCallback::Result::OK, 0); + })); + EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, NULL)); + + EXPECT_CALL(decoder_callback, Decoded(_, _, _)) + .WillOnce(testing::Invoke([](VideoFrame& decodedImage, + absl::optional decode_time_ms, + absl::optional qp) { + EXPECT_EQ(decodedImage.width(), kDefaultWidth / 4); + EXPECT_EQ(decodedImage.height(), kDefaultHeight / 4); + })); + EXPECT_EQ(0, decoder_->Decode(encoded_frame[0], false, NULL, 0)); + + EXPECT_CALL(decoder_callback, Decoded(_, _, _)) + .WillOnce(testing::Invoke([](VideoFrame& decodedImage, + absl::optional decode_time_ms, + absl::optional qp) { + EXPECT_EQ(decodedImage.width(), kDefaultWidth / 2); + EXPECT_EQ(decodedImage.height(), kDefaultHeight / 2); + })); + EXPECT_EQ(0, decoder_->Decode(encoded_frame[1], false, NULL, 0)); + + EXPECT_CALL(decoder_callback, Decoded(_, _, _)) + .WillOnce(testing::Invoke([](VideoFrame& decodedImage, + absl::optional decode_time_ms, + absl::optional qp) { + EXPECT_EQ(decodedImage.width(), kDefaultWidth); + EXPECT_EQ(decodedImage.height(), kDefaultHeight); + })); + EXPECT_EQ(0, decoder_->Decode(encoded_frame[2], false, NULL, 0)); + + for (int i = 0; i < 3; ++i) { + delete [] encoded_frame[i]._buffer; + } +} + } // namespace test } // namespace webrtc diff --git a/modules/video_coding/utility/simulcast_test_fixture_impl.h b/modules/video_coding/utility/simulcast_test_fixture_impl.h index 6634a69ad1..1e64ac5817 100644 --- a/modules/video_coding/utility/simulcast_test_fixture_impl.h +++ b/modules/video_coding/utility/simulcast_test_fixture_impl.h @@ -50,6 +50,7 @@ class SimulcastTestFixtureImpl final : public SimulcastTestFixture { void TestSpatioTemporalLayers333PatternEncoder() override; void TestSpatioTemporalLayers321PatternEncoder() override; void TestStrideEncodeDecode() override; + void TestDecodeWidthHeightSet() override; static void DefaultSettings(VideoCodec* settings, const int* temporal_layer_profile, diff --git a/test/BUILD.gn b/test/BUILD.gn index 6a09c9aedf..666e3c08a4 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -523,6 +523,8 @@ rtc_source_set("fake_video_codecs") { "fake_decoder.h", "fake_encoder.cc", "fake_encoder.h", + "fake_vp8_decoder.cc", + "fake_vp8_decoder.h", "fake_vp8_encoder.cc", "fake_vp8_encoder.h", ] diff --git a/test/fake_encoder.cc b/test/fake_encoder.cc index bc3c7b1087..bc23c32dd2 100644 --- a/test/fake_encoder.cc +++ b/test/fake_encoder.cc @@ -94,7 +94,8 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image, NextFrame(frame_types, keyframe, num_simulcast_streams, target_bitrate, simulcast_streams, framerate); for (uint8_t i = 0; i < frame_info.layers.size(); ++i) { - if (frame_info.layers[i].size == 0) { + constexpr int kMinPayLoadLength = 10; + if (frame_info.layers[i].size < kMinPayLoadLength) { // Drop this temporal layer. continue; } diff --git a/test/fake_vp8_decoder.cc b/test/fake_vp8_decoder.cc new file mode 100644 index 0000000000..f15bb2149d --- /dev/null +++ b/test/fake_vp8_decoder.cc @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#include "test/fake_vp8_decoder.h" + +#include "api/video/i420_buffer.h" +#include "rtc_base/timeutils.h" + +namespace webrtc { +namespace test { + +namespace { +// Read width and height from the payload of the frame if it is a key frame the +// same way as the real VP8 decoder. +// FakeEncoder writes width, height and frame type. +void ParseFakeVp8(const unsigned char* data, int* width, int* height) { + bool key_frame = data[0] == 0; + if (key_frame) { + *width = ((data[7] << 8) + data[6]) & 0x3FFF; + *height = ((data[9] << 8) + data[8]) & 0x3FFF; + } +} +} // namespace + +FakeVp8Decoder::FakeVp8Decoder() : callback_(nullptr), width_(0), height_(0) {} + +int32_t FakeVp8Decoder::InitDecode(const VideoCodec* config, + int32_t number_of_cores) { + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t FakeVp8Decoder::Decode(const EncodedImage& input, + bool missing_frames, + const CodecSpecificInfo* codec_specific_info, + int64_t render_time_ms) { + constexpr size_t kMinPayLoadHeaderLength = 10; + if (input._length < kMinPayLoadHeaderLength) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + ParseFakeVp8(input._buffer, &width_, &height_); + + VideoFrame frame(I420Buffer::Create(width_, height_), + webrtc::kVideoRotation_0, + render_time_ms * rtc::kNumMicrosecsPerMillisec); + frame.set_timestamp(input.Timestamp()); + frame.set_ntp_time_ms(input.ntp_time_ms_); + + callback_->Decoded(frame, /*decode_time_ms=*/absl::nullopt, + /*qp=*/absl::nullopt); + + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t FakeVp8Decoder::RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) { + callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t FakeVp8Decoder::Release() { + return WEBRTC_VIDEO_CODEC_OK; +} + +const char* FakeVp8Decoder::kImplementationName = "fake_vp8_decoder"; +const char* FakeVp8Decoder::ImplementationName() const { + return kImplementationName; +} + +} // namespace test +} // namespace webrtc diff --git a/test/fake_vp8_decoder.h b/test/fake_vp8_decoder.h new file mode 100644 index 0000000000..974af403d6 --- /dev/null +++ b/test/fake_vp8_decoder.h @@ -0,0 +1,51 @@ +/* + * 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 TEST_FAKE_VP8_DECODER_H_ +#define TEST_FAKE_VP8_DECODER_H_ + +#include "modules/video_coding/include/video_codec_interface.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { +namespace test { + +class FakeVp8Decoder : public VideoDecoder { + public: + FakeVp8Decoder(); + ~FakeVp8Decoder() override {} + + int32_t InitDecode(const VideoCodec* config, + int32_t number_of_cores) override; + + int32_t Decode(const EncodedImage& input, + bool missing_frames, + const CodecSpecificInfo* codec_specific_info, + int64_t render_time_ms) override; + + int32_t RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) override; + + int32_t Release() override; + + const char* ImplementationName() const override; + + static const char* kImplementationName; + + private: + DecodedImageCallback* callback_; + int width_; + int height_; +}; + +} // namespace test +} // namespace webrtc + +#endif // TEST_FAKE_VP8_DECODER_H_ diff --git a/test/fake_vp8_encoder.cc b/test/fake_vp8_encoder.cc index 4038837798..5c299a7d5e 100644 --- a/test/fake_vp8_encoder.cc +++ b/test/fake_vp8_encoder.cc @@ -20,6 +20,26 @@ #include "rtc_base/random.h" #include "rtc_base/timeutils.h" +namespace { + +// Write width and height to the payload the same way as the real encoder does. +// It requires that |payload| has a size of at least kMinPayLoadHeaderLength. +void WriteFakeVp8(unsigned char* payload, + int width, + int height, + bool key_frame) { + payload[0] = key_frame ? 0 : 0x01; + + if (key_frame) { + payload[9] = (height & 0x3F00) >> 8; + payload[8] = (height & 0x00FF); + + payload[7] = (width & 0x3F00) >> 8; + payload[6] = (width & 0x00FF); + } +} +} // namespace + namespace webrtc { namespace test { @@ -105,6 +125,11 @@ EncodedImageCallback::Result FakeVP8Encoder::OnEncodedImage( encoded_image._frameType, stream_idx, encoded_image.Timestamp()); + // Write width and height to the payload the same way as the real encoder + // does. + WriteFakeVp8(encoded_image._buffer, encoded_image._encodedWidth, + encoded_image._encodedHeight, + encoded_image._frameType == kVideoFrameKey); return callback_->OnEncodedImage(encoded_image, &overrided_specific_info, fragments); } diff --git a/test/fake_vp8_encoder_unittest.cc b/test/fake_vp8_encoder_unittest.cc index c79ba0c551..d881edcaab 100644 --- a/test/fake_vp8_encoder_unittest.cc +++ b/test/fake_vp8_encoder_unittest.cc @@ -15,7 +15,7 @@ #include "api/test/create_simulcast_test_fixture.h" #include "api/test/simulcast_test_fixture.h" #include "modules/video_coding/utility/simulcast_test_fixture_impl.h" -#include "test/fake_decoder.h" +#include "test/fake_vp8_decoder.h" #include "test/fake_vp8_encoder.h" #include "test/function_video_decoder_factory.h" #include "test/function_video_encoder_factory.h" @@ -32,7 +32,7 @@ std::unique_ptr CreateSpecificSimulcastTestFixture() { }); std::unique_ptr decoder_factory = absl::make_unique( - []() { return absl::make_unique(); }); + []() { return absl::make_unique(); }); return CreateSimulcastTestFixture(std::move(encoder_factory), std::move(decoder_factory), SdpVideoFormat("VP8")); @@ -99,5 +99,10 @@ TEST(TestFakeVp8Codec, TestSpatioTemporalLayers333PatternEncoder) { fixture->TestSpatioTemporalLayers333PatternEncoder(); } +TEST(TestFakeVp8Codec, TestDecodeWidthHeightSet) { + auto fixture = CreateSpecificSimulcastTestFixture(); + fixture->TestDecodeWidthHeightSet(); +} + } // namespace test } // namespace webrtc