Revert "[M120 Migration]Fix for crash during chrome exit"
[platform/framework/web/chromium-efl.git] / media / filters / hls_manifest_demuxer_engine.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_manifest_demuxer_engine.h"
6
7 #include <vector>
8
9 #include "base/logging.h"
10 #include "base/memory/scoped_refptr.h"
11 #include "base/task/bind_post_task.h"
12 #include "base/task/sequenced_task_runner.h"
13 #include "base/time/time.h"
14 #include "media/base/audio_codecs.h"
15 #include "media/base/media_log.h"
16 #include "media/base/media_track.h"
17 #include "media/base/pipeline_status.h"
18 #include "media/base/supported_types.h"
19 #include "media/base/video_codecs.h"
20 #include "media/filters/manifest_demuxer.h"
21 #include "media/formats/hls/audio_rendition.h"
22 #include "media/formats/hls/media_playlist.h"
23 #include "media/formats/hls/multivariant_playlist.h"
24 #include "media/formats/hls/parse_status.h"
25 #include "media/formats/hls/types.h"
26 #include "media/formats/hls/variant_stream.h"
27 #include "third_party/abseil-cpp/absl/types/optional.h"
28
29 namespace media {
30
31 namespace {
32
33 constexpr const char* kPrimary = "primary";
34 constexpr const char* kAudioOverride = "audio-override";
35
36 bool ParseAudioCodec(const std::string& codec, AudioType* audio_type) {
37   audio_type->codec = StringToAudioCodec(codec);
38   audio_type->profile = AudioCodecProfile::kUnknown;
39   audio_type->spatial_rendering = false;
40   return audio_type->codec != AudioCodec::kUnknown;
41 }
42
43 bool AreAllAudioCodecsSupported(std::vector<AudioType> audio_types) {
44   if (audio_types.empty()) {
45     return false;
46   }
47   for (const auto& type : audio_types) {
48     if (!IsSupportedAudioType(type)) {
49       return false;
50     }
51   }
52   return true;
53 }
54
55 bool AreAllVideoCodecsSupported(std::vector<VideoType> video_types) {
56   if (video_types.empty()) {
57     return false;
58   }
59   for (const auto& type : video_types) {
60     if (!IsSupportedVideoType(type)) {
61       return false;
62     }
63   }
64   return true;
65 }
66
67 hls::RenditionSelector::CodecSupportType GetSupportedTypes(
68     base::StringPiece container,
69     base::span<const std::string> codecs) {
70   std::vector<VideoType> video_formats;
71   std::vector<AudioType> audio_formats;
72   for (const std::string& codec : codecs) {
73     // Try parsing it as a video codec first, which will set `video.codec`
74     // to unknown if it fails.
75     VideoType video;
76     uint8_t video_level;
77     video.hdr_metadata_type = gfx::HdrMetadataType::kNone;
78     ParseCodec(codec, video.codec, video.profile, video_level,
79                video.color_space);
80     if (video.codec != VideoCodec::kUnknown) {
81       video.level = video_level;
82       video_formats.push_back(video);
83       continue;
84     }
85
86     AudioType audio;
87     if (ParseAudioCodec(codec, &audio)) {
88       audio_formats.push_back(audio);
89     }
90   }
91
92   bool audio_support = AreAllAudioCodecsSupported(std::move(audio_formats));
93   bool video_support = AreAllVideoCodecsSupported(std::move(video_formats));
94
95   if (audio_support && video_support) {
96     return hls::RenditionSelector::CodecSupportType::kSupportedAudioVideo;
97   }
98   if (audio_support) {
99     return hls::RenditionSelector::CodecSupportType::kSupportedAudioOnly;
100   }
101   if (video_support) {
102     return hls::RenditionSelector::CodecSupportType::kSupportedVideoOnly;
103   }
104   return hls::RenditionSelector::CodecSupportType::kUnsupported;
105 }
106
107 }  // namespace
108
109 HlsManifestDemuxerEngine::~HlsManifestDemuxerEngine() = default;
110
111 HlsManifestDemuxerEngine::HlsManifestDemuxerEngine(
112     base::SequenceBound<HlsDataSourceProvider> dsp,
113     scoped_refptr<base::SequencedTaskRunner> media_task_runner,
114     GURL root_playlist_uri,
115     MediaLog* media_log)
116     : data_source_provider_(std::move(dsp)),
117       media_task_runner_(std::move(media_task_runner)),
118       root_playlist_uri_(std::move(root_playlist_uri)),
119       media_log_(media_log->Clone()) {
120   // This is always created on the main sequence, but used on the media sequence
121   DETACH_FROM_SEQUENCE(media_sequence_checker_);
122 }
123
124 HlsManifestDemuxerEngine::PlaylistParseInfo::PlaylistParseInfo(
125     GURL uri,
126     std::vector<std::string> codecs,
127     std::string role,
128     bool allow_multivariant_playlist)
129     : uri(std::move(uri)),
130       codecs(std::move(codecs)),
131       role(std::move(role)),
132       allow_multivariant_playlist(allow_multivariant_playlist) {}
133
134 HlsManifestDemuxerEngine::PlaylistParseInfo::~PlaylistParseInfo() {}
135
136 HlsManifestDemuxerEngine::PlaylistParseInfo::PlaylistParseInfo(
137     const PlaylistParseInfo& copy) = default;
138
139 std::string HlsManifestDemuxerEngine::GetName() const {
140   return "HlsManifestDemuxer";
141 }
142
143 void HlsManifestDemuxerEngine::InitializeWithMockCodecDetectorForTesting(
144     ManifestDemuxerEngineHost* host,
145     PipelineStatusCallback cb,
146     std::unique_ptr<HlsCodecDetector> codec_detector) {
147   InitializeWithCodecDetector(host, std::move(cb), std::move(codec_detector));
148 }
149
150 void HlsManifestDemuxerEngine::InitializeWithCodecDetector(
151     ManifestDemuxerEngineHost* host,
152     PipelineStatusCallback status_cb,
153     std::unique_ptr<HlsCodecDetector> codec_detector) {
154   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
155
156   // Initialize the codec detector on the media thread.
157   codec_detector_ = std::move(codec_detector);
158   host_ = host;
159   PlaylistParseInfo parse_info(root_playlist_uri_, {}, kPrimary,
160                                /*allow_multivariant_playlist=*/true);
161   ReadFromUrl(root_playlist_uri_, false, absl::nullopt,
162               base::BindOnce(&HlsManifestDemuxerEngine::ParsePlaylist,
163                              weak_factory_.GetWeakPtr(), std::move(status_cb),
164                              std::move(parse_info)));
165 }
166
167 void HlsManifestDemuxerEngine::Initialize(ManifestDemuxerEngineHost* host,
168                                           PipelineStatusCallback status_cb) {
169   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
170   InitializeWithCodecDetector(
171       host, std::move(status_cb),
172       std::make_unique<HlsCodecDetectorImpl>(media_log_.get(), this));
173 }
174
175 void HlsManifestDemuxerEngine::OnTimeUpdate(base::TimeDelta time,
176                                             double playback_rate,
177                                             ManifestDemuxer::DelayCallback cb) {
178   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
179   if (renditions_.empty()) {
180     std::move(cb).Run(kNoTimestamp);
181     return;
182   }
183
184   CheckStateAtIndex(time, playback_rate, std::move(cb), 0, absl::nullopt);
185 }
186
187 void HlsManifestDemuxerEngine::CheckStateAtIndex(
188     base::TimeDelta media_time,
189     double playback_rate,
190     ManifestDemuxer::DelayCallback cb,
191     size_t rendition_index,
192     absl::optional<base::TimeDelta> response_time) {
193   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
194   if (rendition_index >= renditions_.size()) {
195     // The response time collected at this point _must_ be valid.
196     std::move(cb).Run(response_time.value());
197     return;
198   }
199
200   auto recurse = base::BindOnce(
201       &HlsManifestDemuxerEngine::CheckStateAtIndex, weak_factory_.GetWeakPtr(),
202       media_time, playback_rate, std::move(cb), rendition_index + 1);
203
204   auto on_reply = base::BindOnce(
205       &HlsManifestDemuxerEngine::OnStateChecked, weak_factory_.GetWeakPtr(),
206       base::TimeTicks::Now(), response_time, std::move(recurse));
207
208   renditions_[rendition_index]->CheckState(media_time, playback_rate,
209                                            std::move(on_reply));
210 }
211
212 void HlsManifestDemuxerEngine::OnStateChecked(
213     base::TimeTicks call_start,
214     absl::optional<base::TimeDelta> prior_delay,
215     base::OnceCallback<void(absl::optional<base::TimeDelta>)> cb,
216     base::TimeDelta delay_time) {
217   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
218   if (prior_delay.value_or(kNoTimestamp) == kNoTimestamp) {
219     std::move(cb).Run(delay_time);
220     return;
221   }
222
223   base::TimeDelta spent_duration = base::TimeTicks::Now() - call_start;
224   if (spent_duration > prior_delay.value()) {
225     // Some previous rendition requested a delay that we've already spent while
226     // calculating the delay for the current rendition. Going forward then,
227     // we want to have no delay.
228     std::move(cb).Run(base::Seconds(0));
229     return;
230   }
231
232   auto adjusted_prior_delay = prior_delay.value() - spent_duration;
233   if (delay_time == kNoTimestamp) {
234     std::move(cb).Run(adjusted_prior_delay);
235     return;
236   }
237
238   std::move(cb).Run(adjusted_prior_delay > delay_time ? delay_time
239                                                       : adjusted_prior_delay);
240 }
241
242 void HlsManifestDemuxerEngine::Seek(base::TimeDelta time,
243                                     ManifestDemuxer::SeekCallback cb) {
244   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
245   if (!data_source_provider_) {
246     // The pipeline can call Seek just after an error was surfaced. The error
247     // handler resets |data_source_provider_|, so we should just reply with
248     // another error here.
249     std::move(cb).Run(PIPELINE_ERROR_ABORT);
250     return;
251   }
252   data_source_provider_.AsyncCall(&HlsDataSourceProvider::AbortPendingReads)
253       .WithArgs(base::BindPostTaskToCurrentDefault(
254           base::BindOnce(&HlsManifestDemuxerEngine::ContinueSeekInternal,
255                          weak_factory_.GetWeakPtr(), time, std::move(cb))));
256 }
257
258 void HlsManifestDemuxerEngine::ContinueSeekInternal(
259     base::TimeDelta time,
260     ManifestDemuxer::SeekCallback cb) {
261   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
262   bool buffers_needed = false;
263   for (auto& rendition : renditions_) {
264     auto response = rendition->Seek(time);
265     if (!response.has_value()) {
266       std::move(cb).Run(std::move(response).error().AddHere());
267       return;
268     }
269     buffers_needed |=
270         (ManifestDemuxer::SeekState::kNeedsData == std::move(response).value());
271   }
272   std::move(cb).Run(buffers_needed ? ManifestDemuxer::SeekState::kNeedsData
273                                    : ManifestDemuxer::SeekState::kIsReady);
274 }
275
276 void HlsManifestDemuxerEngine::StartWaitingForSeek() {
277   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
278   for (auto& rendition : renditions_) {
279     rendition->StartWaitingForSeek();
280   }
281 }
282
283 void HlsManifestDemuxerEngine::AbortPendingReads() {
284   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
285 }
286
287 bool HlsManifestDemuxerEngine::IsSeekable() const {
288   // `IsSeekable()` is only called after the pipeline has completed successfully
289   // or the initialization step fails. If init fails, we should report that the
290   // player is seekable to keep consistent behavior with other players.
291   return is_seekable_.value_or(true);
292 }
293
294 int64_t HlsManifestDemuxerEngine::GetMemoryUsage() const {
295   // TODO(crbug/1266991): Sum the memory of the renditions and data source
296   // providers.
297   return 0;
298 }
299
300 void HlsManifestDemuxerEngine::Stop() {
301   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
302   AbortPendingReads();
303   for (auto& rendition : renditions_) {
304     rendition->Stop();
305   }
306
307   data_source_provider_.Reset();
308   weak_factory_.InvalidateWeakPtrs();
309
310   multivariant_root_.reset();
311   rendition_selector_.reset();
312   renditions_.clear();
313   host_ = nullptr;
314 }
315
316 void HlsManifestDemuxerEngine::Abort(HlsDemuxerStatus status) {
317   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
318   if (!host_) {
319     return;
320   }
321   host_->OnError({PipelineStatus::Codes::DEMUXER_ERROR_COULD_NOT_PARSE,
322                   std::move(status)});
323 }
324
325 void HlsManifestDemuxerEngine::Abort(hls::ParseStatus status) {
326   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
327   if (!host_) {
328     return;
329   }
330   host_->OnError({PipelineStatus::Codes::DEMUXER_ERROR_COULD_NOT_PARSE,
331                   std::move(status)});
332 }
333
334 void HlsManifestDemuxerEngine::Abort(HlsDataSourceProvider::ReadStatus status) {
335   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
336   if (!host_) {
337     return;
338   }
339   host_->OnError(
340       {PipelineStatus::Codes::DEMUXER_ERROR_COULD_NOT_OPEN, std::move(status)});
341 }
342
343 void HlsManifestDemuxerEngine::ReadUntilExhausted(
344     HlsDataSourceProvider::ReadCb cb,
345     HlsDataSourceProvider::ReadStatus::Or<std::unique_ptr<HlsDataSourceStream>>
346         result) {
347   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
348   if (!result.has_value()) {
349     std::move(cb).Run(std::move(result).error());
350     return;
351   }
352   auto stream = std::move(result).value();
353   if (!stream->CanReadMore()) {
354     std::move(cb).Run(std::move(stream));
355     return;
356   }
357
358   ReadStream(std::move(stream),
359              base::BindOnce(&HlsManifestDemuxerEngine::ReadUntilExhausted,
360                             weak_factory_.GetWeakPtr(), std::move(cb)));
361 }
362
363 void HlsManifestDemuxerEngine::ReadFromUrl(
364     GURL uri,
365     bool read_chunked,
366     absl::optional<hls::types::ByteRange> range,
367     HlsDataSourceProvider::ReadCb cb) {
368   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
369   if (!data_source_provider_) {
370     std::move(cb).Run(HlsDataSourceProvider::ReadStatus::Codes::kAborted);
371     return;
372   }
373
374   if (read_chunked) {
375     cb = base::BindOnce(&HlsManifestDemuxerEngine::ReadUntilExhausted,
376                         weak_factory_.GetWeakPtr(), std::move(cb));
377   }
378
379   data_source_provider_.AsyncCall(&HlsDataSourceProvider::ReadFromUrl)
380       .WithArgs(std::move(uri), range,
381                 base::BindPostTaskToCurrentDefault(std::move(cb)));
382 }
383
384 void HlsManifestDemuxerEngine::ReadStream(
385     std::unique_ptr<HlsDataSourceStream> stream,
386     HlsDataSourceProvider::ReadCb cb) {
387   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
388   if (!data_source_provider_) {
389     std::move(cb).Run(HlsDataSourceProvider::ReadStatus::Codes::kAborted);
390     return;
391   }
392   data_source_provider_
393       .AsyncCall(&HlsDataSourceProvider::ReadFromExistingStream)
394       .WithArgs(std::move(stream),
395                 base::BindPostTaskToCurrentDefault(std::move(cb)));
396 }
397
398 void HlsManifestDemuxerEngine::ParsePlaylist(
399     PipelineStatusCallback parse_complete_cb,
400     PlaylistParseInfo parse_info,
401     HlsDataSourceProvider::ReadResult m_stream) {
402   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
403   if (!m_stream.has_value()) {
404     return Abort(std::move(m_stream).error().AddHere());
405   }
406   auto stream = std::move(m_stream).value();
407
408   // A four hour movie manifest is ~100Kb.
409   if (stream->buffer_size() > 102400) {
410     MEDIA_LOG(WARNING, media_log_)
411         << "Large Manifest detected: " << stream->buffer_size();
412   }
413
414   auto m_info = hls::Playlist::IdentifyPlaylist(stream->AsString());
415   if (!m_info.has_value()) {
416     return Abort(std::move(m_info).error().AddHere());
417   }
418
419   switch ((*m_info).kind) {
420     case hls::Playlist::Kind::kMultivariantPlaylist: {
421       if (!parse_info.allow_multivariant_playlist) {
422         return Abort(HlsDemuxerStatus::Codes::kRecursiveMultivariantPlaylists);
423       }
424       auto playlist = hls::MultivariantPlaylist::Parse(
425           stream->AsString(), parse_info.uri, (*m_info).version);
426       if (!playlist.has_value()) {
427         return Abort(std::move(playlist).error().AddHere());
428       }
429       return OnMultivariantPlaylist(std::move(parse_complete_cb),
430                                     std::move(playlist).value());
431     }
432     case hls::Playlist::Kind::kMediaPlaylist: {
433       auto playlist = ParseMediaPlaylistFromStringSource(
434           stream->AsString(), parse_info.uri, (*m_info).version);
435       if (!playlist.has_value()) {
436         return Abort(std::move(playlist).error().AddHere());
437       }
438       return OnMediaPlaylist(std::move(parse_complete_cb),
439                              std::move(parse_info),
440                              std::move(playlist).value());
441     }
442   }
443 }
444
445 hls::ParseStatus::Or<scoped_refptr<hls::MediaPlaylist>>
446 HlsManifestDemuxerEngine::ParseMediaPlaylistFromStringSource(
447     base::StringPiece source,
448     GURL uri,
449     hls::types::DecimalInteger version) {
450   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
451   return hls::MediaPlaylist::Parse(source, uri, version,
452                                    multivariant_root_.get());
453 }
454
455 void HlsManifestDemuxerEngine::AddRenditionForTesting(
456     std::unique_ptr<HlsRendition> test_rendition) {
457   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
458   bool is_seekable = test_rendition->GetDuration().has_value();
459   CHECK_EQ(is_seekable_.value_or(is_seekable), is_seekable);
460   is_seekable_ = is_seekable;
461   renditions_.push_back(std::move(test_rendition));
462 }
463
464 void HlsManifestDemuxerEngine::OnMultivariantPlaylist(
465     PipelineStatusCallback parse_complete_cb,
466     scoped_refptr<hls::MultivariantPlaylist> playlist) {
467   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
468   CHECK(!rendition_selector_);
469   multivariant_root_ = std::move(playlist);
470   rendition_selector_ = std::make_unique<hls::RenditionSelector>(
471       multivariant_root_, base::BindRepeating(&GetSupportedTypes));
472
473   hls::RenditionSelector::PreferredVariants streams =
474       rendition_selector_->GetPreferredVariants(video_preferences_,
475                                                 audio_preferences_);
476
477   // Possible outcomes of the rendition selector:
478   // | AOVariant | SelVariant | AORend  | primary=? | secondary=? |
479   // |-----------|------------|---------|-----------|-------------|
480   // | null      | null       | null    | X         | X           |
481   // |-----------|------------|---------|-----------|-------------|
482   // | null      | present    | null    | SV        | X           |
483   // |-----------|------------|---------|-----------|-------------|
484   // | present   | null       | present | AOV       | X           |
485   // |-----------|------------|---------|-----------|-------------|
486   // | present   | present    | null    | SV        | X           |
487   // |-----------|------------|---------|-----------|-------------|
488   // | present   | present    | present | SV        | AOV         |
489   // |-----------|------------|---------|-----------|-------------|
490   absl::optional<GURL> audio_override_uri;
491   const GURL& primary_uri = streams.selected_variant->GetPrimaryRenditionUri();
492   if (streams.audio_override_rendition) {
493     CHECK_NE(streams.audio_override_variant, nullptr);
494     audio_override_uri = streams.audio_override_rendition->GetUri().value_or(
495         streams.audio_override_variant->GetPrimaryRenditionUri());
496   }
497
498   std::vector<PlaylistParseInfo> renditions_to_parse;
499   std::vector<std::string> no_codecs;
500
501   if (streams.selected_variant) {
502     renditions_to_parse.emplace_back(
503         streams.selected_variant->GetPrimaryRenditionUri(),
504         streams.selected_variant->GetCodecs().value_or(no_codecs), kPrimary);
505
506     if (streams.audio_override_rendition &&
507         primary_uri != audio_override_uri.value_or(primary_uri)) {
508       CHECK_NE(streams.audio_override_variant, nullptr);
509       renditions_to_parse.emplace_back(
510           *audio_override_uri,
511           streams.audio_override_variant->GetCodecs().value_or(no_codecs),
512           kAudioOverride);
513     }
514   } else if (streams.audio_override_rendition &&
515              primary_uri != audio_override_uri.value_or(primary_uri)) {
516     renditions_to_parse.emplace_back(
517         *audio_override_uri,
518         streams.audio_override_variant->GetCodecs().value_or(no_codecs),
519         kPrimary);
520   } else {
521     Abort(HlsDemuxerStatus::Codes::kNoRenditions);
522     return;
523   }
524
525   SetStreams(std::move(renditions_to_parse), std::move(parse_complete_cb),
526              PIPELINE_OK);
527 }
528
529 void HlsManifestDemuxerEngine::SetStreams(
530     std::vector<PlaylistParseInfo> playlists,
531     PipelineStatusCallback cb,
532     PipelineStatus exit_on_error) {
533   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
534   if (!exit_on_error.is_ok() || playlists.empty()) {
535     // We've either hit the end of the list with a success, or have errored out
536     // early. Either way, the status should be forwarded to the cb.
537     std::move(cb).Run(std::move(exit_on_error));
538     return;
539   }
540
541   const PlaylistParseInfo playlist = playlists.back();
542   playlists.pop_back();
543   PipelineStatusCallback on_parsed = base::BindOnce(
544       &HlsManifestDemuxerEngine::SetStreams, weak_factory_.GetWeakPtr(),
545       std::move(playlists), std::move(cb));
546
547   GURL uri = playlist.uri;
548   ReadFromUrl(uri, false, absl::nullopt,
549               base::BindOnce(&HlsManifestDemuxerEngine::ParsePlaylist,
550                              weak_factory_.GetWeakPtr(), std::move(on_parsed),
551                              std::move(playlist)));
552 }
553
554 void HlsManifestDemuxerEngine::OnMediaPlaylist(
555     PipelineStatusCallback parse_complete_cb,
556     PlaylistParseInfo parse_info,
557     scoped_refptr<hls::MediaPlaylist> playlist) {
558   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
559   hls::MediaPlaylist* playlist_ptr = playlist.get();
560   DetermineStreamContainerAndCodecs(
561       playlist_ptr,
562       base::BindOnce(&HlsManifestDemuxerEngine::OnPlaylistContainerDetermined,
563                      weak_factory_.GetWeakPtr(), std::move(parse_complete_cb),
564                      std::move(parse_info), std::move(playlist)));
565 }
566
567 void HlsManifestDemuxerEngine::OnPlaylistContainerDetermined(
568     PipelineStatusCallback parse_complete_cb,
569     PlaylistParseInfo parse_info,
570     scoped_refptr<hls::MediaPlaylist> playlist,
571     HlsDemuxerStatus::Or<HlsCodecDetector::ContainerAndCodecs> maybe_info) {
572   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
573   if (!maybe_info.has_value()) {
574     std::move(parse_complete_cb)
575         .Run({DEMUXER_ERROR_COULD_NOT_OPEN, std::move(maybe_info).error()});
576     return;
577   }
578
579   auto container_and_codecs = std::move(maybe_info).value();
580   std::string container = std::move(container_and_codecs.container);
581   std::string codecs = std::move(container_and_codecs.codecs);
582
583   if (parse_info.codecs.size()) {
584     // Codecs came from a multivariant playlist rather than from detection. We
585     // want to join whatever came from the playlist and use that instead.
586     std::stringstream codecstream;
587     std::copy(parse_info.codecs.begin(), parse_info.codecs.end(),
588               std::ostream_iterator<std::string>(codecstream, ", "));
589     codecs = codecstream.str();
590
591     // codecs ends with a trailing ", " now, so we need to drop that
592     codecs = codecs.substr(0, codecs.length() - 2);
593   }
594
595   if (!host_->AddRole(parse_info.role, container, codecs)) {
596     std::move(parse_complete_cb).Run(DEMUXER_ERROR_COULD_NOT_OPEN);
597     return;
598   }
599
600   host_->SetSequenceMode(parse_info.role, true);
601
602   auto m_rendition = HlsRendition::CreateRendition(
603       host_, this, parse_info.role, std::move(playlist), parse_info.uri);
604
605   if (!m_rendition.has_value()) {
606     std::move(parse_complete_cb)
607         .Run({DEMUXER_ERROR_COULD_NOT_PARSE,
608               std::move(m_rendition).error().AddHere()});
609     return;
610   }
611
612   auto rendition = std::move(m_rendition).value();
613
614   if (parse_info.role == kPrimary) {
615     auto duration_or_live = rendition->GetDuration();
616     if (duration_or_live.has_value()) {
617       host_->SetDuration(duration_or_live->InSecondsF());
618     }
619   }
620
621   bool seekable = rendition->GetDuration().has_value();
622   if (is_seekable_.value_or(seekable) != seekable) {
623     std::move(parse_complete_cb).Run(DEMUXER_ERROR_COULD_NOT_PARSE);
624     return;
625   }
626   is_seekable_ = seekable;
627   renditions_.push_back(std::move(rendition));
628   std::move(parse_complete_cb).Run(OkStatus());
629 }
630
631 void HlsManifestDemuxerEngine::DetermineStreamContainerAndCodecs(
632     hls::MediaPlaylist* playlist,
633     HlsDemuxerStatusCb<HlsCodecDetector::ContainerAndCodecs> container_cb) {
634   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
635   const auto& segments = playlist->GetSegments();
636   if (segments.empty()) {
637     std::move(container_cb).Run(HlsDemuxerStatus::Codes::kUnsupportedContainer);
638     return;
639   }
640
641   ReadFromUrl(
642       segments[0]->GetUri(), true, segments[0]->GetByteRange(),
643       base::BindOnce(&HlsManifestDemuxerEngine::PeekFirstSegment,
644                      weak_factory_.GetWeakPtr(), std::move(container_cb)));
645 }
646
647 void HlsManifestDemuxerEngine::PeekFirstSegment(
648     HlsDemuxerStatusCb<HlsCodecDetector::ContainerAndCodecs> cb,
649     HlsDataSourceProvider::ReadResult maybe_stream) {
650   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
651   if (!maybe_stream.has_value()) {
652     std::move(cb).Run(HlsDemuxerStatus::Codes::kInvalidSegmentUri);
653     return;
654   }
655   codec_detector_->DetermineContainerAndCodec(std::move(maybe_stream).value(),
656                                               std::move(cb));
657 }
658 void HlsManifestDemuxerEngine::OnChunkDemuxerParseWarning(
659     std::string role,
660     SourceBufferParseWarning warning) {
661   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
662   MEDIA_LOG(WARNING, media_log_)
663       << "ParseWarning (" << role << "): " << static_cast<int>(warning);
664 }
665
666 void HlsManifestDemuxerEngine::OnChunkDemuxerTracksChanged(
667     std::string role,
668     std::unique_ptr<MediaTracks> tracks) {
669   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
670   MEDIA_LOG(WARNING, media_log_) << "TracksChanged for role: " << role;
671 }
672
673 }  // namespace media