1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/test/gmock_callback_support.h"
8 #include "base/test/task_environment.h"
9 #include "base/threading/thread.h"
10 #include "media/base/media_track.h"
11 #include "media/base/media_util.h"
12 #include "media/base/mock_demuxer_host.h"
13 #include "media/base/mock_media_log.h"
14 #include "media/base/test_data_util.h"
15 #include "media/filters/manifest_demuxer.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
20 using ::base::test::RunOnceCallback;
22 using ::testing::NiceMock;
23 using ::testing::Return;
24 using ::testing::SaveArg;
26 // Define a mock implementation of ManifestDemuxer::Engine for testing.
27 class MockEngine : public ManifestDemuxer::Engine {
31 (ManifestDemuxerEngineHost * demuxer,
32 PipelineStatusCallback status_cb),
34 MOCK_METHOD(std::string, GetName, (), (const, override));
37 (base::TimeDelta time,
39 ManifestDemuxer::DelayCallback cb),
43 (base::TimeDelta time, ManifestDemuxer::SeekCallback cb),
45 MOCK_METHOD(void, StartWaitingForSeek, (), (override));
46 MOCK_METHOD(void, AbortPendingReads, (), (override));
47 MOCK_METHOD(bool, IsSeekable, (), (const override));
48 MOCK_METHOD(int64_t, GetMemoryUsage, (), (const, override));
49 MOCK_METHOD(void, Stop, (), (override));
52 // Fixture for ManifestDemuxer tests.
53 class ManifestDemuxerTest : public ::testing::Test {
56 : media_log_(std::make_unique<NiceMock<media::MockMediaLog>>()),
57 mock_host_(std::make_unique<NiceMock<MockDemuxerHost>>()) {
58 auto mock_engine = std::make_unique<MockEngine>();
59 mock_engine_ = mock_engine.get();
60 manifest_demuxer_ = std::make_unique<ManifestDemuxer>(
61 task_environment_.GetMainThreadTaskRunner(),
62 base::BindRepeating(&ManifestDemuxerTest::DemuxerRequestsSeek,
63 base::Unretained(this)),
64 std::move(mock_engine), media_log_.get());
67 ~ManifestDemuxerTest() override {
68 manifest_demuxer_->GetChunkDemuxerForTesting()->MarkEndOfStream(
70 // Reset pointer so that it does not dangle.
71 mock_engine_ = nullptr;
72 manifest_demuxer_.reset();
73 base::RunLoop().RunUntilIdle();
76 MOCK_METHOD(void, DemuxerRequestsSeek, (base::TimeDelta), ());
77 MOCK_METHOD(void, MockInitComplete, (PipelineStatus status), ());
78 MOCK_METHOD(void, MockSeekComplete, (PipelineStatus status), ());
80 void CreateIdAndAppendInitSegment(const std::string& id) {
81 auto* demuxer = manifest_demuxer_->GetChunkDemuxerForTesting();
82 ASSERT_EQ(demuxer->AddId(id, "video/webm", "vorbis,vp8"),
83 ChunkDemuxer::Status::kOk);
85 demuxer->SetTracksWatcher(
86 id, base::BindRepeating([](std::unique_ptr<MediaTracks>) {}));
87 demuxer->SetParseWarningCallback(
88 id, base::BindRepeating([](SourceBufferParseWarning) {}));
90 scoped_refptr<DecoderBuffer> bear1 = ReadTestDataFile("bear-320x240.webm");
92 demuxer->AppendToParseBuffer(id, bear1->data(), bear1->data_size()));
94 base::TimeDelta start = base::Seconds(0), end = base::Seconds(10), offset;
95 auto result = demuxer->RunSegmentParserLoop(id, start, end, &offset);
96 if (result != StreamParser::ParseStatus::kSuccessHasMoreData) {
97 ASSERT_EQ(result, StreamParser::ParseStatus::kSuccess);
103 void InitializeDemuxer() {
104 // Chunk demuxer won't finish initialization until content starts being
105 // added, and we don't have any mock content at this point.
106 EXPECT_CALL(*this, MockInitComplete(_)).Times(1);
108 // Mark the engine as initialized successfully.
109 EXPECT_CALL(*mock_engine_, Initialize(_, _))
110 .WillOnce(RunOnceCallback<1>(media::PIPELINE_OK));
112 manifest_demuxer_->Initialize(
113 mock_host_.get(), base::BindOnce(&ManifestDemuxerTest::MockInitComplete,
114 base::Unretained(this)));
115 CreateIdAndAppendInitSegment("test");
119 base::test::TaskEnvironment task_environment_;
120 std::unique_ptr<MediaLog> media_log_;
121 std::unique_ptr<MockDemuxerHost> mock_host_;
122 raw_ptr<MockEngine> mock_engine_;
123 std::unique_ptr<ManifestDemuxer> manifest_demuxer_;
126 TEST_F(ManifestDemuxerTest, InitializeStartsTimeUpdate) {
127 // When the engine is initialized and the chunk demuxer is opened
128 // (not initialized), the |OnTimeUpdate| events start coming in.
129 // posting `kNoTimestamp` back to this callback signals that we won't delay
130 // and get another event.
131 EXPECT_CALL(*mock_engine_, OnTimeUpdate(_, _, _))
132 .WillOnce(RunOnceCallback<2>(kNoTimestamp));
135 task_environment_.RunUntilIdle();
136 ASSERT_FALSE(manifest_demuxer_->has_next_task_for_testing());
139 TEST_F(ManifestDemuxerTest, PlaybackRateChangeUpTriggersTimeUpdate) {
140 ManifestDemuxer::DelayCallback delay_cb;
141 EXPECT_CALL(*mock_engine_, OnTimeUpdate(_, _, _))
142 .WillRepeatedly([&delay_cb](base::TimeDelta, double,
143 ManifestDemuxer::DelayCallback cb) {
144 delay_cb = std::move(cb);
147 // Initializing the demuxer will cause a time update event at time = 0.
149 ASSERT_TRUE(!!delay_cb);
151 // Setting the playback rate up while there is a pending event should do
153 EXPECT_CALL(*mock_engine_, OnTimeUpdate(_, _, _)).Times(0);
154 manifest_demuxer_->SetPlaybackRate(0.2);
156 // Respond to the loop, but request no new events.
157 std::move(delay_cb).Run(kNoTimestamp);
158 task_environment_.RunUntilIdle();
160 // Setting the playback rate up again while there is not pending event
161 // should trigger a new event.
162 EXPECT_CALL(*mock_engine_, OnTimeUpdate(_, _, _))
163 .WillRepeatedly([&delay_cb](base::TimeDelta, double,
164 ManifestDemuxer::DelayCallback cb) {
165 delay_cb = std::move(cb);
167 manifest_demuxer_->SetPlaybackRate(0.4);
168 task_environment_.RunUntilIdle();
169 ASSERT_TRUE(!!delay_cb);
171 // Respond to the loop, but request no new events.
172 std::move(delay_cb).Run(kNoTimestamp);
173 task_environment_.RunUntilIdle();
175 // Setting the playback rate down should not trigger a new event, even
176 // while there is no event pending.
177 EXPECT_CALL(*mock_engine_, OnTimeUpdate(_, _, _)).Times(0);
178 manifest_demuxer_->SetPlaybackRate(0.2);
180 task_environment_.RunUntilIdle();
181 ASSERT_FALSE(manifest_demuxer_->has_next_task_for_testing());
184 TEST_F(ManifestDemuxerTest, OnTimeUpdateUninterruptedBySeek) {
185 ManifestDemuxer::DelayCallback delay_cb;
186 EXPECT_CALL(*mock_engine_, OnTimeUpdate(_, _, _))
187 .WillRepeatedly([&delay_cb](base::TimeDelta, double,
188 ManifestDemuxer::DelayCallback cb) {
189 delay_cb = std::move(cb);
192 ASSERT_TRUE(!!delay_cb);
194 // a pending event is set, which won't be cleared until `delay_cb` is
196 ASSERT_FALSE(manifest_demuxer_->has_pending_seek_for_testing());
197 ASSERT_TRUE(manifest_demuxer_->has_pending_event_for_testing());
199 // Seek won't be called until we post delay_cb.
200 EXPECT_CALL(*mock_engine_, Seek(_, _)).Times(0);
201 EXPECT_CALL(*mock_engine_, StartWaitingForSeek()).Times(1);
202 EXPECT_CALL(*this, MockSeekComplete(_)).Times(0);
203 manifest_demuxer_->StartWaitingForSeek(base::Seconds(1));
204 manifest_demuxer_->Seek(base::Seconds(1),
205 base::BindOnce(&ManifestDemuxerTest::MockSeekComplete,
206 base::Unretained(this)));
208 // we not have a pending seek and a pending event.
209 ASSERT_TRUE(manifest_demuxer_->has_pending_seek_for_testing());
210 ASSERT_TRUE(manifest_demuxer_->has_pending_event_for_testing());
212 // Return from the pending event. The pending seek will start, which will
213 // kick off an async call to the chunk demuxer. We can make the engine
214 // also request a new event to be called, which means that delay_cb will be
216 EXPECT_CALL(*mock_engine_, Seek(_, _))
217 .WillOnce(RunOnceCallback<1>(ManifestDemuxer::SeekState::kNeedsData));
218 std::move(delay_cb).Run(base::Seconds(10));
219 task_environment_.RunUntilIdle();
221 // There is still a pending seek, and we now have a new event.
222 ASSERT_TRUE(!!delay_cb);
223 ASSERT_TRUE(manifest_demuxer_->has_pending_seek_for_testing());
224 ASSERT_TRUE(manifest_demuxer_->has_pending_event_for_testing());
226 // Executing this delay CB will trigger the seek to finish, since chunk
227 // demuxer has already finished it's seek. After the seek is finished, it
228 // will kick off another event, making the pending event check true and
229 // causing a new `delay_cb` to be set.
230 EXPECT_CALL(*this, MockSeekComplete(_)).Times(1);
231 std::move(delay_cb).Run(base::Seconds(10));
232 task_environment_.RunUntilIdle();
233 ASSERT_FALSE(manifest_demuxer_->has_pending_seek_for_testing());
234 ASSERT_TRUE(manifest_demuxer_->has_pending_event_for_testing());
236 // Running this event with no timestamp will cause the event loop to no longer
237 // run. Only kicking off a seek or playback rate change will re-trigger it.
238 ASSERT_TRUE(!!delay_cb);
239 std::move(delay_cb).Run(kNoTimestamp);
240 task_environment_.RunUntilIdle();
241 ASSERT_FALSE(manifest_demuxer_->has_pending_seek_for_testing());
242 ASSERT_FALSE(manifest_demuxer_->has_pending_event_for_testing());
244 task_environment_.RunUntilIdle();
245 ASSERT_FALSE(manifest_demuxer_->has_next_task_for_testing());
248 TEST_F(ManifestDemuxerTest, SeekInterruptedByError) {
249 ManifestDemuxer::DelayCallback delay_cb;
250 EXPECT_CALL(*mock_engine_, OnTimeUpdate(_, _, _))
251 .WillRepeatedly([&delay_cb](base::TimeDelta, double,
252 ManifestDemuxer::DelayCallback cb) {
253 delay_cb = std::move(cb);
256 ASSERT_TRUE(!!delay_cb);
257 ASSERT_FALSE(manifest_demuxer_->has_pending_seek_for_testing());
258 ASSERT_TRUE(manifest_demuxer_->has_pending_event_for_testing());
260 // Seek won't be called until we post delay_cb.
261 EXPECT_CALL(*mock_engine_, StartWaitingForSeek());
262 EXPECT_CALL(*mock_engine_, Seek(_, _)).Times(0);
263 EXPECT_CALL(*this, MockSeekComplete(_)).Times(0);
264 manifest_demuxer_->StartWaitingForSeek(base::Seconds(100));
265 manifest_demuxer_->Seek(base::Seconds(100),
266 base::BindOnce(&ManifestDemuxerTest::MockSeekComplete,
267 base::Unretained(this)));
268 task_environment_.RunUntilIdle();
270 // respond that data is needed, this will set chunk demuxer waiting for data.
271 EXPECT_CALL(*mock_engine_, Seek(_, _))
272 .WillOnce(RunOnceCallback<1>(ManifestDemuxer::SeekState::kNeedsData));
273 std::move(delay_cb).Run(kNoTimestamp);
274 task_environment_.RunUntilIdle();
276 // Send some generic pipeline error while the pipeline is still waiting for
278 EXPECT_CALL(*this, MockSeekComplete(_));
279 manifest_demuxer_->OnError(PIPELINE_ERROR_ABORT);
280 task_environment_.RunUntilIdle();
282 // Now let the delay_cb "execute", even though the error handler should have
283 // shut down all weak_ptrs and canceled all callbacks.
284 std::move(delay_cb).Run(kNoTimestamp);
285 task_environment_.RunUntilIdle();
288 TEST_F(ManifestDemuxerTest, CancelSeekAfterDemuxerBeforeEngine) {
289 // What happens if we seek, the demuxer replies, and while waiting for the
290 // engine to reply, we get a notice to cancel pending seek?
292 ManifestDemuxer::DelayCallback delay_cb;
293 EXPECT_CALL(*mock_engine_, OnTimeUpdate(_, _, _))
294 .WillRepeatedly([&delay_cb](base::TimeDelta, double,
295 ManifestDemuxer::DelayCallback cb) {
296 delay_cb = std::move(cb);
299 // a pending event is set, which won't be cleared until `delay_cb` is
302 ASSERT_TRUE(!!delay_cb);
303 ASSERT_FALSE(manifest_demuxer_->has_pending_seek_for_testing());
304 ASSERT_TRUE(manifest_demuxer_->has_pending_event_for_testing());
306 // Seek won't be called until we post delay_cb.
307 EXPECT_CALL(*mock_engine_, StartWaitingForSeek());
308 EXPECT_CALL(*mock_engine_, Seek(_, _)).Times(0);
309 EXPECT_CALL(*this, MockSeekComplete(_)).Times(0);
310 manifest_demuxer_->StartWaitingForSeek(base::Seconds(100));
311 manifest_demuxer_->Seek(base::Seconds(100),
312 base::BindOnce(&ManifestDemuxerTest::MockSeekComplete,
313 base::Unretained(this)));
314 task_environment_.RunUntilIdle();
316 // When we execute `delay_cb`, it will trigger `SeekInternal`, which will kick
317 // off a call to ChunkDemuxer::Seek and will also recapture a new `delay_cb`.
318 // The new `delay_cb` is bound to a task which completes the enigne seek step.
319 // The chunk demuxer should have already responded, and the pending seek
320 // should only be waiting on the engine.
321 EXPECT_CALL(*mock_engine_, Seek(_, _))
322 .WillOnce(RunOnceCallback<1>(ManifestDemuxer::SeekState::kNeedsData));
323 std::move(delay_cb).Run(kNoTimestamp);
324 task_environment_.RunUntilIdle();
325 ASSERT_TRUE(!!delay_cb);
326 ASSERT_TRUE(manifest_demuxer_->has_pending_seek_for_testing());
327 ASSERT_TRUE(manifest_demuxer_->has_pending_event_for_testing());
329 EXPECT_CALL(*mock_engine_, AbortPendingReads());
330 manifest_demuxer_->CancelPendingSeek(base::Seconds(5));
331 task_environment_.RunUntilIdle();
332 ASSERT_EQ(manifest_demuxer_->get_media_time_for_testing(),
335 // Running `delay_cb` will finish the seek, and start a new update, even if
336 // it runs with kNoTimestamp.
337 EXPECT_CALL(*this, MockSeekComplete(_));
338 std::move(delay_cb).Run(kNoTimestamp);
339 task_environment_.RunUntilIdle();
340 ASSERT_TRUE(!!delay_cb);
341 ASSERT_EQ(manifest_demuxer_->get_media_time_for_testing(),
344 // Run it again to end the loop.
345 std::move(delay_cb).Run(kNoTimestamp);
346 task_environment_.RunUntilIdle();
347 ASSERT_TRUE(!delay_cb);
348 ASSERT_FALSE(manifest_demuxer_->has_pending_seek_for_testing());
349 ASSERT_FALSE(manifest_demuxer_->has_pending_event_for_testing());