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