2014-02-14 23:21:05 +00:00
|
|
|
// Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file or at
|
|
|
|
// https://developers.google.com/open-source/licenses/bsd
|
2013-10-11 21:44:55 +00:00
|
|
|
|
2014-10-01 22:10:21 +00:00
|
|
|
#include "packager/media/base/muxer.h"
|
2013-10-11 21:44:55 +00:00
|
|
|
|
2016-08-17 17:41:40 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2014-10-01 22:10:21 +00:00
|
|
|
#include "packager/media/base/media_sample.h"
|
2018-05-23 00:26:18 +00:00
|
|
|
#include "packager/media/base/muxer_util.h"
|
|
|
|
#include "packager/status_macros.h"
|
2013-10-11 21:44:55 +00:00
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
namespace shaka {
|
2013-10-11 21:44:55 +00:00
|
|
|
namespace media {
|
2017-03-11 02:49:55 +00:00
|
|
|
namespace {
|
|
|
|
const bool kInitialEncryptionInfo = true;
|
2018-05-23 00:26:18 +00:00
|
|
|
const int64_t kStartTime = 0;
|
2017-03-11 02:49:55 +00:00
|
|
|
} // namespace
|
2013-10-11 21:44:55 +00:00
|
|
|
|
2018-05-23 00:26:18 +00:00
|
|
|
Muxer::Muxer(const MuxerOptions& options) : options_(options) {
|
|
|
|
// "$" is only allowed if the output file name is a template, which is used to
|
|
|
|
// support one file per Representation per Period when there are Ad Cues.
|
|
|
|
if (options_.output_file_name.find("$") != std::string::npos)
|
|
|
|
output_file_template_ = options_.output_file_name;
|
|
|
|
}
|
2013-10-11 21:44:55 +00:00
|
|
|
|
|
|
|
Muxer::~Muxer() {}
|
|
|
|
|
2015-02-09 18:22:28 +00:00
|
|
|
void Muxer::Cancel() {
|
|
|
|
cancelled_ = true;
|
|
|
|
}
|
|
|
|
|
2016-08-17 17:41:40 +00:00
|
|
|
void Muxer::SetMuxerListener(std::unique_ptr<MuxerListener> muxer_listener) {
|
|
|
|
muxer_listener_ = std::move(muxer_listener);
|
2013-12-12 23:49:31 +00:00
|
|
|
}
|
|
|
|
|
2015-05-11 21:07:10 +00:00
|
|
|
void Muxer::SetProgressListener(
|
2016-08-17 17:41:40 +00:00
|
|
|
std::unique_ptr<ProgressListener> progress_listener) {
|
|
|
|
progress_listener_ = std::move(progress_listener);
|
2015-05-11 21:07:10 +00:00
|
|
|
}
|
|
|
|
|
2017-02-21 18:36:50 +00:00
|
|
|
Status Muxer::Process(std::unique_ptr<StreamData> stream_data) {
|
|
|
|
Status status;
|
|
|
|
switch (stream_data->stream_data_type) {
|
|
|
|
case StreamDataType::kStreamInfo:
|
|
|
|
streams_.push_back(std::move(stream_data->stream_info));
|
2018-05-23 00:26:18 +00:00
|
|
|
return ReinitializeMuxer(kStartTime);
|
2017-03-11 02:49:55 +00:00
|
|
|
case StreamDataType::kSegmentInfo: {
|
2017-09-12 17:24:24 +00:00
|
|
|
const auto& segment_info = *stream_data->segment_info;
|
|
|
|
if (muxer_listener_ && segment_info.is_encrypted) {
|
2017-03-11 02:49:55 +00:00
|
|
|
const EncryptionConfig* encryption_config =
|
2017-09-12 17:24:24 +00:00
|
|
|
segment_info.key_rotation_encryption_config.get();
|
2017-06-20 23:30:03 +00:00
|
|
|
// Only call OnEncryptionInfoReady again when key updates.
|
|
|
|
if (encryption_config && encryption_config->key_id != current_key_id_) {
|
2017-03-11 02:49:55 +00:00
|
|
|
muxer_listener_->OnEncryptionInfoReady(
|
|
|
|
!kInitialEncryptionInfo, encryption_config->protection_scheme,
|
|
|
|
encryption_config->key_id, encryption_config->constant_iv,
|
|
|
|
encryption_config->key_system_info);
|
2017-06-20 23:30:03 +00:00
|
|
|
current_key_id_ = encryption_config->key_id;
|
2017-03-11 02:49:55 +00:00
|
|
|
}
|
2017-06-20 23:30:03 +00:00
|
|
|
if (!encryption_started_) {
|
2017-03-11 02:49:55 +00:00
|
|
|
encryption_started_ = true;
|
|
|
|
muxer_listener_->OnEncryptionStart();
|
|
|
|
}
|
|
|
|
}
|
2017-09-12 17:24:24 +00:00
|
|
|
return FinalizeSegment(stream_data->stream_index, segment_info);
|
2017-03-11 02:49:55 +00:00
|
|
|
}
|
2017-02-21 18:36:50 +00:00
|
|
|
case StreamDataType::kMediaSample:
|
2018-03-15 23:01:47 +00:00
|
|
|
return AddSample(stream_data->stream_index, *stream_data->media_sample);
|
2018-01-03 00:10:33 +00:00
|
|
|
case StreamDataType::kCueEvent:
|
|
|
|
if (muxer_listener_) {
|
2018-03-15 23:01:47 +00:00
|
|
|
const int64_t time_scale =
|
|
|
|
streams_[stream_data->stream_index]->time_scale();
|
|
|
|
const double time_in_seconds = stream_data->cue_event->time_in_seconds;
|
|
|
|
const int64_t scaled_time =
|
|
|
|
static_cast<int64_t>(time_in_seconds * time_scale);
|
|
|
|
muxer_listener_->OnCueEvent(scaled_time,
|
2018-01-03 00:10:33 +00:00
|
|
|
stream_data->cue_event->cue_data);
|
2018-05-23 00:26:18 +00:00
|
|
|
|
|
|
|
// Finalize and re-initialize Muxer to generate different content files.
|
|
|
|
if (!output_file_template_.empty()) {
|
|
|
|
RETURN_IF_ERROR(Finalize());
|
|
|
|
RETURN_IF_ERROR(ReinitializeMuxer(scaled_time));
|
|
|
|
}
|
2018-01-03 00:10:33 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-02-21 18:36:50 +00:00
|
|
|
default:
|
|
|
|
VLOG(3) << "Stream data type "
|
|
|
|
<< static_cast<int>(stream_data->stream_data_type) << " ignored.";
|
|
|
|
break;
|
2014-04-09 17:34:55 +00:00
|
|
|
}
|
2017-02-21 18:36:50 +00:00
|
|
|
// No dispatch for muxer.
|
|
|
|
return Status::OK;
|
2014-04-09 17:34:55 +00:00
|
|
|
}
|
|
|
|
|
2018-05-23 00:26:18 +00:00
|
|
|
Status Muxer::OnFlushRequest(size_t input_stream_index) {
|
|
|
|
return Finalize();
|
|
|
|
}
|
|
|
|
|
|
|
|
Status Muxer::ReinitializeMuxer(int64_t timestamp) {
|
|
|
|
if (muxer_listener_ && streams_.back()->is_encrypted()) {
|
|
|
|
const EncryptionConfig& encryption_config =
|
|
|
|
streams_.back()->encryption_config();
|
|
|
|
muxer_listener_->OnEncryptionInfoReady(
|
|
|
|
kInitialEncryptionInfo, encryption_config.protection_scheme,
|
|
|
|
encryption_config.key_id, encryption_config.constant_iv,
|
|
|
|
encryption_config.key_system_info);
|
|
|
|
current_key_id_ = encryption_config.key_id;
|
|
|
|
}
|
|
|
|
if (!output_file_template_.empty()) {
|
Add support for EditLists in ISO-BMFF
- EditLists in input files are parsed and applied to sample timestamps.
- An EditList will be inserted in the ISO-BMFF output if
- There is an offset between the initial presentation timestamp (pts)
and decoding timestamp (dts). Chrome, as of M67, still uses dts in
buffered range API [1], which creates various problems when buffered
range by pts does not align with buffered range by dts. There is
another bug in Chrome that applies EditList to pts only [2]. This
means that we can insert an EditList to align pts range and dts range.
- MediaSamples have negative timestamps (e.g. for Audio Priming).
You may notice the below change on some contents:
- Some media duration is reduced by one or two frames. This is because
EditList in the input file was ignored in the previous code, so video
streams start with a zero dts and a non-zero pts; the smaller of dts
and pts was used as the starting timestamp (related to the earlier
workaround for Chrome's dts bug), so the calculated duration was
actually a bit larger than the actual duration. Now with EditList
applied, the initial pts is reduced to zero, so the media duration is
also reduced to reflect the actual and correct media duration.
It may also result in negative timestamps in TS/HLS Packed Audio, which
will be addressed in a follow up CL.
Fixes #112.
Partially address b/110782437.
[1] https://crbug.com/718641, fixed but behind MseBufferByPts.
[2] https://crbug.com/354518. Chrome is planning to enable the fix for
[1] before addressing this bug, so we are safe.
Change-Id: I59317740ad3807ca66fa74b3a18fdf7f32c96aeb
2018-07-03 00:52:25 +00:00
|
|
|
// Update |output_file_name| with an actual file name, which will be used by
|
|
|
|
// the subclasses.
|
2018-05-23 00:26:18 +00:00
|
|
|
options_.output_file_name =
|
|
|
|
GetSegmentName(output_file_template_, timestamp, output_file_index_++,
|
2020-09-26 01:57:47 +00:00
|
|
|
options_.bandwidth, options_.rep_id);
|
2018-05-23 00:26:18 +00:00
|
|
|
}
|
|
|
|
return InitializeMuxer();
|
|
|
|
}
|
|
|
|
|
2013-10-11 21:44:55 +00:00
|
|
|
} // namespace media
|
2016-05-20 21:19:33 +00:00
|
|
|
} // namespace shaka
|