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/single_segment_segmenter.h"
|
|
|
|
|
|
|
|
#include "packager/media/base/muxer_options.h"
|
2018-01-29 19:04:09 +00:00
|
|
|
#include "packager/media/event/muxer_listener.h"
|
2015-10-28 17:23:08 +00:00
|
|
|
#include "packager/third_party/libwebm/src/mkvmuxer.hpp"
|
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
namespace shaka {
|
2015-10-28 17:23:08 +00:00
|
|
|
namespace media {
|
|
|
|
namespace webm {
|
|
|
|
|
|
|
|
SingleSegmentSegmenter::SingleSegmentSegmenter(const MuxerOptions& options)
|
|
|
|
: Segmenter(options), init_end_(0), index_start_(0) {}
|
|
|
|
|
|
|
|
SingleSegmentSegmenter::~SingleSegmentSegmenter() {}
|
|
|
|
|
2021-08-04 18:56:44 +00:00
|
|
|
Status SingleSegmentSegmenter::FinalizeSegment(int64_t start_timestamp,
|
|
|
|
int64_t duration_timestamp,
|
2017-02-24 01:17:47 +00:00
|
|
|
bool is_subsegment) {
|
2017-05-13 00:02:12 +00:00
|
|
|
Status status = Segmenter::FinalizeSegment(start_timestamp,
|
|
|
|
duration_timestamp, is_subsegment);
|
2017-02-24 01:17:47 +00:00
|
|
|
if (!status.ok())
|
|
|
|
return status;
|
|
|
|
// No-op for subsegment in single segment mode.
|
|
|
|
if (is_subsegment)
|
|
|
|
return Status::OK;
|
|
|
|
CHECK(cluster());
|
|
|
|
if (!cluster()->Finalize())
|
|
|
|
return Status(error::FILE_FAILURE, "Error finalizing cluster.");
|
2018-01-29 19:04:09 +00:00
|
|
|
if (muxer_listener()) {
|
|
|
|
const uint64_t size = cluster()->Size();
|
|
|
|
muxer_listener()->OnNewSegment(options().output_file_name, start_timestamp,
|
|
|
|
duration_timestamp, size);
|
|
|
|
}
|
2017-02-24 01:17:47 +00:00
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SingleSegmentSegmenter::GetInitRangeStartAndEnd(uint64_t* start,
|
|
|
|
uint64_t* end) {
|
|
|
|
*start = 0;
|
|
|
|
*end = init_end_;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SingleSegmentSegmenter::GetIndexRangeStartAndEnd(uint64_t* start,
|
|
|
|
uint64_t* end) {
|
|
|
|
*start = index_start_;
|
|
|
|
*end = index_end_;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-01 23:17:09 +00:00
|
|
|
std::vector<Range> SingleSegmentSegmenter::GetSegmentRanges() {
|
|
|
|
std::vector<Range> ranges;
|
|
|
|
if (cues()->cue_entries_size() == 0) {
|
|
|
|
return ranges;
|
|
|
|
}
|
|
|
|
for (int32_t i = 0; i < cues()->cue_entries_size() - 1; ++i) {
|
|
|
|
const mkvmuxer::CuePoint* cue_point = cues()->GetCueByIndex(i);
|
|
|
|
Range r;
|
2017-07-18 19:23:42 +00:00
|
|
|
// Cue point cluster position is relative to segment payload pos.
|
|
|
|
r.start = segment_payload_pos() + cue_point->cluster_pos();
|
|
|
|
r.end =
|
|
|
|
segment_payload_pos() + cues()->GetCueByIndex(i + 1)->cluster_pos() - 1;
|
2017-05-01 23:17:09 +00:00
|
|
|
ranges.push_back(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
Range last_range;
|
|
|
|
const mkvmuxer::CuePoint* last_cue_point =
|
|
|
|
cues()->GetCueByIndex(cues()->cue_entries_size() - 1);
|
2017-07-18 19:23:42 +00:00
|
|
|
last_range.start = segment_payload_pos() + last_cue_point->cluster_pos();
|
2017-05-01 23:17:09 +00:00
|
|
|
last_range.end = last_range.start + cluster()->Size() - 1;
|
|
|
|
ranges.push_back(last_range);
|
|
|
|
return ranges;
|
|
|
|
}
|
|
|
|
|
2017-03-16 20:56:11 +00:00
|
|
|
Status SingleSegmentSegmenter::DoInitialize() {
|
|
|
|
if (!writer_) {
|
|
|
|
std::unique_ptr<MkvWriter> writer(new MkvWriter);
|
|
|
|
Status status = writer->Open(options().output_file_name);
|
|
|
|
if (!status.ok())
|
|
|
|
return status;
|
|
|
|
writer_ = std::move(writer);
|
|
|
|
}
|
|
|
|
|
2015-10-28 17:23:08 +00:00
|
|
|
Status ret = WriteSegmentHeader(0, writer_.get());
|
|
|
|
init_end_ = writer_->Position() - 1;
|
2016-04-04 18:28:08 +00:00
|
|
|
seek_head()->set_cluster_pos(init_end_ + 1 - segment_payload_pos());
|
2015-10-28 17:23:08 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status SingleSegmentSegmenter::DoFinalize() {
|
|
|
|
// Write the Cues to the end of the file.
|
|
|
|
index_start_ = writer_->Position();
|
2016-04-04 18:28:08 +00:00
|
|
|
seek_head()->set_cues_pos(index_start_ - segment_payload_pos());
|
2015-10-28 17:23:08 +00:00
|
|
|
if (!cues()->Write(writer_.get()))
|
|
|
|
return Status(error::FILE_FAILURE, "Error writing Cues data.");
|
|
|
|
|
2016-04-15 23:00:27 +00:00
|
|
|
// The WebM index is at the end of the file.
|
|
|
|
index_end_ = writer_->Position() - 1;
|
2015-10-28 17:23:08 +00:00
|
|
|
writer_->Position(0);
|
|
|
|
|
2016-04-15 23:00:27 +00:00
|
|
|
Status status = WriteSegmentHeader(index_end_ + 1, writer_.get());
|
|
|
|
status.Update(writer_->Close());
|
2015-10-28 17:23:08 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2021-08-04 18:56:44 +00:00
|
|
|
Status SingleSegmentSegmenter::NewSegment(int64_t start_timestamp,
|
2017-02-24 01:17:47 +00:00
|
|
|
bool is_subsegment) {
|
|
|
|
// No-op for subsegment in single segment mode.
|
|
|
|
if (is_subsegment)
|
|
|
|
return Status::OK;
|
2015-10-28 17:23:08 +00:00
|
|
|
// Create a new Cue point.
|
|
|
|
uint64_t position = writer_->Position();
|
2021-08-04 18:56:44 +00:00
|
|
|
int64_t start_timecode = FromBmffTimestamp(start_timestamp);
|
2015-10-28 17:23:08 +00:00
|
|
|
|
|
|
|
mkvmuxer::CuePoint* cue_point = new mkvmuxer::CuePoint;
|
2017-05-13 00:02:12 +00:00
|
|
|
cue_point->set_time(start_timecode);
|
2015-10-28 17:23:08 +00:00
|
|
|
cue_point->set_track(track_id());
|
|
|
|
cue_point->set_cluster_pos(position - segment_payload_pos());
|
|
|
|
if (!cues()->AddCue(cue_point))
|
|
|
|
return Status(error::INTERNAL_ERROR, "Error adding CuePoint.");
|
|
|
|
|
2017-05-13 00:02:12 +00:00
|
|
|
return SetCluster(start_timecode, position, writer_.get());
|
2015-10-28 17:23:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace webm
|
|
|
|
} // namespace media
|
2016-05-20 21:19:33 +00:00
|
|
|
} // namespace shaka
|