Ensure callback objects held by the Indexed DB
[chromium-blink-merge.git] / media / webm / webm_cluster_parser.cc
blob87cccae4da10683d8c11fdeac2aaec178719f222
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.
5 #include "media/webm/webm_cluster_parser.h"
7 #include <vector>
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"
16 namespace media {
18 WebMClusterParser::TextTrackIterator::TextTrackIterator(
19 const TextTrackMap& text_track_map) :
20 iterator_(text_track_map.begin()),
21 iterator_end_(text_track_map.end()) {
24 WebMClusterParser::TextTrackIterator::TextTrackIterator(
25 const TextTrackIterator& rhs) :
26 iterator_(rhs.iterator_),
27 iterator_end_(rhs.iterator_end_) {
30 WebMClusterParser::TextTrackIterator::~TextTrackIterator() {
33 bool WebMClusterParser::TextTrackIterator::operator()(
34 int* track_num,
35 const BufferQueue** buffers) {
36 if (iterator_ == iterator_end_) {
37 *track_num = 0;
38 *buffers = NULL;
40 return false;
43 *track_num = iterator_->first;
44 *buffers = &iterator_->second.buffers();
46 ++iterator_;
47 return true;
50 WebMClusterParser::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 discard_padding_(-1),
68 cluster_timecode_(-1),
69 cluster_start_time_(kNoTimestamp()),
70 cluster_ended_(false),
71 audio_(audio_track_num, false),
72 video_(video_track_num, true),
73 log_cb_(log_cb) {
74 for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin();
75 it != text_tracks.end();
76 ++it) {
77 text_track_map_.insert(std::make_pair(it->first, Track(it->first, false)));
81 WebMClusterParser::~WebMClusterParser() {}
83 void WebMClusterParser::Reset() {
84 last_block_timecode_ = -1;
85 cluster_timecode_ = -1;
86 cluster_start_time_ = kNoTimestamp();
87 cluster_ended_ = false;
88 parser_.Reset();
89 audio_.Reset();
90 video_.Reset();
91 ResetTextTracks();
94 int WebMClusterParser::Parse(const uint8* buf, int size) {
95 audio_.Reset();
96 video_.Reset();
97 ResetTextTracks();
99 int result = parser_.Parse(buf, size);
101 if (result < 0) {
102 cluster_ended_ = false;
103 return result;
106 cluster_ended_ = parser_.IsParsingComplete();
107 if (cluster_ended_) {
108 // If there were no buffers in this cluster, set the cluster start time to
109 // be the |cluster_timecode_|.
110 if (cluster_start_time_ == kNoTimestamp()) {
111 DCHECK_GT(cluster_timecode_, -1);
112 cluster_start_time_ = base::TimeDelta::FromMicroseconds(
113 cluster_timecode_ * timecode_multiplier_);
116 // Reset the parser if we're done parsing so that
117 // it is ready to accept another cluster on the next
118 // call.
119 parser_.Reset();
121 last_block_timecode_ = -1;
122 cluster_timecode_ = -1;
125 return result;
128 WebMClusterParser::TextTrackIterator
129 WebMClusterParser::CreateTextTrackIterator() const {
130 return TextTrackIterator(text_track_map_);
133 WebMParserClient* WebMClusterParser::OnListStart(int id) {
134 if (id == kWebMIdCluster) {
135 cluster_timecode_ = -1;
136 cluster_start_time_ = kNoTimestamp();
137 } else if (id == kWebMIdBlockGroup) {
138 block_data_.reset();
139 block_data_size_ = -1;
140 block_duration_ = -1;
141 discard_padding_ = -1;
142 discard_padding_set_ = false;
143 } else if (id == kWebMIdBlockAdditions) {
144 block_add_id_ = -1;
145 block_additional_data_.reset();
146 block_additional_data_size_ = -1;
149 return this;
152 bool WebMClusterParser::OnListEnd(int id) {
153 if (id != kWebMIdBlockGroup)
154 return true;
156 // Make sure the BlockGroup actually had a Block.
157 if (block_data_size_ == -1) {
158 MEDIA_LOG(log_cb_) << "Block missing from BlockGroup.";
159 return false;
162 bool result = ParseBlock(false, block_data_.get(), block_data_size_,
163 block_additional_data_.get(),
164 block_additional_data_size_, block_duration_,
165 discard_padding_set_ ? discard_padding_ : 0);
166 block_data_.reset();
167 block_data_size_ = -1;
168 block_duration_ = -1;
169 block_add_id_ = -1;
170 block_additional_data_.reset();
171 block_additional_data_size_ = -1;
172 discard_padding_ = -1;
173 discard_padding_set_ = false;
174 return result;
177 bool WebMClusterParser::OnUInt(int id, int64 val) {
178 int64* dst;
179 switch (id) {
180 case kWebMIdTimecode:
181 dst = &cluster_timecode_;
182 break;
183 case kWebMIdBlockDuration:
184 dst = &block_duration_;
185 break;
186 case kWebMIdBlockAddID:
187 dst = &block_add_id_;
188 break;
189 case kWebMIdDiscardPadding:
190 if (discard_padding_set_)
191 return false;
192 discard_padding_set_ = true;
193 discard_padding_ = val;
194 return true;
195 default:
196 return true;
198 if (*dst != -1)
199 return false;
200 *dst = val;
201 return true;
204 bool WebMClusterParser::ParseBlock(bool is_simple_block, const uint8* buf,
205 int size, const uint8* additional,
206 int additional_size, int duration,
207 int64 discard_padding) {
208 if (size < 4)
209 return false;
211 // Return an error if the trackNum > 127. We just aren't
212 // going to support large track numbers right now.
213 if (!(buf[0] & 0x80)) {
214 MEDIA_LOG(log_cb_) << "TrackNumber over 127 not supported";
215 return false;
218 int track_num = buf[0] & 0x7f;
219 int timecode = buf[1] << 8 | buf[2];
220 int flags = buf[3] & 0xff;
221 int lacing = (flags >> 1) & 0x3;
223 if (lacing) {
224 MEDIA_LOG(log_cb_) << "Lacing " << lacing << " is not supported yet.";
225 return false;
228 // Sign extend negative timecode offsets.
229 if (timecode & 0x8000)
230 timecode |= ~0xffff;
232 const uint8* frame_data = buf + 4;
233 int frame_size = size - (frame_data - buf);
234 return OnBlock(is_simple_block, track_num, timecode, duration, flags,
235 frame_data, frame_size, additional, additional_size,
236 discard_padding);
239 bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) {
240 switch (id) {
241 case kWebMIdSimpleBlock:
242 return ParseBlock(true, data, size, NULL, -1, -1, 0);
244 case kWebMIdBlock:
245 if (block_data_) {
246 MEDIA_LOG(log_cb_) << "More than 1 Block in a BlockGroup is not "
247 "supported.";
248 return false;
250 block_data_.reset(new uint8[size]);
251 memcpy(block_data_.get(), data, size);
252 block_data_size_ = size;
253 return true;
255 case kWebMIdBlockAdditional: {
256 uint64 block_add_id = base::HostToNet64(block_add_id_);
257 if (block_additional_data_) {
258 // TODO(vigneshv): Technically, more than 1 BlockAdditional is allowed
259 // as per matroska spec. But for now we don't have a use case to
260 // support parsing of such files. Take a look at this again when such a
261 // case arises.
262 MEDIA_LOG(log_cb_) << "More than 1 BlockAdditional in a BlockGroup is "
263 "not supported.";
264 return false;
266 // First 8 bytes of side_data in DecoderBuffer is the BlockAddID
267 // element's value in Big Endian format. This is done to mimic ffmpeg
268 // demuxer's behavior.
269 block_additional_data_size_ = size + sizeof(block_add_id);
270 block_additional_data_.reset(new uint8[block_additional_data_size_]);
271 memcpy(block_additional_data_.get(), &block_add_id,
272 sizeof(block_add_id));
273 memcpy(block_additional_data_.get() + 8, data, size);
274 return true;
277 default:
278 return true;
282 bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num,
283 int timecode,
284 int block_duration,
285 int flags,
286 const uint8* data, int size,
287 const uint8* additional, int additional_size,
288 int64 discard_padding) {
289 DCHECK_GE(size, 0);
290 if (cluster_timecode_ == -1) {
291 MEDIA_LOG(log_cb_) << "Got a block before cluster timecode.";
292 return false;
295 // TODO(acolwell): Should relative negative timecode offsets be rejected? Or
296 // only when the absolute timecode is negative? See http://crbug.com/271794
297 if (timecode < 0) {
298 MEDIA_LOG(log_cb_) << "Got a block with negative timecode offset "
299 << timecode;
300 return false;
303 if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
304 MEDIA_LOG(log_cb_)
305 << "Got a block with a timecode before the previous block.";
306 return false;
309 Track* track = NULL;
310 std::string encryption_key_id;
311 if (track_num == audio_.track_num()) {
312 track = &audio_;
313 encryption_key_id = audio_encryption_key_id_;
314 } else if (track_num == video_.track_num()) {
315 track = &video_;
316 encryption_key_id = video_encryption_key_id_;
317 } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
318 return true;
319 } else if (Track* const text_track = FindTextTrack(track_num)) {
320 if (is_simple_block) // BlockGroup is required for WebVTT cues
321 return false;
322 if (block_duration < 0) // not specified
323 return false;
324 track = text_track;
325 } else {
326 MEDIA_LOG(log_cb_) << "Unexpected track number " << track_num;
327 return false;
330 last_block_timecode_ = timecode;
332 base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
333 (cluster_timecode_ + timecode) * timecode_multiplier_);
335 // The first bit of the flags is set when a SimpleBlock contains only
336 // keyframes. If this is a Block, then inspection of the payload is
337 // necessary to determine whether it contains a keyframe or not.
338 // http://www.matroska.org/technical/specs/index.html
339 bool is_keyframe =
340 is_simple_block ? (flags & 0x80) != 0 : track->IsKeyframe(data, size);
342 scoped_refptr<StreamParserBuffer> buffer =
343 StreamParserBuffer::CopyFrom(data, size, additional, additional_size,
344 is_keyframe);
346 // Every encrypted Block has a signal byte and IV prepended to it. Current
347 // encrypted WebM request for comments specification is here
348 // http://wiki.webmproject.org/encryption/webm-encryption-rfc
349 if (!encryption_key_id.empty()) {
350 scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig(
351 data, size,
352 reinterpret_cast<const uint8*>(encryption_key_id.data()),
353 encryption_key_id.size()));
354 if (!config)
355 return false;
356 buffer->set_decrypt_config(config.Pass());
359 buffer->set_timestamp(timestamp);
360 if (cluster_start_time_ == kNoTimestamp())
361 cluster_start_time_ = timestamp;
363 if (block_duration >= 0) {
364 buffer->set_duration(base::TimeDelta::FromMicroseconds(
365 block_duration * timecode_multiplier_));
368 if (discard_padding != 0) {
369 buffer->set_discard_padding(base::TimeDelta::FromMicroseconds(
370 discard_padding / 1000));
373 return track->AddBuffer(buffer);
376 WebMClusterParser::Track::Track(int track_num, bool is_video)
377 : track_num_(track_num),
378 is_video_(is_video) {
381 WebMClusterParser::Track::~Track() {}
383 bool WebMClusterParser::Track::AddBuffer(
384 const scoped_refptr<StreamParserBuffer>& buffer) {
385 DVLOG(2) << "AddBuffer() : " << track_num_
386 << " ts " << buffer->timestamp().InSecondsF()
387 << " dur " << buffer->duration().InSecondsF()
388 << " kf " << buffer->IsKeyframe()
389 << " size " << buffer->data_size();
391 buffers_.push_back(buffer);
392 return true;
395 void WebMClusterParser::Track::Reset() {
396 buffers_.clear();
399 bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const {
400 // For now, assume that all blocks are keyframes for datatypes other than
401 // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
402 if (!is_video_)
403 return true;
405 // Make sure the block is big enough for the minimal keyframe header size.
406 if (size < 7)
407 return false;
409 // The LSb of the first byte must be a 0 for a keyframe.
410 // http://tools.ietf.org/html/rfc6386 Section 19.1
411 if ((data[0] & 0x01) != 0)
412 return false;
414 // Verify VP8 keyframe startcode.
415 // http://tools.ietf.org/html/rfc6386 Section 19.1
416 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a)
417 return false;
419 return true;
422 void WebMClusterParser::ResetTextTracks() {
423 for (TextTrackMap::iterator it = text_track_map_.begin();
424 it != text_track_map_.end();
425 ++it) {
426 it->second.Reset();
430 WebMClusterParser::Track*
431 WebMClusterParser::FindTextTrack(int track_num) {
432 const TextTrackMap::iterator it = text_track_map_.find(track_num);
434 if (it == text_track_map_.end())
435 return NULL;
437 return &it->second;
440 } // namespace media