Shaka Packager SDK
Loading...
Searching...
No Matches
demuxer.cc
1// Copyright 2014 Google LLC. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file or at
5// https://developers.google.com/open-source/licenses/bsd
6
7#include <packager/media/demuxer/demuxer.h>
8
9#include <algorithm>
10#include <cstddef>
11#include <cstdint>
12#include <cstdio>
13#include <functional>
14#include <memory>
15#include <string>
16#include <utility>
17#include <vector>
18
19#include <absl/log/check.h>
20#include <absl/log/log.h>
21#include <absl/strings/escaping.h>
22#include <absl/strings/numbers.h>
23#include <absl/strings/str_format.h>
24#include <absl/strings/string_view.h>
25
26#include <packager/file.h>
27#include <packager/macros/compiler.h>
28#include <packager/macros/logging.h>
29#include <packager/media/base/container_names.h>
30#include <packager/media/base/key_source.h>
31#include <packager/media/base/media_handler.h>
32#include <packager/media/base/media_sample.h>
33#include <packager/media/base/stream_info.h>
34#include <packager/media/base/text_sample.h>
35#include <packager/media/formats/mp2t/mp2t_media_parser.h>
36#include <packager/media/formats/mp4/mp4_media_parser.h>
37#include <packager/media/formats/webm/webm_media_parser.h>
38#include <packager/media/formats/webvtt/webvtt_parser.h>
39#include <packager/media/formats/wvm/wvm_media_parser.h>
40#include <packager/status.h>
41
42namespace {
43// 65KB, sufficient to determine the container and likely all init data.
44const size_t kInitBufSize = 0x10000;
45const size_t kBufSize = 0x200000; // 2MB
46// Maximum number of allowed queued samples. If we are receiving a lot of
47// samples before seeing init_event, something is not right. The number
48// set here is arbitrary though.
49const size_t kQueuedSamplesLimit = 10000;
50const size_t kInvalidStreamIndex = static_cast<size_t>(-1);
51const size_t kBaseVideoOutputStreamIndex = 0x100;
52const size_t kBaseAudioOutputStreamIndex = 0x200;
53const size_t kBaseTextOutputStreamIndex = 0x300;
54
55std::string GetStreamLabel(size_t stream_index) {
56 switch (stream_index) {
57 case kBaseVideoOutputStreamIndex:
58 return "video";
59 case kBaseAudioOutputStreamIndex:
60 return "audio";
61 case kBaseTextOutputStreamIndex:
62 return "text";
63 default:
64 return absl::StrFormat("%u", stream_index);
65 }
66}
67
68bool GetStreamIndex(const std::string& stream_label, size_t* stream_index) {
69 DCHECK(stream_index);
70 if (stream_label == "video") {
71 *stream_index = kBaseVideoOutputStreamIndex;
72 } else if (stream_label == "audio") {
73 *stream_index = kBaseAudioOutputStreamIndex;
74 } else if (stream_label == "text") {
75 *stream_index = kBaseTextOutputStreamIndex;
76 } else {
77 // Expect stream_label to be a zero based stream id.
78 if (!absl::SimpleAtoi(stream_label, stream_index)) {
79 LOG(ERROR) << "Invalid argument --stream=" << stream_label << "; "
80 << "should be 'audio', 'video', 'text', or a number";
81 return false;
82 }
83 }
84 return true;
85}
86
87} // namespace
88
89namespace shaka {
90namespace media {
91
92Demuxer::Demuxer(const std::string& file_name)
93 : file_name_(file_name), buffer_(new uint8_t[kBufSize]) {}
94
95Demuxer::~Demuxer() {
96 if (media_file_)
97 media_file_->Close();
98}
99
100void Demuxer::SetKeySource(std::unique_ptr<KeySource> key_source) {
101 key_source_ = std::move(key_source);
102}
103
104Status Demuxer::Run() {
105 LOG(INFO) << "Demuxer::Run() on file '" << file_name_ << "'.";
106 Status status = InitializeParser();
107 // ParserInitEvent callback is called after a few calls to Parse(), which sets
108 // up the streams. Only after that, we can verify the outputs below.
109 while (!all_streams_ready_ && status.ok())
110 status.Update(Parse());
111 // If no output is defined, then return success after receiving all stream
112 // info.
113 if (all_streams_ready_ && output_handlers().empty())
114 return Status::OK;
115 if (!init_event_status_.ok())
116 return init_event_status_;
117 if (!status.ok())
118 return status;
119 // Check if all specified outputs exists.
120 for (const auto& pair : output_handlers()) {
121 if (std::find(stream_indexes_.begin(), stream_indexes_.end(), pair.first) ==
122 stream_indexes_.end()) {
123 LOG(ERROR) << "Invalid argument, stream=" << GetStreamLabel(pair.first)
124 << " not available.";
125 return Status(error::INVALID_ARGUMENT, "Stream not available");
126 }
127 }
128
129 while (!cancelled_ && status.ok())
130 status.Update(Parse());
131 if (cancelled_ && status.ok())
132 return Status(error::CANCELLED, "Demuxer run cancelled");
133
134 if (status.error_code() == error::END_OF_STREAM) {
135 for (size_t stream_index : stream_indexes_) {
136 status = FlushDownstream(stream_index);
137 if (!status.ok())
138 return status;
139 }
140 return Status::OK;
141 }
142 return status;
143}
144
146 cancelled_ = true;
147}
148
149Status Demuxer::SetHandler(const std::string& stream_label,
150 std::shared_ptr<MediaHandler> handler) {
151 size_t stream_index = kInvalidStreamIndex;
152 if (!GetStreamIndex(stream_label, &stream_index)) {
153 return Status(error::INVALID_ARGUMENT, "Invalid stream: " + stream_label);
154 }
155 return MediaHandler::SetHandler(stream_index, std::move(handler));
156}
157
158void Demuxer::SetLanguageOverride(const std::string& stream_label,
159 const std::string& language_override) {
160 size_t stream_index = kInvalidStreamIndex;
161 if (!GetStreamIndex(stream_label, &stream_index))
162 LOG(WARNING) << "Invalid stream for language override " << stream_label;
163 language_overrides_[stream_index] = language_override;
164}
165
166Status Demuxer::InitializeParser() {
167 DCHECK(!media_file_);
168 DCHECK(!all_streams_ready_);
169
170 LOG(INFO) << "Initialize Demuxer for file '" << file_name_ << "'.";
171
172 media_file_ = File::Open(file_name_.c_str(), "r");
173 if (!media_file_) {
174 return Status(error::FILE_FAILURE,
175 "Cannot open file for reading " + file_name_);
176 }
177
178 int64_t bytes_read = 0;
179 bool eof = false;
180 if (input_format_.empty()) {
181 // Read enough bytes before detecting the container.
182 while (static_cast<size_t>(bytes_read) < kInitBufSize) {
183 int64_t read_result =
184 media_file_->Read(buffer_.get() + bytes_read, kInitBufSize);
185 if (read_result < 0)
186 return Status(error::FILE_FAILURE, "Cannot read file " + file_name_);
187 if (read_result == 0) {
188 eof = true;
189 break;
190 }
191 bytes_read += read_result;
192 }
193 container_name_ = DetermineContainer(buffer_.get(), bytes_read);
194 } else {
195 container_name_ = DetermineContainerFromFormatName(input_format_);
196 }
197
198 // Initialize media parser.
199 switch (container_name_) {
200 case CONTAINER_MOV:
201 parser_.reset(new mp4::MP4MediaParser());
202 break;
203 case CONTAINER_MPEG2TS:
204 parser_.reset(new mp2t::Mp2tMediaParser());
205 break;
206 // Widevine classic (WVM) is derived from MPEG2PS. We do not support
207 // non-WVM MPEG2PS file, thus we do not differentiate between the two.
208 // Every MPEG2PS file is assumed to be WVM file. If it turns out not the
209 // case, an error will be reported when trying to parse the file as WVM
210 // file.
211 case CONTAINER_MPEG2PS:
212 FALLTHROUGH_INTENDED;
213 case CONTAINER_WVM:
214 parser_.reset(new wvm::WvmMediaParser());
215 break;
216 case CONTAINER_WEBM:
217 parser_.reset(new WebMMediaParser());
218 break;
219 case CONTAINER_WEBVTT:
220 parser_.reset(new WebVttParser());
221 break;
222 case CONTAINER_UNKNOWN: {
223 const int64_t kDumpSizeLimit = 512;
224 LOG(ERROR) << "Failed to detect the container type from the buffer: "
225 << absl::BytesToHexString(absl::string_view(
226 reinterpret_cast<const char*>(buffer_.get()),
227 std::min(bytes_read, kDumpSizeLimit)));
228 return Status(error::INVALID_ARGUMENT,
229 "Failed to detect the container type.");
230 }
231 default:
232 NOTIMPLEMENTED() << "Container " << container_name_
233 << " is not supported.";
234 return Status(error::UNIMPLEMENTED, "Container not supported.");
235 }
236
237 parser_->Init(
238 std::bind(&Demuxer::ParserInitEvent, this, std::placeholders::_1),
239 std::bind(&Demuxer::NewMediaSampleEvent, this, std::placeholders::_1,
240 std::placeholders::_2),
241 std::bind(&Demuxer::NewTextSampleEvent, this, std::placeholders::_1,
242 std::placeholders::_2),
243 key_source_.get());
244
245 // Handle trailing 'moov'.
246 if (container_name_ == CONTAINER_MOV &&
247 File::IsLocalRegularFile(file_name_.c_str())) {
248 // TODO(kqyang): Investigate whether we can reuse the existing file
249 // descriptor |media_file_| instead of opening the same file again.
250 static_cast<mp4::MP4MediaParser*>(parser_.get())->LoadMoov(file_name_);
251 }
252 if (!parser_->Parse(buffer_.get(), bytes_read) ||
253 (eof && !parser_->Flush())) {
254 return Status(error::PARSER_FAILURE,
255 "Cannot parse media file " + file_name_);
256 }
257 return Status::OK;
258}
259
260void Demuxer::ParserInitEvent(
261 const std::vector<std::shared_ptr<StreamInfo>>& stream_infos) {
262 if (dump_stream_info_) {
263 printf("\nFile \"%s\":\n", file_name_.c_str());
264 printf("Found %zu stream(s).\n", stream_infos.size());
265 for (size_t i = 0; i < stream_infos.size(); ++i)
266 printf("Stream [%zu] %s\n", i, stream_infos[i]->ToString().c_str());
267 }
268
269 int base_stream_index = 0;
270 bool video_handler_set =
271 output_handlers().find(kBaseVideoOutputStreamIndex) !=
272 output_handlers().end();
273 bool audio_handler_set =
274 output_handlers().find(kBaseAudioOutputStreamIndex) !=
275 output_handlers().end();
276 bool text_handler_set = output_handlers().find(kBaseTextOutputStreamIndex) !=
277 output_handlers().end();
278 for (const std::shared_ptr<StreamInfo>& stream_info : stream_infos) {
279 size_t stream_index = base_stream_index;
280 if (video_handler_set && stream_info->stream_type() == kStreamVideo) {
281 stream_index = kBaseVideoOutputStreamIndex;
282 // Only for the first video stream.
283 video_handler_set = false;
284 }
285 if (audio_handler_set && stream_info->stream_type() == kStreamAudio) {
286 stream_index = kBaseAudioOutputStreamIndex;
287 // Only for the first audio stream.
288 audio_handler_set = false;
289 }
290 if (text_handler_set && stream_info->stream_type() == kStreamText) {
291 stream_index = kBaseTextOutputStreamIndex;
292 text_handler_set = false;
293 }
294
295 const bool handler_set =
296 output_handlers().find(stream_index) != output_handlers().end();
297 if (handler_set) {
298 track_id_to_stream_index_map_[stream_info->track_id()] = stream_index;
299 stream_indexes_.push_back(stream_index);
300 auto iter = language_overrides_.find(stream_index);
301 if (iter != language_overrides_.end() &&
302 stream_info->stream_type() != kStreamVideo) {
303 stream_info->set_language(iter->second);
304 }
305 if (stream_info->is_encrypted()) {
306 init_event_status_.Update(Status(error::INVALID_ARGUMENT,
307 "A decryption key source is not "
308 "provided for an encrypted stream."));
309 } else {
310 init_event_status_.Update(
311 DispatchStreamInfo(stream_index, stream_info));
312 }
313 } else {
314 track_id_to_stream_index_map_[stream_info->track_id()] =
315 kInvalidStreamIndex;
316 }
317 ++base_stream_index;
318 }
319 all_streams_ready_ = true;
320}
321
322bool Demuxer::NewMediaSampleEvent(uint32_t track_id,
323 std::shared_ptr<MediaSample> sample) {
324 if (!all_streams_ready_) {
325 if (queued_media_samples_.size() >= kQueuedSamplesLimit) {
326 LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit;
327 return false;
328 }
329 queued_media_samples_.emplace_back(track_id, sample);
330 return true;
331 }
332 if (!init_event_status_.ok()) {
333 return false;
334 }
335
336 while (!queued_media_samples_.empty()) {
337 if (!PushMediaSample(queued_media_samples_.front().track_id,
338 queued_media_samples_.front().sample)) {
339 return false;
340 }
341 queued_media_samples_.pop_front();
342 }
343 return PushMediaSample(track_id, sample);
344}
345
346bool Demuxer::NewTextSampleEvent(uint32_t track_id,
347 std::shared_ptr<TextSample> sample) {
348 if (!all_streams_ready_) {
349 if (queued_text_samples_.size() >= kQueuedSamplesLimit) {
350 LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit;
351 return false;
352 }
353 queued_text_samples_.emplace_back(track_id, sample);
354 return true;
355 }
356 if (!init_event_status_.ok()) {
357 return false;
358 }
359
360 while (!queued_text_samples_.empty()) {
361 if (!PushTextSample(queued_text_samples_.front().track_id,
362 queued_text_samples_.front().sample)) {
363 return false;
364 }
365 queued_text_samples_.pop_front();
366 }
367 return PushTextSample(track_id, sample);
368}
369
370bool Demuxer::PushMediaSample(uint32_t track_id,
371 std::shared_ptr<MediaSample> sample) {
372 auto stream_index_iter = track_id_to_stream_index_map_.find(track_id);
373 if (stream_index_iter == track_id_to_stream_index_map_.end()) {
374 LOG(ERROR) << "Track " << track_id << " not found.";
375 return false;
376 }
377 if (stream_index_iter->second == kInvalidStreamIndex)
378 return true;
379 Status status = DispatchMediaSample(stream_index_iter->second, sample);
380 if (!status.ok()) {
381 LOG(ERROR) << "Failed to process sample " << stream_index_iter->second
382 << " " << status;
383 return false;
384 }
385 return true;
386}
387
388bool Demuxer::PushTextSample(uint32_t track_id,
389 std::shared_ptr<TextSample> sample) {
390 auto stream_index_iter = track_id_to_stream_index_map_.find(track_id);
391 if (stream_index_iter == track_id_to_stream_index_map_.end()) {
392 LOG(ERROR) << "Track " << track_id << " not found.";
393 return false;
394 }
395 if (stream_index_iter->second == kInvalidStreamIndex)
396 return true;
397 Status status = DispatchTextSample(stream_index_iter->second, sample);
398 if (!status.ok()) {
399 LOG(ERROR) << "Failed to process sample " << stream_index_iter->second
400 << " " << status;
401 return false;
402 }
403 return true;
404}
405
406Status Demuxer::Parse() {
407 DCHECK(media_file_);
408 DCHECK(parser_);
409 DCHECK(buffer_);
410
411 int64_t bytes_read = media_file_->Read(buffer_.get(), kBufSize);
412 if (bytes_read == 0) {
413 if (!parser_->Flush())
414 return Status(error::PARSER_FAILURE, "Failed to flush.");
415 return Status(error::END_OF_STREAM, "");
416 } else if (bytes_read < 0) {
417 return Status(error::FILE_FAILURE, "Cannot read file " + file_name_);
418 }
419
420 return parser_->Parse(buffer_.get(), bytes_read)
421 ? Status::OK
422 : Status(error::PARSER_FAILURE,
423 "Cannot parse media file " + file_name_);
424}
425
426} // namespace media
427} // namespace shaka
Status Run() override
Definition demuxer.cc:104
Status SetHandler(const std::string &stream_label, std::shared_ptr< MediaHandler > handler)
Definition demuxer.cc:149
void Cancel() override
Definition demuxer.cc:145
void SetLanguageOverride(const std::string &stream_label, const std::string &language_override)
Definition demuxer.cc:158
void SetKeySource(std::unique_ptr< KeySource > key_source)
Definition demuxer.cc:100
Demuxer(const std::string &file_name)
Definition demuxer.cc:92
Status SetHandler(size_t output_stream_index, std::shared_ptr< MediaHandler > handler)
Connect downstream handler at the specified output stream index.
Status DispatchMediaSample(size_t stream_index, std::shared_ptr< const MediaSample > media_sample) const
Dispatch the media sample to downstream handlers.
Status DispatchTextSample(size_t stream_index, std::shared_ptr< const TextSample > text_sample) const
Dispatch the text sample to downstream handlers.
Status DispatchStreamInfo(size_t stream_index, std::shared_ptr< const StreamInfo > stream_info) const
Dispatch the stream info to downstream handlers.
Status FlushDownstream(size_t output_stream_index)
Flush the downstream connected at the specified output stream index.
All the methods that are virtual are virtual for mocking.