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