123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972 |
- // Copyright 2020 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include <algorithm>
- #include <limits>
- #include "base/command_line.h"
- #include "base/files/file_path.h"
- #include "base/files/file_util.h"
- #include "base/strings/string_number_conversions.h"
- #include "media/base/media_switches.h"
- #include "media/base/media_util.h"
- #include "media/base/test_data_util.h"
- #include "media/base/video_bitrate_allocation.h"
- #include "media/base/video_codecs.h"
- #include "media/base/video_decoder_config.h"
- #include "media/gpu/buildflags.h"
- #include "media/gpu/gpu_video_encode_accelerator_helpers.h"
- #include "media/gpu/test/video.h"
- #include "media/gpu/test/video_encoder/bitstream_file_writer.h"
- #include "media/gpu/test/video_encoder/bitstream_validator.h"
- #include "media/gpu/test/video_encoder/decoder_buffer_validator.h"
- #include "media/gpu/test/video_encoder/video_encoder.h"
- #include "media/gpu/test/video_encoder/video_encoder_client.h"
- #include "media/gpu/test/video_encoder/video_encoder_test_environment.h"
- #include "media/gpu/test/video_frame_file_writer.h"
- #include "media/gpu/test/video_frame_helpers.h"
- #include "media/gpu/test/video_frame_validator.h"
- #include "media/gpu/test/video_test_environment.h"
- #include "media/gpu/test/video_test_helpers.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- namespace media {
- namespace test {
- namespace {
- // Video encoder tests usage message. Make sure to also update the documentation
- // under docs/media/gpu/video_encoder_test_usage.md when making changes here.
- constexpr const char* usage_msg =
- R"(usage: video_encode_accelerator_tests
- [--codec=<codec>] [--num_temporal_layers=<number>]
- [--num_spatial_layers=<number>] [--bitrate_mode=(cbr|vbr)]
- [--reverse] [--disable_validator] [--output_bitstream]
- [--output_images=(all|corrupt)] [--output_format=(png|yuv)]
- [--output_folder=<filepath>] [--output_limit=<number>]
- [--disable_vaapi_lock]
- [-v=<level>] [--vmodule=<config>]
- [--gtest_help] [--help]
- [<video path>] [<video metadata path>]
- )";
- // Video encoder tests help message.
- constexpr const char* help_msg =
- R"""(Run the video encoder accelerator tests on the video specified by
- <video path>. If no <video path> is given the default
- "bear_320x192_40frames.yuv.webm" video will be used.
- The <video metadata path> should specify the location of a json file
- containing the video's metadata, such as frame checksums. By default
- <video path>.json will be used.
- The following arguments are supported:
- -v enable verbose mode, e.g. -v=2.
- --vmodule enable verbose mode for the specified module,
- e.g. --vmodule=*media/gpu*=2.
- --codec codec profile to encode, "h264" (baseline),
- "h264main, "h264high", "vp8" and "vp9".
- H264 Baseline is selected if unspecified.
- --num_temporal_layers the number of temporal layers of the encoded
- bitstream. A default value is 1.
- --num_spatial_layers the number of spatial layers of the encoded
- bitstream. Only used in --codec=vp9 currently.
- Spatial SVC encoding is applied only in
- NV12Dmabuf test cases.
- --bitrate_mode The rate control mode for encoding, one of "cbr"
- (default) or "vbr".
- --reverse the stream plays backwards if the stream reaches
- end of stream. So the input stream to be encoded
- is consecutive. By default this is false.
- --disable_validator disable validation of encoded bitstream.
- --output_bitstream save the output bitstream in either H264 AnnexB
- format (for H264) or IVF format (for vp8 and
- vp9) to <output_folder>/<testname>.
- --output_images in addition to saving the full encoded,
- bitstream it's also possible to dump individual
- frames to <output_folder>/<testname>, possible
- values are "all|corrupt"
- --output_format set the format of images saved to disk,
- supported formats are "png" (default) and
- "yuv".
- --output_limit limit the number of images saved to disk.
- --output_folder set the basic folder used to store test
- artifacts. The default is the current directory.
- --disable_vaapi_lock disable the global VA-API lock if applicable,
- i.e., only on devices that use the VA-API with a libva
- backend that's known to be thread-safe and only in
- portions of the Chrome stack that should be able to
- deal with the absence of the lock
- (not the VaapiVideoDecodeAccelerator).
- --gtest_help display the gtest help and exit.
- --help display this help and exit.
- )""";
- // Default video to be used if no test video was specified.
- constexpr base::FilePath::CharType kDefaultTestVideoPath[] =
- FILE_PATH_LITERAL("bear_320x192_40frames.yuv.webm");
- // The number of frames to encode for bitrate check test cases.
- // TODO(hiroh): Decrease this values to make the test faster.
- constexpr size_t kNumFramesToEncodeForBitrateCheck = 300;
- // Tolerance factor for how encoded bitrate can differ from requested bitrate.
- constexpr double kBitrateTolerance = 0.1;
- constexpr double kVariableBitrateTolerance = 0.3;
- // The event timeout used in bitrate check tests because encoding 2160p and
- // validating |kNumFramesToEncodeBitrateCheck| frames take much time.
- constexpr base::TimeDelta kBitrateCheckEventTimeout = base::Seconds(180);
- media::test::VideoEncoderTestEnvironment* g_env;
- // Video encode test class. Performs setup and teardown for each single test.
- class VideoEncoderTest : public ::testing::Test {
- public:
- // GetDefaultConfig() creates VideoEncoderClientConfig for SharedMemory input
- // encoding. This function must not be called in spatial SVC encoding.
- VideoEncoderClientConfig GetDefaultConfig() {
- const auto& spatial_layers = g_env->SpatialLayers();
- CHECK_LE(spatial_layers.size(), 1u);
- return VideoEncoderClientConfig(g_env->Video(), g_env->Profile(),
- spatial_layers, g_env->BitrateAllocation(),
- g_env->Reverse());
- }
- std::unique_ptr<VideoEncoder> CreateVideoEncoder(
- Video* video,
- const VideoEncoderClientConfig& config) {
- LOG_ASSERT(video);
- auto video_encoder =
- VideoEncoder::Create(config, CreateBitstreamProcessors(video, config));
- LOG_ASSERT(video_encoder);
- if (!video_encoder->Initialize(video))
- ADD_FAILURE();
- return video_encoder;
- }
- private:
- std::unique_ptr<BitstreamProcessor> CreateBitstreamValidator(
- const Video* video,
- const VideoDecoderConfig& decoder_config,
- const size_t last_frame_index,
- VideoFrameValidator::GetModelFrameCB get_model_frame_cb,
- absl::optional<size_t> spatial_layer_index_to_decode,
- absl::optional<size_t> temporal_layer_index_to_decode,
- const std::vector<gfx::Size>& spatial_layer_resolutions) {
- std::vector<std::unique_ptr<VideoFrameProcessor>> video_frame_processors;
- // Attach a video frame writer to store individual frames to disk if
- // requested.
- std::unique_ptr<VideoFrameProcessor> image_writer;
- auto frame_output_config = g_env->ImageOutputConfig();
- base::FilePath output_folder = base::FilePath(g_env->OutputFolder())
- .Append(g_env->GetTestOutputFilePath());
- if (frame_output_config.output_mode != FrameOutputMode::kNone) {
- base::FilePath::StringType output_file_prefix;
- if (spatial_layer_index_to_decode) {
- output_file_prefix +=
- FILE_PATH_LITERAL("SL") +
- base::NumberToString(*spatial_layer_index_to_decode);
- }
- if (temporal_layer_index_to_decode) {
- output_file_prefix +=
- FILE_PATH_LITERAL("TL") +
- base::NumberToString(*temporal_layer_index_to_decode);
- }
- image_writer = VideoFrameFileWriter::Create(
- output_folder, frame_output_config.output_format,
- frame_output_config.output_limit, output_file_prefix);
- LOG_ASSERT(image_writer);
- if (frame_output_config.output_mode == FrameOutputMode::kAll)
- video_frame_processors.push_back(std::move(image_writer));
- }
- // For a resolution less than 360p, we lower the tolerance. Some platforms
- // couldn't compress a low resolution video efficiently with a low bitrate.
- constexpr gfx::Size k360p(640, 360);
- constexpr double kSSIMToleranceForLowerResolution = 0.65;
- const gfx::Size encode_resolution = decoder_config.visible_rect().size();
- const double ssim_tolerance =
- encode_resolution.GetArea() < k360p.GetArea()
- ? kSSIMToleranceForLowerResolution
- : SSIMVideoFrameValidator::kDefaultTolerance;
- auto ssim_validator = SSIMVideoFrameValidator::Create(
- get_model_frame_cb, std::move(image_writer),
- VideoFrameValidator::ValidationMode::kAverage, ssim_tolerance);
- LOG_ASSERT(ssim_validator);
- video_frame_processors.push_back(std::move(ssim_validator));
- return BitstreamValidator::Create(
- decoder_config, last_frame_index, std::move(video_frame_processors),
- spatial_layer_index_to_decode, temporal_layer_index_to_decode,
- spatial_layer_resolutions);
- }
- std::vector<std::unique_ptr<BitstreamProcessor>> CreateBitstreamProcessors(
- Video* video,
- const VideoEncoderClientConfig& config) {
- std::vector<std::unique_ptr<BitstreamProcessor>> bitstream_processors;
- const gfx::Rect visible_rect(config.output_resolution);
- std::vector<gfx::Size> spatial_layer_resolutions;
- // |config.spatial_layers| is filled only in temporal layer or spatial layer
- // encoding.
- for (const auto& sl : config.spatial_layers)
- spatial_layer_resolutions.emplace_back(sl.width, sl.height);
- const VideoCodec codec =
- VideoCodecProfileToVideoCodec(config.output_profile);
- if (g_env->SaveOutputBitstream()) {
- base::FilePath::StringPieceType extension =
- codec == VideoCodec::kH264 ? FILE_PATH_LITERAL("h264")
- : FILE_PATH_LITERAL("ivf");
- auto output_bitstream_filepath =
- g_env->OutputFolder()
- .Append(g_env->GetTestOutputFilePath())
- .Append(video->FilePath().BaseName().ReplaceExtension(extension));
- if (!spatial_layer_resolutions.empty()) {
- CHECK_GE(config.num_spatial_layers, 1u);
- CHECK_GE(config.num_temporal_layers, 1u);
- for (size_t spatial_layer_index_to_write = 0;
- spatial_layer_index_to_write < config.num_spatial_layers;
- ++spatial_layer_index_to_write) {
- const gfx::Size& layer_size =
- spatial_layer_resolutions[spatial_layer_index_to_write];
- for (size_t temporal_layer_index_to_write = 0;
- temporal_layer_index_to_write < config.num_temporal_layers;
- ++temporal_layer_index_to_write) {
- bitstream_processors.emplace_back(BitstreamFileWriter::Create(
- output_bitstream_filepath.InsertBeforeExtensionASCII(
- FILE_PATH_LITERAL(".SL") +
- base::NumberToString(spatial_layer_index_to_write) +
- FILE_PATH_LITERAL(".TL") +
- base::NumberToString(temporal_layer_index_to_write)),
- codec, layer_size, config.framerate,
- config.num_frames_to_encode, spatial_layer_index_to_write,
- temporal_layer_index_to_write, spatial_layer_resolutions));
- LOG_ASSERT(bitstream_processors.back());
- }
- }
- } else {
- bitstream_processors.emplace_back(BitstreamFileWriter::Create(
- output_bitstream_filepath, codec, visible_rect.size(),
- config.framerate, config.num_frames_to_encode));
- LOG_ASSERT(bitstream_processors.back());
- }
- }
- if (!g_env->IsBitstreamValidatorEnabled()) {
- return bitstream_processors;
- }
- switch (codec) {
- case VideoCodec::kH264:
- bitstream_processors.emplace_back(new H264Validator(
- config.output_profile, visible_rect, config.num_temporal_layers));
- break;
- case VideoCodec::kVP8:
- bitstream_processors.emplace_back(
- new VP8Validator(visible_rect, config.num_temporal_layers));
- break;
- case VideoCodec::kVP9:
- bitstream_processors.emplace_back(new VP9Validator(
- config.output_profile, visible_rect, config.num_spatial_layers,
- config.num_temporal_layers));
- break;
- default:
- LOG(ERROR) << "Unsupported profile: "
- << GetProfileName(config.output_profile);
- break;
- }
- raw_data_helper_ = RawDataHelper::Create(video, g_env->Reverse());
- if (!raw_data_helper_) {
- LOG(ERROR) << "Failed to create raw data helper";
- return bitstream_processors;
- }
- if (!spatial_layer_resolutions.empty()) {
- CHECK_GE(config.num_spatial_layers, 1u);
- CHECK_GE(config.num_temporal_layers, 1u);
- for (size_t spatial_layer_index_to_decode = 0;
- spatial_layer_index_to_decode < config.num_spatial_layers;
- ++spatial_layer_index_to_decode) {
- const gfx::Size& layer_size =
- spatial_layer_resolutions[spatial_layer_index_to_decode];
- VideoDecoderConfig decoder_config(
- codec, config.output_profile,
- VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace(),
- kNoTransformation, layer_size, gfx::Rect(layer_size), layer_size,
- EmptyExtraData(), EncryptionScheme::kUnencrypted);
- VideoFrameValidator::GetModelFrameCB get_model_frame_cb =
- base::BindRepeating(&VideoEncoderTest::GetModelFrame,
- base::Unretained(this), gfx::Rect(layer_size));
- for (size_t temporal_layer_index_to_decode = 0;
- temporal_layer_index_to_decode < config.num_temporal_layers;
- ++temporal_layer_index_to_decode) {
- bitstream_processors.emplace_back(CreateBitstreamValidator(
- video, decoder_config, config.num_frames_to_encode - 1,
- get_model_frame_cb, spatial_layer_index_to_decode,
- temporal_layer_index_to_decode, spatial_layer_resolutions));
- LOG_ASSERT(bitstream_processors.back());
- }
- }
- } else {
- // Attach a bitstream validator to validate all encoded video frames. The
- // bitstream validator uses a software video decoder to validate the
- // encoded buffers by decoding them. Metrics such as the image's SSIM can
- // be calculated for additional quality checks.
- VideoDecoderConfig decoder_config(
- codec, config.output_profile,
- VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace(),
- kNoTransformation, visible_rect.size(), visible_rect,
- visible_rect.size(), EmptyExtraData(),
- EncryptionScheme::kUnencrypted);
- VideoFrameValidator::GetModelFrameCB get_model_frame_cb =
- base::BindRepeating(&VideoEncoderTest::GetModelFrame,
- base::Unretained(this), visible_rect);
- bitstream_processors.emplace_back(CreateBitstreamValidator(
- video, decoder_config, config.num_frames_to_encode - 1,
- get_model_frame_cb, absl::nullopt, absl::nullopt,
- /*spatial_layer_resolutions=*/{}));
- LOG_ASSERT(bitstream_processors.back());
- }
- return bitstream_processors;
- }
- scoped_refptr<const VideoFrame> GetModelFrame(const gfx::Rect& visible_rect,
- size_t frame_index) {
- LOG_ASSERT(raw_data_helper_);
- auto frame = raw_data_helper_->GetFrame(frame_index);
- if (!frame)
- return nullptr;
- if (visible_rect.size() == frame->visible_rect().size())
- return frame;
- return ScaleVideoFrame(frame.get(), visible_rect.size());
- }
- std::unique_ptr<RawDataHelper> raw_data_helper_;
- };
- absl::optional<std::string> SupportsDynamicFramerate() {
- return g_env->IsKeplerUsed()
- ? absl::make_optional<std::string>(
- "The rate controller in the kepler firmware doesn't handle "
- "frame rate changes correctly.")
- : absl::nullopt;
- }
- absl::optional<std::string> SupportsNV12DmaBufInput() {
- return g_env->IsKeplerUsed() ? absl::make_optional<std::string>(
- "Encoding with dmabuf input frames is not "
- "supported in kepler.")
- : absl::nullopt;
- }
- } // namespace
- // Encode video from start to end. Wait for the kFlushDone event at the end of
- // the stream, that notifies us all frames have been encoded.
- TEST_F(VideoEncoderTest, FlushAtEndOfStream) {
- if (g_env->SpatialLayers().size() > 1)
- GTEST_SKIP() << "Skip SHMEM input test cases in spatial SVC encoding";
- auto encoder = CreateVideoEncoder(g_env->Video(), GetDefaultConfig());
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), g_env->Video()->NumFrames());
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- }
- // Test initializing the video encoder. The test will be successful if the video
- // encoder is capable of setting up the encoder for the specified codec and
- // resolution. The test only verifies initialization and doesn't do any
- // encoding.
- TEST_F(VideoEncoderTest, Initialize) {
- if (g_env->SpatialLayers().size() > 1)
- GTEST_SKIP() << "Skip SHMEM input test cases in spatial SVC encoding";
- auto encoder = CreateVideoEncoder(g_env->Video(), GetDefaultConfig());
- EXPECT_EQ(encoder->GetEventCount(VideoEncoder::kInitialized), 1u);
- }
- // Create a video encoder and immediately destroy it without initializing. The
- // video encoder will be automatically destroyed when the video encoder goes out
- // of scope at the end of the test. The test will pass if no asserts or crashes
- // are triggered upon destroying.
- TEST_F(VideoEncoderTest, DestroyBeforeInitialize) {
- if (g_env->SpatialLayers().size() > 1)
- GTEST_SKIP() << "Skip SHMEM input test cases in spatial SVC encoding";
- auto video_encoder = VideoEncoder::Create(GetDefaultConfig());
- EXPECT_NE(video_encoder, nullptr);
- }
- // Test forcing key frames while encoding a video.
- TEST_F(VideoEncoderTest, ForceKeyFrame) {
- if (g_env->SpatialLayers().size() > 1)
- GTEST_SKIP() << "Skip SHMEM input test cases in spatial SVC encoding";
- auto config = GetDefaultConfig();
- const size_t middle_frame = config.num_frames_to_encode;
- config.num_frames_to_encode *= 2;
- auto encoder = CreateVideoEncoder(g_env->Video(), config);
- // It is expected that our hw encoders don't produce key frames in a short
- // time span like a few hundred frames.
- encoder->EncodeUntil(VideoEncoder::kBitstreamReady, 1u);
- EXPECT_TRUE(encoder->WaitUntilIdle());
- EXPECT_EQ(encoder->GetEventCount(VideoEncoder::kKeyFrame), 1u);
- // Encode until the middle of stream and request force_keyframe.
- encoder->EncodeUntil(VideoEncoder::kFrameReleased, middle_frame);
- EXPECT_TRUE(encoder->WaitUntilIdle());
- // Check if there is no keyframe except the first frame.
- EXPECT_EQ(encoder->GetEventCount(VideoEncoder::kKeyFrame), 1u);
- encoder->ForceKeyFrame();
- // Encode until the end of stream.
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- // Check if there are two key frames, first frame and one on ForceKeyFrame().
- EXPECT_EQ(encoder->GetEventCount(VideoEncoder::kKeyFrame), 2u);
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), config.num_frames_to_encode);
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- }
- // Encode video from start to end. Multiple buffer encodes will be queued in the
- // encoder, without waiting for the result of the previous encode requests.
- TEST_F(VideoEncoderTest, FlushAtEndOfStream_MultipleOutstandingEncodes) {
- if (g_env->SpatialLayers().size() > 1)
- GTEST_SKIP() << "Skip SHMEM input test cases in spatial SVC encoding";
- auto config = GetDefaultConfig();
- config.max_outstanding_encode_requests = 4;
- auto encoder = CreateVideoEncoder(g_env->Video(), config);
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), g_env->Video()->NumFrames());
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- }
- // Encode multiple videos simultaneously from start to finish.
- TEST_F(VideoEncoderTest, FlushAtEndOfStream_MultipleConcurrentEncodes) {
- if (g_env->SpatialLayers().size() > 1)
- GTEST_SKIP() << "Skip SHMEM input test cases in spatial SVC encoding";
- // Run two encoders for larger resolutions to avoid creating shared memory
- // buffers during the test on lower end devices.
- constexpr gfx::Size k1080p(1920, 1080);
- const size_t kMinSupportedConcurrentEncoders =
- g_env->Video()->Resolution().GetArea() >= k1080p.GetArea() ? 2 : 3;
- auto config = GetDefaultConfig();
- std::vector<std::unique_ptr<VideoEncoder>> encoders(
- kMinSupportedConcurrentEncoders);
- for (size_t i = 0; i < kMinSupportedConcurrentEncoders; ++i)
- encoders[i] = CreateVideoEncoder(g_env->Video(), config);
- for (size_t i = 0; i < kMinSupportedConcurrentEncoders; ++i)
- encoders[i]->Encode();
- for (size_t i = 0; i < kMinSupportedConcurrentEncoders; ++i) {
- EXPECT_TRUE(encoders[i]->WaitForFlushDone());
- EXPECT_EQ(encoders[i]->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoders[i]->GetFrameReleasedCount(),
- g_env->Video()->NumFrames());
- EXPECT_TRUE(encoders[i]->WaitForBitstreamProcessors());
- }
- }
- TEST_F(VideoEncoderTest, BitrateCheck) {
- if (g_env->SpatialLayers().size() > 1)
- GTEST_SKIP() << "Skip SHMEM input test cases in spatial SVC encoding";
- auto config = GetDefaultConfig();
- config.num_frames_to_encode = kNumFramesToEncodeForBitrateCheck;
- auto encoder = CreateVideoEncoder(g_env->Video(), config);
- // Set longer event timeout than the default (30 sec) because encoding 2160p
- // and validating the stream take much time.
- encoder->SetEventWaitTimeout(kBitrateCheckEventTimeout);
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), config.num_frames_to_encode);
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- // TODO(b/181797390): Reconsider bitrate check for VBR encoding if this fails
- // on some boards.
- const double tolerance =
- config.bitrate_allocation.GetMode() == Bitrate::Mode::kConstant
- ? kBitrateTolerance
- : kVariableBitrateTolerance;
- EXPECT_NEAR(encoder->GetStats().Bitrate(),
- config.bitrate_allocation.GetSumBps(),
- tolerance * config.bitrate_allocation.GetSumBps());
- }
- TEST_F(VideoEncoderTest, BitrateCheck_DynamicBitrate) {
- if (g_env->SpatialLayers().size() > 1)
- GTEST_SKIP() << "Skip SHMEM input test cases in spatial SVC encoding";
- if (g_env->BitrateAllocation().GetMode() != Bitrate::Mode::kConstant) {
- GTEST_SKIP()
- << "Skip Dynamic bitrate change checks for non-CBR bitrate mode";
- }
- auto config = GetDefaultConfig();
- config.num_frames_to_encode = kNumFramesToEncodeForBitrateCheck * 2;
- auto encoder = CreateVideoEncoder(g_env->Video(), config);
- // Set longer event timeout than the default (30 sec) because encoding 2160p
- // and validating the stream take much time.
- encoder->SetEventWaitTimeout(kBitrateCheckEventTimeout);
- // Encode the video with the first bitrate.
- const uint32_t first_bitrate = config.bitrate_allocation.GetSumBps();
- encoder->EncodeUntil(VideoEncoder::kFrameReleased,
- kNumFramesToEncodeForBitrateCheck);
- EXPECT_TRUE(encoder->WaitUntilIdle());
- EXPECT_NEAR(encoder->GetStats().Bitrate(), first_bitrate,
- kBitrateTolerance * first_bitrate);
- // Encode the video with the second bitrate.
- const uint32_t second_bitrate = first_bitrate * 3 / 2;
- encoder->ResetStats();
- encoder->UpdateBitrate(
- AllocateDefaultBitrateForTesting(
- config.num_spatial_layers, config.num_temporal_layers,
- Bitrate::ConstantBitrate(second_bitrate)),
- config.framerate);
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- EXPECT_NEAR(encoder->GetStats().Bitrate(), second_bitrate,
- kBitrateTolerance * second_bitrate);
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), config.num_frames_to_encode);
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- }
- TEST_F(VideoEncoderTest, BitrateCheck_DynamicFramerate) {
- if (g_env->SpatialLayers().size() > 1)
- GTEST_SKIP() << "Skip SHMEM input test cases in spatial SVC encoding";
- if (g_env->BitrateAllocation().GetMode() != Bitrate::Mode::kConstant) {
- GTEST_SKIP()
- << "Skip dynamic framerate change checks for non-CBR bitrate mode";
- }
- if (auto skip_reason = SupportsDynamicFramerate())
- GTEST_SKIP() << *skip_reason;
- auto config = GetDefaultConfig();
- config.num_frames_to_encode = kNumFramesToEncodeForBitrateCheck * 2;
- auto encoder = CreateVideoEncoder(g_env->Video(), config);
- // Set longer event timeout than the default (30 sec) because encoding 2160p
- // and validating the stream take much time.
- encoder->SetEventWaitTimeout(kBitrateCheckEventTimeout);
- // Encode the video with the first framerate.
- const uint32_t first_framerate = config.framerate;
- encoder->EncodeUntil(VideoEncoder::kFrameReleased,
- kNumFramesToEncodeForBitrateCheck);
- EXPECT_TRUE(encoder->WaitUntilIdle());
- EXPECT_NEAR(encoder->GetStats().Bitrate(),
- config.bitrate_allocation.GetSumBps(),
- kBitrateTolerance * config.bitrate_allocation.GetSumBps());
- // Encode the video with the second framerate.
- const uint32_t second_framerate = first_framerate * 3 / 2;
- encoder->ResetStats();
- encoder->UpdateBitrate(config.bitrate_allocation, second_framerate);
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- EXPECT_NEAR(encoder->GetStats().Bitrate(),
- config.bitrate_allocation.GetSumBps(),
- kBitrateTolerance * config.bitrate_allocation.GetSumBps());
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), config.num_frames_to_encode);
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- }
- TEST_F(VideoEncoderTest, FlushAtEndOfStream_NV12Dmabuf) {
- if (auto skip_reason = SupportsNV12DmaBufInput())
- GTEST_SKIP() << *skip_reason;
- Video* nv12_video = g_env->GenerateNV12Video();
- VideoEncoderClientConfig config(nv12_video, g_env->Profile(),
- g_env->SpatialLayers(),
- g_env->BitrateAllocation(), g_env->Reverse());
- config.input_storage_type =
- VideoEncodeAccelerator::Config::StorageType::kGpuMemoryBuffer;
- auto encoder = CreateVideoEncoder(nv12_video, config);
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), nv12_video->NumFrames());
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- }
- // Downscaling is required in VideoEncodeAccelerator when zero-copy video
- // capture is enabled. One example is simulcast, camera produces 360p VideoFrame
- // and there are two VideoEncodeAccelerator for 360p and 180p. VideoEncoder for
- // 180p is fed 360p and thus has to perform the scaling from 360p to 180p.
- TEST_F(VideoEncoderTest, FlushAtEndOfStream_NV12DmabufScaling) {
- if (auto skip_reason = SupportsNV12DmaBufInput())
- GTEST_SKIP() << *skip_reason;
- if (g_env->SpatialLayers().size() > 1)
- GTEST_SKIP() << "Skip simulcast test case for spatial SVC encoding";
- constexpr gfx::Size kMinOutputResolution(240, 180);
- const gfx::Size output_resolution =
- gfx::Size(g_env->Video()->Resolution().width() / 2,
- g_env->Video()->Resolution().height() / 2);
- if (!gfx::Rect(output_resolution).Contains(gfx::Rect(kMinOutputResolution))) {
- GTEST_SKIP() << "Skip test if video resolution is too small, "
- << "output_resolution=" << output_resolution.ToString()
- << ", minimum output resolution="
- << kMinOutputResolution.ToString();
- }
- auto* nv12_video = g_env->GenerateNV12Video();
- // Set 1/4 of the original bitrate because the area of |output_resolution| is
- // 1/4 of the original resolution.
- uint32_t new_target_bitrate = g_env->BitrateAllocation().GetSumBps() / 4;
- // TODO(b/181797390): Reconsider if this peak bitrate is reasonable.
- const Bitrate new_bitrate =
- g_env->BitrateAllocation().GetMode() == Bitrate::Mode::kConstant
- ? Bitrate::ConstantBitrate(new_target_bitrate)
- : Bitrate::VariableBitrate(new_target_bitrate,
- new_target_bitrate * 2);
- auto spatial_layers = g_env->SpatialLayers();
- size_t num_temporal_layers = 1u;
- if (!spatial_layers.empty()) {
- CHECK_EQ(spatial_layers.size(), 1u);
- spatial_layers[0].width = output_resolution.width();
- spatial_layers[0].height = output_resolution.height();
- spatial_layers[0].bitrate_bps /= 4;
- num_temporal_layers = spatial_layers[0].num_of_temporal_layers;
- }
- VideoEncoderClientConfig config(
- nv12_video, g_env->Profile(), spatial_layers,
- AllocateDefaultBitrateForTesting(/*num_spatial_layers=*/1u,
- num_temporal_layers, new_bitrate),
- g_env->Reverse());
- config.output_resolution = output_resolution;
- config.input_storage_type =
- VideoEncodeAccelerator::Config::StorageType::kGpuMemoryBuffer;
- auto encoder = CreateVideoEncoder(nv12_video, config);
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), nv12_video->NumFrames());
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- }
- // Encode VideoFrames with cropping the rectangle (0, 60, size).
- // Cropping is required in VideoEncodeAccelerator when zero-copy video
- // capture is enabled. One example is when 640x360 capture recording is
- // requested, a camera cannot produce the resolution and instead produces
- // 640x480 frames with visible_rect=0, 60, 640x360.
- TEST_F(VideoEncoderTest, FlushAtEndOfStream_NV12DmabufCroppingTopAndBottom) {
- if (auto skip_reason = SupportsNV12DmaBufInput())
- GTEST_SKIP() << *skip_reason;
- constexpr int kGrowHeight = 120;
- const gfx::Size original_resolution = g_env->Video()->Resolution();
- const gfx::Rect expanded_visible_rect(0, kGrowHeight / 2,
- original_resolution.width(),
- original_resolution.height());
- const gfx::Size expanded_resolution(
- original_resolution.width(), original_resolution.height() + kGrowHeight);
- constexpr gfx::Size kMaxExpandedResolution(1920, 1080);
- if (!gfx::Rect(kMaxExpandedResolution)
- .Contains(gfx::Rect(expanded_resolution))) {
- GTEST_SKIP() << "Expanded video resolution is too large, "
- << "expanded_resolution=" << expanded_resolution.ToString()
- << ", maximum expanded resolution="
- << kMaxExpandedResolution.ToString();
- }
- auto nv12_expanded_video = g_env->GenerateNV12Video()->Expand(
- expanded_resolution, expanded_visible_rect);
- ASSERT_TRUE(nv12_expanded_video);
- VideoEncoderClientConfig config(nv12_expanded_video.get(), g_env->Profile(),
- g_env->SpatialLayers(),
- g_env->BitrateAllocation(), g_env->Reverse());
- config.output_resolution = original_resolution;
- config.input_storage_type =
- VideoEncodeAccelerator::Config::StorageType::kGpuMemoryBuffer;
- auto encoder = CreateVideoEncoder(nv12_expanded_video.get(), config);
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), nv12_expanded_video->NumFrames());
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- }
- // Encode VideoFrames with cropping the rectangle (60, 0, size).
- // Cropping is required in VideoEncodeAccelerator when zero-copy video
- // capture is enabled. One example is when 640x360 capture recording is
- // requested, a camera cannot produce the resolution and instead produces
- // 760x360 frames with visible_rect=60, 0, 640x360.
- TEST_F(VideoEncoderTest, FlushAtEndOfStream_NV12DmabufCroppingRightAndLeft) {
- if (auto skip_reason = SupportsNV12DmaBufInput())
- GTEST_SKIP() << *skip_reason;
- constexpr int kGrowWidth = 120;
- const gfx::Size original_resolution = g_env->Video()->Resolution();
- const gfx::Rect expanded_visible_rect(kGrowWidth / 2, 0,
- original_resolution.width(),
- original_resolution.height());
- const gfx::Size expanded_resolution(original_resolution.width() + kGrowWidth,
- original_resolution.height());
- constexpr gfx::Size kMaxExpandedResolution(1920, 1080);
- if (!gfx::Rect(kMaxExpandedResolution)
- .Contains(gfx::Rect(expanded_resolution))) {
- GTEST_SKIP() << "Expanded video resolution is too large, "
- << "expanded_resolution=" << expanded_resolution.ToString()
- << ", maximum expanded resolution="
- << kMaxExpandedResolution.ToString();
- }
- auto nv12_expanded_video = g_env->GenerateNV12Video()->Expand(
- expanded_resolution, expanded_visible_rect);
- ASSERT_TRUE(nv12_expanded_video);
- VideoEncoderClientConfig config(nv12_expanded_video.get(), g_env->Profile(),
- g_env->SpatialLayers(),
- g_env->BitrateAllocation(), g_env->Reverse());
- config.output_resolution = original_resolution;
- config.input_storage_type =
- VideoEncodeAccelerator::Config::StorageType::kGpuMemoryBuffer;
- auto encoder = CreateVideoEncoder(nv12_expanded_video.get(), config);
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), nv12_expanded_video->NumFrames());
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- }
- // This tests deactivate and activating spatial layers during encoding.
- TEST_F(VideoEncoderTest, DeactivateAndActivateSpatialLayers) {
- if (auto skip_reason = SupportsNV12DmaBufInput())
- GTEST_SKIP() << *skip_reason;
- const auto& spatial_layers = g_env->SpatialLayers();
- if (spatial_layers.size() <= 1)
- GTEST_SKIP() << "Skip (de)activate spatial layers test for simple encoding";
- Video* nv12_video = g_env->GenerateNV12Video();
- const size_t bottom_spatial_idx = 0;
- const size_t top_spatial_idx = spatial_layers.size() - 1;
- auto deactivate_spatial_layer =
- [](VideoBitrateAllocation bitrate_allocation,
- size_t deactivate_sid) -> VideoBitrateAllocation {
- for (size_t i = 0; i < VideoBitrateAllocation::kMaxTemporalLayers; ++i)
- bitrate_allocation.SetBitrate(deactivate_sid, i, 0u);
- return bitrate_allocation;
- };
- const auto& default_allocation = g_env->BitrateAllocation();
- std::vector<VideoBitrateAllocation> bitrate_allocations;
- // Deactivate the top layer.
- bitrate_allocations.emplace_back(
- deactivate_spatial_layer(default_allocation, top_spatial_idx));
- // Activate the top layer.
- bitrate_allocations.emplace_back(default_allocation);
- // Deactivate the bottom layer (and top layer if there is still a spatial
- // layer).
- auto bitrate_allocation =
- deactivate_spatial_layer(default_allocation, bottom_spatial_idx);
- if (bottom_spatial_idx + 1 < top_spatial_idx) {
- bitrate_allocation =
- deactivate_spatial_layer(bitrate_allocation, top_spatial_idx);
- }
- bitrate_allocations.emplace_back(bitrate_allocation);
- // Deactivate the layers except bottom layer.
- bitrate_allocation = default_allocation;
- for (size_t i = bottom_spatial_idx + 1; i < spatial_layers.size(); ++i)
- bitrate_allocation = deactivate_spatial_layer(bitrate_allocation, i);
- bitrate_allocations.emplace_back(bitrate_allocation);
- VideoEncoderClientConfig config(nv12_video, g_env->Profile(),
- g_env->SpatialLayers(),
- g_env->BitrateAllocation(), g_env->Reverse());
- config.input_storage_type =
- VideoEncodeAccelerator::Config::StorageType::kGpuMemoryBuffer;
- std::vector<size_t> num_frames_to_encode(bitrate_allocations.size());
- for (size_t i = 0; i < num_frames_to_encode.size(); ++i)
- num_frames_to_encode[i] = config.num_frames_to_encode * (i + 1);
- config.num_frames_to_encode =
- num_frames_to_encode.back() + config.num_frames_to_encode;
- auto encoder = CreateVideoEncoder(nv12_video, config);
- for (size_t i = 0; i < bitrate_allocations.size(); ++i) {
- encoder->EncodeUntil(VideoEncoder::kFrameReleased, num_frames_to_encode[i]);
- EXPECT_TRUE(encoder->WaitUntilIdle());
- encoder->UpdateBitrate(bitrate_allocations[i], config.framerate);
- }
- encoder->Encode();
- EXPECT_TRUE(encoder->WaitForFlushDone());
- EXPECT_EQ(encoder->GetFlushDoneCount(), 1u);
- EXPECT_EQ(encoder->GetFrameReleasedCount(), config.num_frames_to_encode);
- EXPECT_TRUE(encoder->WaitForBitstreamProcessors());
- }
- } // namespace test
- } // namespace media
- int main(int argc, char** argv) {
- // Set the default test data path.
- media::test::Video::SetTestDataPath(media::GetTestDataPath());
- // Print the help message if requested. This needs to be done before
- // initializing gtest, to overwrite the default gtest help message.
- base::CommandLine::Init(argc, argv);
- const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
- LOG_ASSERT(cmd_line);
- if (cmd_line->HasSwitch("help")) {
- std::cout << media::test::usage_msg << "\n" << media::test::help_msg;
- return 0;
- }
- // Check if a video was specified on the command line.
- base::CommandLine::StringVector args = cmd_line->GetArgs();
- base::FilePath video_path =
- (args.size() >= 1) ? base::FilePath(args[0])
- : base::FilePath(media::test::kDefaultTestVideoPath);
- base::FilePath video_metadata_path =
- (args.size() >= 2) ? base::FilePath(args[1]) : base::FilePath();
- std::string codec = "h264";
- size_t num_temporal_layers = 1u;
- size_t num_spatial_layers = 1u;
- bool output_bitstream = false;
- bool reverse = false;
- media::Bitrate::Mode bitrate_mode = media::Bitrate::Mode::kConstant;
- media::test::FrameOutputConfig frame_output_config;
- base::FilePath output_folder =
- base::FilePath(base::FilePath::kCurrentDirectory);
- std::vector<base::Feature> disabled_features;
- // Parse command line arguments.
- bool enable_bitstream_validator = true;
- base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
- for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
- it != switches.end(); ++it) {
- if (it->first.find("gtest_") == 0 || // Handled by GoogleTest
- it->first == "v" || it->first == "vmodule") { // Handled by Chrome
- continue;
- }
- if (it->first == "codec") {
- codec = it->second;
- } else if (it->first == "num_temporal_layers") {
- if (!base::StringToSizeT(it->second, &num_temporal_layers)) {
- std::cout << "invalid number of temporal layers: " << it->second
- << "\n";
- return EXIT_FAILURE;
- }
- } else if (it->first == "num_spatial_layers") {
- if (!base::StringToSizeT(it->second, &num_spatial_layers)) {
- std::cout << "invalid number of spatial layers: " << it->second << "\n";
- return EXIT_FAILURE;
- }
- } else if (it->first == "bitrate_mode") {
- if (it->second == "vbr") {
- bitrate_mode = media::Bitrate::Mode::kVariable;
- } else if (it->second != "cbr") {
- std::cout << "unknown bitrate mode \"" << it->second
- << "\", possible values are \"cbr|vbr\"\n";
- return EXIT_FAILURE;
- }
- } else if (it->first == "disable_validator") {
- enable_bitstream_validator = false;
- } else if (it->first == "output_bitstream") {
- output_bitstream = true;
- } else if (it->first == "reverse") {
- reverse = true;
- } else if (it->first == "output_images") {
- if (it->second == "all") {
- frame_output_config.output_mode = media::test::FrameOutputMode::kAll;
- } else if (it->second == "corrupt") {
- frame_output_config.output_mode =
- media::test::FrameOutputMode::kCorrupt;
- } else {
- std::cout << "unknown image output mode \"" << it->second
- << "\", possible values are \"all|corrupt\"\n";
- return EXIT_FAILURE;
- }
- } else if (it->first == "output_format") {
- if (it->second == "png") {
- frame_output_config.output_format =
- media::test::VideoFrameFileWriter::OutputFormat::kPNG;
- } else if (it->second == "yuv") {
- frame_output_config.output_format =
- media::test::VideoFrameFileWriter::OutputFormat::kYUV;
- } else {
- std::cout << "unknown frame output format \"" << it->second
- << "\", possible values are \"png|yuv\"\n";
- return EXIT_FAILURE;
- }
- } else if (it->first == "output_limit") {
- if (!base::StringToUint64(it->second,
- &frame_output_config.output_limit)) {
- std::cout << "invalid number \"" << it->second << "\n";
- return EXIT_FAILURE;
- }
- } else if (it->first == "output_folder") {
- output_folder = base::FilePath(it->second);
- } else if (it->first == "disable_vaapi_lock") {
- disabled_features.push_back(media::kGlobalVaapiLock);
- } else {
- std::cout << "unknown option: --" << it->first << "\n"
- << media::test::usage_msg;
- return EXIT_FAILURE;
- }
- }
- testing::InitGoogleTest(&argc, argv);
- // Set up our test environment.
- media::test::VideoEncoderTestEnvironment* test_environment =
- media::test::VideoEncoderTestEnvironment::Create(
- video_path, video_metadata_path, enable_bitstream_validator,
- output_folder, codec, num_temporal_layers, num_spatial_layers,
- output_bitstream,
- /*output_bitrate=*/absl::nullopt, bitrate_mode, reverse,
- frame_output_config,
- /*enabled_features=*/{}, disabled_features);
- if (!test_environment)
- return EXIT_FAILURE;
- media::test::g_env = static_cast<media::test::VideoEncoderTestEnvironment*>(
- testing::AddGlobalTestEnvironment(test_environment));
- return RUN_ALL_TESTS();
- }
|