blob: 44ccd1e68bc26c60202eadf50ca441217a614207 [file] [log] [blame]
// Copyright 2017 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 <stdint.h>
#include <string.h>
#include <cstring>
#include <memory>
#include <string>
#include "base/check.h"
#include "base/command_line.h"
#include "base/containers/queue.h"
#include "base/containers/span.h"
#include "base/files/file_util.h"
#include "media/base/test_data_util.h"
#include "media/gpu/h264_decoder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::Args;
using ::testing::Expectation;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::MakeMatcher;
using ::testing::Matcher;
using ::testing::MatcherInterface;
using ::testing::MatchResultListener;
using ::testing::Mock;
using ::testing::Return;
namespace media {
namespace {
const std::string kBaselineFrame0 = "bear-320x192-baseline-frame-0.h264";
const std::string kBaselineFrame1 = "bear-320x192-baseline-frame-1.h264";
const std::string kBaselineFrame2 = "bear-320x192-baseline-frame-2.h264";
const std::string kBaselineFrame3 = "bear-320x192-baseline-frame-3.h264";
const std::string kHighFrame0 = "bear-320x192-high-frame-0.h264";
const std::string kHighFrame1 = "bear-320x192-high-frame-1.h264";
const std::string kHighFrame2 = "bear-320x192-high-frame-2.h264";
const std::string kHighFrame3 = "bear-320x192-high-frame-3.h264";
const std::string k10BitFrame0 = "bear-320x180-10bit-frame-0.h264";
const std::string k10BitFrame1 = "bear-320x180-10bit-frame-1.h264";
const std::string k10BitFrame2 = "bear-320x180-10bit-frame-2.h264";
const std::string k10BitFrame3 = "bear-320x180-10bit-frame-3.h264";
const std::string kYUV444Frame = "blackwhite_yuv444p-frame.h264";
// Checks whether the decrypt config in the picture matches the decrypt config
// passed to this matcher.
MATCHER_P(DecryptConfigMatches, decrypt_config, "") {
return arg->decrypt_config()->Matches(*decrypt_config);
}
MATCHER(SubsampleSizeMatches, "Verify subsample sizes match buffer size") {
const size_t buffer_size = ::testing::get<0>(arg);
const std::vector<SubsampleEntry>& subsamples = ::testing::get<1>(arg);
size_t subsample_total_size = 0;
for (const auto& sample : subsamples) {
subsample_total_size += sample.cypher_bytes;
subsample_total_size += sample.clear_bytes;
}
return subsample_total_size == buffer_size;
}
// Emulates encrypted slice header parsing. We don't actually encrypt the data
// so we can easily do this by just parsing it.
H264Decoder::H264Accelerator::Status ParseSliceHeader(
const std::vector<base::span<const uint8_t>>& data,
const std::vector<SubsampleEntry>& subsamples,
const std::vector<uint8_t>& sps_nalu_data,
const std::vector<uint8_t>& pps_nalu_data,
H264SliceHeader* slice_hdr_out) {
EXPECT_TRUE(!sps_nalu_data.empty());
EXPECT_TRUE(!pps_nalu_data.empty());
// Construct the bitstream for parsing.
std::vector<uint8_t> full_data;
const std::vector<uint8_t> start_code = {0u, 0u, 1u};
full_data.insert(full_data.end(), start_code.begin(), start_code.end());
full_data.insert(full_data.end(), sps_nalu_data.begin(), sps_nalu_data.end());
full_data.insert(full_data.end(), start_code.begin(), start_code.end());
full_data.insert(full_data.end(), pps_nalu_data.begin(), pps_nalu_data.end());
for (const auto& span : data) {
full_data.insert(full_data.end(), start_code.begin(), start_code.end());
full_data.insert(full_data.end(), span.begin(), span.end());
}
H264Parser parser;
parser.SetStream(full_data.data(), full_data.size());
while (true) {
H264NALU nalu;
H264Parser::Result res = parser.AdvanceToNextNALU(&nalu);
if (res == H264Parser::kEOStream)
break;
EXPECT_EQ(H264Parser::kOk, res);
switch (nalu.nal_unit_type) {
case H264NALU::kSPS:
int sps_id;
EXPECT_EQ(H264Parser::kOk, parser.ParseSPS(&sps_id));
break;
case H264NALU::kPPS:
int pps_id;
EXPECT_EQ(H264Parser::kOk, parser.ParsePPS(&pps_id));
break;
case H264NALU::kIDRSlice: // fallthrough
case H264NALU::kNonIDRSlice:
EXPECT_EQ(H264Parser::kOk,
parser.ParseSliceHeader(nalu, slice_hdr_out));
slice_hdr_out->full_sample_encryption = true;
break;
}
}
return H264Decoder::H264Accelerator::Status::kOk;
}
class MockH264Accelerator : public H264Decoder::H264Accelerator {
public:
MockH264Accelerator() = default;
MOCK_METHOD0(CreateH264Picture, scoped_refptr<H264Picture>());
MOCK_METHOD1(SubmitDecode, Status(scoped_refptr<H264Picture> pic));
MOCK_METHOD5(ParseEncryptedSliceHeader,
Status(const std::vector<base::span<const uint8_t>>& data,
const std::vector<SubsampleEntry>& subsamples,
const std::vector<uint8_t>& sps_nalu_data,
const std::vector<uint8_t>& pps_nalu_data,
H264SliceHeader* slice_hdr_out));
MOCK_METHOD7(SubmitFrameMetadata,
Status(const H264SPS* sps,
const H264PPS* pps,
const H264DPB& dpb,
const H264Picture::Vector& ref_pic_listp0,
const H264Picture::Vector& ref_pic_listb0,
const H264Picture::Vector& ref_pic_listb1,
scoped_refptr<H264Picture> pic));
MOCK_METHOD8(SubmitSlice,
Status(const H264PPS* pps,
const H264SliceHeader* slice_hdr,
const H264Picture::Vector& ref_pic_list0,
const H264Picture::Vector& ref_pic_list1,
scoped_refptr<H264Picture> pic,
const uint8_t* data,
size_t size,
const std::vector<SubsampleEntry>& subsamples));
MOCK_METHOD1(OutputPicture, bool(scoped_refptr<H264Picture> pic));
MOCK_METHOD2(SetStream,
Status(base::span<const uint8_t> stream,
const DecryptConfig* decrypt_config));
void Reset() override {}
};
// Test H264Decoder by feeding different of h264 frame sequences and make
// sure it behaves as expected.
class H264DecoderTest : public ::testing::Test {
public:
H264DecoderTest() = default;
void SetUp() override;
// Sets the bitstreams to be decoded, frame by frame. The content of each
// file is the encoded bitstream of a single video frame.
void SetInputFrameFiles(const std::vector<std::string>& frame_files);
// Keeps decoding the input bitstream set at |SetInputFrameFiles| until the
// decoder has consumed all bitstreams or returned from
// |H264Decoder::Decode|. If |full_sample_encryption| is true, then it sets
// a DecryptConfig for the the DecoderBuffer that indicates all but the first
// byte are encrypted. Returns the same result as |H264Decoder::Decode|.
AcceleratedVideoDecoder::DecodeResult Decode(
bool full_sample_encryption = false);
protected:
std::unique_ptr<H264Decoder> decoder_;
MockH264Accelerator* accelerator_;
private:
base::queue<std::string> input_frame_files_;
std::string bitstream_;
scoped_refptr<DecoderBuffer> decoder_buffer_;
};
void H264DecoderTest::SetUp() {
auto mock_accelerator = std::make_unique<MockH264Accelerator>();
accelerator_ = mock_accelerator.get();
decoder_ = std::make_unique<H264Decoder>(std::move(mock_accelerator),
VIDEO_CODEC_PROFILE_UNKNOWN);
// Sets default behaviors for mock methods for convenience.
ON_CALL(*accelerator_, CreateH264Picture()).WillByDefault(Invoke([]() {
return new H264Picture();
}));
ON_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _))
.WillByDefault(Return(H264Decoder::H264Accelerator::Status::kOk));
ON_CALL(*accelerator_, SubmitDecode(_))
.WillByDefault(Return(H264Decoder::H264Accelerator::Status::kOk));
ON_CALL(*accelerator_, OutputPicture(_)).WillByDefault(Return(true));
ON_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _))
.With(Args<6, 7>(SubsampleSizeMatches()))
.WillByDefault(Return(H264Decoder::H264Accelerator::Status::kOk));
ON_CALL(*accelerator_, SetStream(_, _))
.WillByDefault(
Return(H264Decoder::H264Accelerator::Status::kNotSupported));
}
void H264DecoderTest::SetInputFrameFiles(
const std::vector<std::string>& input_frame_files) {
for (auto f : input_frame_files)
input_frame_files_.push(f);
}
AcceleratedVideoDecoder::DecodeResult H264DecoderTest::Decode(
bool full_sample_encryption) {
while (true) {
auto result = decoder_->Decode();
int32_t bitstream_id = 0;
if (result != AcceleratedVideoDecoder::kRanOutOfStreamData ||
input_frame_files_.empty())
return result;
auto input_file = GetTestDataFilePath(input_frame_files_.front());
input_frame_files_.pop();
CHECK(base::ReadFileToString(input_file, &bitstream_));
decoder_buffer_ = DecoderBuffer::CopyFrom(
reinterpret_cast<const uint8_t*>(bitstream_.data()), bitstream_.size());
if (full_sample_encryption) {
// We only use this in 2 tests, each use the same data where the offset to
// the byte after the NALU type for the slice header is 669.
constexpr int kOffsetToSliceHeader = 669;
decoder_buffer_->set_decrypt_config(DecryptConfig::CreateCencConfig(
"kFakeKeyId", std::string(DecryptConfig::kDecryptionKeySize, 'x'),
{SubsampleEntry(kOffsetToSliceHeader,
bitstream_.size() - kOffsetToSliceHeader)}));
}
EXPECT_NE(decoder_buffer_.get(), nullptr);
decoder_->SetStream(bitstream_id++, *decoder_buffer_);
}
}
// To have better description on mismatch.
class WithPocMatcher : public MatcherInterface<scoped_refptr<H264Picture>> {
public:
explicit WithPocMatcher(int expected_poc) : expected_poc_(expected_poc) {}
bool MatchAndExplain(scoped_refptr<H264Picture> p,
MatchResultListener* listener) const override {
if (p->pic_order_cnt == expected_poc_)
return true;
*listener << "with poc: " << p->pic_order_cnt;
return false;
}
void DescribeTo(std::ostream* os) const override {
*os << "with poc " << expected_poc_;
}
private:
int expected_poc_;
};
inline Matcher<scoped_refptr<H264Picture>> WithPoc(int expected_poc) {
return MakeMatcher(new WithPocMatcher(expected_poc));
}
// Test Cases
TEST_F(H264DecoderTest, DecodeSingleFrame) {
SetInputFrameFiles({kBaselineFrame0});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
EXPECT_CALL(*accelerator_, CreateH264Picture()).WillOnce(Return(nullptr));
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfSurfaces, Decode());
ASSERT_TRUE(Mock::VerifyAndClearExpectations(&*accelerator_));
{
InSequence sequence;
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(_));
EXPECT_CALL(*accelerator_, OutputPicture(_));
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
// This is for CENCv1 full sample encryption.
TEST_F(H264DecoderTest, DecodeSingleEncryptedFrame) {
SetInputFrameFiles({kBaselineFrame0});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode(true));
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
EXPECT_CALL(*accelerator_, ParseEncryptedSliceHeader(_, _, _, _, _))
.WillOnce(Invoke(&ParseSliceHeader));
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(_));
EXPECT_CALL(*accelerator_, OutputPicture(_));
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, SkipNonIDRFrames) {
SetInputFrameFiles({kBaselineFrame1, kBaselineFrame2, kBaselineFrame0});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(_));
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, DecodeProfileBaseline) {
SetInputFrameFiles({
kBaselineFrame0, kBaselineFrame1, kBaselineFrame2, kBaselineFrame3,
});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _)).Times(4);
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _)).Times(4);
Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
{
InSequence decode_order;
decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
decode_poc4 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(4)));
decode_poc6 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(6)));
}
{
InSequence display_order;
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, Decode10BitStream) {
SetInputFrameFiles({k10BitFrame0, k10BitFrame1, k10BitFrame2, k10BitFrame3});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(gfx::Rect(320, 180), decoder_->GetVisibleRect());
EXPECT_EQ(H264PROFILE_HIGH10PROFILE, decoder_->GetProfile());
EXPECT_EQ(10u, decoder_->GetBitDepth());
EXPECT_LE(14u, decoder_->GetRequiredNumOfPictures());
// One picture will be kept in the DPB for reordering. The second picture
// should be outputted after feeding the third and fourth frames.
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _)).Times(4);
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _)).Times(4);
Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
{
InSequence decode_order;
decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
decode_poc6 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(6)));
decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
decode_poc4 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(4)));
}
{
InSequence display_order;
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, OutputPictureFailureCausesDecodeToFail) {
// Provide enough data that Decode() will try to output a frame.
SetInputFrameFiles({
kBaselineFrame0,
kBaselineFrame1,
});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_CALL(*accelerator_, OutputPicture(_)).WillRepeatedly(Return(false));
ASSERT_EQ(AcceleratedVideoDecoder::kDecodeError, Decode());
}
TEST_F(H264DecoderTest, DecodeProfileHigh) {
SetInputFrameFiles({kHighFrame0, kHighFrame1, kHighFrame2, kHighFrame3});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_HIGH, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
// Two pictures will be kept in the DPB for reordering. The first picture
// should be outputted after feeding the third frame.
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _)).Times(4);
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _)).Times(4);
Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
{
InSequence decode_order;
decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
decode_poc4 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(4)));
decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
decode_poc6 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(6)));
}
{
InSequence display_order;
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, DenyDecodeNonYUV420) {
// YUV444 frame causes kDecodeError.
SetInputFrameFiles({kYUV444Frame});
ASSERT_EQ(AcceleratedVideoDecoder::kDecodeError, Decode());
}
TEST_F(H264DecoderTest, SwitchBaselineToHigh) {
SetInputFrameFiles({
kBaselineFrame0, kHighFrame0, kHighFrame1, kHighFrame2, kHighFrame3,
});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(_));
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
}
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_HIGH, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
ASSERT_TRUE(Mock::VerifyAndClearExpectations(&*accelerator_));
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _)).Times(4);
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _)).Times(4);
Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
{
InSequence decode_order;
decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
decode_poc4 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(4)));
decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
decode_poc6 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(6)));
}
{
InSequence display_order;
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, SwitchHighToBaseline) {
SetInputFrameFiles({
kHighFrame0, kBaselineFrame0, kBaselineFrame1, kBaselineFrame2,
kBaselineFrame3,
});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_HIGH, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(_));
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
}
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
ASSERT_TRUE(Mock::VerifyAndClearExpectations(&*accelerator_));
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _)).Times(4);
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _)).Times(4);
Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
{
InSequence decode_order;
decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
decode_poc4 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(4)));
decode_poc6 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(6)));
}
{
InSequence display_order;
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, SwitchYUV420ToNonYUV420) {
SetInputFrameFiles({kBaselineFrame0, kYUV444Frame});
// The first frame, YUV420, is decoded with no error.
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(_));
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
}
// The second frame, YUV444, causes kDecodeError.
ASSERT_EQ(AcceleratedVideoDecoder::kDecodeError, Decode());
}
// Verify that the decryption config is passed to the accelerator.
TEST_F(H264DecoderTest, SetEncryptedStream) {
std::string bitstream;
auto input_file = GetTestDataFilePath(kBaselineFrame0);
CHECK(base::ReadFileToString(input_file, &bitstream));
const char kAnyKeyId[] = "any_16byte_keyid";
const char kAnyIv[] = "any_16byte_iv___";
const std::vector<SubsampleEntry> subsamples = {
// No encrypted bytes. This test only checks whether the data is passed
// thru to the acclerator so making this completely clear.
{bitstream.size(), 0},
};
std::unique_ptr<DecryptConfig> decrypt_config =
DecryptConfig::CreateCencConfig(kAnyKeyId, kAnyIv, subsamples);
EXPECT_CALL(*accelerator_,
SubmitFrameMetadata(_, _, _, _, _, _,
DecryptConfigMatches(decrypt_config.get())))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kOk));
EXPECT_CALL(*accelerator_,
SubmitDecode(DecryptConfigMatches(decrypt_config.get())))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kOk));
auto buffer = DecoderBuffer::CopyFrom(
reinterpret_cast<const uint8_t*>(bitstream.data()), bitstream.size());
ASSERT_NE(buffer.get(), nullptr);
buffer->set_decrypt_config(std::move(decrypt_config));
decoder_->SetStream(0, *buffer);
EXPECT_EQ(AcceleratedVideoDecoder::kConfigChange, decoder_->Decode());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, decoder_->Decode());
EXPECT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, ParseEncryptedSliceHeaderRetry) {
SetInputFrameFiles({kBaselineFrame0});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode(true));
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
EXPECT_CALL(*accelerator_, ParseEncryptedSliceHeader(_, _, _, _, _))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kTryAgain));
ASSERT_EQ(AcceleratedVideoDecoder::kTryAgain, Decode(true));
// Try again, assuming key still not set. Only ParseEncryptedSliceHeader()
// should be called again.
EXPECT_CALL(*accelerator_, ParseEncryptedSliceHeader(_, _, _, _, _))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kTryAgain));
ASSERT_EQ(AcceleratedVideoDecoder::kTryAgain, Decode(true));
// Assume key has been provided now, next call to Decode() should proceed.
{
InSequence sequence;
EXPECT_CALL(*accelerator_, ParseEncryptedSliceHeader(_, _, _, _, _))
.WillOnce(Invoke(&ParseSliceHeader));
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode(true));
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, SubmitFrameMetadataRetry) {
SetInputFrameFiles({kBaselineFrame0});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kTryAgain));
}
ASSERT_EQ(AcceleratedVideoDecoder::kTryAgain, Decode());
// Try again, assuming key still not set. Only SubmitFrameMetadata()
// should be called again.
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(0);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kTryAgain));
ASSERT_EQ(AcceleratedVideoDecoder::kTryAgain, Decode());
// Assume key has been provided now, next call to Decode() should proceed.
{
InSequence sequence;
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, SubmitSliceRetry) {
SetInputFrameFiles({kBaselineFrame0});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kTryAgain));
}
ASSERT_EQ(AcceleratedVideoDecoder::kTryAgain, Decode());
// Try again, assuming key still not set. Only SubmitSlice() should be
// called again.
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(0);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _)).Times(0);
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kTryAgain));
ASSERT_EQ(AcceleratedVideoDecoder::kTryAgain, Decode());
// Assume key has been provided now, next call to Decode() should proceed.
{
InSequence sequence;
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, SubmitDecodeRetry) {
SetInputFrameFiles({kBaselineFrame0, kBaselineFrame1});
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(_))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kTryAgain));
}
ASSERT_EQ(AcceleratedVideoDecoder::kTryAgain, Decode());
// Try again, assuming key still not set. Only SubmitDecode() should be
// called again.
EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(0);
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _)).Times(0);
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _)).Times(0);
EXPECT_CALL(*accelerator_, SubmitDecode(_))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kTryAgain));
ASSERT_EQ(AcceleratedVideoDecoder::kTryAgain, Decode());
// Assume key has been provided now, next call to Decode() should output
// the first frame.
{
InSequence sequence;
EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2)));
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
TEST_F(H264DecoderTest, SetStreamRetry) {
SetInputFrameFiles({kBaselineFrame0});
EXPECT_CALL(*accelerator_, SetStream(_, _))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kTryAgain))
.WillOnce(Return(H264Decoder::H264Accelerator::Status::kOk));
ASSERT_EQ(AcceleratedVideoDecoder::kTryAgain, Decode());
ASSERT_EQ(AcceleratedVideoDecoder::kConfigChange, Decode());
EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
EXPECT_EQ(H264PROFILE_BASELINE, decoder_->GetProfile());
EXPECT_EQ(8u, decoder_->GetBitDepth());
EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
{
InSequence sequence;
EXPECT_CALL(*accelerator_, CreateH264Picture());
EXPECT_CALL(*accelerator_, SubmitFrameMetadata(_, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitSlice(_, _, _, _, _, _, _, _));
EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
}
ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
ASSERT_TRUE(decoder_->Flush());
}
} // namespace
} // namespace media