DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs
multi_segment_segmenter.cc
1 // Copyright 2014 Google Inc. 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 "packager/base/strings/string_number_conversions.h"
10 #include "packager/base/strings/string_util.h"
11 #include "packager/media/base/buffer_writer.h"
12 #include "packager/media/base/media_stream.h"
13 #include "packager/media/base/muxer_options.h"
14 #include "packager/media/base/muxer_util.h"
15 #include "packager/media/event/muxer_listener.h"
16 #include "packager/media/file/file.h"
17 #include "packager/media/formats/mp4/box_definitions.h"
18 
19 namespace edash_packager {
20 namespace media {
21 namespace mp4 {
22 
23 MultiSegmentSegmenter::MultiSegmentSegmenter(const MuxerOptions& options,
24  scoped_ptr<FileType> ftyp,
25  scoped_ptr<Movie> moov)
26  : Segmenter(options, ftyp.Pass(), moov.Pass()),
27  styp_(new SegmentType),
28  num_segments_(0) {
29  // Use the same brands for styp as ftyp.
30  styp_->major_brand = Segmenter::ftyp()->major_brand;
31  styp_->compatible_brands = Segmenter::ftyp()->compatible_brands;
32 }
33 
34 MultiSegmentSegmenter::~MultiSegmentSegmenter() {}
35 
36 bool MultiSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) {
37  DLOG(INFO) << "MultiSegmentSegmenter outputs init segment: "
38  << options().output_file_name;
39  return false;
40 }
41 
42 bool MultiSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
43  DLOG(INFO) << "MultiSegmentSegmenter does not have index range.";
44  return false;
45 }
46 
47 Status MultiSegmentSegmenter::DoInitialize() {
48  DCHECK(ftyp());
49  DCHECK(moov());
50  // Generate the output file with init segment.
51  File* file = File::Open(options().output_file_name.c_str(), "w");
52  if (file == NULL) {
53  return Status(error::FILE_FAILURE,
54  "Cannot open file for write " + options().output_file_name);
55  }
56  scoped_ptr<BufferWriter> buffer(new BufferWriter);
57  ftyp()->Write(buffer.get());
58  moov()->Write(buffer.get());
59  Status status = buffer->WriteToFile(file);
60  if (!file->Close()) {
61  LOG(WARNING) << "Failed to close the file properly: "
62  << options().output_file_name;
63  }
64  return status;
65 }
66 
67 Status MultiSegmentSegmenter::DoFinalize() {
68  SetComplete();
69  return Status::OK;
70 }
71 
72 Status MultiSegmentSegmenter::DoFinalizeSegment() {
73  DCHECK(sidx());
74  // earliest_presentation_time is the earliest presentation time of any
75  // access unit in the reference stream in the first subsegment.
76  // It will be re-calculated later when subsegments are finalized.
77  sidx()->earliest_presentation_time =
78  sidx()->references[0].earliest_presentation_time;
79 
80  if (options().num_subsegments_per_sidx <= 0)
81  return WriteSegment();
82 
83  // sidx() contains pre-generated segment references with one reference per
84  // fragment. Calculate |num_fragments_per_subsegment| and combine
85  // pre-generated references into final subsegment references.
86  uint32_t num_fragments = sidx()->references.size();
87  uint32_t num_fragments_per_subsegment =
88  (num_fragments - 1) / options().num_subsegments_per_sidx + 1;
89  if (num_fragments_per_subsegment <= 1)
90  return WriteSegment();
91 
92  uint32_t frag_index = 0;
93  uint32_t subseg_index = 0;
94  std::vector<SegmentReference>& refs = sidx()->references;
95  uint64_t first_sap_time =
96  refs[0].sap_delta_time + refs[0].earliest_presentation_time;
97  for (uint32_t i = 1; i < num_fragments; ++i) {
98  refs[subseg_index].referenced_size += refs[i].referenced_size;
99  refs[subseg_index].subsegment_duration += refs[i].subsegment_duration;
100  refs[subseg_index].earliest_presentation_time =
101  std::min(refs[subseg_index].earliest_presentation_time,
102  refs[i].earliest_presentation_time);
103  if (refs[subseg_index].sap_type == SegmentReference::TypeUnknown &&
104  refs[i].sap_type != SegmentReference::TypeUnknown) {
105  refs[subseg_index].sap_type = refs[i].sap_type;
106  first_sap_time =
107  refs[i].sap_delta_time + refs[i].earliest_presentation_time;
108  }
109  if (++frag_index >= num_fragments_per_subsegment) {
110  // Calculate sap delta time w.r.t. sidx_->earliest_presentation_time.
111  if (refs[subseg_index].sap_type != SegmentReference::TypeUnknown) {
112  refs[subseg_index].sap_delta_time =
113  first_sap_time - refs[subseg_index].earliest_presentation_time;
114  }
115  if (++i >= num_fragments)
116  break;
117  refs[++subseg_index] = refs[i];
118  first_sap_time =
119  refs[i].sap_delta_time + refs[i].earliest_presentation_time;
120  frag_index = 1;
121  }
122  }
123 
124  refs.resize(options().num_subsegments_per_sidx);
125 
126  // earliest_presentation_time is the earliest presentation time of any
127  // access unit in the reference stream in the first subsegment.
128  sidx()->earliest_presentation_time = refs[0].earliest_presentation_time;
129 
130  return WriteSegment();
131 }
132 
133 Status MultiSegmentSegmenter::WriteSegment() {
134  DCHECK(sidx());
135  DCHECK(fragment_buffer());
136  DCHECK(styp_);
137 
138  scoped_ptr<BufferWriter> buffer(new BufferWriter());
139  File* file;
140  std::string file_name;
141  if (options().segment_template.empty()) {
142  // Append the segment to output file if segment template is not specified.
143  file_name = options().output_file_name.c_str();
144  file = File::Open(file_name.c_str(), "a");
145  if (file == NULL) {
146  return Status(
147  error::FILE_FAILURE,
148  "Cannot open file for append " + options().output_file_name);
149  }
150  } else {
151  file = File::Open(GetSegmentName(options().segment_template,
152  sidx()->earliest_presentation_time,
153  num_segments_++,
154  options().bandwidth).c_str(),
155  "w");
156  if (file == NULL) {
157  return Status(error::FILE_FAILURE,
158  "Cannot open file for write " + file_name);
159  }
160  styp_->Write(buffer.get());
161  }
162 
163  // If num_subsegments_per_sidx is negative, no SIDX box is generated.
164  if (options().num_subsegments_per_sidx >= 0)
165  sidx()->Write(buffer.get());
166 
167  const size_t segment_size = buffer->Size() + fragment_buffer()->Size();
168  DCHECK_NE(segment_size, 0u);
169 
170  Status status = buffer->WriteToFile(file);
171  if (status.ok())
172  status = fragment_buffer()->WriteToFile(file);
173 
174  if (!file->Close())
175  LOG(WARNING) << "Failed to close the file properly: " << file_name;
176 
177  if (!status.ok())
178  return status;
179 
180  uint64_t segment_duration = 0;
181  // ISO/IEC 23009-1:2012: the value shall be identical to sum of the the
182  // values of all Subsegment_duration fields in the first ‘sidx’ box.
183  for (size_t i = 0; i < sidx()->references.size(); ++i)
184  segment_duration += sidx()->references[i].subsegment_duration;
185 
186  UpdateProgress(segment_duration);
187  if (muxer_listener()) {
188  muxer_listener()->OnSampleDurationReady(sample_duration());
189  muxer_listener()->OnNewSegment(
190  sidx()->earliest_presentation_time, segment_duration, segment_size);
191  }
192 
193  return Status::OK;
194 }
195 
196 } // namespace mp4
197 } // namespace media
198 } // namespace edash_packager
Define an abstract file interface.
Definition: file.h:22
virtual bool Open()=0
Internal open. Should not be used directly.
virtual bool GetInitRange(size_t *offset, size_t *size) OVERRIDE
virtual bool GetIndexRange(size_t *offset, size_t *size) OVERRIDE
void UpdateProgress(uint64_t progress)
Update segmentation progress using ProgressListener.
Definition: segmenter.cc:333
void Write(BufferWriter *writer)
Definition: box.cc:25
void SetComplete()
Set progress to 100%.
Definition: segmenter.cc:349
virtual void OnSampleDurationReady(uint32_t sample_duration)=0