2015-10-28 17:23:08 +00:00
|
|
|
// Copyright 2015 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
|
|
|
|
|
|
|
|
#include "packager/media/formats/webm/webm_muxer.h"
|
|
|
|
|
2016-04-08 18:41:17 +00:00
|
|
|
#include "packager/media/base/fourccs.h"
|
2015-10-28 17:23:08 +00:00
|
|
|
#include "packager/media/base/media_sample.h"
|
|
|
|
#include "packager/media/base/stream_info.h"
|
|
|
|
#include "packager/media/formats/webm/mkv_writer.h"
|
|
|
|
#include "packager/media/formats/webm/multi_segment_segmenter.h"
|
|
|
|
#include "packager/media/formats/webm/single_segment_segmenter.h"
|
|
|
|
#include "packager/media/formats/webm/two_pass_single_segment_segmenter.h"
|
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
namespace shaka {
|
2015-10-28 17:23:08 +00:00
|
|
|
namespace media {
|
|
|
|
namespace webm {
|
|
|
|
|
|
|
|
WebMMuxer::WebMMuxer(const MuxerOptions& options) : Muxer(options) {}
|
|
|
|
WebMMuxer::~WebMMuxer() {}
|
|
|
|
|
2017-02-21 18:36:50 +00:00
|
|
|
Status WebMMuxer::InitializeMuxer() {
|
2015-10-28 17:23:08 +00:00
|
|
|
CHECK_EQ(streams().size(), 1U);
|
|
|
|
|
2017-03-11 02:49:55 +00:00
|
|
|
if (streams()[0]->is_encrypted() &&
|
|
|
|
streams()[0]->encryption_config().protection_scheme != FOURCC_cenc) {
|
|
|
|
LOG(ERROR) << "WebM does not support protection scheme other than 'cenc'.";
|
|
|
|
return Status(error::INVALID_ARGUMENT,
|
2016-04-08 18:41:17 +00:00
|
|
|
"WebM does not support protection scheme other than 'cenc'.");
|
2016-03-17 17:03:19 +00:00
|
|
|
}
|
|
|
|
|
2017-01-07 02:40:37 +00:00
|
|
|
if (!options().segment_template.empty()) {
|
2015-10-28 17:23:08 +00:00
|
|
|
segmenter_.reset(new MultiSegmentSegmenter(options()));
|
|
|
|
} else {
|
|
|
|
segmenter_.reset(new TwoPassSingleSegmentSegmenter(options()));
|
|
|
|
}
|
|
|
|
|
2015-12-22 00:33:55 +00:00
|
|
|
Status initialized = segmenter_->Initialize(
|
2017-09-12 17:24:24 +00:00
|
|
|
*streams()[0], progress_listener(), muxer_listener());
|
2015-10-28 17:23:08 +00:00
|
|
|
if (!initialized.ok())
|
|
|
|
return initialized;
|
|
|
|
|
|
|
|
FireOnMediaStartEvent();
|
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status WebMMuxer::Finalize() {
|
2021-03-07 23:01:08 +00:00
|
|
|
if (!segmenter_)
|
|
|
|
return Status::OK;
|
2015-10-28 17:23:08 +00:00
|
|
|
Status segmenter_finalized = segmenter_->Finalize();
|
|
|
|
|
|
|
|
if (!segmenter_finalized.ok())
|
|
|
|
return segmenter_finalized;
|
|
|
|
|
|
|
|
FireOnMediaEndEvent();
|
|
|
|
LOG(INFO) << "WEBM file '" << options().output_file_name << "' finalized.";
|
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
2020-07-30 21:26:45 +00:00
|
|
|
Status WebMMuxer::AddMediaSample(size_t stream_id, const MediaSample& sample) {
|
2015-10-28 17:23:08 +00:00
|
|
|
DCHECK(segmenter_);
|
2017-03-03 00:10:30 +00:00
|
|
|
DCHECK_EQ(stream_id, 0u);
|
2018-07-03 23:52:05 +00:00
|
|
|
if (sample.pts() < 0) {
|
|
|
|
LOG(ERROR) << "Seeing negative timestamp " << sample.pts();
|
|
|
|
return Status(error::MUXER_FAILURE, "Unsupported negative timestamp.");
|
|
|
|
}
|
2015-10-28 17:23:08 +00:00
|
|
|
return segmenter_->AddSample(sample);
|
|
|
|
}
|
|
|
|
|
2017-03-03 00:10:30 +00:00
|
|
|
Status WebMMuxer::FinalizeSegment(size_t stream_id,
|
2017-09-12 17:24:24 +00:00
|
|
|
const SegmentInfo& segment_info) {
|
2017-02-24 01:17:47 +00:00
|
|
|
DCHECK(segmenter_);
|
2017-03-03 00:10:30 +00:00
|
|
|
DCHECK_EQ(stream_id, 0u);
|
2017-03-11 02:49:55 +00:00
|
|
|
|
2017-09-12 17:24:24 +00:00
|
|
|
if (segment_info.key_rotation_encryption_config) {
|
2017-03-11 02:49:55 +00:00
|
|
|
NOTIMPLEMENTED() << "Key rotation is not implemented for WebM.";
|
|
|
|
return Status(error::UNIMPLEMENTED,
|
|
|
|
"Key rotation is not implemented for WebM");
|
|
|
|
}
|
2017-09-12 17:24:24 +00:00
|
|
|
return segmenter_->FinalizeSegment(segment_info.start_timestamp,
|
|
|
|
segment_info.duration,
|
|
|
|
segment_info.is_subsegment);
|
2017-02-24 01:17:47 +00:00
|
|
|
}
|
|
|
|
|
2015-10-28 17:23:08 +00:00
|
|
|
void WebMMuxer::FireOnMediaStartEvent() {
|
|
|
|
if (!muxer_listener())
|
|
|
|
return;
|
|
|
|
|
|
|
|
DCHECK(!streams().empty()) << "Media started without a stream.";
|
|
|
|
|
2017-02-21 18:36:50 +00:00
|
|
|
const uint32_t timescale = streams().front()->time_scale();
|
|
|
|
muxer_listener()->OnMediaStart(options(), *streams().front(), timescale,
|
|
|
|
MuxerListener::kContainerWebM);
|
2015-10-28 17:23:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void WebMMuxer::FireOnMediaEndEvent() {
|
|
|
|
if (!muxer_listener())
|
|
|
|
return;
|
|
|
|
|
2017-05-01 23:17:09 +00:00
|
|
|
MuxerListener::MediaRanges media_range;
|
|
|
|
|
2016-04-15 23:00:27 +00:00
|
|
|
uint64_t init_range_start = 0;
|
|
|
|
uint64_t init_range_end = 0;
|
2015-10-28 17:23:08 +00:00
|
|
|
const bool has_init_range =
|
|
|
|
segmenter_->GetInitRangeStartAndEnd(&init_range_start, &init_range_end);
|
2017-05-01 23:17:09 +00:00
|
|
|
if (has_init_range) {
|
|
|
|
Range r;
|
|
|
|
r.start = init_range_start;
|
|
|
|
r.end = init_range_end;
|
|
|
|
media_range.init_range = r;
|
|
|
|
}
|
2015-10-28 17:23:08 +00:00
|
|
|
|
2016-04-15 23:00:27 +00:00
|
|
|
uint64_t index_range_start = 0;
|
|
|
|
uint64_t index_range_end = 0;
|
2015-10-28 17:23:08 +00:00
|
|
|
const bool has_index_range = segmenter_->GetIndexRangeStartAndEnd(
|
|
|
|
&index_range_start, &index_range_end);
|
2017-05-01 23:17:09 +00:00
|
|
|
if (has_index_range) {
|
|
|
|
Range r;
|
|
|
|
r.start = index_range_start;
|
|
|
|
r.end = index_range_end;
|
|
|
|
media_range.index_range = r;
|
|
|
|
}
|
|
|
|
|
|
|
|
media_range.subsegment_ranges = segmenter_->GetSegmentRanges();
|
2015-10-28 17:23:08 +00:00
|
|
|
|
2017-05-13 00:02:12 +00:00
|
|
|
const float duration_seconds = segmenter_->GetDurationInSeconds();
|
2017-07-18 19:23:42 +00:00
|
|
|
muxer_listener()->OnMediaEnd(media_range, duration_seconds);
|
2015-10-28 17:23:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace webm
|
|
|
|
} // namespace media
|
2016-05-20 21:19:33 +00:00
|
|
|
} // namespace shaka
|