Shaka Packager SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
encryption_handler.cc
1 // Copyright 2017 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/crypto/encryption_handler.h"
8 
9 #include <stddef.h>
10 #include <stdint.h>
11 
12 #include <algorithm>
13 #include <limits>
14 
15 #include "packager/media/base/aes_encryptor.h"
16 #include "packager/media/base/aes_pattern_cryptor.h"
17 #include "packager/media/base/key_source.h"
18 #include "packager/media/base/media_sample.h"
19 #include "packager/media/base/audio_stream_info.h"
20 #include "packager/media/base/video_stream_info.h"
21 #include "packager/media/codecs/video_slice_header_parser.h"
22 #include "packager/media/codecs/vp8_parser.h"
23 #include "packager/media/codecs/vp9_parser.h"
24 
25 namespace shaka {
26 namespace media {
27 
28 namespace {
29 const size_t kCencBlockSize = 16u;
30 
31 // The encryption handler only supports a single output.
32 const size_t kStreamIndex = 0;
33 
34 // The default KID for key rotation is all 0s.
35 const uint8_t kKeyRotationDefaultKeyId[] = {
36  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37 };
38 
39 // Adds one or more subsamples to |*decrypt_config|. This may add more than one
40 // if one of the values overflows the integer in the subsample.
41 void AddSubsample(uint64_t clear_bytes,
42  uint64_t cipher_bytes,
43  DecryptConfig* decrypt_config) {
44  CHECK_LT(cipher_bytes, std::numeric_limits<uint32_t>::max());
45  const uint64_t kUInt16Max = std::numeric_limits<uint16_t>::max();
46  while (clear_bytes > kUInt16Max) {
47  decrypt_config->AddSubsample(kUInt16Max, 0);
48  clear_bytes -= kUInt16Max;
49  }
50 
51  if (clear_bytes > 0 || cipher_bytes > 0)
52  decrypt_config->AddSubsample(clear_bytes, cipher_bytes);
53 }
54 
55 uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
56  if (stream_info.stream_type() != kStreamVideo)
57  return 0;
58 
59  const VideoStreamInfo& video_stream_info =
60  static_cast<const VideoStreamInfo&>(stream_info);
61  return video_stream_info.nalu_length_size();
62 }
63 
64 std::string GetStreamLabelForEncryption(
65  const StreamInfo& stream_info,
66  const std::function<std::string(
67  const EncryptionParams::EncryptedStreamAttributes& stream_attributes)>&
68  stream_label_func) {
69  EncryptionParams::EncryptedStreamAttributes stream_attributes;
70  if (stream_info.stream_type() == kStreamAudio) {
71  stream_attributes.stream_type =
72  EncryptionParams::EncryptedStreamAttributes::kAudio;
73  } else if (stream_info.stream_type() == kStreamVideo) {
74  const VideoStreamInfo& video_stream_info =
75  static_cast<const VideoStreamInfo&>(stream_info);
76  stream_attributes.stream_type =
77  EncryptionParams::EncryptedStreamAttributes::kVideo;
78  stream_attributes.oneof.video.width = video_stream_info.width();
79  stream_attributes.oneof.video.height = video_stream_info.height();
80  }
81  return stream_label_func(stream_attributes);
82 }
83 } // namespace
84 
85 EncryptionHandler::EncryptionHandler(const EncryptionParams& encryption_params,
86  KeySource* key_source)
87  : encryption_params_(encryption_params),
88  protection_scheme_(
89  static_cast<FourCC>(encryption_params.protection_scheme)),
90  key_source_(key_source) {}
91 
92 EncryptionHandler::~EncryptionHandler() {}
93 
95  if (!encryption_params_.stream_label_func) {
96  return Status(error::INVALID_ARGUMENT, "Stream label function not set.");
97  }
98  if (num_input_streams() != 1 || next_output_stream_index() != 1) {
99  return Status(error::INVALID_ARGUMENT,
100  "Expects exactly one input and output.");
101  }
102  return Status::OK;
103 }
104 
105 Status EncryptionHandler::Process(std::unique_ptr<StreamData> stream_data) {
106  switch (stream_data->stream_data_type) {
107  case StreamDataType::kStreamInfo:
108  return ProcessStreamInfo(*stream_data->stream_info);
109  case StreamDataType::kSegmentInfo: {
110  std::shared_ptr<SegmentInfo> segment_info(new SegmentInfo(
111  *stream_data->segment_info));
112 
113  segment_info->is_encrypted = remaining_clear_lead_ <= 0;
114 
115  const bool key_rotation_enabled = crypto_period_duration_ != 0;
116  if (key_rotation_enabled)
117  segment_info->key_rotation_encryption_config = encryption_config_;
118  if (!segment_info->is_subsegment) {
119  if (key_rotation_enabled)
120  check_new_crypto_period_ = true;
121  if (remaining_clear_lead_ > 0)
122  remaining_clear_lead_ -= segment_info->duration;
123  }
124 
125  return DispatchSegmentInfo(kStreamIndex, segment_info);
126  }
127  case StreamDataType::kMediaSample:
128  return ProcessMediaSample(std::move(stream_data->media_sample));
129  default:
130  VLOG(3) << "Stream data type "
131  << static_cast<int>(stream_data->stream_data_type) << " ignored.";
132  return Dispatch(std::move(stream_data));
133  }
134 }
135 
136 Status EncryptionHandler::ProcessStreamInfo(const StreamInfo& clear_info) {
137  if (clear_info.is_encrypted()) {
138  return Status(error::INVALID_ARGUMENT,
139  "Input stream is already encrypted.");
140  }
141 
142  DCHECK_NE(kStreamUnknown, clear_info.stream_type());
143  DCHECK_NE(kStreamText, clear_info.stream_type());
144  std::shared_ptr<StreamInfo> stream_info = clear_info.Clone();
145 
146  remaining_clear_lead_ =
147  encryption_params_.clear_lead_in_seconds * stream_info->time_scale();
148  crypto_period_duration_ =
149  encryption_params_.crypto_period_duration_in_seconds *
150  stream_info->time_scale();
151  codec_ = stream_info->codec();
152  nalu_length_size_ = GetNaluLengthSize(*stream_info);
153  stream_label_ = GetStreamLabelForEncryption(
154  *stream_info, encryption_params_.stream_label_func);
155  switch (codec_) {
156  case kCodecVP9:
157  if (encryption_params_.vp9_subsample_encryption)
158  vpx_parser_.reset(new VP9Parser);
159  break;
160  case kCodecH264:
161  header_parser_.reset(new H264VideoSliceHeaderParser);
162  break;
163  case kCodecH265:
164  header_parser_.reset(new H265VideoSliceHeaderParser);
165  break;
166  default:
167  // Other codecs should have nalu length size == 0.
168  if (nalu_length_size_ > 0) {
169  LOG(WARNING) << "Unknown video codec '" << codec_ << "'";
170  return Status(error::ENCRYPTION_FAILURE, "Unknown video codec.");
171  }
172  }
173  if (header_parser_) {
174  CHECK_NE(nalu_length_size_, 0u) << "AnnexB stream is not supported yet";
175  if (!header_parser_->Initialize(stream_info->codec_config())) {
176  return Status(error::ENCRYPTION_FAILURE,
177  "Fail to read SPS and PPS data.");
178  }
179  }
180 
181  Status status = SetupProtectionPattern(stream_info->stream_type());
182  if (!status.ok())
183  return status;
184 
185  EncryptionKey encryption_key;
186  const bool key_rotation_enabled = crypto_period_duration_ != 0;
187  if (key_rotation_enabled) {
188  check_new_crypto_period_ = true;
189  // Setup dummy key id and key to signal encryption for key rotation.
190  encryption_key.key_id.assign(
191  kKeyRotationDefaultKeyId,
192  kKeyRotationDefaultKeyId + sizeof(kKeyRotationDefaultKeyId));
193  // The key is not really used to encrypt any data. It is there just for
194  // convenience.
195  encryption_key.key = encryption_key.key_id;
196  } else {
197  status = key_source_->GetKey(stream_label_, &encryption_key);
198  if (!status.ok())
199  return status;
200  }
201  if (!CreateEncryptor(encryption_key))
202  return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
203 
204  stream_info->set_is_encrypted(true);
205  stream_info->set_has_clear_lead(encryption_params_.clear_lead_in_seconds > 0);
206  stream_info->set_encryption_config(*encryption_config_);
207 
208  return DispatchStreamInfo(kStreamIndex, stream_info);
209 }
210 
211 Status EncryptionHandler::ProcessMediaSample(
212  std::shared_ptr<const MediaSample> clear_sample) {
213  DCHECK(clear_sample);
214 
215  // We need to parse the frame (which also updates the vpx parser) even if the
216  // frame is not encrypted as the next (encrypted) frame may be dependent on
217  // this clear frame.
218  std::vector<VPxFrameInfo> vpx_frames;
219  if (vpx_parser_ && !vpx_parser_->Parse(clear_sample->data(),
220  clear_sample->data_size(),
221  &vpx_frames)) {
222  return Status(error::ENCRYPTION_FAILURE, "Failed to parse vpx frame.");
223  }
224 
225  // Need to setup the encryptor for new segments even if this segment does not
226  // need to be encrypted, so we can signal encryption metadata earlier to
227  // allows clients to prefetch the keys.
228  if (check_new_crypto_period_) {
229  const int64_t current_crypto_period_index =
230  clear_sample->dts() / crypto_period_duration_;
231  if (current_crypto_period_index != prev_crypto_period_index_) {
232  EncryptionKey encryption_key;
233  Status status = key_source_->GetCryptoPeriodKey(
234  current_crypto_period_index, stream_label_, &encryption_key);
235  if (!status.ok())
236  return status;
237  if (!CreateEncryptor(encryption_key))
238  return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
239  prev_crypto_period_index_ = current_crypto_period_index;
240  }
241  check_new_crypto_period_ = false;
242  }
243 
244  // Since there is no encryption needed right now, send the clear copy
245  // downstream so we can save the costs of copying it.
246  if (remaining_clear_lead_ > 0) {
247  return DispatchMediaSample(kStreamIndex, std::move(clear_sample));
248  }
249 
250  std::unique_ptr<DecryptConfig> decrypt_config(new DecryptConfig(
251  encryption_config_->key_id,
252  encryptor_->iv(),
253  std::vector<SubsampleEntry>(),
254  protection_scheme_,
255  crypt_byte_block_,
256  skip_byte_block_));
257 
258  // Now that we know that this sample must be encrypted, make a copy of
259  // the sample first so that all the encryption operations can be done
260  // in-place.
261  std::shared_ptr<MediaSample> cipher_sample(clear_sample->Clone());
262  // |cipher_sample| above still contains the old clear sample data. We will
263  // use |cipher_sample_data| to hold cipher sample data then transfer it to
264  // |cipher_sample| after encryption.
265  std::shared_ptr<uint8_t> cipher_sample_data(
266  new uint8_t[clear_sample->data_size()], std::default_delete<uint8_t[]>());
267 
268  if (vpx_parser_) {
269  if (!EncryptVpxFrame(vpx_frames, clear_sample->data(),
270  clear_sample->data_size(),
271  &cipher_sample_data.get()[0], decrypt_config.get())) {
272  return Status(error::ENCRYPTION_FAILURE, "Failed to encrypt VPX frame.");
273  }
274  DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(),
275  clear_sample->data_size());
276  } else if (header_parser_) {
277  if (!EncryptNalFrame(clear_sample->data(), clear_sample->data_size(),
278  &cipher_sample_data.get()[0], decrypt_config.get())) {
279  return Status(error::ENCRYPTION_FAILURE, "Failed to encrypt NAL frame.");
280  }
281  DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(),
282  clear_sample->data_size());
283  } else {
284  memcpy(&cipher_sample_data.get()[0], clear_sample->data(),
285  std::min(clear_sample->data_size(), leading_clear_bytes_size_));
286  if (clear_sample->data_size() > leading_clear_bytes_size_) {
287  EncryptBytes(clear_sample->data() + leading_clear_bytes_size_,
288  clear_sample->data_size() - leading_clear_bytes_size_,
289  &cipher_sample_data.get()[leading_clear_bytes_size_]);
290  }
291  }
292 
293  cipher_sample->TransferData(std::move(cipher_sample_data),
294  clear_sample->data_size());
295  // Finish initializing the sample before sending it downstream. We must
296  // wait until now to finish the initialization as we will lose access to
297  // |decrypt_config| once we set it.
298  cipher_sample->set_is_encrypted(true);
299  cipher_sample->set_decrypt_config(std::move(decrypt_config));
300 
301  encryptor_->UpdateIv();
302 
303  return DispatchMediaSample(kStreamIndex, std::move(cipher_sample));
304 }
305 
306 Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) {
307  switch (protection_scheme_) {
308  case kAppleSampleAesProtectionScheme: {
309  const size_t kH264LeadingClearBytesSize = 32u;
310  const size_t kSmallNalUnitSize = 32u + 16u;
311  const size_t kAudioLeadingClearBytesSize = 16u;
312  switch (codec_) {
313  case kCodecH264:
314  // Apple Sample AES uses 1:9 pattern for video.
315  crypt_byte_block_ = 1u;
316  skip_byte_block_ = 9u;
317  leading_clear_bytes_size_ = kH264LeadingClearBytesSize;
318  min_protected_data_size_ = kSmallNalUnitSize + 1u;
319  break;
320  case kCodecAAC:
321  FALLTHROUGH_INTENDED;
322  case kCodecAC3:
323  // Audio is whole sample encrypted. We could not use a
324  // crypto_byte_block_ of 1 here as if there is one crypto block
325  // remaining, it need not be encrypted for video but it needs to be
326  // encrypted for audio.
327  crypt_byte_block_ = 0u;
328  skip_byte_block_ = 0u;
329  leading_clear_bytes_size_ = kAudioLeadingClearBytesSize;
330  min_protected_data_size_ = leading_clear_bytes_size_ + 1u;
331  break;
332  default:
333  return Status(error::ENCRYPTION_FAILURE,
334  "Only AAC/AC3 and H264 are supported in Sample AES.");
335  }
336  break;
337  }
338  case FOURCC_cbcs:
339  FALLTHROUGH_INTENDED;
340  case FOURCC_cens:
341  if (stream_type == kStreamVideo) {
342  // Use 1:9 pattern for video.
343  crypt_byte_block_ = 1u;
344  skip_byte_block_ = 9u;
345  } else {
346  // Tracks other than video are protected using whole-block full-sample
347  // encryption, which is essentially a pattern of 1:0. Note that this may
348  // not be the same as the non-pattern based encryption counterparts,
349  // e.g. in 'cens' for full sample encryption, the whole sample is
350  // encrypted up to the last 16-byte boundary, see 23001-7:2016(E) 9.7;
351  // while in 'cenc' for full sample encryption, the last partial 16-byte
352  // block is also encrypted, see 23001-7:2016(E) 9.4.2. Another
353  // difference is the use of constant iv.
354  crypt_byte_block_ = 1u;
355  skip_byte_block_ = 0u;
356  }
357  break;
358  default:
359  // Not using pattern encryption.
360  crypt_byte_block_ = 0u;
361  skip_byte_block_ = 0u;
362  }
363  return Status::OK;
364 }
365 
366 bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) {
367  std::unique_ptr<AesCryptor> encryptor;
368  switch (protection_scheme_) {
369  case FOURCC_cenc:
370  encryptor.reset(new AesCtrEncryptor);
371  break;
372  case FOURCC_cbc1:
373  encryptor.reset(new AesCbcEncryptor(kNoPadding));
374  break;
375  case FOURCC_cens:
376  encryptor.reset(new AesPatternCryptor(
377  crypt_byte_block_, skip_byte_block_,
379  AesCryptor::kDontUseConstantIv,
380  std::unique_ptr<AesCryptor>(new AesCtrEncryptor())));
381  break;
382  case FOURCC_cbcs:
383  encryptor.reset(new AesPatternCryptor(
384  crypt_byte_block_, skip_byte_block_,
386  AesCryptor::kUseConstantIv,
387  std::unique_ptr<AesCryptor>(new AesCbcEncryptor(kNoPadding))));
388  break;
389  case kAppleSampleAesProtectionScheme:
390  if (crypt_byte_block_ == 0 && skip_byte_block_ == 0) {
391  encryptor.reset(
392  new AesCbcEncryptor(kNoPadding, AesCryptor::kUseConstantIv));
393  } else {
394  encryptor.reset(new AesPatternCryptor(
395  crypt_byte_block_, skip_byte_block_,
397  AesCryptor::kUseConstantIv,
398  std::unique_ptr<AesCryptor>(new AesCbcEncryptor(kNoPadding))));
399  }
400  break;
401  default:
402  LOG(ERROR) << "Unsupported protection scheme.";
403  return false;
404  }
405 
406  std::vector<uint8_t> iv = encryption_key.iv;
407  if (iv.empty()) {
408  if (!AesCryptor::GenerateRandomIv(protection_scheme_, &iv)) {
409  LOG(ERROR) << "Failed to generate random iv.";
410  return false;
411  }
412  }
413  const bool initialized =
414  encryptor->InitializeWithIv(encryption_key.key, iv);
415  encryptor_ = std::move(encryptor);
416 
417  encryption_config_.reset(new EncryptionConfig);
418  encryption_config_->protection_scheme = protection_scheme_;
419  encryption_config_->crypt_byte_block = crypt_byte_block_;
420  encryption_config_->skip_byte_block = skip_byte_block_;
421  if (encryptor_->use_constant_iv()) {
422  encryption_config_->per_sample_iv_size = 0;
423  encryption_config_->constant_iv = iv;
424  } else {
425  encryption_config_->per_sample_iv_size = static_cast<uint8_t>(iv.size());
426  }
427  encryption_config_->key_id = encryption_key.key_id;
428  encryption_config_->key_system_info = encryption_key.key_system_info;
429  return initialized;
430 }
431 
432 bool EncryptionHandler::EncryptVpxFrame(
433  const std::vector<VPxFrameInfo>& vpx_frames,
434  const uint8_t* source,
435  size_t source_size,
436  uint8_t* dest,
437  DecryptConfig* decrypt_config) {
438  const uint8_t* data = source;
439  for (const VPxFrameInfo& frame : vpx_frames) {
440  uint16_t clear_bytes =
441  static_cast<uint16_t>(frame.uncompressed_header_size);
442  uint32_t cipher_bytes = static_cast<uint32_t>(
443  frame.frame_size - frame.uncompressed_header_size);
444 
445  // "VP Codec ISO Media File Format Binding" document requires that the
446  // encrypted bytes of each frame within the superframe must be block
447  // aligned so that the counter state can be computed for each frame
448  // within the superframe.
449  // ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
450  // The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
451  // avoid partial blocks in Subsamples.
452  // For consistency, apply block alignment to all frames.
453  const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize;
454  clear_bytes += misalign_bytes;
455  cipher_bytes -= misalign_bytes;
456 
457  decrypt_config->AddSubsample(clear_bytes, cipher_bytes);
458  memcpy(dest, data, clear_bytes);
459  if (cipher_bytes > 0)
460  EncryptBytes(data + clear_bytes, cipher_bytes, dest + clear_bytes);
461  data += frame.frame_size;
462  dest += frame.frame_size;
463  }
464  // Add subsample for the superframe index if exists.
465  const bool is_superframe = vpx_frames.size() > 1;
466  if (is_superframe) {
467  size_t index_size = source + source_size - data;
468  DCHECK_LE(index_size, 2 + vpx_frames.size() * 4);
469  DCHECK_GE(index_size, 2 + vpx_frames.size() * 1);
470  uint16_t clear_bytes = static_cast<uint16_t>(index_size);
471  uint32_t cipher_bytes = 0;
472  decrypt_config->AddSubsample(clear_bytes, cipher_bytes);
473  memcpy(dest, data, clear_bytes);
474  }
475  return true;
476 }
477 
478 bool EncryptionHandler::EncryptNalFrame(const uint8_t* source,
479  size_t source_size,
480  uint8_t* dest,
481  DecryptConfig* decrypt_config) {
482  DCHECK_NE(nalu_length_size_, 0u);
483  DCHECK(header_parser_);
484  const Nalu::CodecType nalu_type =
485  (codec_ == kCodecH265) ? Nalu::kH265 : Nalu::kH264;
486  NaluReader reader(nalu_type, nalu_length_size_, source, source_size);
487 
488  // Store the current length of clear data. This is used to squash
489  // multiple unencrypted NAL units into fewer subsample entries.
490  uint64_t accumulated_clear_bytes = 0;
491 
492  Nalu nalu;
493  NaluReader::Result result;
494  while ((result = reader.Advance(&nalu)) == NaluReader::kOk) {
495  const uint64_t nalu_total_size = nalu.header_size() + nalu.payload_size();
496  if (nalu.is_video_slice() && nalu_total_size >= min_protected_data_size_) {
497  uint64_t current_clear_bytes = leading_clear_bytes_size_;
498  if (current_clear_bytes == 0) {
499  // For video-slice NAL units, encrypt the video slice. This skips
500  // the frame header.
501  const int64_t video_slice_header_size =
502  header_parser_->GetHeaderSize(nalu);
503  if (video_slice_header_size < 0) {
504  LOG(ERROR) << "Failed to read slice header.";
505  return false;
506  }
507  current_clear_bytes = nalu.header_size() + video_slice_header_size;
508  }
509  uint64_t cipher_bytes = nalu_total_size - current_clear_bytes;
510 
511  // ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
512  // The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
513  // avoid partial blocks in Subsamples.
514  // CMAF requires 'cenc' scheme BytesOfProtectedData SHALL be a multiple
515  // of 16 bytes; while 'cbcs' scheme BytesOfProtectedData SHALL start on
516  // the first byte of video data following the slice header.
517  if (protection_scheme_ == FOURCC_cbc1 ||
518  protection_scheme_ == FOURCC_cens ||
519  protection_scheme_ == FOURCC_cenc) {
520  const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize;
521  current_clear_bytes += misalign_bytes;
522  cipher_bytes -= misalign_bytes;
523  }
524 
525  accumulated_clear_bytes += nalu_length_size_ + current_clear_bytes;
526  AddSubsample(accumulated_clear_bytes, cipher_bytes, decrypt_config);
527  memcpy(dest, source, accumulated_clear_bytes);
528  source += accumulated_clear_bytes;
529  dest += accumulated_clear_bytes;
530  accumulated_clear_bytes = 0;
531 
532  DCHECK_EQ(nalu.data() + current_clear_bytes, source);
533  EncryptBytes(source, cipher_bytes, dest);
534  source += cipher_bytes;
535  dest += cipher_bytes;
536  } else {
537  // For non-video-slice or small NAL units, don't encrypt.
538  accumulated_clear_bytes += nalu_length_size_ + nalu_total_size;
539  }
540  }
541  if (result != NaluReader::kEOStream) {
542  LOG(ERROR) << "Failed to parse NAL units.";
543  return false;
544  }
545  AddSubsample(accumulated_clear_bytes, 0, decrypt_config);
546  memcpy(dest, source, accumulated_clear_bytes);
547  return true;
548 }
549 
550 void EncryptionHandler::EncryptBytes(const uint8_t* source,
551  size_t source_size,
552  uint8_t* dest) {
553  DCHECK(source);
554  DCHECK(dest);
555  DCHECK(encryptor_);
556  CHECK(encryptor_->Crypt(source, source_size, dest));
557 }
558 
559 void EncryptionHandler::InjectVpxParserForTesting(
560  std::unique_ptr<VPxParser> vpx_parser) {
561  vpx_parser_ = std::move(vpx_parser);
562 }
563 
564 void EncryptionHandler::InjectVideoSliceHeaderParserForTesting(
565  std::unique_ptr<VideoSliceHeaderParser> header_parser) {
566  header_parser_ = std::move(header_parser);
567 }
568 
569 } // namespace media
570 } // namespace shaka
Abstract class holds stream information.
Definition: stream_info.h:58
virtual std::unique_ptr< StreamInfo > Clone() const =0
std::function< std::string(const EncryptedStreamAttributes &stream_attributes)> stream_label_func
virtual Status GetCryptoPeriodKey(uint32_t crypto_period_index, const std::string &stream_label, EncryptionKey *key)=0
Status Dispatch(std::unique_ptr< StreamData > stream_data)
virtual Status GetKey(const std::string &stream_label, EncryptionKey *key)=0
double clear_lead_in_seconds
Clear lead duration in seconds.
bool vp9_subsample_encryption
Enable/disable subsample encryption for VP9.
Status Process(std::unique_ptr< StreamData > stream_data) override
static bool GenerateRandomIv(FourCC protection_scheme, std::vector< uint8_t > *iv)
Definition: aes_cryptor.cc:107
Status DispatchStreamInfo(size_t stream_index, std::shared_ptr< const StreamInfo > stream_info)
Dispatch the stream info to downstream handlers.
Status DispatchMediaSample(size_t stream_index, std::shared_ptr< const MediaSample > media_sample)
Dispatch the media sample to downstream handlers.
Status DispatchSegmentInfo(size_t stream_index, std::shared_ptr< const SegmentInfo > segment_info)
Dispatch the segment info to downstream handlers.