Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/macros.h" | 5 #include "base/macros.h" |
| 6 #include "base/message_loop/message_loop.h" | 6 #include "base/message_loop/message_loop.h" |
| 7 #include "media/base/android/media_codec_bridge.h" | 7 #include "media/base/android/media_codec_bridge.h" |
| 8 #include "media/base/android/media_codec_loop.h" | 8 #include "media/base/android/media_codec_loop.h" |
| 9 #include "media/base/android/mock_media_codec_bridge.h" | |
| 10 #include "media/base/fake_single_thread_task_runner.h" | |
| 9 #include "testing/gmock/include/gmock/gmock.h" | 11 #include "testing/gmock/include/gmock/gmock.h" |
| 10 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
| 11 | 13 |
| 14 using ::testing::_; | |
| 15 using ::testing::AtLeast; | |
| 16 using ::testing::Eq; | |
| 17 using ::testing::Field; | |
| 18 using ::testing::InSequence; | |
| 19 using ::testing::Mock; | |
| 20 using ::testing::Return; | |
| 21 using ::testing::SetArgPointee; | |
| 22 using ::testing::StrictMock; | |
| 23 | |
| 12 namespace media { | 24 namespace media { |
| 13 | 25 |
| 14 class MockMediaCodecLoopClient : public MediaCodecLoop::Client { | 26 // The client is a strict mock, since we don't want random calls into it. We |
| 27 // want to be sure about the call sequence. | |
| 28 class MockMediaCodecLoopClient : public StrictMock<MediaCodecLoop::Client> { | |
| 15 public: | 29 public: |
| 16 MOCK_CONST_METHOD0(IsAnyInputPending, bool()); | 30 MOCK_CONST_METHOD0(IsAnyInputPending, bool()); |
| 17 MOCK_METHOD0(ProvideInputData, MediaCodecLoop::InputData()); | 31 MOCK_METHOD0(ProvideInputData, MediaCodecLoop::InputData()); |
| 18 MOCK_METHOD1(OnDecodedEos, void(const MediaCodecLoop::OutputBuffer&)); | 32 MOCK_METHOD1(OnDecodedEos, void(const MediaCodecLoop::OutputBuffer&)); |
| 19 MOCK_METHOD1(OnDecodedFrame, bool(const MediaCodecLoop::OutputBuffer&)); | 33 MOCK_METHOD1(OnDecodedFrame, bool(const MediaCodecLoop::OutputBuffer&)); |
| 20 MOCK_METHOD0(OnOutputFormatChanged, bool()); | 34 MOCK_METHOD0(OnOutputFormatChanged, bool()); |
| 21 MOCK_METHOD0(OnCodecLoopError, void()); | 35 MOCK_METHOD0(OnCodecLoopError, void()); |
| 22 }; | 36 }; |
| 23 | 37 |
| 24 class MediaCodecLoopTest : public testing::Test { | 38 class MediaCodecLoopTest : public testing::Test { |
| 25 public: | 39 public: |
| 26 MediaCodecLoopTest() : client_(new MockMediaCodecLoopClient) {} | 40 MediaCodecLoopTest() |
| 41 : client_(new StrictMock<MockMediaCodecLoopClient>()), | |
| 42 task_runner_(new FakeSingleThreadTaskRunner(&clock_)) {} | |
| 43 | |
| 44 ~MediaCodecLoopTest() override {} | |
| 45 | |
| 46 protected: | |
| 47 enum IdleExpectation { | |
| 48 ShouldBeIdle, | |
| 49 ShouldNotBeIdle, | |
| 50 }; | |
| 51 | |
| 52 // Wait until |codec_loop_| is idle. | |
| 53 void WaitUntilIdle(IdleExpectation idleExpectation = ShouldBeIdle) { | |
| 54 switch (idleExpectation) { | |
| 55 case ShouldBeIdle: | |
| 56 EXPECT_CALL(*client_, IsAnyInputPending()).Times(0); | |
| 57 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)).Times(0); | |
| 58 break; | |
| 59 case ShouldNotBeIdle: | |
| 60 // Expect at least one call to see if more work is ready. We will | |
| 61 // return 'no'. | |
| 62 EXPECT_CALL(*client_, IsAnyInputPending()) | |
| 63 .Times(AtLeast(1)) | |
| 64 .WillRepeatedly(Return(false)); | |
| 65 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
| 66 .Times(AtLeast(1)) | |
| 67 .WillRepeatedly(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER)); | |
| 68 break; | |
| 69 } | |
| 70 | |
| 71 // Either way, we expect that MCL should not attempt to dequeue input | |
| 72 // buffers, either because it's idle or because we said that no input | |
| 73 // is pending. | |
| 74 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _)).Times(0); | |
| 75 | |
| 76 // TODO(liberato): assume that MCL doesn't retry for 30 seconds. Note | |
| 77 // that this doesn't actually wall-clock wait. | |
| 78 task_runner_->Sleep(base::TimeDelta::FromSeconds(30)); | |
| 79 } | |
| 80 | |
| 81 void ConstructCodecLoop() { | |
| 82 std::unique_ptr<MediaCodecBridge> codec(new MockMediaCodecBridge()); | |
| 83 // Since we're providing a codec, we do not expect an error. | |
| 84 EXPECT_CALL(*client_, OnCodecLoopError()).Times(0); | |
| 85 codec_loop_.reset( | |
| 86 new MediaCodecLoop(client_.get(), std::move(codec), task_runner_)); | |
| 87 codec_loop_->SetTestTickClock(&clock_); | |
| 88 Mock::VerifyAndClearExpectations(client_.get()); | |
| 89 // We might want to WaitUntilIdle here. | |
| 90 } | |
| 91 | |
| 92 struct OutputBuffer { | |
| 93 int index = 1; | |
| 94 size_t offset = 0; | |
| 95 size_t size = 1024; | |
| 96 base::TimeDelta pts = base::TimeDelta::FromSeconds(1); | |
| 97 bool eos = false; | |
| 98 bool key_frame = true; | |
| 99 }; | |
| 100 | |
| 101 struct EosOutputBuffer : public OutputBuffer { | |
| 102 EosOutputBuffer() { eos = true; } | |
| 103 }; | |
| 104 | |
| 105 void QueueOutputBuffer(OutputBuffer buffer) { | |
| 106 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
| 107 .WillOnce(DoAll( | |
| 108 SetArgPointee<1>(buffer.index), SetArgPointee<2>(buffer.offset), | |
| 109 SetArgPointee<3>(buffer.size), SetArgPointee<4>(buffer.pts), | |
| 110 SetArgPointee<5>(buffer.eos), SetArgPointee<6>(buffer.key_frame), | |
| 111 Return(MEDIA_CODEC_OK))); | |
| 112 } | |
| 113 | |
| 114 MockMediaCodecBridge& Codec() { | |
| 115 return *static_cast<MockMediaCodecBridge*>(codec_loop_->GetCodec()); | |
| 116 } | |
| 27 | 117 |
| 28 public: | 118 public: |
| 29 std::unique_ptr<MediaCodecLoop> codec_loop_; | 119 std::unique_ptr<MediaCodecLoop> codec_loop_; |
| 30 std::unique_ptr<MockMediaCodecLoopClient> client_; | 120 std::unique_ptr<MockMediaCodecLoopClient> client_; |
| 121 // TODO: how is the lifecycle of |clock_| handled? |task_runner_| can outlive | |
| 122 // us, since it's a refptr. | |
| 123 base::SimpleTestTickClock clock_; | |
| 124 scoped_refptr<FakeSingleThreadTaskRunner> task_runner_; | |
| 31 | 125 |
| 32 DISALLOW_COPY_AND_ASSIGN(MediaCodecLoopTest); | 126 DISALLOW_COPY_AND_ASSIGN(MediaCodecLoopTest); |
| 33 }; | 127 }; |
| 34 | 128 |
| 35 TEST_F(MediaCodecLoopTest, TestConstructionWithNullCodec) { | 129 TEST_F(MediaCodecLoopTest, TestConstructionWithNullCodec) { |
| 36 std::unique_ptr<MediaCodecBridge> codec; | 130 std::unique_ptr<MediaCodecBridge> codec; |
| 37 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1); | 131 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1); |
| 38 codec_loop_.reset(new MediaCodecLoop(client_.get(), std::move(codec))); | 132 codec_loop_.reset(new MediaCodecLoop(client_.get(), std::move(codec))); |
| 133 // Do not WaitUntilIdle() here, since that assumes that we have a codec. | |
| 39 | 134 |
| 40 ASSERT_FALSE(codec_loop_->GetCodec()); | 135 ASSERT_FALSE(codec_loop_->GetCodec()); |
| 41 } | 136 } |
| 42 | 137 |
| 138 TEST_F(MediaCodecLoopTest, TestConstructionWithCodec) { | |
| 139 ConstructCodecLoop(); | |
| 140 ASSERT_EQ(codec_loop_->GetCodec(), &Codec()); | |
| 141 WaitUntilIdle(ShouldBeIdle); | |
| 142 } | |
| 143 | |
| 144 TEST_F(MediaCodecLoopTest, TestPendingWorkWithoutInput) { | |
| 145 ConstructCodecLoop(); | |
| 146 // MCL should try ask if there is pending input, and try to dequeue output. | |
| 147 EXPECT_CALL(*client_, IsAnyInputPending()).Times(1).WillOnce(Return(false)); | |
| 148 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
| 149 .Times(1) | |
| 150 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER)); | |
| 151 codec_loop_->DoPendingWork(); | |
| 152 WaitUntilIdle(ShouldNotBeIdle); | |
| 153 } | |
| 154 | |
| 155 TEST_F(MediaCodecLoopTest, TestPendingWorkWithInput) { | |
| 156 ConstructCodecLoop(); | |
| 157 // MCL should try ask if there is pending input, and try to dequeue both an | |
| 158 // output and input buffer. | |
| 159 EXPECT_CALL(*client_, IsAnyInputPending()).Times(1).WillOnce(Return(true)); | |
| 160 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)).Times(1); | |
| 161 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _)).Times(1); | |
| 162 codec_loop_->DoPendingWork(); | |
| 163 WaitUntilIdle(ShouldNotBeIdle); | |
| 164 } | |
| 165 | |
| 166 TEST_F(MediaCodecLoopTest, TestPendingWorkWithOutputBuffer) { | |
| 167 ConstructCodecLoop(); | |
| 168 { | |
| 169 InSequence _s; | |
| 170 | |
| 171 // MCL will first request input, then try to dequeue output. | |
| 172 EXPECT_CALL(*client_, IsAnyInputPending()).WillOnce(Return(false)); | |
| 173 OutputBuffer buf; | |
| 174 QueueOutputBuffer(buf); | |
| 175 EXPECT_CALL(*client_, | |
|
liberato (no reviews please)
2016/07/08 17:49:46
gmock is really amazing. it lets me set an expect
| |
| 176 OnDecodedFrame( | |
| 177 Field(&MediaCodecLoop::OutputBuffer::index, Eq(buf.index)))) | |
| 178 .Times(1) | |
| 179 .WillOnce(Return(true)); | |
| 180 | |
| 181 // MCL will try again for another set of buffers before DoPendingWork() | |
| 182 // returns. This is why we don't just leave them for WaitUntilIdle(). | |
| 183 EXPECT_CALL(*client_, IsAnyInputPending()).WillOnce(Return(false)); | |
| 184 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
| 185 .Times(1) | |
| 186 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER)); | |
| 187 } | |
| 188 codec_loop_->DoPendingWork(); | |
| 189 WaitUntilIdle(ShouldNotBeIdle); | |
| 190 } | |
| 191 | |
| 192 TEST_F(MediaCodecLoopTest, TestUnqueuedEos) { | |
| 193 // Test sending an unsolicited EOS from MCB to MCL. | |
| 194 ConstructCodecLoop(); | |
| 195 { | |
| 196 InSequence _s; | |
| 197 | |
| 198 // MCL will first request input, then try to dequeue output. | |
| 199 EXPECT_CALL(*client_, IsAnyInputPending()).WillOnce(Return(false)); | |
| 200 EosOutputBuffer eos; | |
| 201 QueueOutputBuffer(eos); | |
| 202 EXPECT_CALL(Codec(), ReleaseOutputBuffer(eos.index, false)); | |
| 203 EXPECT_CALL(*client_, OnDecodedEos(_)).Times(1); | |
| 204 | |
| 205 // MCL should not call back for another input buffer once it receives EOS, | |
| 206 // so don't expect a call to IsAnyInputPending. It will try for more | |
| 207 // output, however. It's unclear if it should. | |
| 208 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
| 209 .Times(1) | |
| 210 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER)); | |
| 211 } | |
| 212 codec_loop_->DoPendingWork(); | |
| 213 // Don't WaitUntilIdle() here, since MCL is not requesting buffers. It would | |
| 214 // fail the IsAnyInputPending expectation. | |
| 215 } | |
| 216 | |
| 217 TEST_F(MediaCodecLoopTest, TestQueueEos) { | |
| 218 // Test sending an unsolicited EOS from MCB to MCL. | |
| 219 ConstructCodecLoop(); | |
| 220 { | |
| 221 InSequence _s; | |
| 222 | |
| 223 // MCL will first request input. We will provide an EOS from the client, | |
| 224 // and an input buffer to hold it. | |
| 225 EXPECT_CALL(*client_, IsAnyInputPending()).WillOnce(Return(true)); | |
| 226 int input_buffer_index = 123; | |
| 227 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _)) | |
| 228 .WillOnce(DoAll(SetArgPointee<1>(input_buffer_index), | |
|
liberato (no reviews please)
2016/07/08 17:49:46
this sets the *index parameter of DequeueInputBuff
| |
| 229 Return(MEDIA_CODEC_OK))); | |
| 230 MediaCodecLoop::InputData data; | |
| 231 data.is_eos = true; | |
| 232 EXPECT_CALL(*client_, ProvideInputData()).WillOnce(Return(data)); | |
| 233 EXPECT_CALL(Codec(), QueueEOS(input_buffer_index)); | |
| 234 | |
| 235 // Now send the EOS back on the output queue. | |
| 236 EosOutputBuffer eos; | |
| 237 QueueOutputBuffer(eos); | |
| 238 EXPECT_CALL(Codec(), ReleaseOutputBuffer(eos.index, false)); | |
| 239 EXPECT_CALL(*client_, OnDecodedEos(_)).Times(1); | |
| 240 | |
| 241 // See TestUnqueuedEos. | |
| 242 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
| 243 .Times(1) | |
| 244 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER)); | |
| 245 } | |
| 246 codec_loop_->DoPendingWork(); | |
| 247 // Don't WaitUntilIdle() here. See TestUnqueuedEos. | |
| 248 } | |
| 249 | |
| 43 } // namespace media | 250 } // namespace media |
| OLD | NEW |