DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
fragmenter.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/fragmenter.h"
8 
9 #include <limits>
10 
11 #include "packager/media/base/buffer_writer.h"
12 #include "packager/media/base/audio_stream_info.h"
13 #include "packager/media/base/media_sample.h"
14 #include "packager/media/formats/mp4/box_definitions.h"
15 
16 namespace shaka {
17 namespace media {
18 namespace mp4 {
19 
20 namespace {
21 const int64_t kInvalidTime = std::numeric_limits<int64_t>::max();
22 
23 uint64_t GetSeekPreroll(const StreamInfo& stream_info) {
24  if (stream_info.stream_type() != kStreamAudio)
25  return 0;
26  const AudioStreamInfo& audio_stream_info =
27  static_cast<const AudioStreamInfo&>(stream_info);
28  return audio_stream_info.seek_preroll_ns();
29 }
30 
31 void NewSampleEncryptionEntry(const DecryptConfig& decrypt_config,
32  bool use_constant_iv,
33  TrackFragment* traf) {
34  SampleEncryption& sample_encryption = traf->sample_encryption;
35  SampleEncryptionEntry sample_encryption_entry;
36  if (!use_constant_iv)
37  sample_encryption_entry.initialization_vector = decrypt_config.iv();
38  sample_encryption_entry.subsamples = decrypt_config.subsamples();
39  sample_encryption.sample_encryption_entries.push_back(
40  sample_encryption_entry);
41  traf->auxiliary_size.sample_info_sizes.push_back(
42  sample_encryption_entry.ComputeSize());
43 }
44 
45 } // namespace
46 
47 Fragmenter::Fragmenter(std::shared_ptr<StreamInfo> stream_info,
48  TrackFragment* traf)
49  : stream_info_(std::move(stream_info)),
50  use_decoding_timestamp_in_timeline_(false),
51  traf_(traf),
52  seek_preroll_(GetSeekPreroll(*stream_info_)),
53  fragment_initialized_(false),
54  fragment_finalized_(false),
55  fragment_duration_(0),
56  earliest_presentation_time_(kInvalidTime),
57  first_sap_time_(kInvalidTime) {
58  DCHECK(stream_info_);
59  DCHECK(traf);
60 }
61 
62 Fragmenter::~Fragmenter() {}
63 
64 Status Fragmenter::AddSample(std::shared_ptr<MediaSample> sample) {
65  DCHECK(sample);
66  if (sample->duration() == 0) {
67  LOG(WARNING) << "Unexpected sample with zero duration @ dts "
68  << sample->dts();
69  }
70 
71  if (!fragment_initialized_) {
72  Status status = InitializeFragment(sample->dts());
73  if (!status.ok())
74  return status;
75  }
76 
77  if (sample->side_data_size() > 0)
78  LOG(WARNING) << "MP4 samples do not support side data. Side data ignored.";
79 
80  // Fill in sample parameters. It will be optimized later.
81  traf_->runs[0].sample_sizes.push_back(
82  static_cast<uint32_t>(sample->data_size()));
83  traf_->runs[0].sample_durations.push_back(sample->duration());
84  traf_->runs[0].sample_flags.push_back(
85  sample->is_key_frame() ? 0 : TrackFragmentHeader::kNonKeySampleMask);
86 
87  if (sample->decrypt_config()) {
88  NewSampleEncryptionEntry(
89  *sample->decrypt_config(),
90  !stream_info_->encryption_config().constant_iv.empty(), traf_);
91  }
92 
93  data_->AppendArray(sample->data(), sample->data_size());
94  fragment_duration_ += sample->duration();
95 
96  const int64_t pts = sample->pts();
97  const int64_t dts = sample->dts();
98 
99  const int64_t timestamp = use_decoding_timestamp_in_timeline_ ? dts : pts;
100  // Set |earliest_presentation_time_| to |timestamp| if |timestamp| is smaller
101  // or if it is not yet initialized (kInvalidTime > timestamp is always true).
102  if (earliest_presentation_time_ > timestamp)
103  earliest_presentation_time_ = timestamp;
104 
105  traf_->runs[0].sample_composition_time_offsets.push_back(pts - dts);
106  if (pts != dts)
107  traf_->runs[0].flags |= TrackFragmentRun::kSampleCompTimeOffsetsPresentMask;
108 
109  if (sample->is_key_frame()) {
110  if (first_sap_time_ == kInvalidTime)
111  first_sap_time_ = pts;
112  }
113  return Status::OK;
114 }
115 
116 Status Fragmenter::InitializeFragment(int64_t first_sample_dts) {
117  fragment_initialized_ = true;
118  fragment_finalized_ = false;
119  traf_->decode_time.decode_time = first_sample_dts;
120  traf_->runs.clear();
121  traf_->runs.resize(1);
122  traf_->runs[0].flags = TrackFragmentRun::kDataOffsetPresentMask;
123  traf_->auxiliary_size.sample_info_sizes.clear();
124  traf_->auxiliary_offset.offsets.clear();
125  traf_->sample_encryption.sample_encryption_entries.clear();
126  traf_->sample_group_descriptions.clear();
127  traf_->sample_to_groups.clear();
128  traf_->header.sample_description_index = 1; // 1-based.
129  traf_->header.flags = TrackFragmentHeader::kDefaultBaseIsMoofMask |
130  TrackFragmentHeader::kSampleDescriptionIndexPresentMask;
131 
132  fragment_duration_ = 0;
133  earliest_presentation_time_ = kInvalidTime;
134  first_sap_time_ = kInvalidTime;
135  data_.reset(new BufferWriter());
136  return Status::OK;
137 }
138 
140  if (stream_info_->is_encrypted()) {
141  Status status = FinalizeFragmentForEncryption();
142  if (!status.ok())
143  return status;
144  }
145 
146  // Optimize trun box.
147  traf_->runs[0].sample_count =
148  static_cast<uint32_t>(traf_->runs[0].sample_sizes.size());
149  if (OptimizeSampleEntries(&traf_->runs[0].sample_durations,
150  &traf_->header.default_sample_duration)) {
151  traf_->header.flags |=
152  TrackFragmentHeader::kDefaultSampleDurationPresentMask;
153  } else {
154  traf_->runs[0].flags |= TrackFragmentRun::kSampleDurationPresentMask;
155  }
156  if (OptimizeSampleEntries(&traf_->runs[0].sample_sizes,
157  &traf_->header.default_sample_size)) {
158  traf_->header.flags |= TrackFragmentHeader::kDefaultSampleSizePresentMask;
159  } else {
160  traf_->runs[0].flags |= TrackFragmentRun::kSampleSizePresentMask;
161  }
162  if (OptimizeSampleEntries(&traf_->runs[0].sample_flags,
163  &traf_->header.default_sample_flags)) {
164  traf_->header.flags |= TrackFragmentHeader::kDefaultSampleFlagsPresentMask;
165  } else {
166  traf_->runs[0].flags |= TrackFragmentRun::kSampleFlagsPresentMask;
167  }
168 
169  // Add SampleToGroup boxes. A SampleToGroup box with grouping type of 'roll'
170  // needs to be added if there is seek preroll, referencing sample group
171  // description in track level; Also need to add SampleToGroup boxes
172  // correponding to every SampleGroupDescription boxes, referencing sample
173  // group description in fragment level.
174  DCHECK_EQ(traf_->sample_to_groups.size(), 0u);
175  if (seek_preroll_ > 0) {
176  traf_->sample_to_groups.resize(traf_->sample_to_groups.size() + 1);
177  SampleToGroup& sample_to_group = traf_->sample_to_groups.back();
178  sample_to_group.grouping_type = FOURCC_roll;
179 
180  sample_to_group.entries.resize(1);
181  SampleToGroupEntry& sample_to_group_entry = sample_to_group.entries.back();
182  sample_to_group_entry.sample_count = traf_->runs[0].sample_count;
183  sample_to_group_entry.group_description_index =
184  SampleToGroupEntry::kTrackGroupDescriptionIndexBase + 1;
185  }
186  for (const auto& sample_group_description :
187  traf_->sample_group_descriptions) {
188  traf_->sample_to_groups.resize(traf_->sample_to_groups.size() + 1);
189  SampleToGroup& sample_to_group = traf_->sample_to_groups.back();
190  sample_to_group.grouping_type = sample_group_description.grouping_type;
191 
192  sample_to_group.entries.resize(1);
193  SampleToGroupEntry& sample_to_group_entry = sample_to_group.entries.back();
194  sample_to_group_entry.sample_count = traf_->runs[0].sample_count;
195  sample_to_group_entry.group_description_index =
196  SampleToGroupEntry::kTrackFragmentGroupDescriptionIndexBase + 1;
197  }
198 
199  fragment_finalized_ = true;
200  fragment_initialized_ = false;
201  return Status::OK;
202 }
203 
205  // NOTE: Daisy chain is not supported currently.
206  reference->reference_type = false;
207  reference->subsegment_duration = fragment_duration_;
208  reference->starts_with_sap = StartsWithSAP();
209  if (kInvalidTime == first_sap_time_) {
210  reference->sap_type = SegmentReference::TypeUnknown;
211  reference->sap_delta_time = 0;
212  } else {
213  reference->sap_type = SegmentReference::Type1;
214  reference->sap_delta_time = first_sap_time_ - earliest_presentation_time_;
215  }
216  reference->earliest_presentation_time = earliest_presentation_time_;
217 }
218 
219 Status Fragmenter::FinalizeFragmentForEncryption() {
220  SampleEncryption& sample_encryption = traf_->sample_encryption;
221  if (sample_encryption.sample_encryption_entries.empty()) {
222  // This fragment is not encrypted.
223  // There are two sample description entries, an encrypted entry and a clear
224  // entry, are generated. The 1-based clear entry index is always 2.
225  const uint32_t kClearSampleDescriptionIndex = 2;
226  traf_->header.sample_description_index = kClearSampleDescriptionIndex;
227  return Status::OK;
228  }
229  if (sample_encryption.sample_encryption_entries.size() !=
230  traf_->runs[0].sample_sizes.size()) {
231  LOG(ERROR) << "Partially encrypted segment is not supported";
232  return Status(error::MUXER_FAILURE,
233  "Partially encrypted segment is not supported.");
234  }
235 
236  const SampleEncryptionEntry& sample_encryption_entry =
237  sample_encryption.sample_encryption_entries.front();
238  const bool use_subsample_encryption =
239  !sample_encryption_entry.subsamples.empty();
240  if (use_subsample_encryption)
241  traf_->sample_encryption.flags |= SampleEncryption::kUseSubsampleEncryption;
242  traf_->sample_encryption.iv_size = static_cast<uint8_t>(
243  sample_encryption_entry.initialization_vector.size());
244 
245  // The offset will be adjusted in Segmenter after knowing moof size.
246  traf_->auxiliary_offset.offsets.push_back(0);
247 
248  // Optimize saiz box.
249  SampleAuxiliaryInformationSize& saiz = traf_->auxiliary_size;
250  saiz.sample_count = static_cast<uint32_t>(saiz.sample_info_sizes.size());
251  DCHECK_EQ(saiz.sample_info_sizes.size(),
252  traf_->sample_encryption.sample_encryption_entries.size());
253  if (!OptimizeSampleEntries(&saiz.sample_info_sizes,
254  &saiz.default_sample_info_size)) {
255  saiz.default_sample_info_size = 0;
256  }
257 
258  // It should only happen with full sample encryption + constant iv, i.e.
259  // 'cbcs' applying to audio.
260  if (saiz.default_sample_info_size == 0 && saiz.sample_info_sizes.empty()) {
261  DCHECK(!use_subsample_encryption);
262  // ISO/IEC 23001-7:2016(E) The sample auxiliary information would then be
263  // empty and should be omitted. Clear saiz and saio boxes so they are not
264  // written.
265  saiz.sample_count = 0;
266  traf_->auxiliary_offset.offsets.clear();
267  }
268  return Status::OK;
269 }
270 
271 bool Fragmenter::StartsWithSAP() {
272  DCHECK(!traf_->runs.empty());
273  uint32_t start_sample_flag;
274  if (traf_->runs[0].flags & TrackFragmentRun::kSampleFlagsPresentMask) {
275  DCHECK(!traf_->runs[0].sample_flags.empty());
276  start_sample_flag = traf_->runs[0].sample_flags[0];
277  } else {
278  DCHECK(traf_->header.flags &
279  TrackFragmentHeader::kDefaultSampleFlagsPresentMask);
280  start_sample_flag = traf_->header.default_sample_flags;
281  }
282  return (start_sample_flag & TrackFragmentHeader::kNonKeySampleMask) == 0;
283 }
284 
285 } // namespace mp4
286 } // namespace media
287 } // namespace shaka
Status InitializeFragment(int64_t first_sample_dts)
Definition: fragmenter.cc:116
Fragmenter(std::shared_ptr< StreamInfo > info, TrackFragment *traf)
Definition: fragmenter.cc:47
Status AddSample(std::shared_ptr< MediaSample > sample)
Definition: fragmenter.cc:64
void GenerateSegmentReference(SegmentReference *reference)
Fill reference with current fragment information.
Definition: fragmenter.cc:204
Status FinalizeFragment()
Finalize and optimize the fragment.
Definition: fragmenter.cc:139
bool OptimizeSampleEntries(std::vector< T > *entries, T *default_value)
Definition: fragmenter.h:104