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