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