DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs 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/filters/webvtt_util.h"
14 #include "packager/media/formats/webm/webm_constants.h"
15 #include "packager/media/formats/webm/webm_crypto_helpers.h"
16 #include "packager/media/formats/webm/webm_webvtt_parser.h"
17 
18 // Logs only while |count| < |max|, increments |count| for each log, and warns
19 // in the log if |count| has just reached |max|.
20 #define LIMITED_LOG(level, count, max) \
21  LOG_IF(level, (count) < (max)) \
22  << (((count) + 1 == (max)) \
23  ? "(Log limit reached. Further similar entries " \
24  "may be suppressed): " \
25  : "")
26 #define LIMITED_DLOG(level, count, max) \
27  DLOG_IF(level, (count) < (max)) \
28  << (((count) + 1 == (max)) \
29  ? "(Log limit reached. Further similar entries " \
30  "may be suppressed): " \
31  : "")
32 
33 namespace {
34 const int64_t kMicrosecondsPerMillisecond = 1000;
35 } // namespace
36 
37 namespace edash_packager {
38 namespace media {
39 
41  10000, 20000, 40000, 60000, 10000, 20000, 40000, 60000, 10000, 20000, 40000,
42  60000, 10000, 20000, 10000, 20000, 2500, 5000, 10000, 20000, 2500, 5000,
43  10000, 20000, 2500, 5000, 10000, 20000, 2500, 5000, 10000, 20000};
44 
45 enum {
46  // Limits the number of LOG() calls in the path of reading encoded
47  // duration to avoid spamming for corrupted data.
48  kMaxDurationErrorLogs = 10,
49  // Limits the number of LOG() calls warning the user that buffer
50  // durations have been estimated.
51  kMaxDurationEstimateLogs = 10,
52 };
53 
54 WebMClusterParser::WebMClusterParser(
55  int64_t timecode_scale,
56  int audio_track_num,
57  int64_t audio_default_duration,
58  int video_track_num,
59  int64_t video_default_duration,
60  const WebMTracksParser::TextTracks& text_tracks,
61  const std::set<int64_t>& ignored_tracks,
62  const std::string& audio_encryption_key_id,
63  const std::string& video_encryption_key_id,
64  const AudioCodec audio_codec,
65  const MediaParser::NewSampleCB& new_sample_cb)
66  : timecode_multiplier_(timecode_scale / 1000.0),
67  ignored_tracks_(ignored_tracks),
68  audio_encryption_key_id_(audio_encryption_key_id),
69  video_encryption_key_id_(video_encryption_key_id),
70  audio_codec_(audio_codec),
71  parser_(kWebMIdCluster, this),
72  cluster_start_time_(kNoTimestamp),
73  audio_(audio_track_num, false, audio_default_duration, new_sample_cb),
74  video_(video_track_num, true, video_default_duration, new_sample_cb) {
75  for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin();
76  it != text_tracks.end();
77  ++it) {
78  text_track_map_.insert(std::make_pair(
79  it->first, Track(it->first, false, kNoTimestamp, new_sample_cb)));
80  }
81 }
82 
83 WebMClusterParser::~WebMClusterParser() {}
84 
86  last_block_timecode_ = -1;
87  cluster_timecode_ = -1;
88  cluster_start_time_ = kNoTimestamp;
89  cluster_ended_ = false;
90  parser_.Reset();
91  audio_.Reset();
92  video_.Reset();
93  ResetTextTracks();
94 }
95 
97  // Estimate the duration of the last frame if necessary.
98  audio_.ApplyDurationEstimateIfNeeded();
99  video_.ApplyDurationEstimateIfNeeded();
100  Reset();
101 }
102 
103 int WebMClusterParser::Parse(const uint8_t* buf, int size) {
104  int result = parser_.Parse(buf, size);
105 
106  if (result < 0) {
107  cluster_ended_ = false;
108  return result;
109  }
110 
111  cluster_ended_ = parser_.IsParsingComplete();
112  if (cluster_ended_) {
113  // If there were no buffers in this cluster, set the cluster start time to
114  // be the |cluster_timecode_|.
115  if (cluster_start_time_ == kNoTimestamp) {
116  // If the cluster did not even have a |cluster_timecode_|, signal parse
117  // error.
118  if (cluster_timecode_ < 0)
119  return -1;
120 
121  cluster_start_time_ = cluster_timecode_ * timecode_multiplier_;
122  }
123 
124  // Reset the parser if we're done parsing so that
125  // it is ready to accept another cluster on the next
126  // call.
127  parser_.Reset();
128 
129  last_block_timecode_ = -1;
130  cluster_timecode_ = -1;
131  }
132 
133  return result;
134 }
135 
136 int64_t WebMClusterParser::TryGetEncodedAudioDuration(
137  const uint8_t* data,
138  int size) {
139 
140  // Duration is currently read assuming the *entire* stream is unencrypted.
141  // The special "Signal Byte" prepended to Blocks in encrypted streams is
142  // assumed to not be present.
143  // TODO: Consider parsing "Signal Byte" for encrypted streams to return
144  // duration for any unencrypted blocks.
145 
146  if (audio_codec_ == kCodecOpus) {
147  return ReadOpusDuration(data, size);
148  }
149 
150  // TODO: Implement duration reading for Vorbis. See motivations in
151  // http://crbug.com/396634.
152 
153  return kNoTimestamp;
154 }
155 
156 int64_t WebMClusterParser::ReadOpusDuration(const uint8_t* data, int size) {
157  // Masks and constants for Opus packets. See
158  // https://tools.ietf.org/html/rfc6716#page-14
159  static const uint8_t kTocConfigMask = 0xf8;
160  static const uint8_t kTocFrameCountCodeMask = 0x03;
161  static const uint8_t kFrameCountMask = 0x3f;
162  static const int64_t kPacketDurationMaxMs = 120000;
163 
164  if (size < 1) {
165  LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
166  << "Invalid zero-byte Opus packet; demuxed block duration may be "
167  "imprecise.";
168  return kNoTimestamp;
169  }
170 
171  // Frame count type described by last 2 bits of Opus TOC byte.
172  int frame_count_type = data[0] & kTocFrameCountCodeMask;
173 
174  int frame_count = 0;
175  switch (frame_count_type) {
176  case 0:
177  frame_count = 1;
178  break;
179  case 1:
180  case 2:
181  frame_count = 2;
182  break;
183  case 3:
184  // Type 3 indicates an arbitrary frame count described in the next byte.
185  if (size < 2) {
186  LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
187  << "Second byte missing from 'Code 3' Opus packet; demuxed block "
188  "duration may be imprecise.";
189  return kNoTimestamp;
190  }
191 
192  frame_count = data[1] & kFrameCountMask;
193 
194  if (frame_count == 0) {
195  LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
196  << "Illegal 'Code 3' Opus packet with frame count zero; demuxed "
197  "block duration may be imprecise.";
198  return kNoTimestamp;
199  }
200 
201  break;
202  default:
203  LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
204  << "Unexpected Opus frame count type: " << frame_count_type << "; "
205  << "demuxed block duration may be imprecise.";
206  return kNoTimestamp;
207  }
208 
209  int opusConfig = (data[0] & kTocConfigMask) >> 3;
210  CHECK_GE(opusConfig, 0);
211  CHECK_LT(opusConfig, static_cast<int>(arraysize(kOpusFrameDurationsMu)));
212 
213  DCHECK_GT(frame_count, 0);
214  int64_t duration = kOpusFrameDurationsMu[opusConfig] * frame_count;
215 
216  if (duration > kPacketDurationMaxMs * 1000) {
217  // Intentionally allowing packet to pass through for now. Decoder should
218  // either handle or fail gracefully. LOG as breadcrumbs in case
219  // things go sideways.
220  LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
221  << "Warning, demuxed Opus packet with encoded duration: "
222  << duration / 1000 << "ms. Should be no greater than "
223  << kPacketDurationMaxMs << "ms.";
224  }
225 
226  return duration;
227 }
228 
229 WebMParserClient* WebMClusterParser::OnListStart(int id) {
230  if (id == kWebMIdCluster) {
231  cluster_timecode_ = -1;
232  cluster_start_time_ = kNoTimestamp;
233  } else if (id == kWebMIdBlockGroup) {
234  block_data_.reset();
235  block_data_size_ = -1;
236  block_duration_ = -1;
237  discard_padding_ = -1;
238  discard_padding_set_ = false;
239  } else if (id == kWebMIdBlockAdditions) {
240  block_add_id_ = -1;
241  block_additional_data_.reset();
242  block_additional_data_size_ = 0;
243  }
244 
245  return this;
246 }
247 
248 bool WebMClusterParser::OnListEnd(int id) {
249  if (id != kWebMIdBlockGroup)
250  return true;
251 
252  // Make sure the BlockGroup actually had a Block.
253  if (block_data_size_ == -1) {
254  LOG(ERROR) << "Block missing from BlockGroup.";
255  return false;
256  }
257 
258  bool result = ParseBlock(false, block_data_.get(), block_data_size_,
259  block_additional_data_.get(),
260  block_additional_data_size_, block_duration_,
261  discard_padding_set_ ? discard_padding_ : 0);
262  block_data_.reset();
263  block_data_size_ = -1;
264  block_duration_ = -1;
265  block_add_id_ = -1;
266  block_additional_data_.reset();
267  block_additional_data_size_ = 0;
268  discard_padding_ = -1;
269  discard_padding_set_ = false;
270  return result;
271 }
272 
273 bool WebMClusterParser::OnUInt(int id, int64_t val) {
274  int64_t* dst;
275  switch (id) {
276  case kWebMIdTimecode:
277  dst = &cluster_timecode_;
278  break;
279  case kWebMIdBlockDuration:
280  dst = &block_duration_;
281  break;
282  case kWebMIdBlockAddID:
283  dst = &block_add_id_;
284  break;
285  default:
286  return true;
287  }
288  if (*dst != -1)
289  return false;
290  *dst = val;
291  return true;
292 }
293 
294 bool WebMClusterParser::ParseBlock(bool is_simple_block,
295  const uint8_t* buf,
296  int size,
297  const uint8_t* additional,
298  int additional_size,
299  int duration,
300  int64_t discard_padding) {
301  if (size < 4)
302  return false;
303 
304  // Return an error if the trackNum > 127. We just aren't
305  // going to support large track numbers right now.
306  if (!(buf[0] & 0x80)) {
307  LOG(ERROR) << "TrackNumber over 127 not supported";
308  return false;
309  }
310 
311  int track_num = buf[0] & 0x7f;
312  int timecode = buf[1] << 8 | buf[2];
313  int flags = buf[3] & 0xff;
314  int lacing = (flags >> 1) & 0x3;
315 
316  if (lacing) {
317  LOG(ERROR) << "Lacing " << lacing << " is not supported yet.";
318  return false;
319  }
320 
321  // Sign extend negative timecode offsets.
322  if (timecode & 0x8000)
323  timecode |= ~0xffff;
324 
325  const uint8_t* frame_data = buf + 4;
326  int frame_size = size - (frame_data - buf);
327  return OnBlock(is_simple_block, track_num, timecode, duration, flags,
328  frame_data, frame_size, additional, additional_size,
329  discard_padding);
330 }
331 
332 bool WebMClusterParser::OnBinary(int id, const uint8_t* data, int size) {
333  switch (id) {
334  case kWebMIdSimpleBlock:
335  return ParseBlock(true, data, size, NULL, 0, -1, 0);
336 
337  case kWebMIdBlock:
338  if (block_data_) {
339  LOG(ERROR) << "More than 1 Block in a BlockGroup is not "
340  "supported.";
341  return false;
342  }
343  block_data_.reset(new uint8_t[size]);
344  memcpy(block_data_.get(), data, size);
345  block_data_size_ = size;
346  return true;
347 
348  case kWebMIdBlockAdditional: {
349  uint64_t block_add_id = base::HostToNet64(block_add_id_);
350  if (block_additional_data_) {
351  // TODO: Technically, more than 1 BlockAdditional is allowed as per
352  // matroska spec. But for now we don't have a use case to support
353  // parsing of such files. Take a look at this again when such a case
354  // arises.
355  LOG(ERROR) << "More than 1 BlockAdditional in a "
356  "BlockGroup is not supported.";
357  return false;
358  }
359  // First 8 bytes of side_data in DecoderBuffer is the BlockAddID
360  // element's value in Big Endian format. This is done to mimic ffmpeg
361  // demuxer's behavior.
362  block_additional_data_size_ = size + sizeof(block_add_id);
363  block_additional_data_.reset(new uint8_t[block_additional_data_size_]);
364  memcpy(block_additional_data_.get(), &block_add_id,
365  sizeof(block_add_id));
366  memcpy(block_additional_data_.get() + 8, data, size);
367  return true;
368  }
369  case kWebMIdDiscardPadding: {
370  if (discard_padding_set_ || size <= 0 || size > 8)
371  return false;
372  discard_padding_set_ = true;
373 
374  // Read in the big-endian integer.
375  discard_padding_ = static_cast<int8_t>(data[0]);
376  for (int i = 1; i < size; ++i)
377  discard_padding_ = (discard_padding_ << 8) | data[i];
378 
379  return true;
380  }
381  default:
382  return true;
383  }
384 }
385 
386 bool WebMClusterParser::OnBlock(bool is_simple_block,
387  int track_num,
388  int timecode,
389  int block_duration,
390  int flags,
391  const uint8_t* data,
392  int size,
393  const uint8_t* additional,
394  int additional_size,
395  int64_t discard_padding) {
396  DCHECK_GE(size, 0);
397  if (cluster_timecode_ == -1) {
398  LOG(ERROR) << "Got a block before cluster timecode.";
399  return false;
400  }
401 
402  // TODO: Should relative negative timecode offsets be rejected? Or only when
403  // the absolute timecode is negative? See http://crbug.com/271794
404  if (timecode < 0) {
405  LOG(ERROR) << "Got a block with negative timecode offset " << timecode;
406  return false;
407  }
408 
409  if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
410  LOG(ERROR) << "Got a block with a timecode before the previous block.";
411  return false;
412  }
413 
414  Track* track = NULL;
415  StreamType stream_type = kStreamAudio;
416  std::string encryption_key_id;
417  int64_t encoded_duration = kNoTimestamp;
418  if (track_num == audio_.track_num()) {
419  track = &audio_;
420  encryption_key_id = audio_encryption_key_id_;
421  if (encryption_key_id.empty()) {
422  encoded_duration = TryGetEncodedAudioDuration(data, size);
423  }
424  } else if (track_num == video_.track_num()) {
425  track = &video_;
426  encryption_key_id = video_encryption_key_id_;
427  stream_type = kStreamVideo;
428  } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
429  return true;
430  } else if (Track* const text_track = FindTextTrack(track_num)) {
431  if (is_simple_block) // BlockGroup is required for WebVTT cues
432  return false;
433  if (block_duration < 0) // not specified
434  return false;
435  track = text_track;
436  stream_type = kStreamText;
437  } else {
438  LOG(ERROR) << "Unexpected track number " << track_num;
439  return false;
440  }
441 
442  last_block_timecode_ = timecode;
443 
444  int64_t timestamp = (cluster_timecode_ + timecode) * timecode_multiplier_;
445 
446  scoped_refptr<MediaSample> buffer;
447  if (stream_type != kStreamText) {
448  // The first bit of the flags is set when a SimpleBlock contains only
449  // keyframes. If this is a Block, then inspection of the payload is
450  // necessary to determine whether it contains a keyframe or not.
451  // http://www.matroska.org/technical/specs/index.html
452  bool is_keyframe =
453  is_simple_block ? (flags & 0x80) != 0 : track->IsKeyframe(data, size);
454 
455  // Every encrypted Block has a signal byte and IV prepended to it. Current
456  // encrypted WebM request for comments specification is here
457  // http://wiki.webmproject.org/encryption/webm-encryption-rfc
458  scoped_ptr<DecryptConfig> decrypt_config;
459  int data_offset = 0;
460  if (!encryption_key_id.empty() &&
461  !WebMCreateDecryptConfig(
462  data, size,
463  reinterpret_cast<const uint8_t*>(encryption_key_id.data()),
464  encryption_key_id.size(),
465  &decrypt_config, &data_offset)) {
466  return false;
467  }
468 
469  buffer = MediaSample::CopyFrom(data + data_offset, size - data_offset,
470  additional, additional_size, is_keyframe);
471 
472  if (decrypt_config) {
473  // TODO(kqyang): Decrypt it if it is encrypted.
474  buffer->set_is_encrypted(true);
475  }
476  } else {
477  std::string id, settings, content;
478  WebMWebVTTParser::Parse(data, size, &id, &settings, &content);
479 
480  std::vector<uint8_t> side_data;
481  MakeSideData(id.begin(), id.end(),
482  settings.begin(), settings.end(),
483  &side_data);
484 
485  buffer = MediaSample::CopyFrom(
486  reinterpret_cast<const uint8_t*>(content.data()), content.length(),
487  &side_data[0], side_data.size(), true);
488  }
489 
490  buffer->set_pts(timestamp);
491  if (cluster_start_time_ == kNoTimestamp)
492  cluster_start_time_ = timestamp;
493 
494  int64_t block_duration_time_delta = kNoTimestamp;
495  if (block_duration >= 0) {
496  block_duration_time_delta = block_duration * timecode_multiplier_;
497  }
498 
499  // Prefer encoded duration over BlockGroup->BlockDuration or
500  // TrackEntry->DefaultDuration when available. This layering violation is a
501  // workaround for http://crbug.com/396634, decreasing the likelihood of
502  // fall-back to rough estimation techniques for Blocks that lack a
503  // BlockDuration at the end of a cluster. Duration estimation may still apply
504  // in cases of encryption and codecs for which we do not extract encoded
505  // duration. Estimates are applied as Block Timecode deltas, or once the whole
506  // stream is parsed in the case of the last Block in the stream. See
507  // Track::EmitBuffer and ApplyDurationEstimateIfNeeded().
508  if (encoded_duration != kNoTimestamp) {
509  DCHECK(encoded_duration != kInfiniteDuration);
510  DCHECK(encoded_duration > 0);
511  buffer->set_duration(encoded_duration);
512 
513  DVLOG(3) << __FUNCTION__ << " : "
514  << "Using encoded duration " << encoded_duration;
515 
516  if (block_duration_time_delta != kNoTimestamp) {
517  int64_t duration_difference =
518  block_duration_time_delta - encoded_duration;
519 
520  const auto kWarnDurationDiff = timecode_multiplier_ * 2;
521  if (duration_difference > kWarnDurationDiff) {
522  LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
523  << "BlockDuration (" << block_duration_time_delta / 1000
524  << "ms) differs significantly from encoded duration ("
525  << encoded_duration / 1000 << "ms).";
526  }
527  }
528  } else if (block_duration_time_delta != kNoTimestamp) {
529  buffer->set_duration(block_duration_time_delta);
530  } else {
531  buffer->set_duration(track->default_duration());
532  }
533 
534  return track->EmitBuffer(buffer);
535 }
536 
537 WebMClusterParser::Track::Track(int track_num,
538  bool is_video,
539  int64_t default_duration,
540  const MediaParser::NewSampleCB& new_sample_cb)
541  : track_num_(track_num),
542  is_video_(is_video),
543  default_duration_(default_duration),
544  estimated_next_frame_duration_(kNoTimestamp),
545  new_sample_cb_(new_sample_cb) {
546  DCHECK(default_duration_ == kNoTimestamp || default_duration_ > 0);
547 }
548 
549 WebMClusterParser::Track::~Track() {}
550 
551 bool WebMClusterParser::Track::EmitBuffer(
552  const scoped_refptr<MediaSample>& buffer) {
553  DVLOG(2) << "EmitBuffer() : " << track_num_
554  << " ts " << buffer->pts()
555  << " dur " << buffer->duration()
556  << " kf " << buffer->is_key_frame()
557  << " size " << buffer->data_size();
558 
559  if (last_added_buffer_missing_duration_.get()) {
560  int64_t derived_duration =
561  buffer->pts() - last_added_buffer_missing_duration_->pts();
562  last_added_buffer_missing_duration_->set_duration(derived_duration);
563 
564  DVLOG(2) << "EmitBuffer() : applied derived duration to held-back buffer : "
565  << " ts "
566  << last_added_buffer_missing_duration_->pts()
567  << " dur "
568  << last_added_buffer_missing_duration_->duration()
569  << " kf " << last_added_buffer_missing_duration_->is_key_frame()
570  << " size " << last_added_buffer_missing_duration_->data_size();
571  scoped_refptr<MediaSample> updated_buffer =
572  last_added_buffer_missing_duration_;
573  last_added_buffer_missing_duration_ = NULL;
574  if (!EmitBufferHelp(updated_buffer))
575  return false;
576  }
577 
578  if (buffer->duration() == kNoTimestamp) {
579  last_added_buffer_missing_duration_ = buffer;
580  DVLOG(2) << "EmitBuffer() : holding back buffer that is missing duration";
581  return true;
582  }
583 
584  return EmitBufferHelp(buffer);
585 }
586 
587 void WebMClusterParser::Track::ApplyDurationEstimateIfNeeded() {
588  if (!last_added_buffer_missing_duration_.get())
589  return;
590 
591  int64_t estimated_duration = GetDurationEstimate();
592  last_added_buffer_missing_duration_->set_duration(estimated_duration);
593 
594  LIMITED_LOG(INFO, num_duration_estimates_, kMaxDurationEstimateLogs)
595  << "Estimating WebM block duration to be " << estimated_duration / 1000
596  << "ms for the last (Simple)Block in the Cluster for this Track. Use "
597  "BlockGroups with BlockDurations at the end of each Track in a "
598  "Cluster to avoid estimation.";
599 
600  DVLOG(2) << __FUNCTION__ << " new dur : ts "
601  << last_added_buffer_missing_duration_->pts()
602  << " dur "
603  << last_added_buffer_missing_duration_->duration()
604  << " kf " << last_added_buffer_missing_duration_->is_key_frame()
605  << " size " << last_added_buffer_missing_duration_->data_size();
606 
607  // Don't use the applied duration as a future estimation (don't use
608  // EmitBufferHelp() here.)
609  new_sample_cb_.Run(track_num_, last_added_buffer_missing_duration_);
610  last_added_buffer_missing_duration_ = NULL;
611 }
612 
613 void WebMClusterParser::Track::Reset() {
614  last_added_buffer_missing_duration_ = NULL;
615 }
616 
617 bool WebMClusterParser::Track::IsKeyframe(const uint8_t* data, int size) const {
618  // For now, assume that all blocks are keyframes for datatypes other than
619  // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
620  if (!is_video_)
621  return true;
622 
623  // Make sure the block is big enough for the minimal keyframe header size.
624  if (size < 7)
625  return false;
626 
627  // The LSb of the first byte must be a 0 for a keyframe.
628  // http://tools.ietf.org/html/rfc6386 Section 19.1
629  if ((data[0] & 0x01) != 0)
630  return false;
631 
632  // Verify VP8 keyframe startcode.
633  // http://tools.ietf.org/html/rfc6386 Section 19.1
634  if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a)
635  return false;
636 
637  return true;
638 }
639 
640 bool WebMClusterParser::Track::EmitBufferHelp(
641  const scoped_refptr<MediaSample>& buffer) {
642  DCHECK(!last_added_buffer_missing_duration_.get());
643 
644  int64_t duration = buffer->duration();
645  if (duration < 0 || duration == kNoTimestamp) {
646  LOG(ERROR) << "Invalid buffer duration: " << duration;
647  return false;
648  }
649 
650  // The estimated frame duration is the maximum non-zero duration since the
651  // last initialization segment.
652  if (duration > 0) {
653  int64_t orig_duration_estimate = estimated_next_frame_duration_;
654  if (estimated_next_frame_duration_ == kNoTimestamp) {
655  estimated_next_frame_duration_ = duration;
656  } else {
657  estimated_next_frame_duration_ =
658  std::max(duration, estimated_next_frame_duration_);
659  }
660 
661  if (orig_duration_estimate != estimated_next_frame_duration_) {
662  DVLOG(3) << "Updated duration estimate:"
663  << orig_duration_estimate
664  << " -> "
665  << estimated_next_frame_duration_
666  << " at timestamp: "
667  << buffer->dts();
668  }
669  }
670 
671  new_sample_cb_.Run(track_num_, buffer);
672  return true;
673 }
674 
675 int64_t WebMClusterParser::Track::GetDurationEstimate() {
676  int64_t duration = estimated_next_frame_duration_;
677  if (duration != kNoTimestamp) {
678  DVLOG(3) << __FUNCTION__ << " : using estimated duration";
679  } else {
680  DVLOG(3) << __FUNCTION__ << " : using hardcoded default duration";
681  if (is_video_) {
682  duration = kDefaultVideoBufferDurationInMs * kMicrosecondsPerMillisecond;
683  } else {
684  duration = kDefaultAudioBufferDurationInMs * kMicrosecondsPerMillisecond;
685  }
686  }
687 
688  DCHECK(duration > 0);
689  DCHECK(duration != kNoTimestamp);
690  return duration;
691 }
692 
693 void WebMClusterParser::ResetTextTracks() {
694  for (TextTrackMap::iterator it = text_track_map_.begin();
695  it != text_track_map_.end();
696  ++it) {
697  it->second.Reset();
698  }
699 }
700 
701 WebMClusterParser::Track*
702 WebMClusterParser::FindTextTrack(int track_num) {
703  const TextTrackMap::iterator it = text_track_map_.find(track_num);
704 
705  if (it == text_track_map_.end())
706  return NULL;
707 
708  return &it->second;
709 }
710 
711 } // namespace media
712 } // namespace edash_packager
void Reset()
Resets the parser state so it can accept a new cluster.
static scoped_refptr< MediaSample > CopyFrom(const uint8_t *data, size_t size, bool is_key_frame)
Definition: media_sample.cc:45
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.
int Parse(const uint8_t *buf, int size)
base::Callback< bool(uint32_t track_id, const scoped_refptr< MediaSample > &media_sample)> NewSampleCB
Definition: media_parser.h:43
int Parse(const uint8_t *buf, int size)
Definition: webm_parser.cc:719
void Reset()
Resets the state of the parser so it can start parsing a new list.
Definition: webm_parser.cc:714