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.
5 #include "media/filters/hls_vod_rendition.h"
6 #include "base/test/gmock_callback_support.h"
7 #include "base/test/task_environment.h"
8 #include "media/base/test_helpers.h"
9 #include "media/filters/hls_test_helpers.h"
15 constexpr char kInitialFetchVodPlaylist[] =
18 "#EXT-X-TARGETDURATION:2\n"
19 "#EXT-X-MEDIA-SEQUENCE:14551245\n"
21 "playlist_4500Kb_14551245.ts\n"
23 "playlist_4500Kb_14551246.ts\n"
25 "playlist_4500Kb_14551247.ts\n"
27 "playlist_4500Kb_14551248.ts\n"
29 "playlist_4500Kb_14551249.ts\n"
31 "playlist_4500Kb_14551250.ts\n"
33 "playlist_4500Kb_14551251.ts\n"
35 "playlist_4500Kb_14551252.ts\n"
37 "playlist_4500Kb_14551253.ts\n"
39 "playlist_4500Kb_14551254.ts\n";
44 using testing::Return;
46 class HlsVodRenditionUnittest : public testing::Test {
48 std::unique_ptr<MockManifestDemuxerEngineHost> mock_mdeh_;
49 std::unique_ptr<MockHlsRenditionHost> mock_hrh_;
50 base::test::TaskEnvironment task_environment_{
51 base::test::TaskEnvironment::TimeSource::MOCK_TIME};
53 std::unique_ptr<HlsVodRendition> MakeVodRendition(base::StringPiece content) {
54 constexpr hls::types::DecimalInteger version = 3;
55 auto parsed = hls::MediaPlaylist::Parse(
56 content, GURL("https://example.m3u8"), version, nullptr);
57 if (!parsed.has_value()) {
58 LOG(ERROR) << MediaSerialize(std::move(parsed).error());
61 auto playlist = std::move(parsed).value();
62 auto duration = playlist->GetComputedDuration();
63 return std::make_unique<HlsVodRendition>(mock_mdeh_.get(), mock_hrh_.get(),
64 "test", std::move(playlist),
68 MOCK_METHOD(void, CheckStateComplete, (base::TimeDelta delay), ());
70 ManifestDemuxer::DelayCallback BindCheckState(base::TimeDelta time) {
71 EXPECT_CALL(*this, CheckStateComplete(time));
72 return base::BindOnce(&HlsVodRenditionUnittest::CheckStateComplete,
73 base::Unretained(this));
76 ManifestDemuxer::DelayCallback BindCheckStateNoExpect() {
77 return base::BindOnce(&HlsVodRenditionUnittest::CheckStateComplete,
78 base::Unretained(this));
81 void RespondWithRange(base::TimeDelta start, base::TimeDelta end) {
82 Ranges<base::TimeDelta> ranges;
84 ranges.Add(start, end);
86 EXPECT_CALL(*mock_mdeh_, GetBufferedRanges("test"))
87 .WillOnce(Return(ranges));
90 void SupplyAndExpectJunkData(base::TimeDelta initial_response_start,
91 base::TimeDelta initial_response_end,
92 base::TimeDelta fetch_expected_time) {
93 std::string junk_content = "abcdefg, I dont like to sing rhyming songs";
94 EXPECT_CALL(*mock_hrh_, ReadFromUrl(_, _, _, _))
95 .WillOnce([content = std::move(junk_content), host = mock_hrh_.get()](
96 GURL url, bool, absl::optional<hls::types::ByteRange>,
97 HlsDataSourceProvider::ReadCb cb) {
98 auto stream = StringHlsDataSourceStreamFactory::CreateStream(content);
99 std::move(cb).Run(std::move(stream));
101 EXPECT_CALL(*mock_mdeh_, AppendAndParseData("test", _, _, _, _, 42))
102 .WillOnce(Return(true));
103 Ranges<base::TimeDelta> initial_range;
104 Ranges<base::TimeDelta> appended_range;
105 if (initial_response_end != initial_response_start) {
106 initial_range.Add(initial_response_start, initial_response_end);
108 appended_range.Add(fetch_expected_time - base::Seconds(1),
109 fetch_expected_time + base::Seconds(1));
110 EXPECT_CALL(*mock_mdeh_, GetBufferedRanges("test"))
112 .WillOnce(Return(initial_range))
113 .WillOnce(Return(appended_range));
117 HlsVodRenditionUnittest()
118 : mock_mdeh_(std::make_unique<MockManifestDemuxerEngineHost>()),
119 mock_hrh_(std::make_unique<MockHlsRenditionHost>()) {
120 EXPECT_CALL(*mock_mdeh_, RemoveRole("test"));
124 TEST_F(HlsVodRenditionUnittest, TestCheckStateFromNoData) {
125 auto rendition = MakeVodRendition(kInitialFetchVodPlaylist);
126 ASSERT_NE(rendition, nullptr);
128 SupplyAndExpectJunkData(base::Seconds(0), base::Seconds(0), base::Seconds(1));
129 rendition->CheckState(base::Seconds(0), 1.0,
130 BindCheckState(base::Seconds(0)));
132 task_environment_.RunUntilIdle();
135 TEST_F(HlsVodRenditionUnittest, TestCheckStateWithLargeBufferCached) {
136 auto rendition = MakeVodRendition(kInitialFetchVodPlaylist);
137 ASSERT_NE(rendition, nullptr);
139 // Prime the download speed cache.
140 SupplyAndExpectJunkData(base::Seconds(0), base::Seconds(0), base::Seconds(1));
141 rendition->CheckState(base::Seconds(0), 1.0,
142 BindCheckState(base::Seconds(0)));
143 task_environment_.RunUntilIdle();
145 // This time respond with a large range of loaded data.
146 // Time until underflow is going to be 12 seconds here - the fetch time
147 // average is zero, since this is a unittest, and we subtract 5 seconds flag
148 // giving a delay of 7 seconds.
149 RespondWithRange(base::Seconds(0), base::Seconds(12));
150 rendition->CheckState(base::Seconds(0), 1.0,
151 BindCheckState(base::Seconds(7)));
153 task_environment_.RunUntilIdle();
156 TEST_F(HlsVodRenditionUnittest, TestCheckStateWithTooLateBuffer) {
157 auto rendition = MakeVodRendition(kInitialFetchVodPlaylist);
158 ASSERT_NE(rendition, nullptr);
160 RespondWithRange(base::Seconds(10), base::Seconds(12));
161 EXPECT_CALL(*mock_mdeh_, OnError(_));
162 rendition->CheckState(base::Seconds(0), 1.0, BindCheckStateNoExpect());
164 task_environment_.RunUntilIdle();
167 TEST_F(HlsVodRenditionUnittest, TestStop) {
168 auto rendition = MakeVodRendition(kInitialFetchVodPlaylist);
169 ASSERT_NE(rendition, nullptr);
173 // Should always be kNoTimestamp after `Stop()` and no network requests.
174 rendition->CheckState(base::Seconds(0), 1.0, BindCheckState(kNoTimestamp));