| Index: media/gpu/h264_decoder_unittest.cc
|
| diff --git a/media/gpu/h264_decoder_unittest.cc b/media/gpu/h264_decoder_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fdd01f3715fb356c30d4d4cc2ca141478d3ff3ff
|
| --- /dev/null
|
| +++ b/media/gpu/h264_decoder_unittest.cc
|
| @@ -0,0 +1,332 @@
|
| +// 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 <memory>
|
| +#include <queue>
|
| +#include <string>
|
| +
|
| +#include "base/command_line.h"
|
| +#include "base/files/file_util.h"
|
| +#include "base/logging.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::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";
|
| +
|
| +class MockH264Accelerator : public H264Decoder::H264Accelerator {
|
| + public:
|
| + MockH264Accelerator() {}
|
| +
|
| + MOCK_METHOD0(CreateH264Picture, scoped_refptr<H264Picture>());
|
| + MOCK_METHOD1(SubmitDecode, bool(const scoped_refptr<H264Picture>& pic));
|
| + MOCK_METHOD1(OutputPicture, bool(const scoped_refptr<H264Picture>& pic));
|
| +
|
| + bool SubmitFrameMetadata(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,
|
| + const scoped_refptr<H264Picture>& pic) override {
|
| + return true;
|
| + }
|
| +
|
| + bool SubmitSlice(const H264PPS* pps,
|
| + const H264SliceHeader* slice_hdr,
|
| + const H264Picture::Vector& ref_pic_list0,
|
| + const H264Picture::Vector& ref_pic_list1,
|
| + const scoped_refptr<H264Picture>& pic,
|
| + const uint8_t* data,
|
| + size_t size) override {
|
| + return true;
|
| + }
|
| +
|
| + 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|. Returns the same result as |H264Decoder::Decode|.
|
| + AcceleratedVideoDecoder::DecodeResult Decode();
|
| +
|
| + protected:
|
| + std::unique_ptr<H264Decoder> decoder_;
|
| + MockH264Accelerator accelerator_;
|
| +
|
| + private:
|
| + std::queue<std::string> input_frame_files_;
|
| + std::string bitstream_;
|
| +};
|
| +
|
| +void H264DecoderTest::SetUp() {
|
| + decoder_.reset(new H264Decoder(&accelerator_));
|
| +
|
| + // Sets default behaviors for mock methods for convenience.
|
| + ON_CALL(accelerator_, CreateH264Picture()).WillByDefault(Invoke([]() {
|
| + return new H264Picture();
|
| + }));
|
| + ON_CALL(accelerator_, SubmitDecode(_)).WillByDefault(Return(true));
|
| + ON_CALL(accelerator_, OutputPicture(_)).WillByDefault(Return(true));
|
| +}
|
| +
|
| +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() {
|
| + while (true) {
|
| + auto result = decoder_->Decode();
|
| + 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_->SetStream(reinterpret_cast<const uint8_t*>(bitstream_.data()),
|
| + bitstream_.size());
|
| + }
|
| +}
|
| +
|
| +// To have better description on mismatch.
|
| +class WithPocMatcher
|
| + : public MatcherInterface<const scoped_refptr<H264Picture>&> {
|
| + public:
|
| + explicit WithPocMatcher(int expected_poc) : expected_poc_(expected_poc) {}
|
| +
|
| + bool MatchAndExplain(const 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<const scoped_refptr<H264Picture>&> WithPoc(int expected_poc) {
|
| + return MakeMatcher(new WithPocMatcher(expected_poc));
|
| +}
|
| +
|
| +// Test Cases
|
| +
|
| +TEST_F(H264DecoderTest, DecodeSingleFrame) {
|
| + SetInputFrameFiles({kBaselineFrame0});
|
| + ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
|
| + EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
|
| + 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_, 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::kAllocateNewSurfaces, Decode());
|
| + EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
|
| + EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
|
| + {
|
| + InSequence sequence;
|
| + EXPECT_CALL(accelerator_, CreateH264Picture());
|
| + 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::kAllocateNewSurfaces, Decode());
|
| + EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
|
| + EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
|
| +
|
| + EXPECT_CALL(accelerator_, CreateH264Picture()).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, DecodeProfileHigh) {
|
| + SetInputFrameFiles({kHighFrame0, kHighFrame1, kHighFrame2, kHighFrame3});
|
| + ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
|
| + EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
|
| + EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
|
| +
|
| + // Two pictures will be kept in DPB for reordering. The first picture should
|
| + // be outputted after feeding the third frame.
|
| + EXPECT_CALL(accelerator_, CreateH264Picture()).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, SwitchBaselineToHigh) {
|
| + SetInputFrameFiles({
|
| + kBaselineFrame0, kHighFrame0, kHighFrame1, kHighFrame2, kHighFrame3,
|
| + });
|
| + ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
|
| + EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
|
| + EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
|
| +
|
| + EXPECT_CALL(accelerator_, CreateH264Picture());
|
| + {
|
| + InSequence sequence;
|
| + EXPECT_CALL(accelerator_, SubmitDecode(_));
|
| + EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0)));
|
| + }
|
| + ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
|
| + EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
|
| + EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
|
| +
|
| + ASSERT_TRUE(Mock::VerifyAndClearExpectations(&accelerator_));
|
| +
|
| + EXPECT_CALL(accelerator_, CreateH264Picture()).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::kAllocateNewSurfaces, Decode());
|
| + EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
|
| + EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
|
| +
|
| + EXPECT_CALL(accelerator_, CreateH264Picture());
|
| + {
|
| + InSequence sequence;
|
| + EXPECT_CALL(accelerator_, SubmitDecode(_));
|
| + EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0)));
|
| + }
|
| + ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
|
| + EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
|
| + EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
|
| +
|
| + ASSERT_TRUE(Mock::VerifyAndClearExpectations(&accelerator_));
|
| +
|
| + EXPECT_CALL(accelerator_, CreateH264Picture()).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());
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace media
|
|
|