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 <limits>
10 
11 #include "packager/media/base/aes_encryptor.h"
12 #include "packager/media/base/buffer_reader.h"
13 #include "packager/media/base/key_source.h"
14 #include "packager/media/base/media_sample.h"
15 #include "packager/media/filters/nalu_reader.h"
16 #include "packager/media/filters/vp8_parser.h"
17 #include "packager/media/filters/vp9_parser.h"
18 #include "packager/media/formats/mp4/box_definitions.h"
19 
20 namespace edash_packager {
21 namespace media {
22 namespace mp4 {
23 
24 namespace {
25 // Generate 64bit IV by default.
26 const size_t kDefaultIvSizeForCtr = 8u;
27 const size_t kDefaultIvSizeForCbc = 16u;
28 const size_t kCencBlockSize = 16u;
29 
30 // Adds one or more subsamples to |*subsamples|. This may add more than one
31 // if one of the values overflows the integer in the subsample.
32 void AddSubsamples(uint64_t clear_bytes,
33  uint64_t cipher_bytes,
34  std::vector<SubsampleEntry>* subsamples) {
35  CHECK_LT(cipher_bytes, std::numeric_limits<uint32_t>::max());
36  const uint64_t kUInt16Max = std::numeric_limits<uint16_t>::max();
37  while (clear_bytes > kUInt16Max) {
38  subsamples->push_back(SubsampleEntry(kUInt16Max, 0));
39  clear_bytes -= kUInt16Max;
40  }
41 
42  if (clear_bytes > 0 || cipher_bytes > 0)
43  subsamples->push_back(SubsampleEntry(clear_bytes, cipher_bytes));
44 }
45 
46 VideoCodec GetVideoCodec(const StreamInfo& stream_info) {
47  if (stream_info.stream_type() != kStreamVideo)
48  return kUnknownVideoCodec;
49  const VideoStreamInfo& video_stream_info =
50  static_cast<const VideoStreamInfo&>(stream_info);
51  return video_stream_info.codec();
52 }
53 
54 uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
55  if (stream_info.stream_type() != kStreamVideo)
56  return 0;
57 
58  const VideoStreamInfo& video_stream_info =
59  static_cast<const VideoStreamInfo&>(stream_info);
60  return video_stream_info.nalu_length_size();
61 }
62 } // namespace
63 
65  scoped_refptr<StreamInfo> info,
66  TrackFragment* traf,
67  scoped_ptr<EncryptionKey> encryption_key,
68  int64_t clear_time,
69  EncryptionMode encryption_mode)
70  : Fragmenter(traf),
71  info_(info),
72  encryption_key_(encryption_key.Pass()),
73  nalu_length_size_(GetNaluLengthSize(*info)),
74  video_codec_(GetVideoCodec(*info)),
75  clear_time_(clear_time),
76  encryption_mode_(encryption_mode) {
77  DCHECK(encryption_key_);
78  if (video_codec_ == kCodecVP8) {
79  vpx_parser_.reset(new VP8Parser);
80  } else if (video_codec_ == kCodecVP9) {
81  vpx_parser_.reset(new VP9Parser);
82  } else if (video_codec_ == kCodecH264) {
83  header_parser_.reset(new H264VideoSliceHeaderParser);
84  } else if (video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1) {
85  header_parser_.reset(new H265VideoSliceHeaderParser);
86  } else if (nalu_length_size_ > 0) {
87  LOG(WARNING) << "Unknown video codec '" << video_codec_
88  << "', whole subsamples will be encrypted.";
89  }
90 }
91 
92 EncryptingFragmenter::~EncryptingFragmenter() {}
93 
94 Status EncryptingFragmenter::AddSample(scoped_refptr<MediaSample> sample) {
95  DCHECK(sample);
96  if (!fragment_initialized()) {
97  Status status = InitializeFragment(sample->dts());
98  if (!status.ok())
99  return status;
100  }
101  if (encryptor_) {
102  Status status = EncryptSample(sample);
103  if (!status.ok())
104  return status;
105  }
106  return Fragmenter::AddSample(sample);
107 }
108 
110  Status status = Fragmenter::InitializeFragment(first_sample_dts);
111  if (!status.ok())
112  return status;
113 
114  if (header_parser_ && !header_parser_->Initialize(info_->extra_data()))
115  return Status(error::MUXER_FAILURE, "Fail to read SPS and PPS data.");
116 
117  traf()->auxiliary_size.sample_info_sizes.clear();
118  traf()->auxiliary_offset.offsets.clear();
119  if (IsSubsampleEncryptionRequired()) {
120  traf()->sample_encryption.flags |=
121  SampleEncryption::kUseSubsampleEncryption;
122  }
123  traf()->sample_encryption.sample_encryption_entries.clear();
124 
125  const bool enable_encryption = clear_time_ <= 0;
126  if (!enable_encryption) {
127  // This fragment should be in clear text.
128  // At most two sample description entries, an encrypted entry and a clear
129  // entry, are generated. The 1-based clear entry index is always 2.
130  const uint32_t kClearSampleDescriptionIndex = 2;
131 
132  traf()->header.flags |=
133  TrackFragmentHeader::kSampleDescriptionIndexPresentMask;
134  traf()->header.sample_description_index = kClearSampleDescriptionIndex;
135  }
136  return PrepareFragmentForEncryption(enable_encryption);
137 }
138 
140  if (encryptor_) {
141  DCHECK_LE(clear_time_, 0);
143  } else {
144  DCHECK_GT(clear_time_, 0);
145  clear_time_ -= fragment_duration();
146  }
148 }
149 
151  bool enable_encryption) {
152  return (!enable_encryption || encryptor_) ? Status::OK : CreateEncryptor();
153 }
154 
156  // The offset will be adjusted in Segmenter after knowing moof size.
157  traf()->auxiliary_offset.offsets.push_back(0);
158 
159  // Optimize saiz box.
160  SampleAuxiliaryInformationSize& saiz = traf()->auxiliary_size;
161  saiz.sample_count = traf()->runs[0].sample_sizes.size();
162  if (!saiz.sample_info_sizes.empty()) {
163  if (!OptimizeSampleEntries(&saiz.sample_info_sizes,
164  &saiz.default_sample_info_size)) {
165  saiz.default_sample_info_size = 0;
166  }
167  } else {
168  // |sample_info_sizes| table is filled in only for subsample encryption,
169  // otherwise |sample_info_size| is just the IV size.
170  DCHECK(!IsSubsampleEncryptionRequired());
171  saiz.default_sample_info_size = encryptor_->iv().size();
172  }
173  traf()->sample_encryption.iv_size = encryptor_->iv().size();
174 }
175 
177  DCHECK(encryption_key_);
178  scoped_ptr<AesEncryptor> encryptor;
179  size_t default_iv_size = 0;
180  if (encryption_mode_ == kEncryptionModeAesCtr) {
181  encryptor.reset(new AesCtrEncryptor);
182  default_iv_size = kDefaultIvSizeForCtr;
183  } else if (encryption_mode_ == kEncryptionModeAesCbc) {
184  encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls));
185  default_iv_size = kDefaultIvSizeForCbc;
186  } else {
187  return Status(error::MUXER_FAILURE, "Unsupported encryption mode.");
188  }
189  const bool initialized = encryption_key_->iv.empty()
190  ? encryptor->InitializeWithRandomIv(
191  encryption_key_->key, default_iv_size)
192  : encryptor->InitializeWithIv(
193  encryption_key_->key, encryption_key_->iv);
194  if (!initialized)
195  return Status(error::MUXER_FAILURE, "Failed to create the encryptor.");
196  encryptor_ = encryptor.Pass();
197  return Status::OK;
198 }
199 
200 void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) {
201  DCHECK(encryptor_);
202  CHECK(encryptor_->Encrypt(data, size, data));
203 }
204 
205 Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
206  DCHECK(encryptor_);
207 
208  SampleEncryptionEntry sample_encryption_entry;
209  sample_encryption_entry.initialization_vector = encryptor_->iv();
210  uint8_t* data = sample->writable_data();
211  if (IsSubsampleEncryptionRequired()) {
212  if (vpx_parser_) {
213  std::vector<VPxFrameInfo> vpx_frames;
214  if (!vpx_parser_->Parse(sample->data(), sample->data_size(),
215  &vpx_frames)) {
216  return Status(error::MUXER_FAILURE, "Failed to parse vpx frame.");
217  }
218 
219  const bool is_superframe = vpx_frames.size() > 1;
220  for (const VPxFrameInfo& frame : vpx_frames) {
221  SubsampleEntry subsample;
222  subsample.clear_bytes = frame.uncompressed_header_size;
223  subsample.cipher_bytes =
224  frame.frame_size - frame.uncompressed_header_size;
225 
226  // "VP Codec ISO Media File Format Binding" document requires that the
227  // encrypted bytes of each frame within the superframe must be block
228  // aligned so that the counter state can be computed for each frame
229  // within the superframe.
230  // For AES-CBC mode 'cbc1' scheme, clear data is sized appropriately so
231  // that the cipher data is block aligned.
232  if (is_superframe || encryption_mode_ == kEncryptionModeAesCbc) {
233  const uint16_t misalign_bytes =
234  subsample.cipher_bytes % kCencBlockSize;
235  subsample.clear_bytes += misalign_bytes;
236  subsample.cipher_bytes -= misalign_bytes;
237  }
238 
239  sample_encryption_entry.subsamples.push_back(subsample);
240  if (subsample.cipher_bytes > 0)
241  EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes);
242  data += frame.frame_size;
243  }
244  } else {
245  const NaluReader::NaluType nalu_type =
246  (video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1)
247  ? NaluReader::kH265
248  : NaluReader::kH264;
249  NaluReader reader(nalu_type, nalu_length_size_, data,
250  sample->data_size());
251 
252  // Store the current length of clear data. This is used to squash
253  // multiple unencrypted NAL units into fewer subsample entries.
254  uint64_t accumulated_clear_bytes = 0;
255 
256  Nalu nalu;
257  NaluReader::Result result;
258  while ((result = reader.Advance(&nalu)) == NaluReader::kOk) {
259  if (nalu.is_video_slice()) {
260  // For video-slice NAL units, encrypt the video slice. This skips
261  // the frame header. If this is an unrecognized codec (e.g. H.265),
262  // the whole NAL unit will be encrypted.
263  const int64_t video_slice_header_size =
264  header_parser_ ? header_parser_->GetHeaderSize(nalu) : 0;
265  if (video_slice_header_size < 0)
266  return Status(error::MUXER_FAILURE, "Failed to read slice header.");
267 
268  uint64_t current_clear_bytes =
269  nalu.header_size() + video_slice_header_size;
270  uint64_t cipher_bytes = nalu.payload_size() - video_slice_header_size;
271 
272  // For AES-CBC mode 'cbc1' scheme, clear data is sized appropriately
273  // so that the cipher data is block aligned.
274  if (encryption_mode_ == kEncryptionModeAesCbc) {
275  const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize;
276  current_clear_bytes += misalign_bytes;
277  cipher_bytes -= misalign_bytes;
278  }
279 
280  const uint8_t* nalu_data = nalu.data() + current_clear_bytes;
281  EncryptBytes(const_cast<uint8_t*>(nalu_data), cipher_bytes);
282 
283  AddSubsamples(
284  accumulated_clear_bytes + nalu_length_size_ + current_clear_bytes,
285  cipher_bytes, &sample_encryption_entry.subsamples);
286  accumulated_clear_bytes = 0;
287  } else {
288  // For non-video-slice NAL units, don't encrypt.
289  accumulated_clear_bytes +=
290  nalu_length_size_ + nalu.header_size() + nalu.payload_size();
291  }
292  }
293  if (result != NaluReader::kEOStream)
294  return Status(error::MUXER_FAILURE, "Failed to parse NAL units.");
295  AddSubsamples(accumulated_clear_bytes, 0,
296  &sample_encryption_entry.subsamples);
297  }
298 
299  // The length of per-sample auxiliary datum, defined in CENC ch. 7.
300  traf()->auxiliary_size.sample_info_sizes.push_back(
301  sample_encryption_entry.ComputeSize());
302  } else {
303  uint64_t encryption_data_size = sample->data_size();
304  // AES-CBC mode requires all encrypted cipher blocks to be 16 bytes. The
305  // partial blocks are left unencrypted.
306  if (encryption_mode_ == kEncryptionModeAesCbc)
307  encryption_data_size -= encryption_data_size % kCencBlockSize;
308  EncryptBytes(data, encryption_data_size);
309  }
310 
311  traf()->sample_encryption.sample_encryption_entries.push_back(
312  sample_encryption_entry);
313  encryptor_->UpdateIv();
314  return Status::OK;
315 }
316 
317 bool EncryptingFragmenter::IsSubsampleEncryptionRequired() {
318  return vpx_parser_ || nalu_length_size_ != 0;
319 }
320 
321 } // namespace mp4
322 } // namespace media
323 } // namespace edash_packager
EncryptingFragmenter(scoped_refptr< StreamInfo > info, TrackFragment *traf, scoped_ptr< EncryptionKey > encryption_key, int64_t clear_time, EncryptionMode encryption_mode)
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