Factor encoded frame generation code into own target

Show it can make vp9 tests cleaner too.

Bug: None
Change-Id: I8333a61dec1ef90ade9faffea94e1555ccbfcfaa
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/177013
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31523}
This commit is contained in:
Danil Chapovalov
2020-06-15 12:55:22 +02:00
committed by Commit Bot
parent 9465978a3b
commit 90dc5dae2c
6 changed files with 321 additions and 257 deletions

View File

@ -146,9 +146,8 @@ if (rtc_include_tests) {
":libaom_av1_encoder",
":scalability_structures",
":scalable_video_controller",
"../..:encoded_video_frame_producer",
"../..:video_codec_interface",
"../../../../api:create_frame_generator",
"../../../../api:frame_generator_api",
"../../../../api:mock_video_encoder",
"../../../../api/video:video_frame_i420",
"../../../../api/video_codecs:video_codecs_api",

View File

@ -15,8 +15,6 @@
#include <vector>
#include "absl/types/optional.h"
#include "api/test/create_frame_generator.h"
#include "api/test/frame_generator_interface.h"
#include "api/video_codecs/video_codec.h"
#include "api/video_codecs/video_encoder.h"
#include "modules/video_coding/codecs/av1/libaom_av1_decoder.h"
@ -33,6 +31,7 @@
#include "modules/video_coding/codecs/av1/scalability_structure_s2t1.h"
#include "modules/video_coding/codecs/av1/scalable_video_controller.h"
#include "modules/video_coding/codecs/av1/scalable_video_controller_no_layering.h"
#include "modules/video_coding/codecs/test/encoded_video_frame_producer.h"
#include "modules/video_coding/include/video_codec_interface.h"
#include "modules/video_coding/include/video_error_codes.h"
#include "test/gmock.h"
@ -56,72 +55,21 @@ using ::testing::Values;
constexpr int kWidth = 320;
constexpr int kHeight = 180;
constexpr int kFramerate = 30;
constexpr int kRtpTicksPerSecond = 90000;
class TestAv1Encoder {
public:
struct Encoded {
EncodedImage encoded_image;
CodecSpecificInfo codec_specific_info;
};
TestAv1Encoder() : encoder_(CreateLibaomAv1Encoder()) { InitEncoder(); }
explicit TestAv1Encoder(
std::unique_ptr<ScalableVideoController> svc_controller)
: encoder_(CreateLibaomAv1Encoder(std::move(svc_controller))) {
InitEncoder();
}
// This class requires pointer stability and thus not copyable nor movable.
TestAv1Encoder(const TestAv1Encoder&) = delete;
TestAv1Encoder& operator=(const TestAv1Encoder&) = delete;
void EncodeAndAppend(const VideoFrame& frame, std::vector<Encoded>* encoded) {
callback_.SetEncodeStorage(encoded);
std::vector<VideoFrameType> frame_types = {
VideoFrameType::kVideoFrameDelta};
EXPECT_EQ(encoder_->Encode(frame, &frame_types), WEBRTC_VIDEO_CODEC_OK);
// Prefer to crash checking nullptr rather than writing to random memory.
callback_.SetEncodeStorage(nullptr);
}
private:
class EncoderCallback : public EncodedImageCallback {
public:
void SetEncodeStorage(std::vector<Encoded>* storage) { storage_ = storage; }
private:
Result OnEncodedImage(
const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* /*fragmentation*/) override {
RTC_CHECK(storage_);
storage_->push_back({encoded_image, *codec_specific_info});
return Result(Result::Error::OK);
}
std::vector<Encoded>* storage_ = nullptr;
};
void InitEncoder() {
RTC_CHECK(encoder_);
VideoCodec codec_settings;
codec_settings.width = kWidth;
codec_settings.height = kHeight;
codec_settings.maxFramerate = kFramerate;
codec_settings.maxBitrate = 1000;
codec_settings.qpMax = 63;
VideoEncoder::Settings encoder_settings(
VideoEncoder::Capabilities(/*loss_notification=*/false),
/*number_of_cores=*/1, /*max_payload_size=*/1200);
EXPECT_EQ(encoder_->InitEncode(&codec_settings, encoder_settings),
WEBRTC_VIDEO_CODEC_OK);
EXPECT_EQ(encoder_->RegisterEncodeCompleteCallback(&callback_),
WEBRTC_VIDEO_CODEC_OK);
}
EncoderCallback callback_;
std::unique_ptr<VideoEncoder> encoder_;
};
VideoCodec DefaultCodecSettings() {
VideoCodec codec_settings;
codec_settings.width = kWidth;
codec_settings.height = kHeight;
codec_settings.maxFramerate = kFramerate;
codec_settings.maxBitrate = 1000;
codec_settings.qpMax = 63;
return codec_settings;
}
VideoEncoder::Settings DefaultEncoderSettings() {
return VideoEncoder::Settings(
VideoEncoder::Capabilities(/*loss_notification=*/false),
/*number_of_cores=*/1, /*max_payload_size=*/1200);
}
class TestAv1Decoder {
public:
@ -186,34 +134,15 @@ class TestAv1Decoder {
const std::unique_ptr<VideoDecoder> decoder_;
};
class VideoFrameGenerator {
public:
VideoFrame NextFrame() {
return VideoFrame::Builder()
.set_video_frame_buffer(frame_buffer_generator_->NextFrame().buffer)
.set_timestamp_rtp(timestamp_ += kRtpTicksPerSecond / kFramerate)
.build();
}
private:
uint32_t timestamp_ = 1000;
std::unique_ptr<test::FrameGeneratorInterface> frame_buffer_generator_ =
test::CreateSquareFrameGenerator(
kWidth,
kHeight,
test::FrameGeneratorInterface::OutputType::kI420,
absl::nullopt);
};
TEST(LibaomAv1Test, EncodeDecode) {
TestAv1Decoder decoder(0);
TestAv1Encoder encoder;
VideoFrameGenerator generator;
std::unique_ptr<VideoEncoder> encoder = CreateLibaomAv1Encoder();
VideoCodec codec_settings = DefaultCodecSettings();
ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()),
WEBRTC_VIDEO_CODEC_OK);
std::vector<TestAv1Encoder::Encoded> encoded_frames;
for (size_t i = 0; i < 4; ++i) {
encoder.EncodeAndAppend(generator.NextFrame(), &encoded_frames);
}
std::vector<EncodedVideoFrameProducer::EncodedFrame> encoded_frames =
EncodedVideoFrameProducer(*encoder).SetNumInputFrames(4).Encode();
for (size_t frame_id = 0; frame_id < encoded_frames.size(); ++frame_id) {
decoder.Decode(static_cast<int64_t>(frame_id),
encoded_frames[frame_id].encoded_image);
@ -240,16 +169,20 @@ TEST_P(LibaomAv1SvcTest, EncodeAndDecodeAllDecodeTargets) {
size_t num_decode_targets =
svc_controller->DependencyStructure().num_decode_targets;
std::vector<TestAv1Encoder::Encoded> encoded_frames;
TestAv1Encoder encoder(std::move(svc_controller));
VideoFrameGenerator generator;
for (int temporal_unit = 0; temporal_unit < GetParam().num_frames_to_generate;
++temporal_unit) {
encoder.EncodeAndAppend(generator.NextFrame(), &encoded_frames);
}
std::unique_ptr<VideoEncoder> encoder =
CreateLibaomAv1Encoder(std::move(svc_controller));
VideoCodec codec_settings = DefaultCodecSettings();
ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()),
WEBRTC_VIDEO_CODEC_OK);
std::vector<EncodedVideoFrameProducer::EncodedFrame> encoded_frames =
EncodedVideoFrameProducer(*encoder)
.SetNumInputFrames(GetParam().num_frames_to_generate)
.SetResolution({kWidth, kHeight})
.Encode();
ASSERT_THAT(
encoded_frames, Each(Truly([&](const TestAv1Encoder::Encoded& frame) {
encoded_frames,
Each(Truly([&](const EncodedVideoFrameProducer::EncodedFrame& frame) {
return frame.codec_specific_info.generic_frame_info &&
frame.codec_specific_info.generic_frame_info
->decode_target_indications.size() == num_decode_targets;
@ -260,7 +193,8 @@ TEST_P(LibaomAv1SvcTest, EncodeAndDecodeAllDecodeTargets) {
std::vector<int64_t> requested_ids;
for (int64_t frame_id = 0;
frame_id < static_cast<int64_t>(encoded_frames.size()); ++frame_id) {
const TestAv1Encoder::Encoded& frame = encoded_frames[frame_id];
const EncodedVideoFrameProducer::EncodedFrame& frame =
encoded_frames[frame_id];
if (frame.codec_specific_info.generic_frame_info
->decode_target_indications[dt] !=
DecodeTargetIndication::kNotPresent) {

View File

@ -0,0 +1,78 @@
/*
* Copyright 2020 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 "modules/video_coding/codecs/test/encoded_video_frame_producer.h"
#include <memory>
#include <vector>
#include "api/test/create_frame_generator.h"
#include "api/test/frame_generator_interface.h"
#include "api/transport/rtp/dependency_descriptor.h"
#include "api/video/video_frame.h"
#include "api/video/video_frame_type.h"
#include "api/video_codecs/video_encoder.h"
#include "modules/video_coding/include/video_codec_interface.h"
#include "modules/video_coding/include/video_error_codes.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace {
class EncoderCallback : public EncodedImageCallback {
public:
explicit EncoderCallback(
std::vector<EncodedVideoFrameProducer::EncodedFrame>& output_frames)
: output_frames_(output_frames) {}
private:
Result OnEncodedImage(
const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* /*fragmentation*/) override {
output_frames_.push_back({encoded_image, *codec_specific_info});
return Result(Result::Error::OK);
}
std::vector<EncodedVideoFrameProducer::EncodedFrame>& output_frames_;
};
} // namespace
std::vector<EncodedVideoFrameProducer::EncodedFrame>
EncodedVideoFrameProducer::Encode() {
std::unique_ptr<test::FrameGeneratorInterface> frame_buffer_generator =
test::CreateSquareFrameGenerator(
resolution_.Width(), resolution_.Height(),
test::FrameGeneratorInterface::OutputType::kI420, absl::nullopt);
std::vector<EncodedFrame> encoded_frames;
EncoderCallback encoder_callback(encoded_frames);
RTC_CHECK_EQ(encoder_.RegisterEncodeCompleteCallback(&encoder_callback),
WEBRTC_VIDEO_CODEC_OK);
uint32_t rtp_tick = 90000 / framerate_fps_;
std::vector<VideoFrameType> frame_types = {VideoFrameType::kVideoFrameDelta};
for (int i = 0; i < num_input_frames_; ++i) {
VideoFrame frame =
VideoFrame::Builder()
.set_video_frame_buffer(frame_buffer_generator->NextFrame().buffer)
.set_timestamp_rtp(rtp_timestamp_)
.build();
rtp_timestamp_ += rtp_tick;
RTC_CHECK_EQ(encoder_.Encode(frame, &frame_types), WEBRTC_VIDEO_CODEC_OK);
}
RTC_CHECK_EQ(encoder_.RegisterEncodeCompleteCallback(nullptr),
WEBRTC_VIDEO_CODEC_OK);
return encoded_frames;
}
} // namespace webrtc

View File

@ -0,0 +1,74 @@
/*
* Copyright 2020 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 MODULES_VIDEO_CODING_CODECS_TEST_ENCODED_VIDEO_FRAME_PRODUCER_H_
#define MODULES_VIDEO_CODING_CODECS_TEST_ENCODED_VIDEO_FRAME_PRODUCER_H_
#include <stdint.h>
#include <vector>
#include "api/transport/rtp/dependency_descriptor.h"
#include "api/video/encoded_image.h"
#include "api/video_codecs/video_encoder.h"
#include "modules/video_coding/include/video_codec_interface.h"
namespace webrtc {
// Wrapper around VideoEncoder::Encode for convenient input (generates frames)
// and output (returns encoded frames instead of passing them to callback)
class EncodedVideoFrameProducer {
public:
struct EncodedFrame {
EncodedImage encoded_image;
CodecSpecificInfo codec_specific_info;
};
// `encoder` should be initialized, but shouldn't have `EncoderCallback` set.
explicit EncodedVideoFrameProducer(VideoEncoder& encoder)
: encoder_(encoder) {}
EncodedVideoFrameProducer(const EncodedVideoFrameProducer&) = delete;
EncodedVideoFrameProducer& operator=(const EncodedVideoFrameProducer&) =
delete;
// Number of the input frames to pass to the encoder.
EncodedVideoFrameProducer& SetNumInputFrames(int value);
// Resolution of the input frames.
EncodedVideoFrameProducer& SetResolution(RenderResolution value);
// Generates input video frames and encodes them with `encoder` provided in
// the constructor. Returns frame passed to the `OnEncodedImage` by wraping
// `EncodedImageCallback` underneath.
std::vector<EncodedFrame> Encode();
private:
VideoEncoder& encoder_;
uint32_t rtp_timestamp_ = 1000;
int num_input_frames_ = 1;
int framerate_fps_ = 30;
RenderResolution resolution_ = {320, 180};
};
inline EncodedVideoFrameProducer& EncodedVideoFrameProducer::SetNumInputFrames(
int value) {
RTC_DCHECK_GT(value, 0);
num_input_frames_ = value;
return *this;
}
inline EncodedVideoFrameProducer& EncodedVideoFrameProducer::SetResolution(
RenderResolution value) {
resolution_ = value;
return *this;
}
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_CODECS_TEST_ENCODED_VIDEO_FRAME_PRODUCER_H_

View File

@ -16,6 +16,7 @@
#include "common_video/libyuv/include/webrtc_libyuv.h"
#include "media/base/vp9_profile.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/video_coding/codecs/test/encoded_video_frame_producer.h"
#include "modules/video_coding/codecs/test/video_codec_unittest.h"
#include "modules/video_coding/codecs/vp9/include/vp9.h"
#include "modules/video_coding/codecs/vp9/svc_config.h"
@ -25,20 +26,33 @@
#include "test/video_codec_settings.h"
namespace webrtc {
namespace {
using ::testing::ElementsAreArray;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAreArray;
using EncoderInfo = webrtc::VideoEncoder::EncoderInfo;
using FramerateFractions =
absl::InlinedVector<uint8_t, webrtc::kMaxTemporalStreams>;
namespace {
const size_t kWidth = 1280;
const size_t kHeight = 720;
constexpr size_t kWidth = 1280;
constexpr size_t kHeight = 720;
const VideoEncoder::Capabilities kCapabilities(false);
const VideoEncoder::Settings kSettings(kCapabilities,
/*number_of_cores=*/1,
/*max_payload_size=*/0);
VideoCodec DefaultCodecSettings() {
VideoCodec codec_settings;
webrtc::test::CodecSettings(kVideoCodecVP9, &codec_settings);
codec_settings.width = kWidth;
codec_settings.height = kHeight;
codec_settings.VP9()->numberOfTemporalLayers = 1;
codec_settings.VP9()->numberOfSpatialLayers = 1;
return codec_settings;
}
} // namespace
class TestVp9Impl : public VideoCodecUnitTest {
@ -59,53 +73,6 @@ class TestVp9Impl : public VideoCodecUnitTest {
codec_settings->VP9()->numberOfSpatialLayers = 1;
}
void ExpectFrameWith(uint8_t temporal_idx) {
EncodedImage encoded_frame;
CodecSpecificInfo codec_specific_info;
ASSERT_TRUE(WaitForEncodedFrame(&encoded_frame, &codec_specific_info));
EXPECT_EQ(temporal_idx, codec_specific_info.codecSpecific.VP9.temporal_idx);
}
void ExpectFrameWith(size_t num_spatial_layers,
uint8_t temporal_idx,
bool temporal_up_switch,
uint8_t num_ref_pics,
const std::vector<uint8_t>& p_diff) {
std::vector<EncodedImage> encoded_frame;
std::vector<CodecSpecificInfo> codec_specific;
ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific));
for (size_t spatial_idx = 0; spatial_idx < num_spatial_layers;
++spatial_idx) {
const CodecSpecificInfoVP9& vp9 =
codec_specific[spatial_idx].codecSpecific.VP9;
if (vp9.temporal_idx == kNoTemporalIdx) {
EXPECT_EQ(temporal_idx, 0);
} else {
EXPECT_EQ(vp9.temporal_idx, temporal_idx);
}
if (num_spatial_layers == 1) {
EXPECT_FALSE(encoded_frame[spatial_idx].SpatialIndex());
} else {
EXPECT_EQ(encoded_frame[spatial_idx].SpatialIndex(),
static_cast<int>(spatial_idx));
}
EXPECT_EQ(vp9.temporal_up_switch, temporal_up_switch);
// Ensure there are no duplicates in reference list.
std::vector<uint8_t> vp9_p_diff(vp9.p_diff,
vp9.p_diff + vp9.num_ref_pics);
std::sort(vp9_p_diff.begin(), vp9_p_diff.end());
EXPECT_EQ(std::unique(vp9_p_diff.begin(), vp9_p_diff.end()),
vp9_p_diff.end());
for (size_t ref_pic_num = 0; ref_pic_num < num_ref_pics; ++ref_pic_num) {
EXPECT_NE(
std::find(p_diff.begin(), p_diff.end(), vp9.p_diff[ref_pic_num]),
p_diff.end());
}
}
}
void ConfigureSvc(size_t num_spatial_layers, size_t num_temporal_layers = 1) {
codec_settings_.VP9()->numberOfSpatialLayers =
static_cast<unsigned char>(num_spatial_layers);
@ -187,57 +154,61 @@ TEST_F(TestVp9Impl, DecodedQpEqualsEncodedQp) {
EXPECT_EQ(encoded_frame.qp_, *decoded_qp);
}
TEST_F(TestVp9Impl, ParserQpEqualsEncodedQp) {
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr));
EncodedImage encoded_frame;
CodecSpecificInfo codec_specific_info;
ASSERT_TRUE(WaitForEncodedFrame(&encoded_frame, &codec_specific_info));
TEST(Vp9ImplTest, ParserQpEqualsEncodedQp) {
std::unique_ptr<VideoEncoder> encoder = VP9Encoder::Create();
VideoCodec codec_settings = DefaultCodecSettings();
encoder->InitEncode(&codec_settings, kSettings);
std::vector<EncodedVideoFrameProducer::EncodedFrame> frames =
EncodedVideoFrameProducer(*encoder)
.SetNumInputFrames(1)
.SetResolution({kWidth, kHeight})
.Encode();
ASSERT_THAT(frames, SizeIs(1));
const auto& encoded_frame = frames.front().encoded_image;
int qp = 0;
ASSERT_TRUE(vp9::GetQp(encoded_frame.data(), encoded_frame.size(), &qp));
EXPECT_EQ(encoded_frame.qp_, qp);
}
TEST_F(TestVp9Impl, EncoderWith2TemporalLayers) {
// Override default settings.
codec_settings_.VP9()->numberOfTemporalLayers = 2;
TEST(Vp9ImplTest, EncoderWith2TemporalLayers) {
std::unique_ptr<VideoEncoder> encoder = VP9Encoder::Create();
VideoCodec codec_settings = DefaultCodecSettings();
codec_settings.VP9()->numberOfTemporalLayers = 2;
// Tl0PidIdx is only used in non-flexible mode.
codec_settings_.VP9()->flexibleMode = false;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->InitEncode(&codec_settings_, kSettings));
codec_settings.VP9()->flexibleMode = false;
EXPECT_EQ(encoder->InitEncode(&codec_settings, kSettings),
WEBRTC_VIDEO_CODEC_OK);
// Temporal layer 0.
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr));
EncodedImage encoded_frame;
CodecSpecificInfo codec_specific_info;
ASSERT_TRUE(WaitForEncodedFrame(&encoded_frame, &codec_specific_info));
EXPECT_EQ(0, codec_specific_info.codecSpecific.VP9.temporal_idx);
std::vector<EncodedVideoFrameProducer::EncodedFrame> frames =
EncodedVideoFrameProducer(*encoder)
.SetNumInputFrames(4)
.SetResolution({kWidth, kHeight})
.Encode();
// Temporal layer 1.
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr));
ExpectFrameWith(1);
// Temporal layer 0.
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr));
ExpectFrameWith(0);
// Temporal layer 1.
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr));
ExpectFrameWith(1);
ASSERT_THAT(frames, SizeIs(4));
EXPECT_EQ(frames[0].codec_specific_info.codecSpecific.VP9.temporal_idx, 0);
EXPECT_EQ(frames[1].codec_specific_info.codecSpecific.VP9.temporal_idx, 1);
EXPECT_EQ(frames[2].codec_specific_info.codecSpecific.VP9.temporal_idx, 0);
EXPECT_EQ(frames[3].codec_specific_info.codecSpecific.VP9.temporal_idx, 1);
}
TEST_F(TestVp9Impl, EncoderWith2SpatialLayers) {
codec_settings_.VP9()->numberOfSpatialLayers = 2;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->InitEncode(&codec_settings_, kSettings));
TEST(Vp9ImplTest, EncoderWith2SpatialLayers) {
std::unique_ptr<VideoEncoder> encoder = VP9Encoder::Create();
VideoCodec codec_settings = DefaultCodecSettings();
codec_settings.VP9()->numberOfSpatialLayers = 2;
EXPECT_EQ(encoder->InitEncode(&codec_settings, kSettings),
WEBRTC_VIDEO_CODEC_OK);
SetWaitForEncodedFramesThreshold(2);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr));
std::vector<EncodedImage> encoded_frame;
std::vector<CodecSpecificInfo> codec_info;
ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_info));
EXPECT_EQ(encoded_frame[0].SpatialIndex(), 0);
EXPECT_EQ(encoded_frame[1].SpatialIndex(), 1);
std::vector<EncodedVideoFrameProducer::EncodedFrame> frames =
EncodedVideoFrameProducer(*encoder)
.SetNumInputFrames(1)
.SetResolution({kWidth, kHeight})
.Encode();
ASSERT_THAT(frames, SizeIs(2));
EXPECT_EQ(frames[0].encoded_image.SpatialIndex(), 0);
EXPECT_EQ(frames[1].encoded_image.SpatialIndex(), 1);
}
TEST_F(TestVp9Impl, EncoderExplicitLayering) {
@ -1421,29 +1392,34 @@ TEST_F(TestVp9Impl, EncoderInfoFpsAllocationFlexibleMode) {
::testing::ElementsAreArray(expected_fps_allocation));
}
class TestVp9ImplWithLayering
: public TestVp9Impl,
public ::testing::WithParamInterface<::testing::tuple<uint8_t, uint8_t>> {
class Vp9ImplWithLayeringTest
: public ::testing::TestWithParam<std::tuple<int, int, bool>> {
protected:
TestVp9ImplWithLayering()
: num_spatial_layers_(::testing::get<0>(GetParam())),
num_temporal_layers_(::testing::get<1>(GetParam())) {}
Vp9ImplWithLayeringTest()
: num_spatial_layers_(std::get<0>(GetParam())),
num_temporal_layers_(std::get<1>(GetParam())),
override_field_trials_(std::get<2>(GetParam())
? "WebRTC-Vp9ExternalRefCtrl/Enabled/"
: "") {}
const uint8_t num_spatial_layers_;
const uint8_t num_temporal_layers_;
const test::ScopedFieldTrials override_field_trials_;
};
TEST_P(TestVp9ImplWithLayering, FlexibleMode) {
TEST_P(Vp9ImplWithLayeringTest, FlexibleMode) {
// In flexible mode encoder wrapper obtains actual list of references from
// encoder and writes it into RTP payload descriptor. Check that reference
// list in payload descriptor matches the predefined one, which is used
// in non-flexible mode.
codec_settings_.VP9()->flexibleMode = true;
codec_settings_.VP9()->frameDroppingOn = false;
codec_settings_.VP9()->numberOfSpatialLayers = num_spatial_layers_;
codec_settings_.VP9()->numberOfTemporalLayers = num_temporal_layers_;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->InitEncode(&codec_settings_, kSettings));
std::unique_ptr<VideoEncoder> encoder = VP9Encoder::Create();
VideoCodec codec_settings = DefaultCodecSettings();
codec_settings.VP9()->flexibleMode = true;
codec_settings.VP9()->frameDroppingOn = false;
codec_settings.VP9()->numberOfSpatialLayers = num_spatial_layers_;
codec_settings.VP9()->numberOfTemporalLayers = num_temporal_layers_;
EXPECT_EQ(encoder->InitEncode(&codec_settings, kSettings),
WEBRTC_VIDEO_CODEC_OK);
GofInfoVP9 gof;
if (num_temporal_layers_ == 1) {
@ -1456,65 +1432,48 @@ TEST_P(TestVp9ImplWithLayering, FlexibleMode) {
// Encode at least (num_frames_in_gof + 1) frames to verify references
// of non-key frame with gof_idx = 0.
for (size_t frame_num = 0; frame_num < gof.num_frames_in_gof + 1;
++frame_num) {
SetWaitForEncodedFramesThreshold(num_spatial_layers_);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->Encode(NextInputFrame(), nullptr));
int num_input_frames = gof.num_frames_in_gof + 1;
std::vector<EncodedVideoFrameProducer::EncodedFrame> frames =
EncodedVideoFrameProducer(*encoder)
.SetNumInputFrames(num_input_frames)
.SetResolution({kWidth, kHeight})
.Encode();
ASSERT_THAT(frames, SizeIs(num_input_frames * num_spatial_layers_));
const bool is_key_frame = frame_num == 0;
const size_t gof_idx = frame_num % gof.num_frames_in_gof;
const std::vector<uint8_t> p_diff(std::begin(gof.pid_diff[gof_idx]),
std::end(gof.pid_diff[gof_idx]));
for (size_t i = 0; i < frames.size(); ++i) {
const EncodedVideoFrameProducer::EncodedFrame& frame = frames[i];
const size_t picture_idx = i / num_spatial_layers_;
const size_t gof_idx = picture_idx % gof.num_frames_in_gof;
ExpectFrameWith(num_spatial_layers_, gof.temporal_idx[gof_idx],
gof.temporal_up_switch[gof_idx],
is_key_frame ? 0 : gof.num_ref_pics[gof_idx], p_diff);
}
}
TEST_P(TestVp9ImplWithLayering, ExternalRefControl) {
test::ScopedFieldTrials override_field_trials(
"WebRTC-Vp9ExternalRefCtrl/Enabled/");
codec_settings_.VP9()->flexibleMode = true;
codec_settings_.VP9()->frameDroppingOn = false;
codec_settings_.VP9()->numberOfSpatialLayers = num_spatial_layers_;
codec_settings_.VP9()->numberOfTemporalLayers = num_temporal_layers_;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->InitEncode(&codec_settings_, kSettings));
GofInfoVP9 gof;
if (num_temporal_layers_ == 1) {
gof.SetGofInfoVP9(kTemporalStructureMode1);
} else if (num_temporal_layers_ == 2) {
gof.SetGofInfoVP9(kTemporalStructureMode2);
} else if (num_temporal_layers_ == 3) {
gof.SetGofInfoVP9(kTemporalStructureMode3);
}
// Encode at least (num_frames_in_gof + 1) frames to verify references
// of non-key frame with gof_idx = 0.
for (size_t frame_num = 0; frame_num < gof.num_frames_in_gof + 1;
++frame_num) {
SetWaitForEncodedFramesThreshold(num_spatial_layers_);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->Encode(NextInputFrame(), nullptr));
const bool is_key_frame = frame_num == 0;
const size_t gof_idx = frame_num % gof.num_frames_in_gof;
const std::vector<uint8_t> p_diff(std::begin(gof.pid_diff[gof_idx]),
std::end(gof.pid_diff[gof_idx]));
ExpectFrameWith(num_spatial_layers_, gof.temporal_idx[gof_idx],
gof.temporal_up_switch[gof_idx],
is_key_frame ? 0 : gof.num_ref_pics[gof_idx], p_diff);
const CodecSpecificInfoVP9& vp9 =
frame.codec_specific_info.codecSpecific.VP9;
EXPECT_EQ(frame.encoded_image.SpatialIndex(),
num_spatial_layers_ == 1
? absl::nullopt
: absl::optional<int>(i % num_spatial_layers_))
<< "Frame " << i;
EXPECT_EQ(vp9.temporal_idx, num_temporal_layers_ == 1
? kNoTemporalIdx
: gof.temporal_idx[gof_idx])
<< "Frame " << i;
EXPECT_EQ(vp9.temporal_up_switch, gof.temporal_up_switch[gof_idx])
<< "Frame " << i;
if (picture_idx == 0) {
EXPECT_EQ(vp9.num_ref_pics, 0) << "Frame " << i;
} else {
EXPECT_THAT(rtc::MakeArrayView(vp9.p_diff, vp9.num_ref_pics),
UnorderedElementsAreArray(gof.pid_diff[gof_idx],
gof.num_ref_pics[gof_idx]))
<< "Frame " << i;
}
}
}
INSTANTIATE_TEST_SUITE_P(All,
TestVp9ImplWithLayering,
Vp9ImplWithLayeringTest,
::testing::Combine(::testing::Values(1, 2, 3),
::testing::Values(1, 2, 3)));
::testing::Values(1, 2, 3),
::testing::Bool()));
class TestVp9ImplFrameDropping : public TestVp9Impl {
protected: