DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations 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/logging.h"
12 #include "packager/base/stl_util.h"
13 #include "packager/media/base/aes_cryptor.h"
14 #include "packager/media/base/buffer_writer.h"
15 #include "packager/media/base/key_source.h"
16 #include "packager/media/base/media_sample.h"
17 #include "packager/media/base/media_stream.h"
18 #include "packager/media/base/muxer_options.h"
19 #include "packager/media/base/muxer_util.h"
20 #include "packager/media/base/video_stream_info.h"
21 #include "packager/media/event/muxer_listener.h"
22 #include "packager/media/event/progress_listener.h"
23 #include "packager/media/formats/mp4/box_definitions.h"
24 #include "packager/media/formats/mp4/key_rotation_fragmenter.h"
25 #include "packager/version/version.h"
26 
27 namespace shaka {
28 namespace media {
29 namespace mp4 {
30 
31 namespace {
32 const size_t kCencKeyIdSize = 16u;
33 
34 // The version of cenc implemented here. CENC 4.
35 const int kCencSchemeVersion = 0x00010000;
36 
37 // The default KID for key rotation is all 0s.
38 const uint8_t kKeyRotationDefaultKeyId[] = {
39  0, 0, 0, 0, 0, 0, 0, 0,
40  0, 0, 0, 0, 0, 0, 0, 0
41 };
42 
43 // Defines protection pattern for pattern-based encryption.
44 struct ProtectionPattern {
45  uint8_t crypt_byte_block;
46  uint8_t skip_byte_block;
47 };
48 
49 COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize,
50  cenc_key_id_must_be_size_16);
51 
52 uint64_t Rescale(uint64_t time_in_old_scale,
53  uint32_t old_scale,
54  uint32_t new_scale) {
55  return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
56 }
57 
58 ProtectionPattern GetProtectionPattern(FourCC protection_scheme,
59  TrackType track_type) {
60  ProtectionPattern pattern;
61  if (protection_scheme != FOURCC_cbcs && protection_scheme != FOURCC_cens) {
62  // Not using pattern encryption.
63  pattern.crypt_byte_block = 0u;
64  pattern.skip_byte_block = 0u;
65  } else if (track_type != kVideo) {
66  // Tracks other than video are protected using whole-block full-sample
67  // encryption, which is essentially a pattern of 1:0. Note that this may not
68  // be the same as the non-pattern based encryption counterparts, e.g. in
69  // 'cens' for full sample encryption, the whole sample is encrypted up to
70  // the last 16-byte boundary, see 23001-7:2016(E) 9.7; while in 'cenc' for
71  // full sample encryption, the last partial 16-byte block is also encrypted,
72  // see 23001-7:2016(E) 9.4.2. Another difference is the use of constant iv.
73  pattern.crypt_byte_block = 1u;
74  pattern.skip_byte_block = 0u;
75  } else {
76  // Use 1:9 pattern for video.
77  const uint8_t kCryptByteBlock = 1u;
78  const uint8_t kSkipByteBlock = 9u;
79  pattern.crypt_byte_block = kCryptByteBlock;
80  pattern.skip_byte_block = kSkipByteBlock;
81  }
82  return pattern;
83 }
84 
85 void GenerateSinf(const EncryptionKey& encryption_key,
86  FourCC old_type,
87  FourCC protection_scheme,
88  ProtectionPattern pattern,
89  ProtectionSchemeInfo* sinf) {
90  sinf->format.format = old_type;
91 
92  DCHECK_NE(protection_scheme, FOURCC_NULL);
93  sinf->type.type = protection_scheme;
94  sinf->type.version = kCencSchemeVersion;
95 
96  auto& track_encryption = sinf->info.track_encryption;
97  track_encryption.default_is_protected = 1;
98  DCHECK(!encryption_key.iv.empty());
99  if (protection_scheme == FOURCC_cbcs) {
100  // ISO/IEC 23001-7:2016 10.4.1
101  // For 'cbcs' scheme, Constant IVs SHALL be used.
102  track_encryption.default_per_sample_iv_size = 0;
103  track_encryption.default_constant_iv = encryption_key.iv;
104  } else {
105  track_encryption.default_per_sample_iv_size =
106  static_cast<uint8_t>(encryption_key.iv.size());
107  }
108  track_encryption.default_crypt_byte_block = pattern.crypt_byte_block;
109  track_encryption.default_skip_byte_block = pattern.skip_byte_block;
110  track_encryption.default_kid = encryption_key.key_id;
111 }
112 
113 void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
114  double clear_lead_in_seconds,
115  FourCC protection_scheme,
116  ProtectionPattern pattern,
117  SampleDescription* description) {
118  DCHECK(description);
119  if (description->type == kVideo) {
120  DCHECK_EQ(1u, description->video_entries.size());
121 
122  // Add a second entry for clear content if needed.
123  if (clear_lead_in_seconds > 0)
124  description->video_entries.push_back(description->video_entries[0]);
125 
126  // Convert the first entry to an encrypted entry.
127  VideoSampleEntry& entry = description->video_entries[0];
128  GenerateSinf(encryption_key, entry.format, protection_scheme, pattern,
129  &entry.sinf);
130  entry.format = FOURCC_encv;
131  } else {
132  DCHECK_EQ(kAudio, description->type);
133  DCHECK_EQ(1u, description->audio_entries.size());
134 
135  // Add a second entry for clear content if needed.
136  if (clear_lead_in_seconds > 0)
137  description->audio_entries.push_back(description->audio_entries[0]);
138 
139  // Convert the first entry to an encrypted entry.
140  AudioSampleEntry& entry = description->audio_entries[0];
141  GenerateSinf(encryption_key, entry.format, protection_scheme, pattern,
142  &entry.sinf);
143  entry.format = FOURCC_enca;
144  }
145 }
146 
147 } // namespace
148 
149 Segmenter::Segmenter(const MuxerOptions& options,
150  scoped_ptr<FileType> ftyp,
151  scoped_ptr<Movie> moov)
152  : options_(options),
153  ftyp_(ftyp.Pass()),
154  moov_(moov.Pass()),
155  moof_(new MovieFragment()),
156  fragment_buffer_(new BufferWriter()),
157  sidx_(new SegmentIndex()),
158  muxer_listener_(NULL),
159  progress_listener_(NULL),
160  progress_target_(0),
161  accumulated_progress_(0),
162  sample_duration_(0u) {}
163 
164 Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
165 
166 Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
167  MuxerListener* muxer_listener,
168  ProgressListener* progress_listener,
169  KeySource* encryption_key_source,
170  uint32_t max_sd_pixels,
171  double clear_lead_in_seconds,
172  double crypto_period_duration_in_seconds,
173  FourCC protection_scheme) {
174  DCHECK_LT(0u, streams.size());
175  muxer_listener_ = muxer_listener;
176  progress_listener_ = progress_listener;
177  moof_->header.sequence_number = 0;
178 
179  moof_->tracks.resize(streams.size());
180  segment_durations_.resize(streams.size());
181  fragmenters_.resize(streams.size());
182  const bool key_rotation_enabled = crypto_period_duration_in_seconds != 0;
183  const bool kInitialEncryptionInfo = true;
184 
185  for (uint32_t i = 0; i < streams.size(); ++i) {
186  stream_map_[streams[i]] = i;
187  moof_->tracks[i].header.track_id = i + 1;
188  if (streams[i]->info()->stream_type() == kStreamVideo) {
189  // Use the first video stream as the reference stream (which is 1-based).
190  if (sidx_->reference_id == 0)
191  sidx_->reference_id = i + 1;
192  }
193  if (!encryption_key_source) {
194  fragmenters_[i] = new Fragmenter(streams[i]->info(), &moof_->tracks[i]);
195  continue;
196  }
197 
198  KeySource::TrackType track_type =
199  GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
200  SampleDescription& description =
201  moov_->tracks[i].media.information.sample_table.description;
202  ProtectionPattern pattern =
203  GetProtectionPattern(protection_scheme, description.type);
204 
205  if (key_rotation_enabled) {
206  // Fill encrypted sample entry with default key.
207  EncryptionKey encryption_key;
208  encryption_key.key_id.assign(
209  kKeyRotationDefaultKeyId,
210  kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
211  if (!AesCryptor::GenerateRandomIv(protection_scheme,
212  &encryption_key.iv)) {
213  return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
214  }
215  GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
216  protection_scheme, pattern, &description);
217  if (muxer_listener_) {
218  muxer_listener_->OnEncryptionInfoReady(
219  kInitialEncryptionInfo, protection_scheme, encryption_key.key_id,
220  encryption_key.iv, encryption_key.key_system_info);
221  }
222 
223  fragmenters_[i] = new KeyRotationFragmenter(
224  moof_.get(), streams[i]->info(), &moof_->tracks[i],
225  encryption_key_source, track_type,
226  crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
227  clear_lead_in_seconds * streams[i]->info()->time_scale(),
228  protection_scheme, pattern.crypt_byte_block, pattern.skip_byte_block,
229  muxer_listener_);
230  continue;
231  }
232 
233  scoped_ptr<EncryptionKey> encryption_key(new EncryptionKey());
234  Status status =
235  encryption_key_source->GetKey(track_type, encryption_key.get());
236  if (!status.ok())
237  return status;
238  if (encryption_key->iv.empty()) {
239  if (!AesCryptor::GenerateRandomIv(protection_scheme,
240  &encryption_key->iv)) {
241  return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
242  }
243  }
244 
245  GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
246  protection_scheme, pattern, &description);
247 
248  if (moov_->pssh.empty()) {
249  moov_->pssh.resize(encryption_key->key_system_info.size());
250  for (size_t i = 0; i < encryption_key->key_system_info.size(); i++) {
251  moov_->pssh[i].raw_box = encryption_key->key_system_info[i].CreateBox();
252  }
253 
254  if (muxer_listener_) {
255  muxer_listener_->OnEncryptionInfoReady(
256  kInitialEncryptionInfo, protection_scheme, encryption_key->key_id,
257  encryption_key->iv, encryption_key->key_system_info);
258  }
259  }
260 
261  fragmenters_[i] = new EncryptingFragmenter(
262  streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(),
263  clear_lead_in_seconds * streams[i]->info()->time_scale(),
264  protection_scheme, pattern.crypt_byte_block, pattern.skip_byte_block,
265  muxer_listener_);
266  }
267 
269  for (uint32_t i = 0; i < streams.size(); ++i)
270  fragmenters_[i]->set_use_decoding_timestamp_in_timeline(true);
271  }
272 
273  // Choose the first stream if there is no VIDEO.
274  if (sidx_->reference_id == 0)
275  sidx_->reference_id = 1;
276  sidx_->timescale = streams[GetReferenceStreamId()]->info()->time_scale();
277 
278  // Use media duration as progress target.
279  progress_target_ = streams[GetReferenceStreamId()]->info()->duration();
280 
281  // Use the reference stream's time scale as movie time scale.
282  moov_->header.timescale = sidx_->timescale;
283  moof_->header.sequence_number = 1;
284 
285  // Fill in version information.
286  const std::string version = GetPackagerVersion();
287  if (!version.empty()) {
288  moov_->metadata.handler.handler_type = FOURCC_ID32;
289  moov_->metadata.id3v2.language.code = "eng";
290  moov_->metadata.id3v2.private_frame.owner = GetPackagerProjectUrl();
291  moov_->metadata.id3v2.private_frame.value = version;
292  }
293  return DoInitialize();
294 }
295 
297  for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
298  it != fragmenters_.end();
299  ++it) {
300  Status status = FinalizeFragment(true, *it);
301  if (!status.ok())
302  return status;
303  }
304 
305  // Set tracks and moov durations.
306  // Note that the updated moov box will be written to output file for VOD case
307  // only.
308  for (std::vector<Track>::iterator track = moov_->tracks.begin();
309  track != moov_->tracks.end();
310  ++track) {
311  track->header.duration = Rescale(track->media.header.duration,
312  track->media.header.timescale,
313  moov_->header.timescale);
314  if (track->header.duration > moov_->header.duration)
315  moov_->header.duration = track->header.duration;
316  }
317  moov_->extends.header.fragment_duration = moov_->header.duration;
318 
319  return DoFinalize();
320 }
321 
323  scoped_refptr<MediaSample> sample) {
324  // Find the fragmenter for this stream.
325  DCHECK(stream);
326  DCHECK(stream_map_.find(stream) != stream_map_.end());
327  uint32_t stream_id = stream_map_[stream];
328  Fragmenter* fragmenter = fragmenters_[stream_id];
329 
330  // Set default sample duration if it has not been set yet.
331  if (moov_->extends.tracks[stream_id].default_sample_duration == 0) {
332  moov_->extends.tracks[stream_id].default_sample_duration =
333  sample->duration();
334  }
335 
336  if (fragmenter->fragment_finalized()) {
337  return Status(error::FRAGMENT_FINALIZED,
338  "Current fragment is finalized already.");
339  }
340 
341  bool finalize_fragment = false;
342  if (fragmenter->fragment_duration() >=
343  options_.fragment_duration * stream->info()->time_scale()) {
344  if (sample->is_key_frame() || !options_.fragment_sap_aligned) {
345  finalize_fragment = true;
346  }
347  }
348  bool finalize_segment = false;
349  if (segment_durations_[stream_id] >=
350  options_.segment_duration * stream->info()->time_scale()) {
351  if (sample->is_key_frame() || !options_.segment_sap_aligned) {
352  finalize_segment = true;
353  finalize_fragment = true;
354  }
355  }
356 
357  Status status;
358  if (finalize_fragment) {
359  status = FinalizeFragment(finalize_segment, fragmenter);
360  if (!status.ok())
361  return status;
362  }
363 
364  status = fragmenter->AddSample(sample);
365  if (!status.ok())
366  return status;
367 
368  if (sample_duration_ == 0)
369  sample_duration_ = sample->duration();
370  moov_->tracks[stream_id].media.header.duration += sample->duration();
371  segment_durations_[stream_id] += sample->duration();
372  DCHECK_GE(segment_durations_[stream_id], fragmenter->fragment_duration());
373  return Status::OK;
374 }
375 
376 uint32_t Segmenter::GetReferenceTimeScale() const {
377  return moov_->header.timescale;
378 }
379 
380 double Segmenter::GetDuration() const {
381  if (moov_->header.timescale == 0) {
382  // Handling the case where this is not properly initialized.
383  return 0.0;
384  }
385 
386  return static_cast<double>(moov_->header.duration) / moov_->header.timescale;
387 }
388 
389 void Segmenter::UpdateProgress(uint64_t progress) {
390  accumulated_progress_ += progress;
391 
392  if (!progress_listener_) return;
393  if (progress_target_ == 0) return;
394  // It might happen that accumulated progress exceeds progress_target due to
395  // computation errors, e.g. rounding error. Cap it so it never reports > 100%
396  // progress.
397  if (accumulated_progress_ >= progress_target_) {
398  progress_listener_->OnProgress(1.0);
399  } else {
400  progress_listener_->OnProgress(static_cast<double>(accumulated_progress_) /
401  progress_target_);
402  }
403 }
404 
405 void Segmenter::SetComplete() {
406  if (!progress_listener_) return;
407  progress_listener_->OnProgress(1.0);
408 }
409 
410 Status Segmenter::FinalizeSegment() {
411  Status status = DoFinalizeSegment();
412 
413  // Reset segment information to initial state.
414  sidx_->references.clear();
415  std::vector<uint64_t>::iterator it = segment_durations_.begin();
416  for (; it != segment_durations_.end(); ++it)
417  *it = 0;
418 
419  return status;
420 }
421 
422 uint32_t Segmenter::GetReferenceStreamId() {
423  DCHECK(sidx_);
424  return sidx_->reference_id - 1;
425 }
426 
427 Status Segmenter::FinalizeFragment(bool finalize_segment,
428  Fragmenter* fragmenter) {
429  fragmenter->FinalizeFragment();
430 
431  // Check if all tracks are ready for fragmentation.
432  for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
433  it != fragmenters_.end();
434  ++it) {
435  if (!(*it)->fragment_finalized())
436  return Status::OK;
437  }
438 
439  MediaData mdat;
440  // Data offset relative to 'moof': moof size + mdat header size.
441  // The code will also update box sizes for moof_ and its child boxes.
442  uint64_t data_offset = moof_->ComputeSize() + mdat.HeaderSize();
443  // 'traf' should follow 'mfhd' moof header box.
444  uint64_t next_traf_position = moof_->HeaderSize() + moof_->header.box_size();
445  for (size_t i = 0; i < moof_->tracks.size(); ++i) {
446  TrackFragment& traf = moof_->tracks[i];
447  if (traf.auxiliary_offset.offsets.size() > 0) {
448  DCHECK_EQ(traf.auxiliary_offset.offsets.size(), 1u);
449  DCHECK(!traf.sample_encryption.sample_encryption_entries.empty());
450 
451  next_traf_position += traf.box_size();
452  // SampleEncryption 'senc' box should be the last box in 'traf'.
453  // |auxiliary_offset| should point to the data of SampleEncryption.
454  traf.auxiliary_offset.offsets[0] =
455  next_traf_position - traf.sample_encryption.box_size() +
456  traf.sample_encryption.HeaderSize() +
457  sizeof(uint32_t); // for sample count field in 'senc'
458  }
459  traf.runs[0].data_offset = data_offset + mdat.data_size;
460  mdat.data_size += fragmenters_[i]->data()->Size();
461  }
462 
463  // Generate segment reference.
464  sidx_->references.resize(sidx_->references.size() + 1);
465  fragmenters_[GetReferenceStreamId()]->GenerateSegmentReference(
466  &sidx_->references[sidx_->references.size() - 1]);
467  sidx_->references[sidx_->references.size() - 1].referenced_size =
468  data_offset + mdat.data_size;
469 
470  // Write the fragment to buffer.
471  moof_->Write(fragment_buffer_.get());
472  mdat.WriteHeader(fragment_buffer_.get());
473  for (Fragmenter* fragmenter : fragmenters_)
474  fragment_buffer_->AppendBuffer(*fragmenter->data());
475 
476  // Increase sequence_number for next fragment.
477  ++moof_->header.sequence_number;
478 
479  if (finalize_segment)
480  return FinalizeSegment();
481 
482  return Status::OK;
483 }
484 
485 } // namespace mp4
486 } // namespace media
487 } // namespace shaka
Status AddSample(scoped_refptr< MediaSample > sample)
Definition: segmenter.cc:118
virtual Status AddSample(scoped_refptr< MediaSample > sample)
Definition: fragmenter.cc:47
virtual Status GetKey(TrackType track_type, EncryptionKey *key)=0
This class listens to progress updates events.
Status Initialize(scoped_ptr< MkvWriter > writer, StreamInfo *info, ProgressListener *progress_listener, MuxerListener *muxer_listener, KeySource *encryption_key_source, uint32_t max_sd_pixels, double clear_lead_in_seconds)
Definition: segmenter.cc:49
static bool GenerateRandomIv(FourCC protection_scheme, std::vector< uint8_t > *iv)
Definition: aes_cryptor.cc:109
EncryptingFragmenter generates MP4 fragments with sample encrypted.
virtual void OnProgress(double progress)=0
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:31
void UpdateProgress(uint64_t progress)
Update segmentation progress using ProgressListener.
Definition: segmenter.cc:255