[M120 Migration][hbbtv] Audio tracks count notification
[platform/framework/web/chromium-efl.git] / media / filters / hls_vod_rendition.cc
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.
4
5 #include "media/filters/hls_vod_rendition.h"
6
7 #include "base/task/bind_post_task.h"
8 #include "media/filters/hls_manifest_demuxer_engine.h"
9
10 namespace media {
11
12 // Chosen mostly arbitrarily.
13 constexpr size_t kChunkSize = 1024 * 32;
14
15 constexpr base::TimeDelta kBufferDuration = base::Seconds(10);
16
17 // A nice round number, chosen to make sure that we get a good average network
18 // speed calculation.
19 constexpr size_t kMovingAverageSampleSize = 128;
20
21 HlsVodRendition::SegmentInfo::SegmentInfo() {}
22 HlsVodRendition::SegmentInfo::SegmentInfo(const HlsVodRendition::SegmentInfo&) =
23     default;
24 HlsVodRendition::SegmentInfo::~SegmentInfo() {}
25
26 HlsVodRendition::PendingSegment::~PendingSegment() = default;
27 HlsVodRendition::PendingSegment::PendingSegment(
28     std::unique_ptr<HlsDataSourceStream> stream,
29     size_t index)
30     : stream(std::move(stream)), index(index) {}
31
32 HlsVodRendition::~HlsVodRendition() {
33   pending_stream_fetch_ = absl::nullopt;
34   engine_host_->RemoveRole(role_);
35 }
36
37 HlsVodRendition::HlsVodRendition(ManifestDemuxerEngineHost* engine_host,
38                                  HlsRenditionHost* rendition_host,
39                                  std::string role,
40                                  scoped_refptr<hls::MediaPlaylist> playlist,
41                                  base::TimeDelta duration)
42     : engine_host_(engine_host),
43       rendition_host_(rendition_host),
44       role_(role),
45       segment_duration_upper_limit_(playlist->GetTargetDuration()),
46       duration_(duration),
47       fetch_time_(kMovingAverageSampleSize) {
48   base::TimeDelta time;
49   for (const auto& segment : playlist->GetSegments()) {
50     SegmentInfo info;
51     info.index = segments_.size();
52     info.segment = segment;
53     info.absolute_start = time;
54     time += segment->GetDuration();
55     info.absolute_end = time;
56     segments_.push_back(info);
57   }
58
59   fetch_queue_ = segments_.begin();
60 }
61
62 absl::optional<base::TimeDelta> HlsVodRendition::GetDuration() {
63   return duration_;
64 }
65
66 void HlsVodRendition::CheckState(
67     base::TimeDelta media_time,
68     double playback_rate,
69     ManifestDemuxer::DelayCallback time_remaining_cb) {
70   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
71   if (is_stopped_for_shutdown_ || segments_.empty()) {
72     std::move(time_remaining_cb).Run(kNoTimestamp);
73     return;
74   }
75
76   auto ranges = engine_host_->GetBufferedRanges(role_);
77   if (ranges.empty()) {
78     // Ensure that we don't have a half-fetched stream when there's nothing
79     // loaded, since this case implies playback has just started or just
80     // finished a seek.
81     CHECK(!pending_stream_fetch_.has_value());
82
83     if (fetch_queue_ == segments_.end()) {
84       std::move(time_remaining_cb).Run(kNoTimestamp);
85       return;
86     }
87
88     FetchNext(base::BindOnce(std::move(time_remaining_cb), base::Seconds(0)),
89               media_time);
90     return;
91   }
92
93   auto time_until_underflow = base::Seconds(0);
94   if (ranges.start(ranges.size() - 1) > media_time) {
95     // If media time comes before the last loaded range, then a seek probably
96     // failed, and this should be an error.
97     PipelineStatus error = DEMUXER_ERROR_COULD_NOT_PARSE;
98     engine_host_->OnError(std::move(error)
99                               .WithData("timestamp", media_time)
100                               .WithData("range_start", ranges.back().first)
101                               .WithData("range_end", ranges.back().second));
102     return;
103   }
104
105   if (ranges.contains(ranges.size() - 1, media_time)) {
106     // If media time is inside the last range, then we might have time to
107     // recalculate and clear buffers.
108     time_until_underflow = ranges.back().second - media_time;
109     time_until_underflow /= playback_rate ? playback_rate : 1;
110   }
111
112   // If the remaining time is large enough, then there is time to clear old
113   // data and delay for a bit. "Large enough" is calculated to be at least 10
114   // seconds, chosen based on the "slow 3g" connection setting in devtools for
115   // a 1080p h264 video stream. "Large enough" must also be much more than the
116   // amount of time it would take to fetch the next segment, and the 6x
117   // multiplier was again chosen after messing about with the devtools network
118   // speed panel. The delay is then calculated such that roughly half the buffer
119   // has been rendered by the time another state check happens.
120   if (time_until_underflow > kBufferDuration &&
121       time_until_underflow > fetch_time_.Mean() * 6) {
122     // We have buffered enough to have time to clear old segments and delay.
123     base::TimeDelta time_to_clear = ClearOldSegments(media_time);
124     auto delay_time = kNoTimestamp;
125     if (playback_rate) {
126       delay_time = time_until_underflow - (fetch_time_.Mean() * 10) -
127                    time_to_clear - base::Seconds(5);
128       if (delay_time < base::TimeDelta()) {
129         delay_time = base::TimeDelta();
130       }
131     }
132     std::move(time_remaining_cb).Run(delay_time);
133     return;
134   }
135
136   // If there is nothing more to fetch, then playback should just continue until
137   // the end and stop.
138   if (!pending_stream_fetch_.has_value() && fetch_queue_ == segments_.end()) {
139     if (!set_stream_end_) {
140       engine_host_->SetEndOfStream();
141       set_stream_end_ = true;
142     }
143     std::move(time_remaining_cb).Run(kNoTimestamp);
144     return;
145   }
146
147   FetchNext(base::BindOnce(std::move(time_remaining_cb), base::Seconds(0)),
148             media_time);
149 }
150
151 ManifestDemuxer::SeekResponse HlsVodRendition::Seek(base::TimeDelta seek_time) {
152   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
153
154   if (is_stopped_for_shutdown_) {
155     return PIPELINE_ERROR_ABORT;
156   }
157
158   if (set_stream_end_) {
159     set_stream_end_ = false;
160     engine_host_->UnsetEndOfStream();
161   }
162
163   auto ranges = engine_host_->GetBufferedRanges(role_);
164   if (!ranges.empty() && ranges.contains(ranges.size() - 1, seek_time)) {
165     // If the seek time is in the last loaded range, then there is no need to
166     // update the pending fetch state or clear/flush any buffers.
167     return ManifestDemuxer::SeekState::kIsReady;
168   }
169
170   // If we seek anywhere else, we should evict everything in order to avoid
171   // fragmented loaded sections and large memory consumption.
172   engine_host_->EvictCodedFrames(role_, base::Seconds(0), 0);
173   engine_host_->RemoveAndReset(role_, base::TimeDelta(), duration_,
174                                &parse_offset_);
175
176   // reset the queue of segments to the current seek time.
177   pending_stream_fetch_ = absl::nullopt;
178   fetch_queue_ =
179       std::lower_bound(segments_.begin(), segments_.end(), seek_time,
180                        [](const SegmentInfo& segment, base::TimeDelta time) {
181                          return segment.absolute_end < time;
182                        });
183
184   // If we havent seeked to the end, we can then reset the sequence modes.
185   if (fetch_queue_ != segments_.end()) {
186     engine_host_->SetGroupStartIfParsingAndSequenceMode(
187         role_, (*fetch_queue_).absolute_start);
188   }
189
190   return ManifestDemuxer::SeekState::kNeedsData;
191 }
192
193 void HlsVodRendition::StartWaitingForSeek() {
194   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
195   pending_stream_fetch_ = absl::nullopt;
196 }
197
198 void HlsVodRendition::Stop() {
199   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
200   pending_stream_fetch_ = absl::nullopt;
201   is_stopped_for_shutdown_ = true;
202 }
203
204 base::TimeDelta HlsVodRendition::ClearOldSegments(base::TimeDelta media_time) {
205   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
206   CHECK(!is_stopped_for_shutdown_);
207   base::TimeTicks removal_start = base::TimeTicks::Now();
208   // Keep 10 seconds of content before the current media time. `media_time` is
209   // more accurately described as the highest timestamp yet seen in the current
210   // continual stream of playback, and depending on framerate, might be slightly
211   // ahead of where the actual currently-being-rendered frame is. Additionally,
212   // the ten seconds allows a small backwards-seek to not totally reset the
213   // buffer and redownload old content.
214   auto it = std::lower_bound(
215       segments_.begin(), segments_.end(), media_time - kBufferDuration,
216       [](const SegmentInfo& segment, base::TimeDelta time) {
217         return segment.absolute_end < time;
218       });
219   if (it != segments_.end()) {
220     if ((*it).absolute_start > base::TimeDelta()) {
221       engine_host_->Remove(role_, base::TimeDelta(), (*it).absolute_start);
222     }
223   }
224   return base::TimeTicks::Now() - removal_start;
225 }
226
227 void HlsVodRendition::FetchNext(base::OnceClosure cb, base::TimeDelta time) {
228   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
229   CHECK(!is_stopped_for_shutdown_);
230   CHECK(pending_stream_fetch_.has_value() || fetch_queue_ != segments_.end());
231   if (pending_stream_fetch_.has_value()) {
232     FetchMoreDataFromPendingStream(std::move(cb), time);
233     return;
234   }
235
236   SegmentInfo* segment = &*fetch_queue_;
237   std::advance(fetch_queue_, 1);
238   LoadSegment(segment, time, std::move(cb));
239 }
240
241 void HlsVodRendition::LoadSegment(SegmentInfo* segment,
242                                   base::TimeDelta fetch_required_time,
243                                   base::OnceClosure cb) {
244   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
245   CHECK(!is_stopped_for_shutdown_);
246   CHECK(segment);
247   rendition_host_->ReadFromUrl(
248       segment->segment->GetUri(), true, segment->segment->GetByteRange(),
249       base::BindPostTaskToCurrentDefault(base::BindOnce(
250           &HlsVodRendition::OnSegmentData, weak_factory_.GetWeakPtr(),
251           std::move(cb), std::move(fetch_required_time), segment->index,
252           base::TimeTicks::Now())));
253 }
254
255 void HlsVodRendition::FetchMoreDataFromPendingStream(
256     base::OnceClosure cb,
257     base::TimeDelta fetch_required_time) {
258   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
259   CHECK(!is_stopped_for_shutdown_);
260   CHECK(pending_stream_fetch_.has_value());
261   const SegmentInfo& segment = segments_[pending_stream_fetch_->index];
262   auto stream = std::move(pending_stream_fetch_->stream);
263   pending_stream_fetch_.reset();
264   rendition_host_->ReadStream(
265       std::move(stream),
266       base::BindOnce(&HlsVodRendition::OnSegmentData,
267                      weak_factory_.GetWeakPtr(), std::move(cb),
268                      std::move(fetch_required_time), segment.index,
269                      base::TimeTicks::Now()));
270 }
271
272 void HlsVodRendition::OnSegmentData(base::OnceClosure cb,
273                                     base::TimeDelta required_time,
274                                     size_t segment_index,
275                                     base::TimeTicks net_req_start,
276                                     HlsDataSourceProvider::ReadResult result) {
277   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
278   if (is_stopped_for_shutdown_) {
279     std::move(cb).Run();
280     return;
281   }
282
283   if (!result.has_value()) {
284     // Drop |cb| here, and let the abort handler pick up the pieces.
285     return engine_host_->OnError(
286         {DEMUXER_ERROR_COULD_NOT_PARSE, std::move(result).error()});
287   }
288   CHECK_LT(segment_index, segments_.size());
289
290   base::TimeDelta end =
291       segments_[segment_index].absolute_start + segment_duration_upper_limit_;
292
293   std::unique_ptr<HlsDataSourceStream> stream = std::move(result).value();
294
295   if (!engine_host_->AppendAndParseData(
296           role_, base::TimeDelta(), end + base::Seconds(1), &parse_offset_,
297           stream->raw_data(), stream->buffer_size())) {
298     return engine_host_->OnError(DEMUXER_ERROR_COULD_NOT_PARSE);
299   }
300
301   auto fetch_duration = base::TimeTicks::Now() - net_req_start;
302   // Store the time it took to download this chunk. The time should be scaled
303   // for situations where we only have a few bytes left to download.
304   auto scaled = (fetch_duration * stream->buffer_size()) / kChunkSize;
305   fetch_time_.AddSample(scaled);
306
307   if (stream->CanReadMore()) {
308     stream->Clear();
309     pending_stream_fetch_.emplace(std::move(stream), segment_index);
310   }
311
312   // After a seek especially, we will start loading content that comes
313   // potentially much earlier than the seek time, and it's possible that the
314   // loaded ranges won't yet contain the timestamp that is required to be loaded
315   // for the seek to complete. In this case, we just keep fetching until
316   // the seek time is loaded.
317
318   auto ranges = engine_host_->GetBufferedRanges(role_);
319   if (ranges.size() && ranges.contains(ranges.size() - 1, required_time)) {
320     std::move(cb).Run();
321     return;
322   }
323
324   // If the last range doesn't contain the timestamp, keep parsing until it
325   // does. If there is nothing left to download, then we can return.
326   if (!pending_stream_fetch_.has_value() && fetch_queue_ == segments_.end()) {
327     std::move(cb).Run();
328     return;
329   }
330
331   FetchNext(std::move(cb), required_time);
332 }
333
334 }  // namespace media