Shaka Packager SDK
Loading...
Searching...
No Matches
multi_segment_segmenter.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/formats/mp4/multi_segment_segmenter.h>
8
9#include <algorithm>
10
11#include <absl/log/check.h>
12#include <absl/strings/numbers.h>
13#include <absl/strings/str_format.h>
14
15#include <packager/file.h>
16#include <packager/file/file_closer.h>
17#include <packager/macros/logging.h>
18#include <packager/macros/status.h>
19#include <packager/media/base/buffer_writer.h>
20#include <packager/media/base/muxer_options.h>
21#include <packager/media/base/muxer_util.h>
22#include <packager/media/event/muxer_listener.h>
23#include <packager/media/formats/mp4/box_definitions.h>
24#include <packager/media/formats/mp4/key_frame_info.h>
25
26namespace shaka {
27namespace media {
28namespace mp4 {
29
30MultiSegmentSegmenter::MultiSegmentSegmenter(const MuxerOptions& options,
31 std::unique_ptr<FileType> ftyp,
32 std::unique_ptr<Movie> moov)
33 : Segmenter(options, std::move(ftyp), std::move(moov)),
34 styp_(new SegmentType) {
35 // Use the same brands for styp as ftyp.
36 styp_->major_brand = Segmenter::ftyp()->major_brand;
37 styp_->compatible_brands = Segmenter::ftyp()->compatible_brands;
38 // Replace 'cmfc' with 'cmfs' for CMAF segments compatibility.
39 std::replace(styp_->compatible_brands.begin(), styp_->compatible_brands.end(),
40 FOURCC_cmfc, FOURCC_cmfs);
41}
42
43MultiSegmentSegmenter::~MultiSegmentSegmenter() {}
44
45bool MultiSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) {
46 VLOG(1) << "MultiSegmentSegmenter outputs init segment: "
47 << options().output_file_name;
48 return false;
49}
50
51bool MultiSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
52 VLOG(1) << "MultiSegmentSegmenter does not have index range.";
53 return false;
54}
55
56std::vector<Range> MultiSegmentSegmenter::GetSegmentRanges() {
57 VLOG(1) << "MultiSegmentSegmenter does not have media segment ranges.";
58 return std::vector<Range>();
59}
60
61Status MultiSegmentSegmenter::DoInitialize() {
62 return WriteInitSegment();
63}
64
65Status MultiSegmentSegmenter::DoFinalize() {
66 // Update init segment with media duration set.
67 RETURN_IF_ERROR(WriteInitSegment());
68 SetComplete();
69 return Status::OK;
70}
71
72Status MultiSegmentSegmenter::DoFinalizeSegment(int64_t segment_number) {
73 return WriteSegment(segment_number);
74}
75
76Status MultiSegmentSegmenter::WriteInitSegment() {
77 DCHECK(ftyp());
78 DCHECK(moov());
79 // Generate the output file with init segment.
80 std::unique_ptr<File, FileCloser> file(
81 File::Open(options().output_file_name.c_str(), "w"));
82 if (!file) {
83 return Status(error::FILE_FAILURE,
84 "Cannot open file for write " + options().output_file_name);
85 }
86 std::unique_ptr<BufferWriter> buffer(new BufferWriter);
87 ftyp()->Write(buffer.get());
88 moov()->Write(buffer.get());
89 return buffer->WriteToFile(file.get());
90}
91
92Status MultiSegmentSegmenter::WriteSegment(int64_t segment_number) {
93 DCHECK(sidx());
94 DCHECK(fragment_buffer());
95 DCHECK(styp_);
96
97 DCHECK(!sidx()->references.empty());
98 // earliest_presentation_time is the earliest presentation time of any access
99 // unit in the reference stream in the first subsegment.
100 sidx()->earliest_presentation_time =
101 sidx()->references[0].earliest_presentation_time;
102
103 std::unique_ptr<BufferWriter> buffer(new BufferWriter());
104 std::unique_ptr<File, FileCloser> file;
105 std::string file_name;
106 if (options().segment_template.empty()) {
107 // Append the segment to output file if segment template is not specified.
108 file_name = options().output_file_name.c_str();
109 file.reset(File::Open(file_name.c_str(), "a"));
110 if (!file) {
111 return Status(error::FILE_FAILURE, "Cannot open file for append " +
112 options().output_file_name);
113 }
114 } else {
115 file_name = GetSegmentName(options().segment_template,
116 sidx()->earliest_presentation_time,
117 segment_number, options().bandwidth);
118 file.reset(File::Open(file_name.c_str(), "w"));
119 if (!file) {
120 return Status(error::FILE_FAILURE,
121 "Cannot open file for write " + file_name);
122 }
123 styp_->Write(buffer.get());
124 }
125
126 if (options().mp4_params.generate_sidx_in_media_segments)
127 sidx()->Write(buffer.get());
128
129 const size_t segment_header_size = buffer->Size();
130 const size_t segment_size = segment_header_size + fragment_buffer()->Size();
131 DCHECK_NE(segment_size, 0u);
132
133 RETURN_IF_ERROR(buffer->WriteToFile(file.get()));
134 if (muxer_listener()) {
135 for (const KeyFrameInfo& key_frame_info : key_frame_infos()) {
136 muxer_listener()->OnKeyFrame(
137 key_frame_info.timestamp,
138 segment_header_size + key_frame_info.start_byte_offset,
139 key_frame_info.size);
140 }
141 }
142 RETURN_IF_ERROR(fragment_buffer()->WriteToFile(file.get()));
143
144 // Close the file, which also does flushing, to make sure the file is written
145 // before manifest is updated.
146 if (!file.release()->Close()) {
147 return Status(
148 error::FILE_FAILURE,
149 "Cannot close file " + file_name +
150 ", possibly file permission issue or running out of disk space.");
151 }
152
153 int64_t segment_duration = 0;
154 // ISO/IEC 23009-1:2012: the value shall be identical to sum of the the
155 // values of all Subsegment_duration fields in the first ‘sidx’ box.
156 for (size_t i = 0; i < sidx()->references.size(); ++i)
157 segment_duration += sidx()->references[i].subsegment_duration;
158
159 UpdateProgress(segment_duration);
160 if (muxer_listener()) {
161 muxer_listener()->OnSampleDurationReady(sample_duration());
162 muxer_listener()->OnNewSegment(
163 file_name, sidx()->earliest_presentation_time, segment_duration,
164 segment_size, segment_number);
165 }
166
167 return Status::OK;
168}
169
170} // namespace mp4
171} // namespace media
172} // namespace shaka
All the methods that are virtual are virtual for mocking.