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