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 const size_t kCencBlockSize = 16u;
21 } // namespace
22 
23 namespace edash_packager {
24 namespace media {
25 namespace mp4 {
26 
28  TrackFragment* traf,
29  scoped_ptr<EncryptionKey> encryption_key,
30  int64_t clear_time,
31  VideoCodec video_codec,
32  uint8_t nalu_length_size)
33  : Fragmenter(traf),
34  encryption_key_(encryption_key.Pass()),
35  video_codec_(video_codec),
36  nalu_length_size_(nalu_length_size),
37  clear_time_(clear_time) {
38  DCHECK(encryption_key_);
39  if (video_codec == kCodecVP8) {
40  vpx_parser_.reset(new VP8Parser);
41  } else if (video_codec == kCodecVP9) {
42  vpx_parser_.reset(new VP9Parser);
43  }
44 }
45 
46 EncryptingFragmenter::~EncryptingFragmenter() {}
47 
48 Status EncryptingFragmenter::AddSample(scoped_refptr<MediaSample> sample) {
49  DCHECK(sample);
50  if (!fragment_initialized()) {
51  Status status = InitializeFragment(sample->dts());
52  if (!status.ok())
53  return status;
54  }
55  if (encryptor_) {
56  Status status = EncryptSample(sample);
57  if (!status.ok())
58  return status;
59  }
60  return Fragmenter::AddSample(sample);
61 }
62 
64  Status status = Fragmenter::InitializeFragment(first_sample_dts);
65  if (!status.ok())
66  return status;
67 
68  traf()->auxiliary_size.sample_info_sizes.clear();
69  traf()->auxiliary_offset.offsets.clear();
70  if (IsSubsampleEncryptionRequired()) {
71  traf()->sample_encryption.flags |=
72  SampleEncryption::kUseSubsampleEncryption;
73  }
74  traf()->sample_encryption.sample_encryption_entries.clear();
75 
76  const bool enable_encryption = clear_time_ <= 0;
77  if (!enable_encryption) {
78  // This fragment should be in clear text.
79  // At most two sample description entries, an encrypted entry and a clear
80  // entry, are generated. The 1-based clear entry index is always 2.
81  const uint32_t kClearSampleDescriptionIndex = 2;
82 
83  traf()->header.flags |=
84  TrackFragmentHeader::kSampleDescriptionIndexPresentMask;
85  traf()->header.sample_description_index = kClearSampleDescriptionIndex;
86  }
87  return PrepareFragmentForEncryption(enable_encryption);
88 }
89 
91  if (encryptor_) {
92  DCHECK_LE(clear_time_, 0);
94  } else {
95  DCHECK_GT(clear_time_, 0);
96  clear_time_ -= fragment_duration();
97  }
99 }
100 
102  bool enable_encryption) {
103  return (!enable_encryption || encryptor_) ? Status::OK : CreateEncryptor();
104 }
105 
107  // The offset will be adjusted in Segmenter after knowing moof size.
108  traf()->auxiliary_offset.offsets.push_back(0);
109 
110  // Optimize saiz box.
111  SampleAuxiliaryInformationSize& saiz = traf()->auxiliary_size;
112  saiz.sample_count = traf()->runs[0].sample_sizes.size();
113  if (!saiz.sample_info_sizes.empty()) {
114  if (!OptimizeSampleEntries(&saiz.sample_info_sizes,
115  &saiz.default_sample_info_size)) {
116  saiz.default_sample_info_size = 0;
117  }
118  } else {
119  // |sample_info_sizes| table is filled in only for subsample encryption,
120  // otherwise |sample_info_size| is just the IV size.
121  DCHECK(!IsSubsampleEncryptionRequired());
122  saiz.default_sample_info_size = encryptor_->iv().size();
123  }
124  traf()->sample_encryption.iv_size = encryptor_->iv().size();
125 }
126 
128  DCHECK(encryption_key_);
129 
130  scoped_ptr<AesCtrEncryptor> encryptor(new AesCtrEncryptor());
131  const bool initialized = encryption_key_->iv.empty()
132  ? encryptor->InitializeWithRandomIv(
133  encryption_key_->key, kDefaultIvSize)
134  : encryptor->InitializeWithIv(
135  encryption_key_->key, encryption_key_->iv);
136  if (!initialized)
137  return Status(error::MUXER_FAILURE, "Failed to create the encryptor.");
138  encryptor_ = encryptor.Pass();
139  return Status::OK;
140 }
141 
142 void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) {
143  DCHECK(encryptor_);
144  CHECK(encryptor_->Encrypt(data, size, data));
145 }
146 
147 Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
148  DCHECK(encryptor_);
149 
150  SampleEncryptionEntry sample_encryption_entry;
151  sample_encryption_entry.initialization_vector = encryptor_->iv();
152  uint8_t* data = sample->writable_data();
153  if (IsSubsampleEncryptionRequired()) {
154  if (vpx_parser_) {
155  std::vector<VPxFrameInfo> vpx_frames;
156  if (!vpx_parser_->Parse(sample->data(), sample->data_size(),
157  &vpx_frames)) {
158  return Status(error::MUXER_FAILURE, "Failed to parse vpx frame.");
159  }
160 
161  const bool is_superframe = vpx_frames.size() > 1;
162  for (const VPxFrameInfo& frame : vpx_frames) {
163  SubsampleEntry subsample;
164  subsample.clear_bytes = frame.uncompressed_header_size;
165  subsample.cipher_bytes =
166  frame.frame_size - frame.uncompressed_header_size;
167 
168  // "VP Codec ISO Media File Format Binding" document requires that the
169  // encrypted bytes of each frame within the superframe must be block
170  // aligned so that the counter state can be computed for each frame
171  // within the superframe.
172  if (is_superframe) {
173  uint16_t misalign_bytes = subsample.cipher_bytes % kCencBlockSize;
174  subsample.clear_bytes += misalign_bytes;
175  subsample.cipher_bytes -= misalign_bytes;
176  }
177 
178  sample_encryption_entry.subsamples.push_back(subsample);
179  if (subsample.cipher_bytes > 0)
180  EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes);
181  data += frame.frame_size;
182  }
183  } else {
184  BufferReader reader(data, sample->data_size());
185  while (reader.HasBytes(1)) {
186  uint64_t nalu_length;
187  if (!reader.ReadNBytesInto8(&nalu_length, nalu_length_size_))
188  return Status(error::MUXER_FAILURE, "Fail to read nalu_length.");
189 
190  if (!reader.SkipBytes(nalu_length)) {
191  return Status(error::MUXER_FAILURE,
192  "Sample size does not match nalu_length.");
193  }
194 
195  SubsampleEntry subsample;
196  subsample.clear_bytes = nalu_length_size_ + 1;
197  subsample.cipher_bytes = nalu_length - 1;
198  sample_encryption_entry.subsamples.push_back(subsample);
199 
200  EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes);
201  data += nalu_length_size_ + nalu_length;
202  }
203  }
204 
205  // The length of per-sample auxiliary datum, defined in CENC ch. 7.
206  traf()->auxiliary_size.sample_info_sizes.push_back(
207  sample_encryption_entry.ComputeSize());
208  } else {
209  EncryptBytes(data, sample->data_size());
210  }
211 
212  traf()->sample_encryption.sample_encryption_entries.push_back(
213  sample_encryption_entry);
214  encryptor_->UpdateIv();
215  return Status::OK;
216 }
217 
218 } // namespace mp4
219 } // namespace media
220 } // 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:76
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:93