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