Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(775)

Unified Diff: media/gpu/h264_decoder_unittest.cc

Issue 2760513002: h264_decoder_unittests: Initial Change. (Closed)
Patch Set: Address review comments from posciak. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/gpu/BUILD.gn ('k') | media/gpu/h264_dpb.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..e16b1b4943d97250325cedf763ed5a98842711e5
--- /dev/null
+++ b/media/gpu/h264_decoder_unittest.cc
@@ -0,0 +1,417 @@
+// 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/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "media/base/test_data_util.h"
+#include "media/gpu/h264_decoder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+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 FakeH264Accelerator;
+
+// Test H264Decoder by feeding different of h264 frame sequences and make
+// sure it behaves as expected.
+class H264DecoderTest : public ::testing::Test {
+ public:
+ // The events we used to verify the correctness. Will check if these
+ // events are triggers in order.
Pawel Osciak 2017/06/23 02:07:34 s/triggers/triggered/ Also probably s/in order/at
Owen Lin 2017/06/26 09:09:00 The code is refactored out.
+ enum class EventType {
Pawel Osciak 2017/06/23 02:07:34 Could this be inside the Event class still?
Owen Lin 2017/06/26 09:09:00 Ditto.
+ CREATE_H264_PICTURE,
+ SUBMIT_DECODE,
+ OUTPUT_PICTURE,
+ RAN_OUT_OF_SURFACES,
+ NEED_CONTEXT_UPDATE,
+ ALLOCATE_NEW_SURFACES,
+ DECODE_ERROR,
+ };
+
+ struct Event {
+ EventType type;
+
+ explicit Event(EventType type) : type(type) {}
+ virtual std::string ToString() const;
+ };
+
+ // The specialize Event for OutputPicture as we will also check the POC.
+ struct OutputPictureEvent : public Event {
+ int pic_order_cnt;
+
+ OutputPictureEvent(int pic_order_cnt)
+ : Event(EventType::OUTPUT_PICTURE), pic_order_cnt(pic_order_cnt) {}
+
+ std::string ToString() const override;
+ };
+
+ // Function called when an event dispatch to the test.
+ using EventHandler = base::Callback<void(const Event& event)>;
Pawel Osciak 2017/06/23 02:07:34 Nit: OnceCallback?
Owen Lin 2017/06/26 09:09:00 Done.
+
+ H264DecoderTest() = default;
+
+ void SetUp() override;
+
+ // Helper function to feed bitstreams.
+ void FeedData(const std::vector<std::string>& frames);
+
+ // Called when an event happens.
+ void OnEvent(const Event& event);
+
+ // A candy function to call OnEvent(Event(type)).
+ void OnEvent(const EventType type);
+
+ // Expects the specified event will be dispatched to the test. When it
+ // happens, handler will be called. The expected events will be queued.
+ // This function could be called multiple times and we expect those
+ // events will happen in order.
+ void ExpectEvent(EventType type, const EventHandler& handler = {});
Pawel Osciak 2017/06/23 02:07:34 To be honest it took me a bit to understand the id
Owen Lin 2017/06/26 09:09:00 Same. Code has been removed.
+
+ void ExpectEvent(EventType type, const base::Closure& closure);
Pawel Osciak 2017/06/23 02:07:34 ... this ExpectEventAndRun() ? Do we also need to
Owen Lin 2017/06/26 09:09:00 Ditto.
+
+ // Expects the decoder output a picture with the specified POC..
+ void ExpectOutputPictureWithPoc(int expected_poc);
+
+ // Expects the decoder request for new surfaces of the expected size.
+ void ExpectAllocateNewSurfaces(const gfx::Size& size);
+
+ // Gets the number of remaining expected events.
+ size_t number_of_expected_events() const { return expected_events_.size(); }
+
+ protected:
+ std::unique_ptr<H264Decoder> decoder_;
+ std::unique_ptr<FakeH264Accelerator> accelerator_;
+
+ private:
+ struct ExpectedEvent {
+ EventType type;
+ EventHandler handler;
+
+ ExpectedEvent(EventType type, const EventHandler& handler)
+ : type(type), handler(handler) {}
+ };
+
+ std::queue<ExpectedEvent> expected_events_;
+
+ static void TestOutputPictureEvent(int expected_poc, const Event& e);
+ void TestAllocateNewSurfaceEvent(const gfx::Size& size);
+};
+
+class FakeH264Accelerator : public H264Decoder::H264Accelerator {
+ public:
+ FakeH264Accelerator(H264DecoderTest* test) : test_(test) {}
+
+ scoped_refptr<H264Picture> CreateH264Picture() override {
+ if (new_picture_quota_ > 0) {
+ --new_picture_quota_;
+ return new H264Picture();
+ }
+ return nullptr;
+ }
+
+ // Implementations of H264Decoder::H264Accelerator interface.
+
+ 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;
+ }
+
+ bool SubmitDecode(const scoped_refptr<H264Picture>& pic) override {
+ test_->OnEvent(H264DecoderTest::EventType::SUBMIT_DECODE);
+ return true;
+ }
+
+ bool OutputPicture(const scoped_refptr<H264Picture>& pic) override {
+ test_->OnEvent(H264DecoderTest::OutputPictureEvent(pic->pic_order_cnt));
+ return true;
+ }
+
+ void Reset() override {}
+
+ // How many times CreateH264Picture() will returns valid H264Picture.
+ void SetNewPictureQuota(size_t quota) { new_picture_quota_ = quota; }
+
+ size_t GetNewPictureQuota() const { return new_picture_quota_; }
+
+ private:
+ H264DecoderTest* test_;
+ size_t new_picture_quota_ = 0;
+};
+
+std::string H264DecoderTest::Event::ToString() const {
+ switch (type) {
+ case EventType::CREATE_H264_PICTURE:
+ return "CreateH264Picture";
+ case EventType::SUBMIT_DECODE:
+ return "SubmitDecode";
+ case EventType::OUTPUT_PICTURE:
+ return "OutputPicture";
+ case EventType::RAN_OUT_OF_SURFACES:
+ return "RanOutOfSurfaces";
+ case EventType::NEED_CONTEXT_UPDATE:
+ return "NeedContextUpdate";
+ case EventType::ALLOCATE_NEW_SURFACES:
+ return "AllocateNewSurfaces";
+ case EventType::DECODE_ERROR:
+ return "DecodeError";
+ }
+ NOTREACHED();
+}
+
+std::string H264DecoderTest::OutputPictureEvent::ToString() const {
+ return base::StringPrintf("%s (poc=%d)", Event::ToString().c_str(),
+ pic_order_cnt);
+}
+
+void H264DecoderTest::SetUp() {
+ accelerator_.reset(new FakeH264Accelerator(this));
+ decoder_.reset(new H264Decoder(accelerator_.get()));
+}
+
+void H264DecoderTest::FeedData(const std::vector<std::string>& frames) {
+ std::string bitstream;
+ auto frame_iter = frames.begin();
+ while (true) {
+ switch (decoder_->Decode()) {
+ case AcceleratedVideoDecoder::kAllocateNewSurfaces:
+ ASSERT_NO_FATAL_FAILURE(
+ OnEvent(Event(EventType::ALLOCATE_NEW_SURFACES)));
+ break;
+ case AcceleratedVideoDecoder::kRanOutOfStreamData: {
+ if (frame_iter == frames.end())
+ return;
+ base::FilePath input_file = GetTestDataFilePath(*frame_iter++);
+ ASSERT_TRUE(base::ReadFileToString(input_file, &bitstream))
+ << "failed to read input data from " << input_file.value();
+ decoder_->SetStream(reinterpret_cast<const uint8_t*>(bitstream.data()),
+ bitstream.size());
+ break;
+ }
+ case AcceleratedVideoDecoder::kRanOutOfSurfaces:
+ ASSERT_NO_FATAL_FAILURE(OnEvent(EventType::RAN_OUT_OF_SURFACES));
+ break;
+ case AcceleratedVideoDecoder::kNeedContextUpdate:
+ ASSERT_NO_FATAL_FAILURE(OnEvent(EventType::NEED_CONTEXT_UPDATE));
+ break;
+ case AcceleratedVideoDecoder::kDecodeError:
+ ASSERT_NO_FATAL_FAILURE(OnEvent(EventType::DECODE_ERROR));
+ break;
+ }
+ }
+}
+
+void H264DecoderTest::OnEvent(const Event& event) {
+ VLOG(2) << "OnEvent(" << event.ToString() << ")";
+ ASSERT_FALSE(expected_events_.empty()) << "Unexpected event";
+ ExpectedEvent ee = expected_events_.front();
+ expected_events_.pop();
+ ASSERT_EQ(ee.type, event.type);
+ if (ee.handler)
+ ASSERT_NO_FATAL_FAILURE(ee.handler.Run(event));
+}
+
+void H264DecoderTest::OnEvent(EventType type) {
+ OnEvent(Event(type));
+}
+
+void H264DecoderTest::ExpectEvent(EventType type, const EventHandler& handler) {
+ expected_events_.push({type, handler});
+}
+
+static void RunClosureAndIgnoreEvent(const base::Closure& closure,
+ const H264DecoderTest::Event&) {
+ closure.Run();
+}
+
+void H264DecoderTest::ExpectEvent(EventType type,
+ const base::Closure& closure) {
+ ExpectEvent(type, base::Bind(RunClosureAndIgnoreEvent, closure));
Pawel Osciak 2017/06/23 02:07:34 Are we really ignoring the event here? I think we
Owen Lin 2017/06/26 09:09:00 Ditto.
+}
+
+void H264DecoderTest::TestOutputPictureEvent(int expected_poc, const Event& e) {
+ auto event = static_cast<const OutputPictureEvent&>(e);
Pawel Osciak 2017/06/23 02:07:34 Could we instead create a specialized event in Exp
Owen Lin 2017/06/26 09:09:00 Ditto.
+ EXPECT_EQ(expected_poc, event.pic_order_cnt);
+}
+
+void H264DecoderTest::ExpectOutputPictureWithPoc(int expected_poc) {
+ ExpectEvent(EventType::OUTPUT_PICTURE,
+ base::Bind(TestOutputPictureEvent, expected_poc));
+}
+
+void H264DecoderTest::TestAllocateNewSurfaceEvent(const gfx::Size& size) {
+ EXPECT_EQ(size, decoder_->GetPicSize());
+}
+
+void H264DecoderTest::ExpectAllocateNewSurfaces(const gfx::Size& size) {
+ ExpectEvent(EventType::ALLOCATE_NEW_SURFACES,
+ base::Bind(&H264DecoderTest::TestAllocateNewSurfaceEvent,
+ base::Unretained(this), size));
+}
+
+// Test Cases
+
+TEST_F(H264DecoderTest, DecodeSingleFrame) {
+ ExpectAllocateNewSurfaces(gfx::Size(320, 192));
+
+ ExpectEvent(EventType::RAN_OUT_OF_SURFACES,
+ base::Bind(&FakeH264Accelerator::SetNewPictureQuota,
+ base::Unretained(accelerator_.get()), 1));
+
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectEvent(EventType::OUTPUT_PICTURE);
+
+ ASSERT_NO_FATAL_FAILURE(FeedData({kBaselineFrame0}));
+ ASSERT_TRUE(decoder_->Flush());
+
+ EXPECT_EQ(0u, accelerator_->GetNewPictureQuota());
+ EXPECT_EQ(0u, number_of_expected_events());
+}
+
+TEST_F(H264DecoderTest, SkipNoneIDRFrames) {
+ ExpectAllocateNewSurfaces(gfx::Size(320, 192));
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ accelerator_->SetNewPictureQuota(1);
+
+ ASSERT_NO_FATAL_FAILURE(FeedData({
+ kBaselineFrame1, kBaselineFrame2, kBaselineFrame0,
+ }));
+ ASSERT_TRUE(decoder_->Flush());
+
+ EXPECT_EQ(0u, accelerator_->GetNewPictureQuota());
+ EXPECT_EQ(0u, number_of_expected_events());
+}
+
+TEST_F(H264DecoderTest, DecodeProfileBaseline) {
+ accelerator_->SetNewPictureQuota(4);
+ ExpectEvent(EventType::ALLOCATE_NEW_SURFACES);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(2);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(4);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(6);
+
+ ASSERT_NO_FATAL_FAILURE(FeedData({
+ kBaselineFrame0, kBaselineFrame1, kBaselineFrame2, kBaselineFrame3,
+ }));
+ ASSERT_TRUE(decoder_->Flush());
+
+ EXPECT_EQ(0u, accelerator_->GetNewPictureQuota());
+ EXPECT_EQ(0u, number_of_expected_events());
+}
+
+TEST_F(H264DecoderTest, DecodeProfileHigh) {
+ accelerator_->SetNewPictureQuota(4);
+ ExpectEvent(EventType::ALLOCATE_NEW_SURFACES);
+
+ // Two pictures will be kept in DPB for reordering. The first picture should
+ // be outputted after feeding the third frame.
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(2);
+ ExpectOutputPictureWithPoc(4);
+ ExpectOutputPictureWithPoc(6);
+
+ ASSERT_NO_FATAL_FAILURE(FeedData({
+ kHighFrame0, kHighFrame1, kHighFrame2, kHighFrame3,
+ }));
+ ASSERT_TRUE(decoder_->Flush());
+
+ EXPECT_EQ(0u, accelerator_->GetNewPictureQuota());
+ EXPECT_EQ(0u, number_of_expected_events());
+}
+
+TEST_F(H264DecoderTest, SwitchBaselineToHigh) {
+ accelerator_->SetNewPictureQuota(5);
+
+ ExpectEvent(EventType::ALLOCATE_NEW_SURFACES);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(EventType::ALLOCATE_NEW_SURFACES);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(2);
+ ExpectOutputPictureWithPoc(4);
+ ExpectOutputPictureWithPoc(6);
+
+ ASSERT_NO_FATAL_FAILURE(FeedData({
+ kBaselineFrame0, kHighFrame0, kHighFrame1, kHighFrame2, kHighFrame3,
+ }));
+ ASSERT_TRUE(decoder_->Flush());
+
+ EXPECT_EQ(0u, accelerator_->GetNewPictureQuota());
+ EXPECT_EQ(0u, number_of_expected_events());
+}
+
+TEST_F(H264DecoderTest, SwitchHighToBaseline) {
+ accelerator_->SetNewPictureQuota(5);
+
+ ExpectEvent(EventType::ALLOCATE_NEW_SURFACES);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(EventType::ALLOCATE_NEW_SURFACES);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(2);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(4);
+ ExpectEvent(EventType::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(6);
+
+ ASSERT_NO_FATAL_FAILURE(FeedData({
+ kHighFrame0, kBaselineFrame0, kBaselineFrame1, kBaselineFrame2,
+ kBaselineFrame3,
+ }));
+ ASSERT_TRUE(decoder_->Flush());
+
+ EXPECT_EQ(0u, accelerator_->GetNewPictureQuota());
+ EXPECT_EQ(0u, number_of_expected_events());
+}
+
+} // namespace
+} // namespace media
« no previous file with comments | « media/gpu/BUILD.gn ('k') | media/gpu/h264_dpb.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698