blob: 7b4f3cb3795d87aef4b000d93e66f16037bfb03a [file] [log] [blame]
// 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 "media/gpu/vaapi/vp9_temporal_layers.h"
#include <algorithm>
#include <array>
#include <map>
#include <vector>
#include "base/optional.h"
#include "media/filters/vp9_parser.h"
#include "media/gpu/vp9_picture.h"
#include "media/gpu/vp9_reference_frame_vector.h"
#include "media/video/video_encode_accelerator.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
class VP9TemporalLayersTest : public ::testing::TestWithParam<size_t> {
public:
VP9TemporalLayersTest() = default;
~VP9TemporalLayersTest() = default;
protected:
void VerifyRefFrames(
const Vp9FrameHeader& frame_hdr,
const Vp9Metadata& metadata,
const std::array<bool, kVp9NumRefsPerFrame>& ref_frames_used,
const Vp9ReferenceFrameVector& ref_frames);
void VerifyTemporalLayersStructure(bool keyframe,
size_t num_layers,
const Vp9Metadata& metadata);
private:
std::vector<uint8_t> temporal_indices_;
};
void VP9TemporalLayersTest::VerifyTemporalLayersStructure(
bool keyframe,
size_t num_layers,
const Vp9Metadata& metadata) {
const uint8_t temporal_index = metadata.temporal_idx;
if (keyframe) {
temporal_indices_.clear();
temporal_indices_.push_back(temporal_index);
return;
}
ASSERT_FALSE(temporal_indices_.empty());
EXPECT_NE(temporal_indices_.back(), temporal_index);
// Check that the number of temporal layer ids are expected.
temporal_indices_.push_back(temporal_index);
constexpr size_t kTemporalLayerCycle = 4u;
if (temporal_indices_.size() % kTemporalLayerCycle == 0) {
std::vector<size_t> count(num_layers, 0u);
for (const uint8_t index : temporal_indices_) {
ASSERT_LE(index, num_layers);
count[index]++;
}
// The number of frames in a higher layer is not less than one in a lower
// layer.
EXPECT_TRUE(std::is_sorted(count.begin(), count.end()));
}
}
void VP9TemporalLayersTest::VerifyRefFrames(
const Vp9FrameHeader& frame_hdr,
const Vp9Metadata& metadata,
const std::array<bool, kVp9NumRefsPerFrame>& ref_frames_used,
const Vp9ReferenceFrameVector& ref_frames) {
const uint8_t current_temporal_index = metadata.temporal_idx;
if (frame_hdr.IsKeyframe()) {
EXPECT_EQ(frame_hdr.refresh_frame_flags, 0xff);
EXPECT_FALSE(base::Contains(ref_frames_used, true));
EXPECT_EQ(current_temporal_index, 0u);
return;
}
// Two slots at most in the reference pool are used in temporal layer
// encoding. Additionally, non-keyframe must reference some frames.
EXPECT_EQ(frame_hdr.refresh_frame_flags & ~(0b11u), 0u);
EXPECT_FALSE(ref_frames_used[VP9TemporalLayers::kMaxNumUsedReferenceFrames]);
EXPECT_TRUE(base::Contains(ref_frames_used, true));
EXPECT_TRUE(metadata.has_reference);
// Check that the current frame doesn't reference upper layer frames.
for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) {
if (!ref_frames_used[i])
continue;
const uint8_t index = frame_hdr.ref_frame_idx[i];
scoped_refptr<VP9Picture> ref_frame = ref_frames.GetFrame(index);
ASSERT_TRUE(!!ref_frame);
const auto& ref_metadata = ref_frame->metadata_for_encoding;
ASSERT_TRUE(ref_metadata.has_value());
const size_t ref_temporal_index = ref_metadata->temporal_idx;
EXPECT_LE(ref_temporal_index, current_temporal_index);
}
return;
}
TEST_P(VP9TemporalLayersTest, ) {
const size_t num_temporal_layers = GetParam();
VP9TemporalLayers temporal_layers(num_temporal_layers);
EXPECT_EQ(temporal_layers.num_layers(), num_temporal_layers);
constexpr size_t kNumFramesToEncode = 32;
Vp9ReferenceFrameVector ref_frames;
constexpr size_t kKeyFrameInterval = 16;
for (size_t i = 0; i < kNumFramesToEncode; i++) {
const bool keyframe = i % kKeyFrameInterval == 0;
scoped_refptr<VP9Picture> picture(new VP9Picture);
picture->frame_hdr = std::make_unique<Vp9FrameHeader>();
picture->frame_hdr->frame_type =
keyframe ? Vp9FrameHeader::KEYFRAME : Vp9FrameHeader::INTERFRAME;
std::array<bool, kVp9NumRefsPerFrame> ref_frames_used;
temporal_layers.FillUsedRefFramesAndMetadata(picture.get(),
&ref_frames_used);
ASSERT_TRUE(picture->metadata_for_encoding.has_value());
VerifyRefFrames(*picture->frame_hdr, *picture->metadata_for_encoding,
ref_frames_used, ref_frames);
VerifyTemporalLayersStructure(keyframe, num_temporal_layers,
*picture->metadata_for_encoding);
ref_frames.Refresh(picture);
}
}
constexpr size_t kNumTemporalLayers[] = {
VP9TemporalLayers::kMinSupportedTemporalLayers,
VP9TemporalLayers::kMaxSupportedTemporalLayers,
};
INSTANTIATE_TEST_SUITE_P(,
VP9TemporalLayersTest,
::testing::ValuesIn(kNumTemporalLayers));
} // namespace media