2023-12-01 17:32:19 +00:00
|
|
|
|
// Copyright 2014 Google LLC. All rights reserved.
|
2014-02-14 23:21:05 +00:00
|
|
|
|
//
|
|
|
|
|
// 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-11-12 20:37:58 +00:00
|
|
|
|
|
2023-12-01 17:32:19 +00:00
|
|
|
|
#include <packager/media/formats/mp4/multi_segment_segmenter.h>
|
2013-11-12 20:37:58 +00:00
|
|
|
|
|
2016-08-19 22:32:27 +00:00
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
2023-12-01 17:32:19 +00:00
|
|
|
|
#include <absl/log/check.h>
|
|
|
|
|
#include <absl/strings/numbers.h>
|
|
|
|
|
#include <absl/strings/str_format.h>
|
|
|
|
|
|
|
|
|
|
#include <packager/file.h>
|
|
|
|
|
#include <packager/file/file_closer.h>
|
|
|
|
|
#include <packager/macros/logging.h>
|
|
|
|
|
#include <packager/macros/status.h>
|
|
|
|
|
#include <packager/media/base/buffer_writer.h>
|
|
|
|
|
#include <packager/media/base/muxer_options.h>
|
|
|
|
|
#include <packager/media/base/muxer_util.h>
|
|
|
|
|
#include <packager/media/event/muxer_listener.h>
|
|
|
|
|
#include <packager/media/formats/mp4/box_definitions.h>
|
|
|
|
|
#include <packager/media/formats/mp4/key_frame_info.h>
|
2013-11-12 20:37:58 +00:00
|
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
|
namespace shaka {
|
2013-11-12 20:37:58 +00:00
|
|
|
|
namespace media {
|
|
|
|
|
namespace mp4 {
|
|
|
|
|
|
2014-04-08 20:21:07 +00:00
|
|
|
|
MultiSegmentSegmenter::MultiSegmentSegmenter(const MuxerOptions& options,
|
2016-08-17 17:41:40 +00:00
|
|
|
|
std::unique_ptr<FileType> ftyp,
|
|
|
|
|
std::unique_ptr<Movie> moov)
|
|
|
|
|
: Segmenter(options, std::move(ftyp), std::move(moov)),
|
2024-05-02 20:25:49 +00:00
|
|
|
|
styp_(new SegmentType) {
|
2013-11-12 20:37:58 +00:00
|
|
|
|
// Use the same brands for styp as ftyp.
|
2014-04-08 20:21:07 +00:00
|
|
|
|
styp_->major_brand = Segmenter::ftyp()->major_brand;
|
|
|
|
|
styp_->compatible_brands = Segmenter::ftyp()->compatible_brands;
|
2017-03-15 19:42:00 +00:00
|
|
|
|
// Replace 'cmfc' with 'cmfs' for CMAF segments compatibility.
|
|
|
|
|
std::replace(styp_->compatible_brands.begin(), styp_->compatible_brands.end(),
|
|
|
|
|
FOURCC_cmfc, FOURCC_cmfs);
|
2013-11-12 20:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-08 20:21:07 +00:00
|
|
|
|
MultiSegmentSegmenter::~MultiSegmentSegmenter() {}
|
2013-11-12 20:37:58 +00:00
|
|
|
|
|
2014-04-18 22:00:30 +00:00
|
|
|
|
bool MultiSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) {
|
2017-05-01 23:17:09 +00:00
|
|
|
|
VLOG(1) << "MultiSegmentSegmenter outputs init segment: "
|
|
|
|
|
<< options().output_file_name;
|
2014-04-18 22:00:30 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MultiSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
|
2017-05-01 23:17:09 +00:00
|
|
|
|
VLOG(1) << "MultiSegmentSegmenter does not have index range.";
|
2014-04-18 22:00:30 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-11-12 20:37:58 +00:00
|
|
|
|
|
2017-05-01 23:17:09 +00:00
|
|
|
|
std::vector<Range> MultiSegmentSegmenter::GetSegmentRanges() {
|
|
|
|
|
VLOG(1) << "MultiSegmentSegmenter does not have media segment ranges.";
|
|
|
|
|
return std::vector<Range>();
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-18 22:00:30 +00:00
|
|
|
|
Status MultiSegmentSegmenter::DoInitialize() {
|
2018-03-05 17:44:22 +00:00
|
|
|
|
return WriteInitSegment();
|
2013-11-12 20:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-18 22:00:30 +00:00
|
|
|
|
Status MultiSegmentSegmenter::DoFinalize() {
|
2018-03-05 17:44:22 +00:00
|
|
|
|
// Update init segment with media duration set.
|
2018-05-01 20:16:12 +00:00
|
|
|
|
RETURN_IF_ERROR(WriteInitSegment());
|
|
|
|
|
SetComplete();
|
|
|
|
|
return Status::OK;
|
2013-12-12 23:49:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-02 20:25:49 +00:00
|
|
|
|
Status MultiSegmentSegmenter::DoFinalizeSegment(int64_t segment_number) {
|
|
|
|
|
return WriteSegment(segment_number);
|
2013-11-12 20:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-05 17:44:22 +00:00
|
|
|
|
Status MultiSegmentSegmenter::WriteInitSegment() {
|
|
|
|
|
DCHECK(ftyp());
|
|
|
|
|
DCHECK(moov());
|
|
|
|
|
// Generate the output file with init segment.
|
|
|
|
|
std::unique_ptr<File, FileCloser> file(
|
|
|
|
|
File::Open(options().output_file_name.c_str(), "w"));
|
|
|
|
|
if (!file) {
|
|
|
|
|
return Status(error::FILE_FAILURE,
|
|
|
|
|
"Cannot open file for write " + options().output_file_name);
|
|
|
|
|
}
|
|
|
|
|
std::unique_ptr<BufferWriter> buffer(new BufferWriter);
|
|
|
|
|
ftyp()->Write(buffer.get());
|
|
|
|
|
moov()->Write(buffer.get());
|
|
|
|
|
return buffer->WriteToFile(file.get());
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-02 20:25:49 +00:00
|
|
|
|
Status MultiSegmentSegmenter::WriteSegment(int64_t segment_number) {
|
2014-01-23 00:13:41 +00:00
|
|
|
|
DCHECK(sidx());
|
|
|
|
|
DCHECK(fragment_buffer());
|
|
|
|
|
DCHECK(styp_);
|
2013-11-12 20:37:58 +00:00
|
|
|
|
|
2018-09-12 21:47:20 +00:00
|
|
|
|
DCHECK(!sidx()->references.empty());
|
|
|
|
|
// earliest_presentation_time is the earliest presentation time of any access
|
|
|
|
|
// unit in the reference stream in the first subsegment.
|
|
|
|
|
sidx()->earliest_presentation_time =
|
|
|
|
|
sidx()->references[0].earliest_presentation_time;
|
|
|
|
|
|
2016-08-17 17:41:40 +00:00
|
|
|
|
std::unique_ptr<BufferWriter> buffer(new BufferWriter());
|
2018-03-05 17:44:22 +00:00
|
|
|
|
std::unique_ptr<File, FileCloser> file;
|
2013-11-12 20:37:58 +00:00
|
|
|
|
std::string file_name;
|
|
|
|
|
if (options().segment_template.empty()) {
|
|
|
|
|
// Append the segment to output file if segment template is not specified.
|
|
|
|
|
file_name = options().output_file_name.c_str();
|
2018-03-05 17:44:22 +00:00
|
|
|
|
file.reset(File::Open(file_name.c_str(), "a"));
|
|
|
|
|
if (!file) {
|
|
|
|
|
return Status(error::FILE_FAILURE, "Cannot open file for append " +
|
|
|
|
|
options().output_file_name);
|
2013-11-12 20:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2016-03-28 08:23:20 +00:00
|
|
|
|
file_name = GetSegmentName(options().segment_template,
|
|
|
|
|
sidx()->earliest_presentation_time,
|
2024-05-02 20:25:49 +00:00
|
|
|
|
segment_number, options().bandwidth);
|
2018-03-05 17:44:22 +00:00
|
|
|
|
file.reset(File::Open(file_name.c_str(), "w"));
|
|
|
|
|
if (!file) {
|
2013-11-12 20:37:58 +00:00
|
|
|
|
return Status(error::FILE_FAILURE,
|
|
|
|
|
"Cannot open file for write " + file_name);
|
|
|
|
|
}
|
|
|
|
|
styp_->Write(buffer.get());
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-12 21:47:20 +00:00
|
|
|
|
if (options().mp4_params.generate_sidx_in_media_segments)
|
2013-11-12 20:37:58 +00:00
|
|
|
|
sidx()->Write(buffer.get());
|
|
|
|
|
|
2018-02-01 20:25:07 +00:00
|
|
|
|
const size_t segment_header_size = buffer->Size();
|
|
|
|
|
const size_t segment_size = segment_header_size + fragment_buffer()->Size();
|
2014-05-22 18:51:55 +00:00
|
|
|
|
DCHECK_NE(segment_size, 0u);
|
|
|
|
|
|
2018-05-01 20:16:12 +00:00
|
|
|
|
RETURN_IF_ERROR(buffer->WriteToFile(file.get()));
|
|
|
|
|
if (muxer_listener()) {
|
|
|
|
|
for (const KeyFrameInfo& key_frame_info : key_frame_infos()) {
|
|
|
|
|
muxer_listener()->OnKeyFrame(
|
|
|
|
|
key_frame_info.timestamp,
|
|
|
|
|
segment_header_size + key_frame_info.start_byte_offset,
|
|
|
|
|
key_frame_info.size);
|
2018-02-01 20:25:07 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-01 20:16:12 +00:00
|
|
|
|
RETURN_IF_ERROR(fragment_buffer()->WriteToFile(file.get()));
|
|
|
|
|
|
|
|
|
|
// Close the file, which also does flushing, to make sure the file is written
|
|
|
|
|
// before manifest is updated.
|
|
|
|
|
if (!file.release()->Close()) {
|
|
|
|
|
return Status(
|
|
|
|
|
error::FILE_FAILURE,
|
|
|
|
|
"Cannot close file " + file_name +
|
|
|
|
|
", possibly file permission issue or running out of disk space.");
|
|
|
|
|
}
|
2015-05-11 21:07:10 +00:00
|
|
|
|
|
2021-08-04 18:56:44 +00:00
|
|
|
|
int64_t segment_duration = 0;
|
2015-05-11 21:07:10 +00:00
|
|
|
|
// ISO/IEC 23009-1:2012: the value shall be identical to sum of the the
|
|
|
|
|
// values of all Subsegment_duration fields in the first ‘sidx’ box.
|
|
|
|
|
for (size_t i = 0; i < sidx()->references.size(); ++i)
|
|
|
|
|
segment_duration += sidx()->references[i].subsegment_duration;
|
|
|
|
|
|
|
|
|
|
UpdateProgress(segment_duration);
|
|
|
|
|
if (muxer_listener()) {
|
2015-06-15 21:12:42 +00:00
|
|
|
|
muxer_listener()->OnSampleDurationReady(sample_duration());
|
2024-05-02 20:25:49 +00:00
|
|
|
|
muxer_listener()->OnNewSegment(
|
|
|
|
|
file_name, sidx()->earliest_presentation_time, segment_duration,
|
|
|
|
|
segment_size, segment_number);
|
2014-05-22 18:51:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-11 21:07:10 +00:00
|
|
|
|
return Status::OK;
|
2013-11-12 20:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace mp4
|
|
|
|
|
} // namespace media
|
2016-05-20 21:19:33 +00:00
|
|
|
|
} // namespace shaka
|