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