blob: 261ada781c5bf2c1a0e535012d99f3149b2a3a74 [file] [log] [blame]
// Copyright 2018 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 "base/command_line.h"
#include "media/base/test_data_util.h"
#include "media/gpu/test/video_frame_file_writer.h"
#include "media/gpu/test/video_frame_validator.h"
#include "media/gpu/test/video_player/frame_renderer_dummy.h"
#include "media/gpu/test/video_player/frame_renderer_thumbnail.h"
#include "media/gpu/test/video_player/video.h"
#include "media/gpu/test/video_player/video_decoder_client.h"
#include "media/gpu/test/video_player/video_player.h"
#include "media/gpu/test/video_player/video_player_test_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace test {
namespace {
// Video decoder tests usage message. Make sure to also update the documentation
// under docs/media/gpu/video_decoder_test_usage.md when making changes here.
constexpr const char* usage_msg =
"usage: video_decode_accelerator_tests\n"
" [-v=<level>] [--vmodule=<config>] [--disable_validator]\n"
" [--output_frames] [output_folder] [--use_vd] [--gtest_help]\n"
" [--help] [<video path>] [<video metadata path>]\n";
// Video decoder tests help message.
constexpr const char* help_msg =
"Run the video decode accelerator tests on the video specified by\n"
"<video path>. If no <video path> is given the default\n"
"\"test-25fps.h264\" video will be used.\n"
"\nThe <video metadata path> should specify the location of a json file\n"
"containing the video's metadata, such as frame checksums. By default\n"
"<video path>.json will be used.\n"
"\nThe following arguments are supported:\n"
" -v enable verbose mode, e.g. -v=2.\n"
" --vmodule enable verbose mode for the specified module,\n"
" e.g. --vmodule=*media/gpu*=2.\n"
" --disable_validator disable frame validation.\n"
" --output_frames write all decoded video frames to the\n"
" \"<testname>\" folder.\n"
" --output_folder overwrite the default output folder used when\n"
" \"--output_frames\" is specified.\n"
" --use_vd use the new VD-based video decoders, instead of\n"
" the default VDA-based video decoders.\n"
" --gtest_help display the gtest help and exit.\n"
" --help display this help and exit.\n";
media::test::VideoPlayerTestEnvironment* g_env;
// Video decode test class. Performs setup and teardown for each single test.
class VideoDecoderTest : public ::testing::Test {
public:
std::unique_ptr<VideoPlayer> CreateVideoPlayer(
const Video* video,
VideoDecoderClientConfig config = VideoDecoderClientConfig(),
std::unique_ptr<FrameRenderer> frame_renderer =
FrameRendererDummy::Create()) {
LOG_ASSERT(video);
std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors;
// Force allocate mode if import mode is not supported.
if (!g_env->ImportSupported())
config.allocation_mode = AllocationMode::kAllocate;
// Use the video frame validator to validate decoded video frames if import
// mode is supported and enabled.
if (g_env->IsValidatorEnabled() &&
config.allocation_mode == AllocationMode::kImport) {
frame_processors.push_back(
media::test::VideoFrameValidator::Create(video->FrameChecksums()));
}
// Write decoded video frames to the '<testname>' folder.
if (g_env->IsFramesOutputEnabled()) {
base::FilePath output_folder =
base::FilePath(g_env->OutputFolder())
.Append(base::FilePath(g_env->GetTestName()));
frame_processors.push_back(VideoFrameFileWriter::Create(output_folder));
VLOG(0) << "Writing video frames to: " << output_folder;
}
// Use the new VD-based video decoders if requested.
config.use_vd = g_env->UseVD();
return VideoPlayer::Create(video, std::move(frame_renderer),
std::move(frame_processors), config);
}
};
} // namespace
// Play video from start to end. Wait for the kFlushDone event at the end of the
// stream, that notifies us all frames have been decoded.
TEST_F(VideoDecoderTest, FlushAtEndOfStream) {
auto tvp = CreateVideoPlayer(g_env->Video());
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->Video()->NumFrames());
EXPECT_TRUE(tvp->WaitForFrameProcessors());
}
// Flush the decoder immediately after initialization.
TEST_F(VideoDecoderTest, FlushAfterInitialize) {
auto tvp = CreateVideoPlayer(g_env->Video());
tvp->Flush();
EXPECT_TRUE(tvp->WaitForFlushDone());
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
EXPECT_EQ(tvp->GetFlushDoneCount(), 2u);
EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->Video()->NumFrames());
EXPECT_TRUE(tvp->WaitForFrameProcessors());
}
// Reset the decoder immediately after initialization.
TEST_F(VideoDecoderTest, ResetAfterInitialize) {
auto tvp = CreateVideoPlayer(g_env->Video());
tvp->Reset();
EXPECT_TRUE(tvp->WaitForResetDone());
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->Video()->NumFrames());
EXPECT_TRUE(tvp->WaitForFrameProcessors());
}
// Reset the decoder when the middle of the stream is reached.
TEST_F(VideoDecoderTest, ResetMidStream) {
auto tvp = CreateVideoPlayer(g_env->Video());
tvp->Play();
EXPECT_TRUE(tvp->WaitForFrameDecoded(g_env->Video()->NumFrames() / 2));
tvp->Reset();
EXPECT_TRUE(tvp->WaitForResetDone());
size_t numFramesDecoded = tvp->GetFrameDecodedCount();
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
EXPECT_EQ(tvp->GetFrameDecodedCount(),
numFramesDecoded + g_env->Video()->NumFrames());
EXPECT_TRUE(tvp->WaitForFrameProcessors());
}
// Reset the decoder when the end of the stream is reached.
TEST_F(VideoDecoderTest, ResetEndOfStream) {
auto tvp = CreateVideoPlayer(g_env->Video());
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->Video()->NumFrames());
tvp->Reset();
EXPECT_TRUE(tvp->WaitForResetDone());
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
EXPECT_EQ(tvp->GetFlushDoneCount(), 2u);
EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->Video()->NumFrames() * 2);
EXPECT_TRUE(tvp->WaitForFrameProcessors());
}
// Reset the decoder immediately when the end-of-stream flush starts, without
// waiting for a kFlushDone event.
TEST_F(VideoDecoderTest, ResetBeforeFlushDone) {
auto tvp = CreateVideoPlayer(g_env->Video());
// Reset when a kFlushing event is received.
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
tvp->Reset();
EXPECT_TRUE(tvp->WaitForResetDone());
// Reset will cause the decoder to drop everything it's doing, including the
// ongoing flush operation. However the flush might have been completed
// already by the time reset is called. So depending on the timing of the
// calls we should see 0 or 1 flushes, and the last few video frames might
// have been dropped.
EXPECT_LE(tvp->GetFlushDoneCount(), 1u);
EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
EXPECT_LE(tvp->GetFrameDecodedCount(), g_env->Video()->NumFrames());
EXPECT_TRUE(tvp->WaitForFrameProcessors());
}
// Reset the decoder immediately when encountering the first config info in a
// H.264 video stream. After resetting the video is played until the end.
TEST_F(VideoDecoderTest, ResetAfterFirstConfigInfo) {
// This test is only relevant for H.264 video streams.
if (g_env->Video()->Profile() < H264PROFILE_MIN ||
g_env->Video()->Profile() > H264PROFILE_MAX)
GTEST_SKIP();
auto tvp = CreateVideoPlayer(g_env->Video());
tvp->PlayUntil(VideoPlayerEvent::kConfigInfo);
EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kConfigInfo));
tvp->Reset();
EXPECT_TRUE(tvp->WaitForResetDone());
size_t numFramesDecoded = tvp->GetFrameDecodedCount();
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
EXPECT_EQ(tvp->GetFrameDecodedCount(),
numFramesDecoded + g_env->Video()->NumFrames());
EXPECT_GE(tvp->GetEventCount(VideoPlayerEvent::kConfigInfo), 1u);
EXPECT_TRUE(tvp->WaitForFrameProcessors());
}
// Play video from start to end. Multiple buffer decodes will be queued in the
// decoder, without waiting for the result of the previous decode requests.
TEST_F(VideoDecoderTest, FlushAtEndOfStream_MultipleOutstandingDecodes) {
VideoDecoderClientConfig config;
config.max_outstanding_decode_requests = 4;
auto tvp = CreateVideoPlayer(g_env->Video(), config);
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->Video()->NumFrames());
EXPECT_TRUE(tvp->WaitForFrameProcessors());
}
// Play multiple videos simultaneously from start to finish.
TEST_F(VideoDecoderTest, FlushAtEndOfStream_MultipleConcurrentDecodes) {
// The minimal number of concurrent decoders we expect to be supported.
constexpr size_t kMinSupportedConcurrentDecoders = 3;
std::vector<std::unique_ptr<VideoPlayer>> tvps(
kMinSupportedConcurrentDecoders);
for (size_t i = 0; i < kMinSupportedConcurrentDecoders; ++i)
tvps[i] = CreateVideoPlayer(g_env->Video());
for (size_t i = 0; i < kMinSupportedConcurrentDecoders; ++i)
tvps[i]->Play();
for (size_t i = 0; i < kMinSupportedConcurrentDecoders; ++i) {
EXPECT_TRUE(tvps[i]->WaitForFlushDone());
EXPECT_EQ(tvps[i]->GetFlushDoneCount(), 1u);
EXPECT_EQ(tvps[i]->GetFrameDecodedCount(), g_env->Video()->NumFrames());
EXPECT_TRUE(tvps[i]->WaitForFrameProcessors());
}
}
// Play a video from start to finish. Thumbnails of the decoded frames will be
// rendered into a image, whose checksum is compared to a golden value. This
// test is only run on older platforms that don't support the video frame
// validator, which requires import mode. If no thumbnail checksums are present
// in the video metadata the test will be skipped. This test will be deprecated
// once all devices support import mode.
TEST_F(VideoDecoderTest, FlushAtEndOfStream_RenderThumbnails) {
if (!g_env->IsValidatorEnabled() || g_env->ImportSupported() ||
g_env->Video()->ThumbnailChecksums().empty()) {
GTEST_SKIP();
}
base::FilePath output_folder =
base::FilePath(g_env->OutputFolder())
.Append(base::FilePath(g_env->GetTestName()));
VideoDecoderClientConfig config;
config.allocation_mode = AllocationMode::kAllocate;
auto tvp = CreateVideoPlayer(
g_env->Video(), config,
FrameRendererThumbnail::Create(g_env->Video()->ThumbnailChecksums(),
output_folder));
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->Video()->NumFrames());
EXPECT_TRUE(tvp->WaitForFrameProcessors());
EXPECT_TRUE(static_cast<FrameRendererThumbnail*>(tvp->GetFrameRenderer())
->ValidateThumbnail());
}
// Play a video from start to finish, using allocate mode. This test is only run
// on platforms that support import mode, as on allocate-mode only platforms all
// tests are run in allocate mode. The test will be skipped when --use_vd is
// specified as the new video decoders only support import mode.
// TODO(dstaessens): Deprecate after switching to new VD-based video decoders.
TEST_F(VideoDecoderTest, FlushAtEndOfStream_Allocate) {
if (!g_env->ImportSupported() || g_env->UseVD())
GTEST_SKIP();
VideoDecoderClientConfig config;
config.allocation_mode = AllocationMode::kAllocate;
auto tvp = CreateVideoPlayer(g_env->Video(), config);
tvp->Play();
EXPECT_TRUE(tvp->WaitForFlushDone());
EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->Video()->NumFrames());
EXPECT_TRUE(tvp->WaitForFrameProcessors());
}
} // 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();
base::FilePath video_metadata_path =
(args.size() >= 2) ? base::FilePath(args[1]) : base::FilePath();
// Parse command line arguments.
bool enable_validator = true;
bool output_frames = false;
base::FilePath::StringType output_folder = base::FilePath::kCurrentDirectory;
bool use_vd = false;
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 == "disable_validator") {
enable_validator = false;
} else if (it->first == "output_frames") {
output_frames = true;
} else if (it->first == "output_folder") {
output_folder = it->second;
} else if (it->first == "use_vd") {
use_vd = true;
} 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::VideoPlayerTestEnvironment* test_environment =
media::test::VideoPlayerTestEnvironment::Create(
video_path, video_metadata_path, enable_validator, output_frames,
base::FilePath(output_folder), use_vd);
if (!test_environment)
return EXIT_FAILURE;
media::test::g_env = static_cast<media::test::VideoPlayerTestEnvironment*>(
testing::AddGlobalTestEnvironment(test_environment));
return RUN_ALL_TESTS();
}