1// Copyright (c) 2012 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 "media/webm/webm_cluster_parser.h"
6
7#include <vector>
8
9#include "base/logging.h"
10#include "base/sys_byteorder.h"
11#include "media/base/buffers.h"
12#include "media/base/decrypt_config.h"
13#include "media/webm/webm_constants.h"
14#include "media/webm/webm_crypto_helpers.h"
15
16namespace media {
17
18WebMClusterParser::TextTrackIterator::TextTrackIterator(
19    const TextTrackMap& text_track_map) :
20    iterator_(text_track_map.begin()),
21    iterator_end_(text_track_map.end()) {
22}
23
24WebMClusterParser::TextTrackIterator::TextTrackIterator(
25    const TextTrackIterator& rhs) :
26    iterator_(rhs.iterator_),
27    iterator_end_(rhs.iterator_end_) {
28}
29
30WebMClusterParser::TextTrackIterator::~TextTrackIterator() {
31}
32
33bool WebMClusterParser::TextTrackIterator::operator()(
34  int* track_num,
35  const BufferQueue** buffers) {
36  if (iterator_ == iterator_end_) {
37    *track_num = 0;
38    *buffers = NULL;
39
40    return false;
41  }
42
43  *track_num = iterator_->first;
44  *buffers = &iterator_->second.buffers();
45
46  ++iterator_;
47  return true;
48}
49
50WebMClusterParser::WebMClusterParser(
51    int64 timecode_scale, int audio_track_num, int video_track_num,
52    const WebMTracksParser::TextTracks& text_tracks,
53    const std::set<int64>& ignored_tracks,
54    const std::string& audio_encryption_key_id,
55    const std::string& video_encryption_key_id,
56    const LogCB& log_cb)
57    : timecode_multiplier_(timecode_scale / 1000.0),
58      ignored_tracks_(ignored_tracks),
59      audio_encryption_key_id_(audio_encryption_key_id),
60      video_encryption_key_id_(video_encryption_key_id),
61      parser_(kWebMIdCluster, this),
62      last_block_timecode_(-1),
63      block_data_size_(-1),
64      block_duration_(-1),
65      block_add_id_(-1),
66      block_additional_data_size_(-1),
67      cluster_timecode_(-1),
68      cluster_start_time_(kNoTimestamp()),
69      cluster_ended_(false),
70      audio_(audio_track_num, false),
71      video_(video_track_num, true),
72      log_cb_(log_cb) {
73  for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin();
74       it != text_tracks.end();
75       ++it) {
76    text_track_map_.insert(std::make_pair(it->first, Track(it->first, false)));
77  }
78}
79
80WebMClusterParser::~WebMClusterParser() {}
81
82void WebMClusterParser::Reset() {
83  last_block_timecode_ = -1;
84  cluster_timecode_ = -1;
85  cluster_start_time_ = kNoTimestamp();
86  cluster_ended_ = false;
87  parser_.Reset();
88  audio_.Reset();
89  video_.Reset();
90  ResetTextTracks();
91}
92
93int WebMClusterParser::Parse(const uint8* buf, int size) {
94  audio_.Reset();
95  video_.Reset();
96  ResetTextTracks();
97
98  int result = parser_.Parse(buf, size);
99
100  if (result < 0) {
101    cluster_ended_ = false;
102    return result;
103  }
104
105  cluster_ended_ = parser_.IsParsingComplete();
106  if (cluster_ended_) {
107    // If there were no buffers in this cluster, set the cluster start time to
108    // be the |cluster_timecode_|.
109    if (cluster_start_time_ == kNoTimestamp()) {
110      DCHECK_GT(cluster_timecode_, -1);
111      cluster_start_time_ = base::TimeDelta::FromMicroseconds(
112          cluster_timecode_ * timecode_multiplier_);
113    }
114
115    // Reset the parser if we're done parsing so that
116    // it is ready to accept another cluster on the next
117    // call.
118    parser_.Reset();
119
120    last_block_timecode_ = -1;
121    cluster_timecode_ = -1;
122  }
123
124  return result;
125}
126
127WebMClusterParser::TextTrackIterator
128WebMClusterParser::CreateTextTrackIterator() const {
129  return TextTrackIterator(text_track_map_);
130}
131
132WebMParserClient* WebMClusterParser::OnListStart(int id) {
133  if (id == kWebMIdCluster) {
134    cluster_timecode_ = -1;
135    cluster_start_time_ = kNoTimestamp();
136  } else if (id == kWebMIdBlockGroup) {
137    block_data_.reset();
138    block_data_size_ = -1;
139    block_duration_ = -1;
140  } else if (id == kWebMIdBlockAdditions) {
141    block_add_id_ = -1;
142    block_additional_data_.reset();
143    block_additional_data_size_ = -1;
144  }
145
146  return this;
147}
148
149bool WebMClusterParser::OnListEnd(int id) {
150  if (id != kWebMIdBlockGroup)
151    return true;
152
153  // Make sure the BlockGroup actually had a Block.
154  if (block_data_size_ == -1) {
155    MEDIA_LOG(log_cb_) << "Block missing from BlockGroup.";
156    return false;
157  }
158
159  bool result = ParseBlock(false, block_data_.get(), block_data_size_,
160                           block_additional_data_.get(),
161                           block_additional_data_size_, block_duration_);
162  block_data_.reset();
163  block_data_size_ = -1;
164  block_duration_ = -1;
165  block_add_id_ = -1;
166  block_additional_data_.reset();
167  block_additional_data_size_ = -1;
168  return result;
169}
170
171bool WebMClusterParser::OnUInt(int id, int64 val) {
172  int64* dst;
173  switch (id) {
174    case kWebMIdTimecode:
175      dst = &cluster_timecode_;
176      break;
177    case kWebMIdBlockDuration:
178      dst = &block_duration_;
179      break;
180    case kWebMIdBlockAddID:
181      dst = &block_add_id_;
182      break;
183    default:
184      return true;
185  }
186  if (*dst != -1)
187    return false;
188  *dst = val;
189  return true;
190}
191
192bool WebMClusterParser::ParseBlock(bool is_simple_block, const uint8* buf,
193                                   int size, const uint8* additional,
194                                   int additional_size, int duration) {
195  if (size < 4)
196    return false;
197
198  // Return an error if the trackNum > 127. We just aren't
199  // going to support large track numbers right now.
200  if (!(buf[0] & 0x80)) {
201    MEDIA_LOG(log_cb_) << "TrackNumber over 127 not supported";
202    return false;
203  }
204
205  int track_num = buf[0] & 0x7f;
206  int timecode = buf[1] << 8 | buf[2];
207  int flags = buf[3] & 0xff;
208  int lacing = (flags >> 1) & 0x3;
209
210  if (lacing) {
211    MEDIA_LOG(log_cb_) << "Lacing " << lacing << " is not supported yet.";
212    return false;
213  }
214
215  // Sign extend negative timecode offsets.
216  if (timecode & 0x8000)
217    timecode |= (-1 << 16);
218
219  const uint8* frame_data = buf + 4;
220  int frame_size = size - (frame_data - buf);
221  return OnBlock(is_simple_block, track_num, timecode, duration, flags,
222                 frame_data, frame_size, additional, additional_size);
223}
224
225bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) {
226  switch (id) {
227    case kWebMIdSimpleBlock:
228      return ParseBlock(true, data, size, NULL, -1, -1);
229
230    case kWebMIdBlock:
231      if (block_data_) {
232        MEDIA_LOG(log_cb_) << "More than 1 Block in a BlockGroup is not "
233                              "supported.";
234        return false;
235      }
236      block_data_.reset(new uint8[size]);
237      memcpy(block_data_.get(), data, size);
238      block_data_size_ = size;
239      return true;
240
241    case kWebMIdBlockAdditional: {
242      uint64 block_add_id = base::HostToNet64(block_add_id_);
243      if (block_additional_data_) {
244        // TODO(vigneshv): Technically, more than 1 BlockAdditional is allowed
245        // as per matroska spec. But for now we don't have a use case to
246        // support parsing of such files. Take a look at this again when such a
247        // case arises.
248        MEDIA_LOG(log_cb_) << "More than 1 BlockAdditional in a BlockGroup is "
249                              "not supported.";
250        return false;
251      }
252      // First 8 bytes of side_data in DecoderBuffer is the BlockAddID
253      // element's value in Big Endian format. This is done to mimic ffmpeg
254      // demuxer's behavior.
255      block_additional_data_size_ = size + sizeof(block_add_id);
256      block_additional_data_.reset(new uint8[block_additional_data_size_]);
257      memcpy(block_additional_data_.get(), &block_add_id,
258             sizeof(block_add_id));
259      memcpy(block_additional_data_.get() + 8, data, size);
260      return true;
261    }
262
263    default:
264      return true;
265  }
266}
267
268bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num,
269                                int timecode,
270                                int  block_duration,
271                                int flags,
272                                const uint8* data, int size,
273                                const uint8* additional, int additional_size) {
274  DCHECK_GE(size, 0);
275  if (cluster_timecode_ == -1) {
276    MEDIA_LOG(log_cb_) << "Got a block before cluster timecode.";
277    return false;
278  }
279
280  if (timecode < 0) {
281    MEDIA_LOG(log_cb_) << "Got a block with negative timecode offset "
282                       << timecode;
283    return false;
284  }
285
286  if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
287    MEDIA_LOG(log_cb_)
288        << "Got a block with a timecode before the previous block.";
289    return false;
290  }
291
292  Track* track = NULL;
293  std::string encryption_key_id;
294  if (track_num == audio_.track_num()) {
295    track = &audio_;
296    encryption_key_id = audio_encryption_key_id_;
297  } else if (track_num == video_.track_num()) {
298    track = &video_;
299    encryption_key_id = video_encryption_key_id_;
300  } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
301    return true;
302  } else if (Track* const text_track = FindTextTrack(track_num)) {
303    if (is_simple_block)  // BlockGroup is required for WebVTT cues
304      return false;
305    if (block_duration < 0)  // not specified
306      return false;
307    track = text_track;
308  } else {
309    MEDIA_LOG(log_cb_) << "Unexpected track number " << track_num;
310    return false;
311  }
312
313  last_block_timecode_ = timecode;
314
315  base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
316      (cluster_timecode_ + timecode) * timecode_multiplier_);
317
318  // The first bit of the flags is set when a SimpleBlock contains only
319  // keyframes. If this is a Block, then inspection of the payload is
320  // necessary to determine whether it contains a keyframe or not.
321  // http://www.matroska.org/technical/specs/index.html
322  bool is_keyframe =
323      is_simple_block ? (flags & 0x80) != 0 : track->IsKeyframe(data, size);
324
325  scoped_refptr<StreamParserBuffer> buffer =
326      StreamParserBuffer::CopyFrom(data, size, additional, additional_size,
327                                   is_keyframe);
328
329  // Every encrypted Block has a signal byte and IV prepended to it. Current
330  // encrypted WebM request for comments specification is here
331  // http://wiki.webmproject.org/encryption/webm-encryption-rfc
332  if (!encryption_key_id.empty()) {
333    scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig(
334        data, size,
335        reinterpret_cast<const uint8*>(encryption_key_id.data()),
336        encryption_key_id.size()));
337    if (!config)
338      return false;
339    buffer->set_decrypt_config(config.Pass());
340  }
341
342  buffer->set_timestamp(timestamp);
343  if (cluster_start_time_ == kNoTimestamp())
344    cluster_start_time_ = timestamp;
345
346  if (block_duration >= 0) {
347    buffer->set_duration(base::TimeDelta::FromMicroseconds(
348        block_duration * timecode_multiplier_));
349  }
350
351  return track->AddBuffer(buffer);
352}
353
354WebMClusterParser::Track::Track(int track_num, bool is_video)
355    : track_num_(track_num),
356      is_video_(is_video) {
357}
358
359WebMClusterParser::Track::~Track() {}
360
361bool WebMClusterParser::Track::AddBuffer(
362    const scoped_refptr<StreamParserBuffer>& buffer) {
363  DVLOG(2) << "AddBuffer() : " << track_num_
364           << " ts " << buffer->timestamp().InSecondsF()
365           << " dur " << buffer->duration().InSecondsF()
366           << " kf " << buffer->IsKeyframe()
367           << " size " << buffer->data_size();
368
369  buffers_.push_back(buffer);
370  return true;
371}
372
373void WebMClusterParser::Track::Reset() {
374  buffers_.clear();
375}
376
377bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const {
378  // For now, assume that all blocks are keyframes for datatypes other than
379  // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
380  if (!is_video_)
381    return true;
382
383  // Make sure the block is big enough for the minimal keyframe header size.
384  if (size < 7)
385    return false;
386
387  // The LSb of the first byte must be a 0 for a keyframe.
388  // http://tools.ietf.org/html/rfc6386 Section 19.1
389  if ((data[0] & 0x01) != 0)
390    return false;
391
392  // Verify VP8 keyframe startcode.
393  // http://tools.ietf.org/html/rfc6386 Section 19.1
394  if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a)
395    return false;
396
397  return true;
398}
399
400void WebMClusterParser::ResetTextTracks() {
401  for (TextTrackMap::iterator it = text_track_map_.begin();
402       it != text_track_map_.end();
403       ++it) {
404    it->second.Reset();
405  }
406}
407
408WebMClusterParser::Track*
409WebMClusterParser::FindTextTrack(int track_num) {
410  const TextTrackMap::iterator it = text_track_map_.find(track_num);
411
412  if (it == text_track_map_.end())
413    return NULL;
414
415  return &it->second;
416}
417
418}  // namespace media
419