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

Unified Diff: media/gpu/h264_decoder_unittest.cc

Issue 2760513002: h264_decoder_unittests: Initial Change. (Closed)
Patch Set: Created 3 years, 9 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
« media/gpu/BUILD.gn ('K') | « media/gpu/BUILD.gn ('k') | no next file » | 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..571b3ceb4290ac77164a3222043ed83f30ad1c4d
--- /dev/null
+++ b/media/gpu/h264_decoder_unittest.cc
@@ -0,0 +1,401 @@
+// 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 "media/base/test_data_util.h"
+#include "media/gpu/h264_decoder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace {
+
+const char* kBaselineFrame0 = "bear-320x192-baseline-frame-0.h264";
kcwu 2017/03/17 06:47:44 Forgot to git add?
Owen Lin 2017/03/20 08:00:05 It was in another CL, but I have merge the two CL
+const char* kBaselineFrame1 = "bear-320x192-baseline-frame-1.h264";
+const char* kBaselineFrame2 = "bear-320x192-baseline-frame-2.h264";
+const char* kBaselineFrame3 = "bear-320x192-baseline-frame-3.h264";
+const char* kHighFrame0 = "bear-320x192-high-frame-0.h264";
+const char* kHighFrame1 = "bear-320x192-high-frame-1.h264";
+const char* kHighFrame2 = "bear-320x192-high-frame-2.h264";
+const char* kHighFrame3 = "bear-320x192-high-frame-3.h264";
+
+// Forward declaration
+class FakeH264Accelerator;
+
+class H264DecoderTest : public ::testing::Test {
+ public:
+ // The events we used to verify the correctness. Will check if these
+ // events are triggers in order.
+ struct Event {
+ enum Type {
+ CREATE_H264_PICTURE,
+ SUBMIT_DECODE,
+ OUTPUT_PICTURE,
+ RAN_OUT_OF_SURFACES,
+ NEED_CONTEXT_UPDATE,
+ ALLOCATE_NEW_SURFACES,
+ DECODE_ERROR,
+ };
+
+ Type type;
+
+ Event(Type type) : type(type) {}
+ };
+
+ // 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(OUTPUT_PICTURE), pic_order_cnt(pic_order_cnt) {}
+ };
+
+ // Function called when an event dispatch to the test.
+ typedef std::function<void(const Event& event)> EventHandler;
kcwu 2017/03/17 06:47:43 Suggest to use c++11's "using" syntax.
Owen Lin 2017/03/20 08:00:05 Done.
+
+ H264DecoderTest() {}
+
+ void SetUp() override;
+
+ // Helper function to feed bitstreams.
+ void FeedData(std::vector<const char*> frames);
+
+ // Called when an event happens.
+ void OnEvent(const Event& event);
+
+ // 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(Event::Type type, EventHandler handler = {});
+
+ // Expects the decoder output a picture with the specified POC..
+ void ExpectOutputPictureWithPoc(int expected_poc);
+
+ // Verifies there is no more expected events in the queue.
+ void VerifyNoMoreExpectedEvents();
+
+ protected:
+ std::unique_ptr<H264Decoder> decoder_;
+ std::unique_ptr<FakeH264Accelerator> accelerator_;
+
+ private:
+ struct ExpectedEvent {
+ Event::Type type;
+ EventHandler handler;
+
+ ExpectedEvent(Event::Type type, EventHandler handler)
+ : type(type), handler(handler) {}
+ };
+
+ std::queue<ExpectedEvent> expected_events_;
+};
+
+class FakeH264Accelerator : public H264Decoder::H264Accelerator {
kcwu 2017/03/17 06:47:43 Have you considered using gmock?
Owen Lin 2017/03/20 08:00:04 I don't think using mock will simplify the code or
+ 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::Event::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::ostream& operator<<(std::ostream& os,
+ const H264DecoderTest::Event::Type type) {
+ switch (type) {
+ case H264DecoderTest::Event::CREATE_H264_PICTURE:
+ return os << "CreateH264Picture";
+ case H264DecoderTest::Event::SUBMIT_DECODE:
+ return os << "SubmitDecode";
+ case H264DecoderTest::Event::OUTPUT_PICTURE:
+ return os << "OutputPicture";
+ case H264DecoderTest::Event::RAN_OUT_OF_SURFACES:
+ return os << "RanOutOfSurfaces";
+ case H264DecoderTest::Event::NEED_CONTEXT_UPDATE:
+ return os << "NeedContextUpdate";
+ case H264DecoderTest::Event::ALLOCATE_NEW_SURFACES:
+ return os << "AllocateNewSurfaces";
+ case H264DecoderTest::Event::DECODE_ERROR:
+ return os << "DecodeError";
+ }
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const H264DecoderTest::Event& e) {
+ switch (e.type) {
+ case H264DecoderTest::Event::OUTPUT_PICTURE: {
+ const H264DecoderTest::OutputPictureEvent& event =
+ static_cast<const H264DecoderTest::OutputPictureEvent&>(e);
+ return os << e.type << "(poc=" << event.pic_order_cnt << ")";
+ }
+ default:
+ return os << e.type;
+ }
+}
+
+void H264DecoderTest::SetUp() {
+ accelerator_.reset(new FakeH264Accelerator(this));
+ decoder_.reset(new H264Decoder(accelerator_.get()));
+}
+
+void H264DecoderTest::FeedData(std::vector<const char*> frames) {
+ std::string bitstream;
+ auto frame_iter = frames.begin();
+ while (true) {
+ switch (decoder_->Decode()) {
+ case AcceleratedVideoDecoder::kAllocateNewSurfaces:
+ ASSERT_NO_FATAL_FAILURE(OnEvent(Event::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(Event::RAN_OUT_OF_SURFACES));
+ break;
+ case AcceleratedVideoDecoder::kNeedContextUpdate:
+ ASSERT_NO_FATAL_FAILURE(OnEvent(Event::NEED_CONTEXT_UPDATE));
+ break;
+ case AcceleratedVideoDecoder::kDecodeError:
+ ASSERT_NO_FATAL_FAILURE(OnEvent(Event::DECODE_ERROR));
+ break;
+ }
+ }
+}
+
+void H264DecoderTest::OnEvent(const Event& event) {
+ VLOG(2) << "OnEvent(" << event << ")";
+ 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(event));
+}
+
+void H264DecoderTest::ExpectEvent(Event::Type type, EventHandler handler) {
+ expected_events_.push({type, handler});
+}
+
+void H264DecoderTest::ExpectOutputPictureWithPoc(int expected_poc) {
+ ExpectEvent(Event::OUTPUT_PICTURE, [expected_poc](const Event& e) {
+ auto event = static_cast<const OutputPictureEvent&>(e);
+ EXPECT_EQ(expected_poc, event.pic_order_cnt);
+ });
+}
+
+void H264DecoderTest::VerifyNoMoreExpectedEvents() {
+ ASSERT_EQ(0u, expected_events_.size());
kcwu 2017/03/17 06:47:43 1. IIRC, we prefer inline this kind of assertions
Owen Lin 2017/03/20 08:00:04 I agree.
kcwu 2017/03/20 08:28:09 You forgot to change code?
Owen Lin 2017/03/20 09:29:27 Done.
+}
+
+// Test Cases
+
+TEST_F(H264DecoderTest, DecodeSingleFrame) {
+ ExpectEvent(Event::ALLOCATE_NEW_SURFACES, [&](const Event&) {
+ EXPECT_EQ(decoder_->GetPicSize(), gfx::Size(320, 192));
+ });
+
+ ExpectEvent(Event::RAN_OUT_OF_SURFACES,
+ [&](const Event&) { accelerator_->SetNewPictureQuota(1); });
+
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectEvent(Event::OUTPUT_PICTURE);
+
+ ASSERT_NO_FATAL_FAILURE(FeedData({kBaselineFrame0}));
+ ASSERT_TRUE(decoder_->Flush());
+
+ EXPECT_EQ(0u, accelerator_->GetNewPictureQuota());
+ VerifyNoMoreExpectedEvents();
+}
+
+TEST_F(H264DecoderTest, SkipNoneIDRFrames) {
+ ExpectEvent(Event::ALLOCATE_NEW_SURFACES, [&](const Event&) {
+ EXPECT_EQ(decoder_->GetPicSize(), gfx::Size(320, 192));
+ });
+
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+
+ accelerator_->SetNewPictureQuota(1);
+ ASSERT_NO_FATAL_FAILURE(FeedData({
+ kBaselineFrame1, kBaselineFrame2, kBaselineFrame0,
+ }));
+ ASSERT_TRUE(decoder_->Flush());
+
+ EXPECT_EQ(0u, accelerator_->GetNewPictureQuota());
+ VerifyNoMoreExpectedEvents();
+}
+
+TEST_F(H264DecoderTest, DecodeProfileBaseline) {
+ accelerator_->SetNewPictureQuota(4);
+ ExpectEvent(Event::ALLOCATE_NEW_SURFACES);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(2);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(4);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(6);
+
+ ASSERT_NO_FATAL_FAILURE(FeedData({
+ kBaselineFrame0, kBaselineFrame1, kBaselineFrame2, kBaselineFrame3,
+ }));
+ ASSERT_TRUE(decoder_->Flush());
+
+ EXPECT_EQ(0u, accelerator_->GetNewPictureQuota());
+ VerifyNoMoreExpectedEvents();
+}
+
+TEST_F(H264DecoderTest, DecodeProfileHigh) {
+ accelerator_->SetNewPictureQuota(4);
+ ExpectEvent(Event::ALLOCATE_NEW_SURFACES);
+
+ // Two pictures will be kept in DPB for reordering. The first picture should
+ // be outputted after feeding the third frame.
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(Event::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());
+ VerifyNoMoreExpectedEvents();
+}
+
+TEST_F(H264DecoderTest, SwitchBaselineToHigh) {
+ accelerator_->SetNewPictureQuota(5);
+
+ ExpectEvent(Event::ALLOCATE_NEW_SURFACES);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(Event::ALLOCATE_NEW_SURFACES);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(Event::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());
+ VerifyNoMoreExpectedEvents();
+}
+
+TEST_F(H264DecoderTest, SwitchHighToBaseline) {
+ accelerator_->SetNewPictureQuota(5);
+
+ ExpectEvent(Event::ALLOCATE_NEW_SURFACES);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(Event::ALLOCATE_NEW_SURFACES);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(0);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(2);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(4);
+ ExpectEvent(Event::SUBMIT_DECODE);
+ ExpectOutputPictureWithPoc(6);
+
+ ASSERT_NO_FATAL_FAILURE(FeedData({
+ kHighFrame0, kBaselineFrame0, kBaselineFrame1, kBaselineFrame2,
+ kBaselineFrame3,
+ }));
+ ASSERT_TRUE(decoder_->Flush());
+
+ EXPECT_EQ(0u, accelerator_->GetNewPictureQuota());
+ VerifyNoMoreExpectedEvents();
+}
+
+} // namespace
+} // namespace media
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ base::CommandLine::Init(argc, argv);
+
+ // Needed to enable DVLOG through --vmodule.
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ LOG_ASSERT(logging::InitLogging(settings));
+
+ return RUN_ALL_TESTS();
+}
« media/gpu/BUILD.gn ('K') | « media/gpu/BUILD.gn ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698