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