Index: webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter_unittest.cc |
diff --git a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter_unittest.cc b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter_unittest.cc |
index 83e633eb2953101817feb015f42a70e689ccf219..99387c9c74df1855f5ee8d641502952a3a27da23 100644 |
--- a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter_unittest.cc |
+++ b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter_unittest.cc |
@@ -8,6 +8,7 @@ |
* be found in the AUTHORS file in the root of the source tree. |
*/ |
+#include <array> |
#include <memory> |
#include <vector> |
@@ -117,7 +118,7 @@ class MockVideoEncoder : public VideoEncoder { |
return 0; |
} |
- int32_t Release() /* override */ { return 0; } |
+ MOCK_METHOD0(Release, int32_t()); |
int32_t SetRateAllocation(const BitrateAllocation& bitrate_allocation, |
uint32_t framerate) { |
@@ -142,7 +143,7 @@ class MockVideoEncoder : public VideoEncoder { |
image._encodedHeight = height; |
CodecSpecificInfo codec_specific_info; |
memset(&codec_specific_info, 0, sizeof(codec_specific_info)); |
- callback_->OnEncodedImage(image, &codec_specific_info, NULL); |
+ callback_->OnEncodedImage(image, &codec_specific_info, nullptr); |
} |
void set_supports_native_handle(bool enabled) { |
@@ -243,7 +244,11 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test, |
last_encoded_image_width_(-1), |
last_encoded_image_height_(-1), |
last_encoded_image_simulcast_index_(-1) {} |
- virtual ~TestSimulcastEncoderAdapterFake() {} |
+ virtual ~TestSimulcastEncoderAdapterFake() { |
+ if (adapter_) { |
+ adapter_->Release(); |
+ } |
+ } |
Result OnEncodedImage(const EncodedImage& encoded_image, |
const CodecSpecificInfo* codec_specific_info, |
@@ -367,6 +372,17 @@ TEST_F(TestSimulcastEncoderAdapterFake, InitEncode) { |
VerifyCodecSettings(); |
} |
+TEST_F(TestSimulcastEncoderAdapterFake, ReleaseWithoutInitEncode) { |
+ EXPECT_EQ(0, adapter_->Release()); |
+} |
+ |
+TEST_F(TestSimulcastEncoderAdapterFake, Reinit) { |
+ SetupCodec(); |
+ EXPECT_EQ(0, adapter_->Release()); |
+ |
+ EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200)); |
+} |
+ |
TEST_F(TestSimulcastEncoderAdapterFake, SetChannelParameters) { |
SetupCodec(); |
const uint32_t packetLoss = 5; |
@@ -386,8 +402,9 @@ TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) { |
// resolutions, to test that the adapter forwards on the correct resolution |
// and simulcast index values, going only off the encoder that generates the |
// image. |
- EXPECT_EQ(3u, helper_->factory()->encoders().size()); |
- helper_->factory()->encoders()[0]->SendEncodedImage(1152, 704); |
+ std::vector<MockVideoEncoder*> encoders = helper_->factory()->encoders(); |
+ ASSERT_EQ(3u, encoders.size()); |
+ encoders[0]->SendEncodedImage(1152, 704); |
int width; |
int height; |
int simulcast_index; |
@@ -396,19 +413,224 @@ TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) { |
EXPECT_EQ(704, height); |
EXPECT_EQ(0, simulcast_index); |
- helper_->factory()->encoders()[1]->SendEncodedImage(300, 620); |
+ encoders[1]->SendEncodedImage(300, 620); |
EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); |
EXPECT_EQ(300, width); |
EXPECT_EQ(620, height); |
EXPECT_EQ(1, simulcast_index); |
- helper_->factory()->encoders()[2]->SendEncodedImage(120, 240); |
+ encoders[2]->SendEncodedImage(120, 240); |
EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); |
EXPECT_EQ(120, width); |
EXPECT_EQ(240, height); |
EXPECT_EQ(2, simulcast_index); |
} |
+// This test verifies that the underlying encoders are reused, when the adapter |
+// is reinited with different number of simulcast streams. It further checks |
+// that the allocated encoders are reused in the same order as before, starting |
+// with the lowest stream. |
+TEST_F(TestSimulcastEncoderAdapterFake, ReusesEncodersInOrder) { |
+ // Set up common settings for three streams. |
+ TestVp8Simulcast::DefaultSettings( |
+ &codec_, static_cast<const int*>(kTestTemporalLayerProfile)); |
+ rate_allocator_.reset(new SimulcastRateAllocator(codec_, nullptr)); |
+ tl_factory_.SetListener(rate_allocator_.get()); |
+ codec_.VP8()->tl_factory = &tl_factory_; |
+ adapter_->RegisterEncodeCompleteCallback(this); |
+ |
+ // Input data. |
+ rtc::scoped_refptr<VideoFrameBuffer> buffer(I420Buffer::Create(1280, 720)); |
+ VideoFrame input_frame(buffer, 100, 1000, kVideoRotation_180); |
+ std::vector<FrameType> frame_types; |
+ |
+ // Encode with three streams. |
+ EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200)); |
+ VerifyCodecSettings(); |
+ std::vector<MockVideoEncoder*> original_encoders = |
+ helper_->factory()->encoders(); |
+ ASSERT_EQ(3u, original_encoders.size()); |
+ EXPECT_CALL(*original_encoders[0], Encode(_, _, _)) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_CALL(*original_encoders[1], Encode(_, _, _)) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_CALL(*original_encoders[2], Encode(_, _, _)) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ frame_types.resize(3, kVideoFrameKey); |
+ EXPECT_EQ(0, adapter_->Encode(input_frame, nullptr, &frame_types)); |
+ EXPECT_CALL(*original_encoders[0], Release()) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_CALL(*original_encoders[1], Release()) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_CALL(*original_encoders[2], Release()) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_EQ(0, adapter_->Release()); |
+ |
+ // Encode with two streams. |
+ codec_.width /= 2; |
+ codec_.height /= 2; |
+ codec_.numberOfSimulcastStreams = 2; |
+ EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200)); |
+ std::vector<MockVideoEncoder*> new_encoders = helper_->factory()->encoders(); |
+ ASSERT_EQ(2u, new_encoders.size()); |
+ ASSERT_EQ(original_encoders[0], new_encoders[0]); |
+ EXPECT_CALL(*original_encoders[0], Encode(_, _, _)) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ ASSERT_EQ(original_encoders[1], new_encoders[1]); |
+ EXPECT_CALL(*original_encoders[1], Encode(_, _, _)) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ frame_types.resize(2, kVideoFrameKey); |
+ EXPECT_EQ(0, adapter_->Encode(input_frame, nullptr, &frame_types)); |
+ EXPECT_CALL(*original_encoders[0], Release()) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_CALL(*original_encoders[1], Release()) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_EQ(0, adapter_->Release()); |
+ |
+ // Encode with single stream. |
+ codec_.width /= 2; |
+ codec_.height /= 2; |
+ codec_.numberOfSimulcastStreams = 1; |
+ EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200)); |
+ new_encoders = helper_->factory()->encoders(); |
+ ASSERT_EQ(1u, new_encoders.size()); |
+ ASSERT_EQ(original_encoders[0], new_encoders[0]); |
+ EXPECT_CALL(*original_encoders[0], Encode(_, _, _)) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ frame_types.resize(1, kVideoFrameKey); |
+ EXPECT_EQ(0, adapter_->Encode(input_frame, nullptr, &frame_types)); |
+ EXPECT_CALL(*original_encoders[0], Release()) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_EQ(0, adapter_->Release()); |
+ |
+ // Encode with three streams, again. |
+ codec_.width *= 4; |
+ codec_.height *= 4; |
+ codec_.numberOfSimulcastStreams = 3; |
+ EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200)); |
+ new_encoders = helper_->factory()->encoders(); |
+ ASSERT_EQ(3u, new_encoders.size()); |
+ // The first encoder is reused. |
+ ASSERT_EQ(original_encoders[0], new_encoders[0]); |
+ EXPECT_CALL(*original_encoders[0], Encode(_, _, _)) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ // The second and third encoders are new. |
+ EXPECT_CALL(*new_encoders[1], Encode(_, _, _)) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_CALL(*new_encoders[2], Encode(_, _, _)) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ frame_types.resize(3, kVideoFrameKey); |
+ EXPECT_EQ(0, adapter_->Encode(input_frame, nullptr, &frame_types)); |
+ EXPECT_CALL(*original_encoders[0], Release()) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_CALL(*new_encoders[1], Release()) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_CALL(*new_encoders[2], Release()) |
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); |
+ EXPECT_EQ(0, adapter_->Release()); |
+} |
+ |
+TEST_F(TestSimulcastEncoderAdapterFake, DoesNotLeakEncoders) { |
+ SetupCodec(); |
+ VerifyCodecSettings(); |
+ |
+ EXPECT_EQ(3u, helper_->factory()->encoders().size()); |
+ |
+ // The adapter should destroy all encoders it has allocated. Since |
+ // |helper_->factory()| is owned by |adapter_|, however, we need to rely on |
+ // lsan to find leaks here. |
+ EXPECT_EQ(0, adapter_->Release()); |
+ adapter_.reset(); |
+} |
+ |
+// This test verifies that an adapter reinit with the same codec settings as |
+// before does not change the underlying encoder codec settings. |
+TEST_F(TestSimulcastEncoderAdapterFake, ReinitDoesNotReorderEncoderSettings) { |
+ SetupCodec(); |
+ VerifyCodecSettings(); |
+ |
+ // Capture current codec settings. |
+ std::vector<MockVideoEncoder*> encoders = helper_->factory()->encoders(); |
+ ASSERT_EQ(3u, encoders.size()); |
+ std::array<VideoCodec, 3> codecs_before; |
+ for (int i = 0; i < 3; ++i) { |
+ codecs_before[i] = encoders[i]->codec(); |
+ } |
+ |
+ // Reinitialize and verify that the new codec settings are the same. |
+ EXPECT_EQ(0, adapter_->Release()); |
+ EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200)); |
+ for (int i = 0; i < 3; ++i) { |
+ const VideoCodec& codec_before = codecs_before[i]; |
+ const VideoCodec& codec_after = encoders[i]->codec(); |
+ |
+ // webrtc::VideoCodec does not implement operator==. |
+ EXPECT_EQ(codec_before.codecType, codec_after.codecType); |
+ EXPECT_EQ(codec_before.plType, codec_after.plType); |
+ EXPECT_EQ(codec_before.width, codec_after.width); |
+ EXPECT_EQ(codec_before.height, codec_after.height); |
+ EXPECT_EQ(codec_before.startBitrate, codec_after.startBitrate); |
+ EXPECT_EQ(codec_before.maxBitrate, codec_after.maxBitrate); |
+ EXPECT_EQ(codec_before.minBitrate, codec_after.minBitrate); |
+ EXPECT_EQ(codec_before.targetBitrate, codec_after.targetBitrate); |
+ EXPECT_EQ(codec_before.maxFramerate, codec_after.maxFramerate); |
+ EXPECT_EQ(codec_before.qpMax, codec_after.qpMax); |
+ EXPECT_EQ(codec_before.numberOfSimulcastStreams, |
+ codec_after.numberOfSimulcastStreams); |
+ EXPECT_EQ(codec_before.mode, codec_after.mode); |
+ EXPECT_EQ(codec_before.expect_encode_from_texture, |
+ codec_after.expect_encode_from_texture); |
+ } |
+} |
+ |
+// This test is similar to the one above, except that it tests the simulcastIdx |
+// from the CodecSpecificInfo that is connected to an encoded frame. The |
+// PayloadRouter demuxes the incoming encoded frames on different RTP modules |
+// using the simulcastIdx, so it's important that there is no corresponding |
+// encoder reordering in between adapter reinits as this would lead to PictureID |
+// discontinuities. |
+TEST_F(TestSimulcastEncoderAdapterFake, ReinitDoesNotReorderFrameSimulcastIdx) { |
+ SetupCodec(); |
+ adapter_->SetRateAllocation(rate_allocator_->GetAllocation(1200, 30), 30); |
+ VerifyCodecSettings(); |
+ |
+ // Send frames on all streams. |
+ std::vector<MockVideoEncoder*> encoders = helper_->factory()->encoders(); |
+ ASSERT_EQ(3u, encoders.size()); |
+ encoders[0]->SendEncodedImage(1152, 704); |
+ int width; |
+ int height; |
+ int simulcast_index; |
+ EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); |
+ EXPECT_EQ(0, simulcast_index); |
+ |
+ encoders[1]->SendEncodedImage(300, 620); |
+ EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); |
+ EXPECT_EQ(1, simulcast_index); |
+ |
+ encoders[2]->SendEncodedImage(120, 240); |
+ EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); |
+ EXPECT_EQ(2, simulcast_index); |
+ |
+ // Reinitialize. |
+ EXPECT_EQ(0, adapter_->Release()); |
+ EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200)); |
+ adapter_->SetRateAllocation(rate_allocator_->GetAllocation(1200, 30), 30); |
+ |
+ // Verify that the same encoder sends out frames on the same simulcast index. |
+ encoders[0]->SendEncodedImage(1152, 704); |
+ EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); |
+ EXPECT_EQ(0, simulcast_index); |
+ |
+ encoders[1]->SendEncodedImage(300, 620); |
+ EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); |
+ EXPECT_EQ(1, simulcast_index); |
+ |
+ encoders[2]->SendEncodedImage(120, 240); |
+ EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); |
+ EXPECT_EQ(2, simulcast_index); |
+} |
+ |
TEST_F(TestSimulcastEncoderAdapterFake, SupportsNativeHandleForSingleStreams) { |
TestVp8Simulcast::DefaultSettings( |
&codec_, static_cast<const int*>(kTestTemporalLayerProfile)); |
@@ -467,7 +689,7 @@ TEST_F(TestSimulcastEncoderAdapterFake, SupportsImplementationName) { |
adapter_->ImplementationName()); |
// Single streams should not expose "SimulcastEncoderAdapter" in name. |
- adapter_->Release(); |
+ EXPECT_EQ(0, adapter_->Release()); |
codec_.numberOfSimulcastStreams = 1; |
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200)); |
adapter_->RegisterEncodeCompleteCallback(this); |
@@ -528,7 +750,7 @@ TEST_F(TestSimulcastEncoderAdapterFake, |
for (MockVideoEncoder* encoder : helper_->factory()->encoders()) |
EXPECT_CALL(*encoder, Encode(::testing::Ref(input_frame), _, _)).Times(1); |
std::vector<FrameType> frame_types(3, kVideoFrameKey); |
- EXPECT_EQ(0, adapter_->Encode(input_frame, NULL, &frame_types)); |
+ EXPECT_EQ(0, adapter_->Encode(input_frame, nullptr, &frame_types)); |
} |
TEST_F(TestSimulcastEncoderAdapterFake, TestFailureReturnCodesFromEncodeCalls) { |