DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerator
encrypting_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/encrypting_fragmenter.h"
8 
9 #include "packager/media/base/aes_encryptor.h"
10 #include "packager/media/base/buffer_reader.h"
11 #include "packager/media/base/key_source.h"
12 #include "packager/media/base/media_sample.h"
13 #include "packager/media/filters/vp8_parser.h"
14 #include "packager/media/filters/vp9_parser.h"
15 #include "packager/media/formats/mp4/box_definitions.h"
16 
17 namespace {
18 // Generate 64bit IV by default.
19 const size_t kDefaultIvSize = 8u;
20 } // namespace
21 
22 namespace edash_packager {
23 namespace media {
24 namespace mp4 {
25 
27  TrackFragment* traf,
28  scoped_ptr<EncryptionKey> encryption_key,
29  int64_t clear_time,
30  VideoCodec video_codec,
31  uint8_t nalu_length_size)
32  : Fragmenter(traf),
33  encryption_key_(encryption_key.Pass()),
34  video_codec_(video_codec),
35  nalu_length_size_(nalu_length_size),
36  clear_time_(clear_time) {
37  DCHECK(encryption_key_);
38  if (video_codec == kCodecVP8) {
39  vpx_parser_.reset(new VP8Parser);
40  } else if (video_codec == kCodecVP9) {
41  vpx_parser_.reset(new VP9Parser);
42  }
43 }
44 
45 EncryptingFragmenter::~EncryptingFragmenter() {}
46 
47 Status EncryptingFragmenter::AddSample(scoped_refptr<MediaSample> sample) {
48  DCHECK(sample);
49  if (!fragment_initialized()) {
50  Status status = InitializeFragment(sample->dts());
51  if (!status.ok())
52  return status;
53  }
54  if (encryptor_) {
55  Status status = EncryptSample(sample);
56  if (!status.ok())
57  return status;
58  }
59  return Fragmenter::AddSample(sample);
60 }
61 
63  Status status = Fragmenter::InitializeFragment(first_sample_dts);
64  if (!status.ok())
65  return status;
66 
67  traf()->auxiliary_size.sample_info_sizes.clear();
68  traf()->auxiliary_offset.offsets.clear();
69  if (IsSubsampleEncryptionRequired()) {
70  traf()->sample_encryption.flags |=
71  SampleEncryption::kUseSubsampleEncryption;
72  }
73  traf()->sample_encryption.sample_encryption_entries.clear();
74 
75  const bool enable_encryption = clear_time_ <= 0;
76  if (!enable_encryption) {
77  // This fragment should be in clear text.
78  // At most two sample description entries, an encrypted entry and a clear
79  // entry, are generated. The 1-based clear entry index is always 2.
80  const uint32_t kClearSampleDescriptionIndex = 2;
81 
82  traf()->header.flags |=
83  TrackFragmentHeader::kSampleDescriptionIndexPresentMask;
84  traf()->header.sample_description_index = kClearSampleDescriptionIndex;
85  }
86  return PrepareFragmentForEncryption(enable_encryption);
87 }
88 
90  if (encryptor_) {
91  DCHECK_LE(clear_time_, 0);
93  } else {
94  DCHECK_GT(clear_time_, 0);
95  clear_time_ -= fragment_duration();
96  }
98 }
99 
101  bool enable_encryption) {
102  return (!enable_encryption || encryptor_) ? Status::OK : CreateEncryptor();
103 }
104 
106  // The offset will be adjusted in Segmenter after knowing moof size.
107  traf()->auxiliary_offset.offsets.push_back(0);
108 
109  // Optimize saiz box.
110  SampleAuxiliaryInformationSize& saiz = traf()->auxiliary_size;
111  saiz.sample_count = traf()->runs[0].sample_sizes.size();
112  if (!saiz.sample_info_sizes.empty()) {
113  if (!OptimizeSampleEntries(&saiz.sample_info_sizes,
114  &saiz.default_sample_info_size)) {
115  saiz.default_sample_info_size = 0;
116  }
117  } else {
118  // |sample_info_sizes| table is filled in only for subsample encryption,
119  // otherwise |sample_info_size| is just the IV size.
120  DCHECK(!IsSubsampleEncryptionRequired());
121  saiz.default_sample_info_size = encryptor_->iv().size();
122  }
123  traf()->sample_encryption.iv_size = encryptor_->iv().size();
124 }
125 
127  DCHECK(encryption_key_);
128 
129  scoped_ptr<AesCtrEncryptor> encryptor(new AesCtrEncryptor());
130  const bool initialized = encryption_key_->iv.empty()
131  ? encryptor->InitializeWithRandomIv(
132  encryption_key_->key, kDefaultIvSize)
133  : encryptor->InitializeWithIv(
134  encryption_key_->key, encryption_key_->iv);
135  if (!initialized)
136  return Status(error::MUXER_FAILURE, "Failed to create the encryptor.");
137  encryptor_ = encryptor.Pass();
138  return Status::OK;
139 }
140 
141 void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) {
142  DCHECK(encryptor_);
143  CHECK(encryptor_->Encrypt(data, size, data));
144 }
145 
146 Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
147  DCHECK(encryptor_);
148 
149  SampleEncryptionEntry sample_encryption_entry;
150  sample_encryption_entry.initialization_vector = encryptor_->iv();
151  uint8_t* data = sample->writable_data();
152  if (IsSubsampleEncryptionRequired()) {
153  if (vpx_parser_) {
154  std::vector<VPxFrameInfo> vpx_frames;
155  if (!vpx_parser_->Parse(sample->data(), sample->data_size(),
156  &vpx_frames)) {
157  return Status(error::MUXER_FAILURE, "Failed to parse vpx frame.");
158  }
159  for (const VPxFrameInfo& frame : vpx_frames) {
160  SubsampleEntry subsample;
161  subsample.clear_bytes = frame.uncompressed_header_size;
162  subsample.cipher_bytes =
163  frame.frame_size - frame.uncompressed_header_size;
164  sample_encryption_entry.subsamples.push_back(subsample);
165  if (subsample.cipher_bytes > 0)
166  EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes);
167  data += frame.frame_size;
168  }
169  } else {
170  BufferReader reader(data, sample->data_size());
171  while (reader.HasBytes(1)) {
172  uint64_t nalu_length;
173  if (!reader.ReadNBytesInto8(&nalu_length, nalu_length_size_))
174  return Status(error::MUXER_FAILURE, "Fail to read nalu_length.");
175 
176  if (!reader.SkipBytes(nalu_length)) {
177  return Status(error::MUXER_FAILURE,
178  "Sample size does not match nalu_length.");
179  }
180 
181  SubsampleEntry subsample;
182  subsample.clear_bytes = nalu_length_size_ + 1;
183  subsample.cipher_bytes = nalu_length - 1;
184  sample_encryption_entry.subsamples.push_back(subsample);
185 
186  EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes);
187  data += nalu_length_size_ + nalu_length;
188  }
189  }
190 
191  // The length of per-sample auxiliary datum, defined in CENC ch. 7.
192  traf()->auxiliary_size.sample_info_sizes.push_back(
193  sample_encryption_entry.ComputeSize());
194  } else {
195  EncryptBytes(data, sample->data_size());
196  }
197 
198  traf()->sample_encryption.sample_encryption_entries.push_back(
199  sample_encryption_entry);
200  encryptor_->UpdateIv();
201  return Status::OK;
202 }
203 
204 } // namespace mp4
205 } // namespace media
206 } // namespace edash_packager
EncryptingFragmenter(TrackFragment *traf, scoped_ptr< EncryptionKey > encryption_key, int64_t clear_time, VideoCodec video_codec, uint8_t nalu_length_size)
Status InitializeFragment(int64_t first_sample_dts) override
virtual Status InitializeFragment(int64_t first_sample_dts)
Definition: fragmenter.cc:73
virtual Status AddSample(scoped_refptr< MediaSample > sample)
Definition: fragmenter.cc:36
Class to parse a vp9 bit stream.
Definition: vp9_parser.h:20
bool OptimizeSampleEntries(std::vector< T > *entries, T *default_value)
Definition: fragmenter.h:89
Status AddSample(scoped_refptr< MediaSample > sample) override
void FinalizeFragment() override
Finalize and optimize the fragment.
virtual Status PrepareFragmentForEncryption(bool enable_encryption)
virtual void FinalizeFragmentForEncryption()
Finalize current fragment for encryption.
virtual void FinalizeFragment()
Finalize and optimize the fragment.
Definition: fragmenter.cc:90