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