Shaka Packager SDK
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 
26 namespace shaka {
27 namespace media {
28 namespace mp4 {
29 
30 LowLatencySegmentSegmenter::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 
45 LowLatencySegmentSegmenter::~LowLatencySegmentSegmenter() {}
46 
47 bool 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 
53 bool LowLatencySegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
54  VLOG(1) << "LowLatencySegmentSegmenter does not have index range.";
55  return false;
56 }
57 
58 std::vector<Range> LowLatencySegmentSegmenter::GetSegmentRanges() {
59  VLOG(1) << "LowLatencySegmentSegmenter does not have media segment ranges.";
60  return std::vector<Range>();
61 }
62 
63 Status LowLatencySegmentSegmenter::DoInitialize() {
64  return WriteInitSegment();
65 }
66 
67 Status LowLatencySegmentSegmenter::DoFinalize() {
68  // Update init segment with media duration set.
69  RETURN_IF_ERROR(WriteInitSegment());
70  SetComplete();
71  return Status::OK;
72 }
73 
74 Status LowLatencySegmentSegmenter::DoFinalizeSegment(int64_t segment_number) {
75  return FinalizeSegment();
76 }
77 
78 Status LowLatencySegmentSegmenter::DoFinalizeChunk(int64_t segment_number) {
79  if (is_initial_chunk_in_seg_) {
80  return WriteInitialChunk(segment_number);
81  }
82  return WriteChunk();
83 }
84 
85 Status 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 
101 Status 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 
173 Status 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 
184 Status 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 
205 uint64_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.
Definition: crypto_flags.cc:66