DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
webm_cluster_parser.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "packager/media/formats/webm/webm_cluster_parser.h"
6 
7 #include <vector>
8 
9 #include "packager/base/logging.h"
10 #include "packager/base/sys_byteorder.h"
11 #include "packager/media/base/decrypt_config.h"
12 #include "packager/media/base/timestamp.h"
13 #include "packager/media/codecs/vp8_parser.h"
14 #include "packager/media/codecs/vp9_parser.h"
15 #include "packager/media/codecs/webvtt_util.h"
16 #include "packager/media/formats/webm/webm_constants.h"
17 #include "packager/media/formats/webm/webm_crypto_helpers.h"
18 #include "packager/media/formats/webm/webm_webvtt_parser.h"
19 
20 namespace shaka {
21 namespace media {
22 namespace {
23 
24 const int64_t kMicrosecondsPerMillisecond = 1000;
25 
26 // Helper function used to inspect block data to determine if the
27 // block is a keyframe.
28 // |data| contains the bytes in the block.
29 // |size| indicates the number of bytes in |data|.
30 bool IsKeyframe(bool is_video, Codec codec, const uint8_t* data, int size) {
31  // For now, assume that all blocks are keyframes for datatypes other than
32  // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
33  if (!is_video)
34  return true;
35 
36  switch (codec) {
37  case kCodecVP8:
38  return VP8Parser::IsKeyframe(data, size);
39  case kCodecVP9:
40  return VP9Parser::IsKeyframe(data, size);
41  default:
42  NOTIMPLEMENTED() << "Unsupported codec " << codec;
43  return false;
44  }
45 }
46 
47 } // namespace
48 
50  int64_t timecode_scale,
51  scoped_refptr<AudioStreamInfo> audio_stream_info,
52  scoped_refptr<VideoStreamInfo> video_stream_info,
53  int64_t audio_default_duration,
54  int64_t video_default_duration,
55  const WebMTracksParser::TextTracks& text_tracks,
56  const std::set<int64_t>& ignored_tracks,
57  const std::string& audio_encryption_key_id,
58  const std::string& video_encryption_key_id,
59  const MediaParser::NewSampleCB& new_sample_cb,
60  const MediaParser::InitCB& init_cb,
61  KeySource* decryption_key_source)
62  : timecode_multiplier_(timecode_scale / 1000.0),
63  audio_stream_info_(audio_stream_info),
64  video_stream_info_(video_stream_info),
65  ignored_tracks_(ignored_tracks),
66  audio_encryption_key_id_(audio_encryption_key_id),
67  video_encryption_key_id_(video_encryption_key_id),
68  parser_(kWebMIdCluster, this),
69  initialized_(false),
70  init_cb_(init_cb),
71  cluster_start_time_(kNoTimestamp),
72  audio_(audio_stream_info ? audio_stream_info->track_id() : -1,
73  false,
74  audio_default_duration,
75  new_sample_cb),
76  video_(video_stream_info ? video_stream_info->track_id() : -1,
77  true,
78  video_default_duration,
79  new_sample_cb) {
80  if (decryption_key_source)
81  decryptor_source_.reset(new DecryptorSource(decryption_key_source));
82  for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin();
83  it != text_tracks.end();
84  ++it) {
85  text_track_map_.insert(std::make_pair(
86  it->first, Track(it->first, false, kNoTimestamp, new_sample_cb)));
87  }
88 }
89 
90 WebMClusterParser::~WebMClusterParser() {}
91 
93  last_block_timecode_ = -1;
94  cluster_timecode_ = -1;
95  cluster_start_time_ = kNoTimestamp;
96  cluster_ended_ = false;
97  parser_.Reset();
98  audio_.Reset();
99  video_.Reset();
100  ResetTextTracks();
101 }
102 
104  // Estimate the duration of the last frame if necessary.
105  bool audio_result = audio_.ApplyDurationEstimateIfNeeded();
106  bool video_result = video_.ApplyDurationEstimateIfNeeded();
107  Reset();
108  return audio_result && video_result;
109 }
110 
111 int WebMClusterParser::Parse(const uint8_t* buf, int size) {
112  int result = parser_.Parse(buf, size);
113 
114  if (result < 0) {
115  cluster_ended_ = false;
116  return result;
117  }
118 
119  cluster_ended_ = parser_.IsParsingComplete();
120  if (cluster_ended_) {
121  // If there were no buffers in this cluster, set the cluster start time to
122  // be the |cluster_timecode_|.
123  if (cluster_start_time_ == kNoTimestamp) {
124  // If the cluster did not even have a |cluster_timecode_|, signal parse
125  // error.
126  if (cluster_timecode_ < 0)
127  return -1;
128 
129  cluster_start_time_ = cluster_timecode_ * timecode_multiplier_;
130  }
131 
132  // Reset the parser if we're done parsing so that
133  // it is ready to accept another cluster on the next
134  // call.
135  parser_.Reset();
136 
137  last_block_timecode_ = -1;
138  cluster_timecode_ = -1;
139  }
140 
141  return result;
142 }
143 
144 WebMParserClient* WebMClusterParser::OnListStart(int id) {
145  if (id == kWebMIdCluster) {
146  cluster_timecode_ = -1;
147  cluster_start_time_ = kNoTimestamp;
148  } else if (id == kWebMIdBlockGroup) {
149  block_data_.reset();
150  block_data_size_ = -1;
151  block_duration_ = -1;
152  discard_padding_ = -1;
153  discard_padding_set_ = false;
154  } else if (id == kWebMIdBlockAdditions) {
155  block_add_id_ = -1;
156  block_additional_data_.reset();
157  block_additional_data_size_ = 0;
158  }
159 
160  return this;
161 }
162 
163 bool WebMClusterParser::OnListEnd(int id) {
164  if (id != kWebMIdBlockGroup)
165  return true;
166 
167  // Make sure the BlockGroup actually had a Block.
168  if (block_data_size_ == -1) {
169  LOG(ERROR) << "Block missing from BlockGroup.";
170  return false;
171  }
172 
173  bool result = ParseBlock(false, block_data_.get(), block_data_size_,
174  block_additional_data_.get(),
175  block_additional_data_size_, block_duration_,
176  discard_padding_set_ ? discard_padding_ : 0);
177  block_data_.reset();
178  block_data_size_ = -1;
179  block_duration_ = -1;
180  block_add_id_ = -1;
181  block_additional_data_.reset();
182  block_additional_data_size_ = 0;
183  discard_padding_ = -1;
184  discard_padding_set_ = false;
185  return result;
186 }
187 
188 bool WebMClusterParser::OnUInt(int id, int64_t val) {
189  int64_t* dst;
190  switch (id) {
191  case kWebMIdTimecode:
192  dst = &cluster_timecode_;
193  break;
194  case kWebMIdBlockDuration:
195  dst = &block_duration_;
196  break;
197  case kWebMIdBlockAddID:
198  dst = &block_add_id_;
199  break;
200  default:
201  return true;
202  }
203  if (*dst != -1)
204  return false;
205  *dst = val;
206  return true;
207 }
208 
209 bool WebMClusterParser::ParseBlock(bool is_simple_block,
210  const uint8_t* buf,
211  int size,
212  const uint8_t* additional,
213  int additional_size,
214  int duration,
215  int64_t discard_padding) {
216  if (size < 4)
217  return false;
218 
219  // Return an error if the trackNum > 127. We just aren't
220  // going to support large track numbers right now.
221  if (!(buf[0] & 0x80)) {
222  LOG(ERROR) << "TrackNumber over 127 not supported";
223  return false;
224  }
225 
226  int track_num = buf[0] & 0x7f;
227  int timecode = buf[1] << 8 | buf[2];
228  int flags = buf[3] & 0xff;
229  int lacing = (flags >> 1) & 0x3;
230 
231  if (lacing) {
232  LOG(ERROR) << "Lacing " << lacing << " is not supported yet.";
233  return false;
234  }
235 
236  // Sign extend negative timecode offsets.
237  if (timecode & 0x8000)
238  timecode |= ~0xffff;
239 
240  const uint8_t* frame_data = buf + 4;
241  int frame_size = size - (frame_data - buf);
242  return OnBlock(is_simple_block, track_num, timecode, duration, flags,
243  frame_data, frame_size, additional, additional_size,
244  discard_padding);
245 }
246 
247 bool WebMClusterParser::OnBinary(int id, const uint8_t* data, int size) {
248  switch (id) {
249  case kWebMIdSimpleBlock:
250  return ParseBlock(true, data, size, NULL, 0, -1, 0);
251 
252  case kWebMIdBlock:
253  if (block_data_) {
254  LOG(ERROR) << "More than 1 Block in a BlockGroup is not "
255  "supported.";
256  return false;
257  }
258  block_data_.reset(new uint8_t[size]);
259  memcpy(block_data_.get(), data, size);
260  block_data_size_ = size;
261  return true;
262 
263  case kWebMIdBlockAdditional: {
264  uint64_t block_add_id = base::HostToNet64(block_add_id_);
265  if (block_additional_data_) {
266  // TODO: Technically, more than 1 BlockAdditional is allowed as per
267  // matroska spec. But for now we don't have a use case to support
268  // parsing of such files. Take a look at this again when such a case
269  // arises.
270  LOG(ERROR) << "More than 1 BlockAdditional in a "
271  "BlockGroup is not supported.";
272  return false;
273  }
274  // First 8 bytes of side_data in DecoderBuffer is the BlockAddID
275  // element's value in Big Endian format. This is done to mimic ffmpeg
276  // demuxer's behavior.
277  block_additional_data_size_ = size + sizeof(block_add_id);
278  block_additional_data_.reset(new uint8_t[block_additional_data_size_]);
279  memcpy(block_additional_data_.get(), &block_add_id,
280  sizeof(block_add_id));
281  memcpy(block_additional_data_.get() + 8, data, size);
282  return true;
283  }
284  case kWebMIdDiscardPadding: {
285  if (discard_padding_set_ || size <= 0 || size > 8)
286  return false;
287  discard_padding_set_ = true;
288 
289  // Read in the big-endian integer.
290  discard_padding_ = static_cast<int8_t>(data[0]);
291  for (int i = 1; i < size; ++i)
292  discard_padding_ = (discard_padding_ << 8) | data[i];
293 
294  return true;
295  }
296  default:
297  return true;
298  }
299 }
300 
301 bool WebMClusterParser::OnBlock(bool is_simple_block,
302  int track_num,
303  int timecode,
304  int block_duration,
305  int flags,
306  const uint8_t* data,
307  int size,
308  const uint8_t* additional,
309  int additional_size,
310  int64_t discard_padding) {
311  DCHECK_GE(size, 0);
312  if (cluster_timecode_ == -1) {
313  LOG(ERROR) << "Got a block before cluster timecode.";
314  return false;
315  }
316 
317  // TODO: Should relative negative timecode offsets be rejected? Or only when
318  // the absolute timecode is negative? See http://crbug.com/271794
319  if (timecode < 0) {
320  LOG(ERROR) << "Got a block with negative timecode offset " << timecode;
321  return false;
322  }
323 
324  if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
325  LOG(ERROR) << "Got a block with a timecode before the previous block.";
326  return false;
327  }
328 
329  Track* track = NULL;
330  StreamType stream_type = kStreamUnknown;
331  std::string encryption_key_id;
332  if (track_num == audio_.track_num()) {
333  track = &audio_;
334  encryption_key_id = audio_encryption_key_id_;
335  stream_type = kStreamAudio;
336  } else if (track_num == video_.track_num()) {
337  track = &video_;
338  encryption_key_id = video_encryption_key_id_;
339  stream_type = kStreamVideo;
340  } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
341  return true;
342  } else if (Track* const text_track = FindTextTrack(track_num)) {
343  if (is_simple_block) // BlockGroup is required for WebVTT cues
344  return false;
345  if (block_duration < 0) // not specified
346  return false;
347  track = text_track;
348  stream_type = kStreamText;
349  } else {
350  LOG(ERROR) << "Unexpected track number " << track_num;
351  return false;
352  }
353  DCHECK_NE(stream_type, kStreamUnknown);
354 
355  last_block_timecode_ = timecode;
356 
357  int64_t timestamp = (cluster_timecode_ + timecode) * timecode_multiplier_;
358 
359  scoped_refptr<MediaSample> buffer;
360  if (stream_type != kStreamText) {
361  // The first bit of the flags is set when a SimpleBlock contains only
362  // keyframes. If this is a Block, then inspection of the payload is
363  // necessary to determine whether it contains a keyframe or not.
364  // http://www.matroska.org/technical/specs/index.html
365  bool is_keyframe =
366  is_simple_block
367  ? (flags & 0x80) != 0
368  : IsKeyframe(stream_type == kStreamVideo,
369  video_stream_info_ ? video_stream_info_->codec()
370  : kUnknownCodec,
371  data, size);
372 
373  // Every encrypted Block has a signal byte and IV prepended to it. Current
374  // encrypted WebM request for comments specification is here
375  // http://wiki.webmproject.org/encryption/webm-encryption-rfc
376  scoped_ptr<DecryptConfig> decrypt_config;
377  int data_offset = 0;
378  if (!encryption_key_id.empty() &&
379  !WebMCreateDecryptConfig(
380  data, size,
381  reinterpret_cast<const uint8_t*>(encryption_key_id.data()),
382  encryption_key_id.size(),
383  &decrypt_config, &data_offset)) {
384  return false;
385  }
386 
387  buffer = MediaSample::CopyFrom(data + data_offset, size - data_offset,
388  additional, additional_size, is_keyframe);
389 
390  if (decrypt_config) {
391  if (!decryptor_source_) {
392  LOG(ERROR) << "Encrypted media sample encountered, but decryption is "
393  "not enabled";
394  return false;
395  }
396  if (!decryptor_source_->DecryptSampleBuffer(decrypt_config.get(),
397  buffer->writable_data(),
398  buffer->data_size())) {
399  LOG(ERROR) << "Cannot decrypt samples";
400  return false;
401  }
402  }
403  } else {
404  std::string id, settings, content;
405  WebMWebVTTParser::Parse(data, size, &id, &settings, &content);
406 
407  std::vector<uint8_t> side_data;
408  MakeSideData(id.begin(), id.end(),
409  settings.begin(), settings.end(),
410  &side_data);
411 
412  buffer = MediaSample::CopyFrom(
413  reinterpret_cast<const uint8_t*>(content.data()), content.length(),
414  &side_data[0], side_data.size(), true);
415  }
416 
417  buffer->set_dts(timestamp);
418  buffer->set_pts(timestamp);
419  if (cluster_start_time_ == kNoTimestamp)
420  cluster_start_time_ = timestamp;
421  buffer->set_duration(block_duration > 0
422  ? (block_duration * timecode_multiplier_)
423  : kNoTimestamp);
424 
425  if (!init_cb_.is_null() && !initialized_) {
426  std::vector<scoped_refptr<StreamInfo>> streams;
427  if (audio_stream_info_)
428  streams.push_back(audio_stream_info_);
429  if (video_stream_info_) {
430  if (stream_type == kStreamVideo) {
431  scoped_ptr<VPxParser> vpx_parser;
432  switch (video_stream_info_->codec()) {
433  case kCodecVP8:
434  vpx_parser.reset(new VP8Parser);
435  break;
436  case kCodecVP9:
437  vpx_parser.reset(new VP9Parser);
438  break;
439  default:
440  NOTIMPLEMENTED() << "Unsupported codec "
441  << video_stream_info_->codec();
442  return false;
443  }
444  std::vector<VPxFrameInfo> vpx_frames;
445  if (!vpx_parser->Parse(buffer->data(), buffer->data_size(),
446  &vpx_frames)) {
447  LOG(ERROR) << "Failed to parse vpx frame.";
448  return false;
449  }
450  if (vpx_frames.size() != 1u || !vpx_frames[0].is_keyframe) {
451  LOG(ERROR) << "The first frame should be a key frame.";
452  return false;
453  }
454 
455  VPCodecConfigurationRecord codec_config;
456  if (!video_stream_info_->codec_config().empty())
457  codec_config.ParseWebM(video_stream_info_->codec_config());
458  codec_config.MergeFrom(vpx_parser->codec_config());
459 
460  video_stream_info_->set_codec_string(
461  codec_config.GetCodecString(video_stream_info_->codec()));
462  std::vector<uint8_t> config_serialized;
463  codec_config.WriteMP4(&config_serialized);
464  video_stream_info_->set_codec_config(config_serialized);
465  streams.push_back(video_stream_info_);
466  init_cb_.Run(streams);
467  initialized_ = true;
468  }
469  } else {
470  init_cb_.Run(streams);
471  initialized_ = true;
472  }
473  }
474 
475  return track->EmitBuffer(buffer);
476 }
477 
478 WebMClusterParser::Track::Track(int track_num,
479  bool is_video,
480  int64_t default_duration,
481  const MediaParser::NewSampleCB& new_sample_cb)
482  : track_num_(track_num),
483  is_video_(is_video),
484  default_duration_(default_duration),
485  estimated_next_frame_duration_(kNoTimestamp),
486  new_sample_cb_(new_sample_cb) {
487  DCHECK(default_duration_ == kNoTimestamp || default_duration_ > 0);
488 }
489 
490 WebMClusterParser::Track::~Track() {}
491 
492 bool WebMClusterParser::Track::EmitBuffer(
493  const scoped_refptr<MediaSample>& buffer) {
494  DVLOG(2) << "EmitBuffer() : " << track_num_
495  << " ts " << buffer->pts()
496  << " dur " << buffer->duration()
497  << " kf " << buffer->is_key_frame()
498  << " size " << buffer->data_size();
499 
500  if (last_added_buffer_missing_duration_.get()) {
501  int64_t derived_duration =
502  buffer->pts() - last_added_buffer_missing_duration_->pts();
503  last_added_buffer_missing_duration_->set_duration(derived_duration);
504 
505  DVLOG(2) << "EmitBuffer() : applied derived duration to held-back buffer : "
506  << " ts "
507  << last_added_buffer_missing_duration_->pts()
508  << " dur "
509  << last_added_buffer_missing_duration_->duration()
510  << " kf " << last_added_buffer_missing_duration_->is_key_frame()
511  << " size " << last_added_buffer_missing_duration_->data_size();
512  scoped_refptr<MediaSample> updated_buffer =
513  last_added_buffer_missing_duration_;
514  last_added_buffer_missing_duration_ = NULL;
515  if (!EmitBufferHelp(updated_buffer))
516  return false;
517  }
518 
519  if (buffer->duration() == kNoTimestamp) {
520  last_added_buffer_missing_duration_ = buffer;
521  DVLOG(2) << "EmitBuffer() : holding back buffer that is missing duration";
522  return true;
523  }
524 
525  return EmitBufferHelp(buffer);
526 }
527 
528 bool WebMClusterParser::Track::ApplyDurationEstimateIfNeeded() {
529  if (!last_added_buffer_missing_duration_.get())
530  return true;
531 
532  int64_t estimated_duration = GetDurationEstimate();
533  last_added_buffer_missing_duration_->set_duration(estimated_duration);
534 
535  VLOG(1) << "Track " << track_num_ << ": Estimating WebM block duration to be "
536  << estimated_duration / 1000
537  << "ms for the last (Simple)Block in the Cluster for this Track. Use "
538  "BlockGroups with BlockDurations at the end of each Track in a "
539  "Cluster to avoid estimation.";
540 
541  DVLOG(2) << " new dur : ts " << last_added_buffer_missing_duration_->pts()
542  << " dur " << last_added_buffer_missing_duration_->duration()
543  << " kf " << last_added_buffer_missing_duration_->is_key_frame()
544  << " size " << last_added_buffer_missing_duration_->data_size();
545 
546  // Don't use the applied duration as a future estimation (don't use
547  // EmitBufferHelp() here.)
548  if (!new_sample_cb_.Run(track_num_, last_added_buffer_missing_duration_))
549  return false;
550  last_added_buffer_missing_duration_ = NULL;
551  return true;
552 }
553 
554 void WebMClusterParser::Track::Reset() {
555  last_added_buffer_missing_duration_ = NULL;
556 }
557 
558 bool WebMClusterParser::Track::EmitBufferHelp(
559  const scoped_refptr<MediaSample>& buffer) {
560  DCHECK(!last_added_buffer_missing_duration_.get());
561 
562  int64_t duration = buffer->duration();
563  if (duration < 0 || duration == kNoTimestamp) {
564  LOG(ERROR) << "Invalid buffer duration: " << duration;
565  return false;
566  }
567 
568  // The estimated frame duration is the maximum non-zero duration since the
569  // last initialization segment.
570  if (duration > 0) {
571  int64_t orig_duration_estimate = estimated_next_frame_duration_;
572  if (estimated_next_frame_duration_ == kNoTimestamp) {
573  estimated_next_frame_duration_ = duration;
574  } else {
575  estimated_next_frame_duration_ =
576  std::max(duration, estimated_next_frame_duration_);
577  }
578 
579  if (orig_duration_estimate != estimated_next_frame_duration_) {
580  DVLOG(3) << "Updated duration estimate:"
581  << orig_duration_estimate
582  << " -> "
583  << estimated_next_frame_duration_
584  << " at timestamp: "
585  << buffer->dts();
586  }
587  }
588 
589  return new_sample_cb_.Run(track_num_, buffer);
590 }
591 
592 int64_t WebMClusterParser::Track::GetDurationEstimate() {
593  int64_t duration = kNoTimestamp;
594  if (default_duration_ != kNoTimestamp) {
595  duration = default_duration_;
596  DVLOG(3) << __FUNCTION__ << " : using track default duration " << duration;
597  } else if (estimated_next_frame_duration_ != kNoTimestamp) {
598  duration = estimated_next_frame_duration_;
599  DVLOG(3) << __FUNCTION__ << " : using estimated duration " << duration;
600  } else {
601  if (is_video_) {
602  duration = kDefaultVideoBufferDurationInMs * kMicrosecondsPerMillisecond;
603  } else {
604  duration = kDefaultAudioBufferDurationInMs * kMicrosecondsPerMillisecond;
605  }
606  DVLOG(3) << __FUNCTION__ << " : using hardcoded default duration "
607  << duration;
608  }
609 
610  DCHECK_GT(duration, 0);
611  DCHECK_NE(duration, kNoTimestamp);
612  return duration;
613 }
614 
615 void WebMClusterParser::ResetTextTracks() {
616  for (TextTrackMap::iterator it = text_track_map_.begin();
617  it != text_track_map_.end();
618  ++it) {
619  it->second.Reset();
620  }
621 }
622 
623 WebMClusterParser::Track*
624 WebMClusterParser::FindTextTrack(int track_num) {
625  const TextTrackMap::iterator it = text_track_map_.find(track_num);
626 
627  if (it == text_track_map_.end())
628  return NULL;
629 
630  return &it->second;
631 }
632 
633 } // namespace media
634 } // namespace shaka
static bool IsKeyframe(const uint8_t *data, size_t data_size)
Definition: vp9_parser.cc:535
base::Callback< void(const std::vector< scoped_refptr< StreamInfo > > &stream_info)> InitCB
Definition: media_parser.h:35
int Parse(const uint8_t *buf, int size)
void Reset()
Resets the state of the parser so it can start parsing a new list.
Definition: webm_parser.cc:714
int Parse(const uint8_t *buf, int size)
Definition: webm_parser.cc:719
bool Flush() WARN_UNUSED_RESULT
static void Parse(const uint8_t *payload, int payload_size, std::string *id, std::string *settings, std::string *content)
Utility function to parse the WebVTT cue from a byte stream.
static bool IsKeyframe(const uint8_t *data, size_t data_size)
Definition: vp8_parser.cc:183
base::Callback< bool(uint32_t track_id, const scoped_refptr< MediaSample > &media_sample)> NewSampleCB
Definition: media_parser.h:44
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:31
static scoped_refptr< MediaSample > CopyFrom(const uint8_t *data, size_t size, bool is_key_frame)
Definition: media_sample.cc:45
WebMClusterParser(int64_t timecode_scale, scoped_refptr< AudioStreamInfo > audio_stream_info, scoped_refptr< VideoStreamInfo > video_stream_info, int64_t audio_default_duration, int64_t video_default_duration, const WebMTracksParser::TextTracks &text_tracks, const std::set< int64_t > &ignored_tracks, const std::string &audio_encryption_key_id, const std::string &video_encryption_key_id, const MediaParser::NewSampleCB &new_sample_cb, const MediaParser::InitCB &init_cb, KeySource *decryption_key_source)
DecryptorSource wraps KeySource and is responsible for decryptor management.
void Reset()
Resets the parser state so it can accept a new cluster.