Shaka Packager SDK
Loading...
Searching...
No Matches
low_latency_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/low_latency_segment_segmenter.h>
8
9#include <algorithm>
10
11#include <absl/log/check.h>
12
13#include <packager/file.h>
14#include <packager/file/file_closer.h>
15#include <packager/macros/logging.h>
16#include <packager/macros/status.h>
17#include <packager/media/base/buffer_writer.h>
18#include <packager/media/base/media_handler.h>
19#include <packager/media/base/muxer_options.h>
20#include <packager/media/base/muxer_util.h>
21#include <packager/media/event/muxer_listener.h>
22#include <packager/media/formats/mp4/box_definitions.h>
23#include <packager/media/formats/mp4/fragmenter.h>
24#include <packager/media/formats/mp4/key_frame_info.h>
25
26namespace shaka {
27namespace media {
28namespace mp4 {
29
30LowLatencySegmentSegmenter::LowLatencySegmentSegmenter(
31 const MuxerOptions& options,
32 std::unique_ptr<FileType> ftyp,
33 std::unique_ptr<Movie> moov)
34 : Segmenter(options, std::move(ftyp), std::move(moov)),
35 styp_(new SegmentType),
36 num_segments_(0) {
37 // Use the same brands for styp as ftyp.
38 styp_->major_brand = Segmenter::ftyp()->major_brand;
39 styp_->compatible_brands = Segmenter::ftyp()->compatible_brands;
40 // Replace 'cmfc' with 'cmfs' for CMAF segments compatibility.
41 std::replace(styp_->compatible_brands.begin(), styp_->compatible_brands.end(),
42 FOURCC_cmfc, FOURCC_cmfs);
43}
44
45LowLatencySegmentSegmenter::~LowLatencySegmentSegmenter() {}
46
47bool LowLatencySegmentSegmenter::GetInitRange(size_t* offset, size_t* size) {
48 VLOG(1) << "LowLatencySegmentSegmenter outputs init segment: "
49 << options().output_file_name;
50 return false;
51}
52
53bool LowLatencySegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
54 VLOG(1) << "LowLatencySegmentSegmenter does not have index range.";
55 return false;
56}
57
58std::vector<Range> LowLatencySegmentSegmenter::GetSegmentRanges() {
59 VLOG(1) << "LowLatencySegmentSegmenter does not have media segment ranges.";
60 return std::vector<Range>();
61}
62
63Status LowLatencySegmentSegmenter::DoInitialize() {
64 return WriteInitSegment();
65}
66
67Status LowLatencySegmentSegmenter::DoFinalize() {
68 // Update init segment with media duration set.
69 RETURN_IF_ERROR(WriteInitSegment());
70 SetComplete();
71 return Status::OK;
72}
73
74Status LowLatencySegmentSegmenter::DoFinalizeSegment(int64_t segment_number) {
75 return FinalizeSegment();
76}
77
78Status LowLatencySegmentSegmenter::DoFinalizeChunk(int64_t segment_number) {
79 if (is_initial_chunk_in_seg_) {
80 return WriteInitialChunk(segment_number);
81 }
82 return WriteChunk();
83}
84
85Status LowLatencySegmentSegmenter::WriteInitSegment() {
86 DCHECK(ftyp());
87 DCHECK(moov());
88 // Generate the output file with init segment.
89 std::unique_ptr<File, FileCloser> file(
90 File::Open(options().output_file_name.c_str(), "w"));
91 if (!file) {
92 return Status(error::FILE_FAILURE,
93 "Cannot open file for write " + options().output_file_name);
94 }
95 std::unique_ptr<BufferWriter> buffer(new BufferWriter);
96 ftyp()->Write(buffer.get());
97 moov()->Write(buffer.get());
98 return buffer->WriteToFile(file.get());
99}
100
101Status LowLatencySegmentSegmenter::WriteInitialChunk(int64_t segment_number) {
102 DCHECK(sidx());
103 DCHECK(fragment_buffer());
104 DCHECK(styp_);
105
106 DCHECK(!sidx()->references.empty());
107 // earliest_presentation_time is the earliest presentation time of any access
108 // unit in the reference stream in the first subsegment.
109 sidx()->earliest_presentation_time =
110 sidx()->references[0].earliest_presentation_time;
111
112 if (options().segment_template.empty()) {
113 // Append the segment to output file if segment template is not specified.
114 file_name_ = options().output_file_name.c_str();
115 } else {
116 file_name_ = GetSegmentName(options().segment_template,
117 sidx()->earliest_presentation_time,
118 num_segments_, options().bandwidth);
119 }
120
121 // Create the segment file
122 segment_file_.reset(File::Open(file_name_.c_str(), "a"));
123 if (!segment_file_) {
124 return Status(error::FILE_FAILURE,
125 "Cannot open segment file: " + file_name_);
126 }
127
128 std::unique_ptr<BufferWriter> buffer(new BufferWriter());
129
130 // Write the styp header to the beginning of the segment.
131 styp_->Write(buffer.get());
132
133 const size_t segment_header_size = buffer->Size();
134 segment_size_ = segment_header_size + fragment_buffer()->Size();
135 DCHECK_NE(segment_size_, 0u);
136
137 RETURN_IF_ERROR(buffer->WriteToFile(segment_file_.get()));
138 if (muxer_listener()) {
139 for (const KeyFrameInfo& key_frame_info : key_frame_infos()) {
140 muxer_listener()->OnKeyFrame(
141 key_frame_info.timestamp,
142 segment_header_size + key_frame_info.start_byte_offset,
143 key_frame_info.size);
144 }
145 }
146
147 // Write the chunk data to the file
148 RETURN_IF_ERROR(fragment_buffer()->WriteToFile(segment_file_.get()));
149
150 uint64_t segment_duration = GetSegmentDuration();
151 UpdateProgress(segment_duration);
152
153 if (muxer_listener()) {
154 if (!ll_dash_mpd_values_initialized_) {
155 // Set necessary values for LL-DASH mpd after the first chunk has been
156 // processed.
157 muxer_listener()->OnSampleDurationReady(sample_duration());
158 muxer_listener()->OnAvailabilityOffsetReady();
159 muxer_listener()->OnSegmentDurationReady();
160 ll_dash_mpd_values_initialized_ = true;
161 }
162 // Add the current segment in the manifest.
163 // Following chunks will be appended to the open segment file.
164 muxer_listener()->OnNewSegment(
165 file_name_, sidx()->earliest_presentation_time, segment_duration,
166 segment_size_, segment_number);
167 is_initial_chunk_in_seg_ = false;
168 }
169
170 return Status::OK;
171}
172
173Status LowLatencySegmentSegmenter::WriteChunk() {
174 DCHECK(fragment_buffer());
175
176 // Write the chunk data to the file
177 RETURN_IF_ERROR(fragment_buffer()->WriteToFile(segment_file_.get()));
178
179 UpdateProgress(GetSegmentDuration());
180
181 return Status::OK;
182}
183
184Status LowLatencySegmentSegmenter::FinalizeSegment() {
185 if (muxer_listener()) {
186 muxer_listener()->OnCompletedSegment(GetSegmentDuration(), segment_size_);
187 }
188 // Close the file now that the final chunk has been written
189 if (!segment_file_.release()->Close()) {
190 return Status(
191 error::FILE_FAILURE,
192 "Cannot close file " + file_name_ +
193 ", possibly file permission issue or running out of disk space.");
194 }
195
196 // Current segment is complete. Reset state in preparation for the next
197 // segment.
198 is_initial_chunk_in_seg_ = true;
199 segment_size_ = 0u;
200 num_segments_++;
201
202 return Status::OK;
203}
204
205uint64_t LowLatencySegmentSegmenter::GetSegmentDuration() {
206 DCHECK(sidx());
207
208 uint64_t segment_duration = 0;
209 // ISO/IEC 23009-1:2012: the value shall be identical to sum of the the
210 // values of all Subsegment_duration fields in the first ‘sidx’ box.
211 for (size_t i = 0; i < sidx()->references.size(); ++i)
212 segment_duration += sidx()->references[i].subsegment_duration;
213
214 return segment_duration;
215}
216
217} // namespace mp4
218} // namespace media
219} // namespace shaka
All the methods that are virtual are virtual for mocking.