DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerator
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/segmenter.h"
8 
9 #include <algorithm>
10 
11 #include "packager/base/stl_util.h"
12 #include "packager/media/base/buffer_writer.h"
13 #include "packager/media/base/key_source.h"
14 #include "packager/media/base/media_sample.h"
15 #include "packager/media/base/media_stream.h"
16 #include "packager/media/base/muxer_options.h"
17 #include "packager/media/base/video_stream_info.h"
18 #include "packager/media/event/muxer_listener.h"
19 #include "packager/media/event/progress_listener.h"
20 #include "packager/media/formats/mp4/box_definitions.h"
21 #include "packager/media/formats/mp4/key_rotation_fragmenter.h"
22 
23 namespace edash_packager {
24 namespace media {
25 namespace mp4 {
26 
27 namespace {
28 
29 // Generate 64bit IV by default.
30 const size_t kDefaultIvSize = 8u;
31 const size_t kCencKeyIdSize = 16u;
32 
33 // The version of cenc implemented here. CENC 4.
34 const int kCencSchemeVersion = 0x00010000;
35 
36 // The default KID for key rotation is all 0s.
37 const uint8_t kKeyRotationDefaultKeyId[] = {
38  0, 0, 0, 0, 0, 0, 0, 0,
39  0, 0, 0, 0, 0, 0, 0, 0
40 };
41 COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize,
42  cenc_key_id_must_be_size_16);
43 
44 uint64_t Rescale(uint64_t time_in_old_scale,
45  uint32_t old_scale,
46  uint32_t new_scale) {
47  return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
48 }
49 
50 void GenerateSinf(const EncryptionKey& encryption_key,
51  FourCC old_type,
52  ProtectionSchemeInfo* sinf) {
53  sinf->format.format = old_type;
54  sinf->type.type = FOURCC_CENC;
55  sinf->type.version = kCencSchemeVersion;
56  sinf->info.track_encryption.is_encrypted = true;
57  sinf->info.track_encryption.default_iv_size =
58  encryption_key.iv.empty() ? kDefaultIvSize : encryption_key.iv.size();
59  sinf->info.track_encryption.default_kid = encryption_key.key_id;
60 }
61 
62 void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
63  double clear_lead_in_seconds,
64  SampleDescription* description) {
65  DCHECK(description);
66  if (description->type == kVideo) {
67  DCHECK_EQ(1u, description->video_entries.size());
68 
69  // Add a second entry for clear content if needed.
70  if (clear_lead_in_seconds > 0)
71  description->video_entries.push_back(description->video_entries[0]);
72 
73  // Convert the first entry to an encrypted entry.
74  VideoSampleEntry& entry = description->video_entries[0];
75  GenerateSinf(encryption_key, entry.format, &entry.sinf);
76  entry.format = FOURCC_ENCV;
77  } else {
78  DCHECK_EQ(kAudio, description->type);
79  DCHECK_EQ(1u, description->audio_entries.size());
80 
81  // Add a second entry for clear content if needed.
82  if (clear_lead_in_seconds > 0)
83  description->audio_entries.push_back(description->audio_entries[0]);
84 
85  // Convert the first entry to an encrypted entry.
86  AudioSampleEntry& entry = description->audio_entries[0];
87  GenerateSinf(encryption_key, entry.format, &entry.sinf);
88  entry.format = FOURCC_ENCA;
89  }
90 }
91 
92 uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
93  if (stream_info.stream_type() != kStreamVideo)
94  return 0;
95  const VideoStreamInfo& video_stream_info =
96  static_cast<const VideoStreamInfo&>(stream_info);
97  return video_stream_info.nalu_length_size();
98 }
99 
100 KeySource::TrackType GetTrackTypeForEncryption(const StreamInfo& stream_info,
101  uint32_t max_sd_pixels) {
102  if (stream_info.stream_type() == kStreamAudio)
103  return KeySource::TRACK_TYPE_AUDIO;
104 
105  DCHECK_EQ(kStreamVideo, stream_info.stream_type());
106  const VideoStreamInfo& video_stream_info =
107  static_cast<const VideoStreamInfo&>(stream_info);
108  uint32_t pixels = video_stream_info.width() * video_stream_info.height();
109  return (pixels > max_sd_pixels) ? KeySource::TRACK_TYPE_HD
110  : KeySource::TRACK_TYPE_SD;
111 }
112 
113 } // namespace
114 
115 Segmenter::Segmenter(const MuxerOptions& options,
116  scoped_ptr<FileType> ftyp,
117  scoped_ptr<Movie> moov)
118  : options_(options),
119  ftyp_(ftyp.Pass()),
120  moov_(moov.Pass()),
121  moof_(new MovieFragment()),
122  fragment_buffer_(new BufferWriter()),
123  sidx_(new SegmentIndex()),
124  muxer_listener_(NULL),
125  progress_listener_(NULL),
126  progress_target_(0),
127  accumulated_progress_(0),
128  sample_duration_(0u) {}
129 
130 Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
131 
132 Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
133  MuxerListener* muxer_listener,
134  ProgressListener* progress_listener,
135  KeySource* encryption_key_source,
136  uint32_t max_sd_pixels,
137  double clear_lead_in_seconds,
138  double crypto_period_duration_in_seconds) {
139  DCHECK_LT(0u, streams.size());
140  muxer_listener_ = muxer_listener;
141  progress_listener_ = progress_listener;
142  moof_->header.sequence_number = 0;
143 
144  moof_->tracks.resize(streams.size());
145  segment_durations_.resize(streams.size());
146  fragmenters_.resize(streams.size());
147  const bool key_rotation_enabled = crypto_period_duration_in_seconds != 0;
148  const bool kInitialEncryptionInfo = true;
149 
150  for (uint32_t i = 0; i < streams.size(); ++i) {
151  stream_map_[streams[i]] = i;
152  moof_->tracks[i].header.track_id = i + 1;
153  if (streams[i]->info()->stream_type() == kStreamVideo) {
154  // Use the first video stream as the reference stream (which is 1-based).
155  if (sidx_->reference_id == 0)
156  sidx_->reference_id = i + 1;
157  }
158  if (!encryption_key_source) {
159  fragmenters_[i] = new Fragmenter(&moof_->tracks[i]);
160  continue;
161  }
162 
163  uint8_t nalu_length_size = GetNaluLengthSize(*streams[i]->info());
164  KeySource::TrackType track_type =
165  GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
166  SampleDescription& description =
167  moov_->tracks[i].media.information.sample_table.description;
168 
169  if (key_rotation_enabled) {
170  // Fill encrypted sample entry with default key.
171  EncryptionKey encryption_key;
172  encryption_key.key_id.assign(
173  kKeyRotationDefaultKeyId,
174  kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
175  GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
176  &description);
177  if (muxer_listener_) {
178  muxer_listener_->OnEncryptionInfoReady(
179  kInitialEncryptionInfo, encryption_key_source->UUID(),
180  encryption_key_source->SystemName(), encryption_key.key_id,
181  std::vector<uint8_t>());
182  }
183 
184  fragmenters_[i] = new KeyRotationFragmenter(
185  moof_.get(),
186  &moof_->tracks[i],
187  encryption_key_source,
188  track_type,
189  crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
190  clear_lead_in_seconds * streams[i]->info()->time_scale(),
191  nalu_length_size,
192  muxer_listener_);
193  continue;
194  }
195 
196  scoped_ptr<EncryptionKey> encryption_key(new EncryptionKey());
197  Status status =
198  encryption_key_source->GetKey(track_type, encryption_key.get());
199  if (!status.ok())
200  return status;
201 
202  GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
203  &description);
204 
205  // One and only one pssh box is needed.
206  if (moov_->pssh.empty()) {
207  moov_->pssh.resize(1);
208  moov_->pssh[0].raw_box = encryption_key->pssh;
209 
210  // Also only one default key id.
211  if (muxer_listener_) {
212  muxer_listener_->OnEncryptionInfoReady(
213  kInitialEncryptionInfo,
214  encryption_key_source->UUID(), encryption_key_source->SystemName(),
215  encryption_key->key_id, encryption_key->pssh);
216  }
217  }
218 
219  fragmenters_[i] = new EncryptingFragmenter(
220  &moof_->tracks[i],
221  encryption_key.Pass(),
222  clear_lead_in_seconds * streams[i]->info()->time_scale(),
223  nalu_length_size);
224  }
225 
226  // Choose the first stream if there is no VIDEO.
227  if (sidx_->reference_id == 0)
228  sidx_->reference_id = 1;
229  sidx_->timescale = streams[GetReferenceStreamId()]->info()->time_scale();
230 
231  // Use media duration as progress target.
232  progress_target_ = streams[GetReferenceStreamId()]->info()->duration();
233 
234  // Use the reference stream's time scale as movie time scale.
235  moov_->header.timescale = sidx_->timescale;
236  moof_->header.sequence_number = 1;
237  return DoInitialize();
238 }
239 
241  for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
242  it != fragmenters_.end();
243  ++it) {
244  Status status = FinalizeFragment(true, *it);
245  if (!status.ok())
246  return status;
247  }
248 
249  // Set tracks and moov durations.
250  // Note that the updated moov box will be written to output file for VOD case
251  // only.
252  for (std::vector<Track>::iterator track = moov_->tracks.begin();
253  track != moov_->tracks.end();
254  ++track) {
255  track->header.duration = Rescale(track->media.header.duration,
256  track->media.header.timescale,
257  moov_->header.timescale);
258  if (track->header.duration > moov_->header.duration)
259  moov_->header.duration = track->header.duration;
260  }
261  moov_->extends.header.fragment_duration = moov_->header.duration;
262 
263  return DoFinalize();
264 }
265 
267  scoped_refptr<MediaSample> sample) {
268  // Find the fragmenter for this stream.
269  DCHECK(stream);
270  DCHECK(stream_map_.find(stream) != stream_map_.end());
271  uint32_t stream_id = stream_map_[stream];
272  Fragmenter* fragmenter = fragmenters_[stream_id];
273 
274  // Set default sample duration if it has not been set yet.
275  if (moov_->extends.tracks[stream_id].default_sample_duration == 0) {
276  moov_->extends.tracks[stream_id].default_sample_duration =
277  sample->duration();
278  }
279 
280  if (fragmenter->fragment_finalized()) {
281  return Status(error::FRAGMENT_FINALIZED,
282  "Current fragment is finalized already.");
283  }
284 
285  bool finalize_fragment = false;
286  if (fragmenter->fragment_duration() >=
287  options_.fragment_duration * stream->info()->time_scale()) {
288  if (sample->is_key_frame() || !options_.fragment_sap_aligned) {
289  finalize_fragment = true;
290  }
291  }
292  bool finalize_segment = false;
293  if (segment_durations_[stream_id] >=
294  options_.segment_duration * stream->info()->time_scale()) {
295  if (sample->is_key_frame() || !options_.segment_sap_aligned) {
296  finalize_segment = true;
297  finalize_fragment = true;
298  }
299  }
300 
301  Status status;
302  if (finalize_fragment) {
303  status = FinalizeFragment(finalize_segment, fragmenter);
304  if (!status.ok())
305  return status;
306  }
307 
308  status = fragmenter->AddSample(sample);
309  if (!status.ok())
310  return status;
311 
312  if (sample_duration_ == 0)
313  sample_duration_ = sample->duration();
314  moov_->tracks[stream_id].media.header.duration += sample->duration();
315  segment_durations_[stream_id] += sample->duration();
316  DCHECK_GE(segment_durations_[stream_id], fragmenter->fragment_duration());
317  return Status::OK;
318 }
319 
320 uint32_t Segmenter::GetReferenceTimeScale() const {
321  return moov_->header.timescale;
322 }
323 
324 double Segmenter::GetDuration() const {
325  if (moov_->header.timescale == 0) {
326  // Handling the case where this is not properly initialized.
327  return 0.0;
328  }
329 
330  return static_cast<double>(moov_->header.duration) / moov_->header.timescale;
331 }
332 
333 void Segmenter::UpdateProgress(uint64_t progress) {
334  accumulated_progress_ += progress;
335 
336  if (!progress_listener_) return;
337  if (progress_target_ == 0) return;
338  // It might happen that accumulated progress exceeds progress_target due to
339  // computation errors, e.g. rounding error. Cap it so it never reports > 100%
340  // progress.
341  if (accumulated_progress_ >= progress_target_) {
342  progress_listener_->OnProgress(1.0);
343  } else {
344  progress_listener_->OnProgress(static_cast<double>(accumulated_progress_) /
345  progress_target_);
346  }
347 }
348 
349 void Segmenter::SetComplete() {
350  if (!progress_listener_) return;
351  progress_listener_->OnProgress(1.0);
352 }
353 
354 Status Segmenter::FinalizeSegment() {
355  Status status = DoFinalizeSegment();
356 
357  // Reset segment information to initial state.
358  sidx_->references.clear();
359  std::vector<uint64_t>::iterator it = segment_durations_.begin();
360  for (; it != segment_durations_.end(); ++it)
361  *it = 0;
362 
363  return status;
364 }
365 
366 uint32_t Segmenter::GetReferenceStreamId() {
367  DCHECK(sidx_);
368  return sidx_->reference_id - 1;
369 }
370 
371 Status Segmenter::FinalizeFragment(bool finalize_segment,
372  Fragmenter* fragmenter) {
373  fragmenter->FinalizeFragment();
374 
375  // Check if all tracks are ready for fragmentation.
376  for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
377  it != fragmenters_.end();
378  ++it) {
379  if (!(*it)->fragment_finalized())
380  return Status::OK;
381  }
382 
383  MediaData mdat;
384  // Fill in data offsets. Data offset base is moof size + mdat box size.
385  // (mdat is still empty, mdat size is the same as mdat box size).
386  uint64_t base = moof_->ComputeSize() + mdat.ComputeSize();
387  for (size_t i = 0; i < moof_->tracks.size(); ++i) {
388  TrackFragment& traf = moof_->tracks[i];
389  Fragmenter* fragmenter = fragmenters_[i];
390  if (fragmenter->aux_data()->Size() > 0) {
391  traf.auxiliary_offset.offsets[0] += base;
392  base += fragmenter->aux_data()->Size();
393  }
394  traf.runs[0].data_offset += base;
395  base += fragmenter->data()->Size();
396  }
397 
398  // Generate segment reference.
399  sidx_->references.resize(sidx_->references.size() + 1);
400  fragmenters_[GetReferenceStreamId()]->GenerateSegmentReference(
401  &sidx_->references[sidx_->references.size() - 1]);
402  sidx_->references[sidx_->references.size() - 1].referenced_size = base;
403 
404  // Write the fragment to buffer.
405  moof_->Write(fragment_buffer_.get());
406 
407  for (size_t i = 0; i < moof_->tracks.size(); ++i) {
408  Fragmenter* fragmenter = fragmenters_[i];
409  mdat.data_size =
410  fragmenter->aux_data()->Size() + fragmenter->data()->Size();
411  mdat.WriteHeader(fragment_buffer_.get());
412  if (fragmenter->aux_data()->Size()) {
413  fragment_buffer_->AppendBuffer(*fragmenter->aux_data());
414  }
415  fragment_buffer_->AppendBuffer(*fragmenter->data());
416  }
417 
418  // Increase sequence_number for next fragment.
419  ++moof_->header.sequence_number;
420 
421  if (finalize_segment)
422  return FinalizeSegment();
423 
424  return Status::OK;
425 }
426 
427 } // namespace mp4
428 } // namespace media
429 } // namespace edash_packager
virtual Status GetKey(TrackType track_type, EncryptionKey *key)
Definition: key_source.cc:46
This class listens to progress updates events.
EncryptingFragmenter generates MP4 fragments with sample encrypted.
virtual Status AddSample(scoped_refptr< MediaSample > sample)
Definition: fragmenter.cc:36
virtual std::string UUID()
Definition: key_source.cc:92
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:29
virtual void OnProgress(double progress)=0
void UpdateProgress(uint64_t progress)
Update segmentation progress using ProgressListener.
Definition: segmenter.cc:199
virtual std::string SystemName()
Definition: key_source.cc:96
Status Initialize(scoped_ptr< MkvWriter > writer, MediaStream *streams, ProgressListener *progress_listener, MuxerListener *muxer_listener, KeySource *encryption_key_source)
Definition: segmenter.cc:45
Status AddSample(scoped_refptr< MediaSample > sample)
Definition: segmenter.cc:92