Shaka Packager SDK
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 
26 namespace shaka {
27 namespace media {
28 namespace mp4 {
29 
30 MultiSegmentSegmenter::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 
43 MultiSegmentSegmenter::~MultiSegmentSegmenter() {}
44 
45 bool 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 
51 bool MultiSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
52  VLOG(1) << "MultiSegmentSegmenter does not have index range.";
53  return false;
54 }
55 
56 std::vector<Range> MultiSegmentSegmenter::GetSegmentRanges() {
57  VLOG(1) << "MultiSegmentSegmenter does not have media segment ranges.";
58  return std::vector<Range>();
59 }
60 
61 Status MultiSegmentSegmenter::DoInitialize() {
62  return WriteInitSegment();
63 }
64 
65 Status MultiSegmentSegmenter::DoFinalize() {
66  // Update init segment with media duration set.
67  RETURN_IF_ERROR(WriteInitSegment());
68  SetComplete();
69  return Status::OK;
70 }
71 
72 Status MultiSegmentSegmenter::DoFinalizeSegment(int64_t segment_number) {
73  return WriteSegment(segment_number);
74 }
75 
76 Status 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 
92 Status 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.
Definition: crypto_flags.cc:66