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