DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations 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/aes_pattern_cryptor.h"
13 #include "packager/media/base/buffer_reader.h"
14 #include "packager/media/base/key_source.h"
15 #include "packager/media/base/media_sample.h"
16 #include "packager/media/codecs/nalu_reader.h"
17 #include "packager/media/codecs/vp8_parser.h"
18 #include "packager/media/codecs/vp9_parser.h"
19 #include "packager/media/formats/mp4/box_definitions.h"
20 
21 namespace shaka {
22 namespace media {
23 namespace mp4 {
24 
25 namespace {
26 const size_t kCencBlockSize = 16u;
27 
28 // Adds one or more subsamples to |*subsamples|. This may add more than one
29 // if one of the values overflows the integer in the subsample.
30 void AddSubsamples(uint64_t clear_bytes,
31  uint64_t cipher_bytes,
32  std::vector<SubsampleEntry>* subsamples) {
33  CHECK_LT(cipher_bytes, std::numeric_limits<uint32_t>::max());
34  const uint64_t kUInt16Max = std::numeric_limits<uint16_t>::max();
35  while (clear_bytes > kUInt16Max) {
36  subsamples->push_back(SubsampleEntry(kUInt16Max, 0));
37  clear_bytes -= kUInt16Max;
38  }
39 
40  if (clear_bytes > 0 || cipher_bytes > 0)
41  subsamples->push_back(SubsampleEntry(clear_bytes, cipher_bytes));
42 }
43 
44 VideoCodec GetVideoCodec(const StreamInfo& stream_info) {
45  if (stream_info.stream_type() != kStreamVideo)
46  return kUnknownVideoCodec;
47  const VideoStreamInfo& video_stream_info =
48  static_cast<const VideoStreamInfo&>(stream_info);
49  return video_stream_info.codec();
50 }
51 
52 uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
53  if (stream_info.stream_type() != kStreamVideo)
54  return 0;
55 
56  const VideoStreamInfo& video_stream_info =
57  static_cast<const VideoStreamInfo&>(stream_info);
58  return video_stream_info.nalu_length_size();
59 }
60 } // namespace
61 
63  scoped_refptr<StreamInfo> info,
64  TrackFragment* traf,
65  scoped_ptr<EncryptionKey> encryption_key,
66  int64_t clear_time,
67  FourCC protection_scheme,
68  uint8_t crypt_byte_block,
69  uint8_t skip_byte_block,
70  MuxerListener* listener)
71  : Fragmenter(info, traf),
72  info_(info),
73  encryption_key_(encryption_key.Pass()),
74  nalu_length_size_(GetNaluLengthSize(*info)),
75  video_codec_(GetVideoCodec(*info)),
76  clear_time_(clear_time),
77  protection_scheme_(protection_scheme),
78  crypt_byte_block_(crypt_byte_block),
79  skip_byte_block_(skip_byte_block),
80  listener_(listener) {
81  DCHECK(encryption_key_);
82  switch (video_codec_) {
83  case kCodecVP8:
84  vpx_parser_.reset(new VP8Parser);
85  break;
86  case kCodecVP9:
87  vpx_parser_.reset(new VP9Parser);
88  break;
89  case kCodecH264:
90  header_parser_.reset(new H264VideoSliceHeaderParser);
91  break;
92  case kCodecHVC1:
93  FALLTHROUGH_INTENDED;
94  case kCodecHEV1:
95  header_parser_.reset(new H265VideoSliceHeaderParser);
96  break;
97  default:
98  if (nalu_length_size_ > 0) {
99  LOG(WARNING) << "Unknown video codec '" << video_codec_
100  << "', whole subsamples will be encrypted.";
101  }
102  }
103 }
104 
105 EncryptingFragmenter::~EncryptingFragmenter() {}
106 
107 Status EncryptingFragmenter::AddSample(scoped_refptr<MediaSample> sample) {
108  DCHECK(sample);
109  if (!fragment_initialized()) {
110  Status status = InitializeFragment(sample->dts());
111  if (!status.ok())
112  return status;
113  }
114  if (encryptor_) {
115  Status status = EncryptSample(sample);
116  if (!status.ok())
117  return status;
118  }
119  return Fragmenter::AddSample(sample);
120 }
121 
123  Status status = Fragmenter::InitializeFragment(first_sample_dts);
124  if (!status.ok())
125  return status;
126 
127  if (header_parser_ && !header_parser_->Initialize(info_->codec_config()))
128  return Status(error::MUXER_FAILURE, "Fail to read SPS and PPS data.");
129 
130  traf()->auxiliary_size.sample_info_sizes.clear();
131  traf()->auxiliary_offset.offsets.clear();
132  if (IsSubsampleEncryptionRequired()) {
133  traf()->sample_encryption.flags |=
134  SampleEncryption::kUseSubsampleEncryption;
135  }
136  traf()->sample_encryption.sample_encryption_entries.clear();
137 
138  const bool enable_encryption = clear_time_ <= 0;
139  if (!enable_encryption) {
140  // This fragment should be in clear text.
141  // At most two sample description entries, an encrypted entry and a clear
142  // entry, are generated. The 1-based clear entry index is always 2.
143  const uint32_t kClearSampleDescriptionIndex = 2;
144 
145  traf()->header.flags |=
146  TrackFragmentHeader::kSampleDescriptionIndexPresentMask;
147  traf()->header.sample_description_index = kClearSampleDescriptionIndex;
148  } else {
149  if (listener_)
150  listener_->OnEncryptionStart();
151  }
152  return PrepareFragmentForEncryption(enable_encryption);
153 }
154 
156  if (encryptor_) {
157  DCHECK_LE(clear_time_, 0);
159  } else {
160  DCHECK_GT(clear_time_, 0);
161  clear_time_ -= fragment_duration();
162  }
164 }
165 
167  bool enable_encryption) {
168  return (!enable_encryption || encryptor_) ? Status::OK : CreateEncryptor();
169 }
170 
172  // The offset will be adjusted in Segmenter after knowing moof size.
173  traf()->auxiliary_offset.offsets.push_back(0);
174 
175  // For 'cbcs' scheme, Constant IVs SHALL be used.
176  const size_t per_sample_iv_size =
177  (protection_scheme_ == FOURCC_cbcs) ? 0 : encryptor_->iv().size();
178  traf()->sample_encryption.iv_size = per_sample_iv_size;
179 
180  // Optimize saiz box.
181  SampleAuxiliaryInformationSize& saiz = traf()->auxiliary_size;
182  saiz.sample_count = traf()->runs[0].sample_sizes.size();
183  if (!saiz.sample_info_sizes.empty()) {
184  if (!OptimizeSampleEntries(&saiz.sample_info_sizes,
185  &saiz.default_sample_info_size)) {
186  saiz.default_sample_info_size = 0;
187  }
188  } else {
189  // |sample_info_sizes| table is filled in only for subsample encryption,
190  // otherwise |sample_info_size| is just the IV size.
191  DCHECK(!IsSubsampleEncryptionRequired());
192  saiz.default_sample_info_size = per_sample_iv_size;
193  }
194 
195  // It should only happen with full sample encryption + constant iv, i.e.
196  // 'cbcs' applying to audio.
197  if (saiz.default_sample_info_size == 0 && saiz.sample_info_sizes.empty()) {
198  DCHECK_EQ(protection_scheme_, FOURCC_cbcs);
199  DCHECK(!IsSubsampleEncryptionRequired());
200  // ISO/IEC 23001-7:2016(E) The sample auxiliary information would then be
201  // empty and should be emitted. Clear saiz and saio boxes so they are not
202  // written.
203  saiz.sample_count = 0;
204  traf()->auxiliary_offset.offsets.clear();
205  }
206 }
207 
209  DCHECK(encryption_key_);
210  scoped_ptr<AesCryptor> encryptor;
211  switch (protection_scheme_) {
212  case FOURCC_cenc:
213  encryptor.reset(new AesCtrEncryptor);
214  break;
215  case FOURCC_cbc1:
216  encryptor.reset(new AesCbcEncryptor(kNoPadding));
217  break;
218  case FOURCC_cens:
219  encryptor.reset(new AesPatternCryptor(
220  crypt_byte_block(), skip_byte_block(),
222  AesCryptor::kDontUseConstantIv,
223  scoped_ptr<AesCryptor>(new AesCtrEncryptor())));
224  break;
225  case FOURCC_cbcs:
226  encryptor.reset(new AesPatternCryptor(
227  crypt_byte_block(), skip_byte_block(),
229  AesCryptor::kUseConstantIv,
230  scoped_ptr<AesCryptor>(new AesCbcEncryptor(kNoPadding))));
231  break;
232  default:
233  return Status(error::MUXER_FAILURE, "Unsupported protection scheme.");
234  }
235 
236  DCHECK(!encryption_key_->iv.empty());
237  const bool initialized =
238  encryptor->InitializeWithIv(encryption_key_->key, encryption_key_->iv);
239  if (!initialized)
240  return Status(error::MUXER_FAILURE, "Failed to create the encryptor.");
241  encryptor_ = encryptor.Pass();
242  return Status::OK;
243 }
244 
245 void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) {
246  DCHECK(encryptor_);
247  CHECK(encryptor_->Crypt(data, size, data));
248 }
249 
250 Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
251  DCHECK(encryptor_);
252 
253  SampleEncryptionEntry sample_encryption_entry;
254  // For 'cbcs' scheme, Constant IVs SHALL be used.
255  if (protection_scheme_ != FOURCC_cbcs)
256  sample_encryption_entry.initialization_vector = encryptor_->iv();
257  uint8_t* data = sample->writable_data();
258  if (IsSubsampleEncryptionRequired()) {
259  if (vpx_parser_) {
260  std::vector<VPxFrameInfo> vpx_frames;
261  if (!vpx_parser_->Parse(sample->data(), sample->data_size(),
262  &vpx_frames)) {
263  return Status(error::MUXER_FAILURE, "Failed to parse vpx frame.");
264  }
265 
266  const bool is_superframe = vpx_frames.size() > 1;
267  for (const VPxFrameInfo& frame : vpx_frames) {
268  SubsampleEntry subsample;
269  subsample.clear_bytes = frame.uncompressed_header_size;
270  subsample.cipher_bytes =
271  frame.frame_size - frame.uncompressed_header_size;
272 
273  // "VP Codec ISO Media File Format Binding" document requires that the
274  // encrypted bytes of each frame within the superframe must be block
275  // aligned so that the counter state can be computed for each frame
276  // within the superframe.
277  // ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
278  // The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
279  // avoid partial blocks in Subsamples.
280  if (is_superframe || protection_scheme_ == FOURCC_cbc1 ||
281  protection_scheme_ == FOURCC_cens) {
282  const uint16_t misalign_bytes =
283  subsample.cipher_bytes % kCencBlockSize;
284  subsample.clear_bytes += misalign_bytes;
285  subsample.cipher_bytes -= misalign_bytes;
286  }
287 
288  sample_encryption_entry.subsamples.push_back(subsample);
289  if (subsample.cipher_bytes > 0)
290  EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes);
291  data += frame.frame_size;
292  }
293  // Add subsample for the superframe index if exists.
294  if (is_superframe) {
295  size_t index_size = sample->data() + sample->data_size() - data;
296  DCHECK_LE(index_size, 2 + vpx_frames.size() * 4);
297  DCHECK_GE(index_size, 2 + vpx_frames.size() * 1);
298  SubsampleEntry subsample;
299  subsample.clear_bytes = index_size;
300  subsample.cipher_bytes = 0;
301  sample_encryption_entry.subsamples.push_back(subsample);
302  }
303  } else {
304  const Nalu::CodecType nalu_type =
305  (video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1)
306  ? Nalu::kH265
307  : Nalu::kH264;
308  NaluReader reader(nalu_type, nalu_length_size_, data,
309  sample->data_size());
310 
311  // Store the current length of clear data. This is used to squash
312  // multiple unencrypted NAL units into fewer subsample entries.
313  uint64_t accumulated_clear_bytes = 0;
314 
315  Nalu nalu;
316  NaluReader::Result result;
317  while ((result = reader.Advance(&nalu)) == NaluReader::kOk) {
318  if (nalu.is_video_slice()) {
319  // For video-slice NAL units, encrypt the video slice. This skips
320  // the frame header. If this is an unrecognized codec (e.g. H.265),
321  // the whole NAL unit will be encrypted.
322  const int64_t video_slice_header_size =
323  header_parser_ ? header_parser_->GetHeaderSize(nalu) : 0;
324  if (video_slice_header_size < 0)
325  return Status(error::MUXER_FAILURE, "Failed to read slice header.");
326 
327  uint64_t current_clear_bytes =
328  nalu.header_size() + video_slice_header_size;
329  uint64_t cipher_bytes = nalu.payload_size() - video_slice_header_size;
330 
331  // ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
332  // The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
333  // avoid partial blocks in Subsamples.
334  if (protection_scheme_ == FOURCC_cbc1 ||
335  protection_scheme_ == FOURCC_cens) {
336  const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize;
337  current_clear_bytes += misalign_bytes;
338  cipher_bytes -= misalign_bytes;
339  }
340 
341  const uint8_t* nalu_data = nalu.data() + current_clear_bytes;
342  EncryptBytes(const_cast<uint8_t*>(nalu_data), cipher_bytes);
343 
344  AddSubsamples(
345  accumulated_clear_bytes + nalu_length_size_ + current_clear_bytes,
346  cipher_bytes, &sample_encryption_entry.subsamples);
347  accumulated_clear_bytes = 0;
348  } else {
349  // For non-video-slice NAL units, don't encrypt.
350  accumulated_clear_bytes +=
351  nalu_length_size_ + nalu.header_size() + nalu.payload_size();
352  }
353  }
354  if (result != NaluReader::kEOStream)
355  return Status(error::MUXER_FAILURE, "Failed to parse NAL units.");
356  AddSubsamples(accumulated_clear_bytes, 0,
357  &sample_encryption_entry.subsamples);
358  }
359  DCHECK_EQ(sample_encryption_entry.GetTotalSizeOfSubsamples(),
360  sample->data_size());
361 
362  // The length of per-sample auxiliary datum, defined in CENC ch. 7.
363  traf()->auxiliary_size.sample_info_sizes.push_back(
364  sample_encryption_entry.ComputeSize());
365  } else {
366  DCHECK_LE(crypt_byte_block(), 1u);
367  DCHECK_EQ(skip_byte_block(), 0u);
368  EncryptBytes(data, sample->data_size());
369  }
370 
371  traf()->sample_encryption.sample_encryption_entries.push_back(
372  sample_encryption_entry);
373  encryptor_->UpdateIv();
374  return Status::OK;
375 }
376 
377 bool EncryptingFragmenter::IsSubsampleEncryptionRequired() {
378  return vpx_parser_ || nalu_length_size_ != 0;
379 }
380 
381 } // namespace mp4
382 } // namespace media
383 } // namespace shaka
Status AddSample(scoped_refptr< MediaSample > sample) override
Status InitializeFragment(int64_t first_sample_dts) override
EncryptingFragmenter(scoped_refptr< StreamInfo > info, TrackFragment *traf, scoped_ptr< EncryptionKey > encryption_key, int64_t clear_time, FourCC protection_scheme, uint8_t crypt_byte_block, uint8_t skip_byte_block, MuxerListener *listener)
virtual Status AddSample(scoped_refptr< MediaSample > sample)
Definition: fragmenter.cc:45
virtual void OnEncryptionStart()=0
void FinalizeFragment() override
Finalize and optimize the fragment.
virtual Status InitializeFragment(int64_t first_sample_dts)
Definition: fragmenter.cc:85
virtual void FinalizeFragment()
Finalize and optimize the fragment.
Definition: fragmenter.cc:104
virtual void FinalizeFragmentForEncryption()
Finalize current fragment for encryption.
Class to parse a vp9 bit stream.
Definition: vp9_parser.h:20
Implements pattern-based encryption/decryption.
virtual Status PrepareFragmentForEncryption(bool enable_encryption)
bool OptimizeSampleEntries(std::vector< T > *entries, T *default_value)
Definition: fragmenter.h:92