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:
committed by
Commit Bot
parent
9465978a3b
commit
90dc5dae2c
@ -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",
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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:
|
||||
|
||||
Reference in New Issue
Block a user