[M120 Migration][hbbtv] Audio tracks count notification
[platform/framework/web/chromium-efl.git] / media / filters / hls_codec_detector.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_codec_detector.h"
6 #include "base/task/bind_post_task.h"
7 #include "media/formats/mp2t/mp2t_stream_parser.h"
8 #include "media/formats/mp4/mp4_stream_parser.h"
9
10 namespace media {
11
12 HlsCodecDetector::~HlsCodecDetector() = default;
13 HlsCodecDetectorImpl::~HlsCodecDetectorImpl() = default;
14 HlsCodecDetectorImpl::HlsCodecDetectorImpl(MediaLog* log,
15                                            HlsRenditionHost* host)
16     : log_(log->Clone()), rendition_host_(host) {
17   CHECK(host);
18 }
19
20 void HlsCodecDetectorImpl::DetermineContainerOnly(
21     std::unique_ptr<HlsDataSourceStream> stream,
22     CodecCallback cb) {
23   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
24   CHECK(!callback_);
25   callback_ = std::move(cb);
26   parser_ = nullptr;
27   rendition_host_->ReadStream(
28       std::move(stream), base::BindOnce(&HlsCodecDetectorImpl::OnStreamFetched,
29                                         weak_factory_.GetWeakPtr(),
30                                         /*container_only=*/true));
31 }
32
33 void HlsCodecDetectorImpl::DetermineContainerAndCodec(
34     std::unique_ptr<HlsDataSourceStream> stream,
35     CodecCallback cb) {
36   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
37   CHECK(!callback_);
38   callback_ = std::move(cb);
39   parser_ = nullptr;
40   rendition_host_->ReadStream(
41       std::move(stream), base::BindOnce(&HlsCodecDetectorImpl::OnStreamFetched,
42                                         weak_factory_.GetWeakPtr(),
43                                         /*container_only=*/false));
44 }
45
46 void HlsCodecDetectorImpl::OnStreamFetched(
47     bool container_only,
48     HlsDataSourceProvider::ReadResult maybe_stream) {
49   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
50   CHECK(callback_);
51
52   if (!maybe_stream.has_value()) {
53     HlsDemuxerStatus status = {HlsDemuxerStatus::Codes::kPlaylistUrlInvalid,
54                                std::move(maybe_stream).error()};
55     std::move(callback_).Run(std::move(status));
56     return;
57   }
58
59   auto stream = std::move(maybe_stream).value();
60   auto data_size = stream->buffer_size();
61   if (!data_size) {
62     // If no data came back, then the data source has been exhausted and we
63     // have failed to determine a codec.
64     std::move(callback_).Run(HlsDemuxerStatus::Codes::kInvalidBitstream);
65     return;
66   }
67
68   // If this is the first call to `OnStreamFetched`, then `parser_` should be
69   // null, and needs to be created. If `DetermineContainer` fails to determine
70   // the container being used, then it will call `callback_` and not set
71   // `parser_`.
72   if (!parser_) {
73     DetermineContainer(container_only, stream->raw_data(), data_size);
74   }
75
76   if (!parser_) {
77     // `DetermineContainer` MUST execute `callback_` if it fails to create a
78     // parser.
79     CHECK(!callback_);
80     return;
81   }
82
83   // `DetermineContainer` MUST set `container_` if it also sets `parser_`.
84   CHECK(!container_.empty());
85
86   if (container_only) {
87     // If we only want the container, don't parse any data, and just return
88     // the container with an empty codec string.
89     std::move(callback_).Run(ContainerAndCodecs{
90         .container = std::move(container_),
91         .codecs = "",
92     });
93     return;
94   }
95
96   if (!parser_->AppendToParseBuffer(stream->raw_data(), data_size)) {
97     std::move(callback_).Run(HlsDemuxerStatus::Codes::kInvalidBitstream);
98     return;
99   }
100
101   auto parse_result = StreamParser::ParseStatus::kSuccessHasMoreData;
102   while (StreamParser::ParseStatus::kSuccessHasMoreData == parse_result) {
103     parse_result = parser_->Parse(StreamParser::kMaxPendingBytesPerParse);
104     if (!callback_) {
105       // The parser has triggered the codec callback and we no longer need to
106       // parse data.
107       return;
108     }
109   }
110
111   CHECK(callback_);
112   if (StreamParser::ParseStatus::kFailed == parse_result) {
113     std::move(callback_).Run(HlsDemuxerStatus::Codes::kInvalidBitstream);
114     return;
115   }
116
117   CHECK_EQ(StreamParser::ParseStatus::kSuccess, parse_result);
118   if (!stream->CanReadMore()) {
119     std::move(callback_).Run(HlsDemuxerStatus::Codes::kInvalidBitstream);
120     return;
121   }
122
123   // If the existing data was parsed but parsing hasn't resulted in a successful
124   // detection, keep reading from the data source until it is exhausted. The
125   // HLS chunks are usually not too large, and playback will need to read this
126   // chunk initially anyway, so fetching the whole thing isn't going to be an
127   // issue.
128   stream->Clear();
129   rendition_host_->ReadStream(
130       std::move(stream),
131       base::BindOnce(&HlsCodecDetectorImpl::OnStreamFetched,
132                      weak_factory_.GetWeakPtr(), container_only));
133 }
134
135 void HlsCodecDetectorImpl::DetermineContainer(bool container_only,
136                                               const uint8_t* data,
137                                               size_t size) {
138   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
139   constexpr uint8_t kMP4FirstByte = 0x66;
140   constexpr uint8_t kMPEGTSFirstByte = 0x47;
141
142   CHECK(callback_);
143   CHECK(!parser_);
144   CHECK(container_.empty());
145
146   if (!size) {
147     std::move(callback_).Run(HlsDemuxerStatus::Codes::kInvalidBitstream);
148     return;
149   }
150
151   // Supported headers
152   const std::vector<uint8_t> mp4 = {kMP4FirstByte, 0x74, 0x79, 0x70,
153                                     0x69,          0x73, 0x6F, 0x6D};
154
155   StreamParser::NewConfigCB on_container_configs;
156   switch (data[0]) {
157     case kMPEGTSFirstByte: {
158       container_ = "video/mp2t";
159       // The Mp2t parser wants a list of all codecs it's allowed to check for,
160       // which means we need one codec for H264, one for AAC, and one for MP3.
161       // It doesn't actually care about the codecs details like profile or
162       // level, so we can give it the most basic of each type, and it will still
163       // find the actual codecs present in the stream.
164       const std::string codecs[]{
165           "avc1.420000",  // The H264 baseline bitstream codec
166           "aac",          // This is sufficient to get the AAC codec.
167           "mp3",          // This is sufficient to get the MP3 codec.
168       };
169       // TODO(crbug/1266991): The mp2t parser isn't able to determine whether
170       // aac audio codecs use sbr (aka double samples per second), so the parser
171       // will have to be modified in the future to detect that, and provide it
172       // so that we can determine it's presence.
173       parser_ =
174           std::make_unique<mp2t::Mp2tStreamParser>(base::span{codecs}, false);
175       on_container_configs = base::BindRepeating(
176           &HlsCodecDetectorImpl::OnNewConfigMP2T, base::Unretained(this));
177       break;
178     }
179     case kMP4FirstByte: {
180       // TODO(crbug/1266991): Android Media Player doesn't currently support
181       // the fragmented mp4 playback case. We'd like to get there someday, but
182       // it's not on the initial roadmap. The fragmented mp4 container will
183       // start with the bytes 0x66 0x74 0x79 0x70 0x69 0x73 0x6F 0x6D, and we
184       // can check for that later.
185       std::move(callback_).Run(HlsDemuxerStatus::Codes::kUnsupportedContainer);
186       return;
187     }
188     default: {
189       std::move(callback_).Run(HlsDemuxerStatus::Codes::kUnsupportedContainer);
190       return;
191     }
192   }
193
194   if (container_only) {
195     // Don't initialize the parser when we only care about querying the
196     // container.
197     return;
198   }
199
200   // `this` owns `parser_` and never transfers it, while `parser` owns these
201   // callbacks and never transfers them. It's therefore safe to use
202   // base::Unretained here, and we actually have to since weak_ptr + repeating
203   // callback + return type isn't allowed.
204   parser_->Init(base::DoNothingAs<void(const StreamParser::InitParameters&)>(),
205                 std::move(on_container_configs),
206                 base::BindRepeating(&HlsCodecDetectorImpl::OnNewBuffers,
207                                     base::Unretained(this)),
208                 base::BindRepeating(&HlsCodecDetectorImpl::OnEncryptedMediaInit,
209                                     base::Unretained(this)),
210                 base::DoNothing(), base::DoNothing(), log_.get());
211 }
212
213 void HlsCodecDetectorImpl::AddCodecToResponse(std::string codec) {
214   if (codec_response_ == "") {
215     codec_response_ = codec;
216   } else {
217     codec_response_ = codec_response_ + ", " + codec;
218   }
219 }
220
221 void HlsCodecDetectorImpl::ParserInit(
222     const StreamParser::InitParameters& params) {}
223
224 bool HlsCodecDetectorImpl::OnNewConfigMP2T(
225     std::unique_ptr<MediaTracks> tracks) {
226   for (const auto& [id, video_config] : tracks->GetVideoConfigs()) {
227     if (video_config.codec() != VideoCodec::kH264) {
228       HlsDemuxerStatus error = HlsDemuxerStatus::Codes::kUnsupportedCodec;
229       std::move(callback_).Run(
230           std::move(error).WithData("codec", video_config.codec()));
231       return false;
232     }
233     // Any avc1 codec will do, since the mp2t parser throws all the info away
234     // except for the codec type being h264.
235     AddCodecToResponse("avc1.420000");
236   }
237
238   for (const auto& [id, audio_config] : tracks->GetAudioConfigs()) {
239     if (audio_config.codec() == AudioCodec::kAAC) {
240       // Just use a dummy codec here for aac. The actual parser doesn't care
241       // when we start demuxing for real.
242       AddCodecToResponse("mp4a.40.05");
243     } else if (audio_config.codec() == AudioCodec::kMP3) {
244       AddCodecToResponse("mp3");
245     } else {
246       HlsDemuxerStatus error = HlsDemuxerStatus::Codes::kUnsupportedCodec;
247       std::move(callback_).Run(
248           std::move(error).WithData("codec", audio_config.codec()));
249       return false;
250     }
251   }
252   return true;
253 }
254
255 bool HlsCodecDetectorImpl::OnNewBuffers(
256     const StreamParser::BufferQueueMap& buffers) {
257   // Buffers come after all the configs, so once we hit the buffers, we can
258   // reply to `callback`. Move `codec_reponse_` and `container_` to clear them
259   // for the next parse.
260   std::move(callback_).Run(ContainerAndCodecs{
261       .container = std::move(container_),
262       .codecs = std::move(codec_response_),
263   });
264   return true;
265 }
266
267 void HlsCodecDetectorImpl::OnEncryptedMediaInit(
268     EmeInitDataType type,
269     const std::vector<uint8_t>& data) {
270   std::move(callback_).Run(
271       HlsDemuxerStatus::Codes::kEncryptedMediaNotSupported);
272 }
273
274 }  // namespace media