- add sources.
[platform/framework/web/crosswalk.git] / src / media / webm / webm_cluster_parser.cc
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
16 namespace media {
17
18 WebMClusterParser::TextTrackIterator::TextTrackIterator(
19     const TextTrackMap& text_track_map) :
20     iterator_(text_track_map.begin()),
21     iterator_end_(text_track_map.end()) {
22 }
23
24 WebMClusterParser::TextTrackIterator::TextTrackIterator(
25     const TextTrackIterator& rhs) :
26     iterator_(rhs.iterator_),
27     iterator_end_(rhs.iterator_end_) {
28 }
29
30 WebMClusterParser::TextTrackIterator::~TextTrackIterator() {
31 }
32
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;
39
40     return false;
41   }
42
43   *track_num = iterator_->first;
44   *buffers = &iterator_->second.buffers();
45
46   ++iterator_;
47   return true;
48 }
49
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)));
78   }
79 }
80
81 WebMClusterParser::~WebMClusterParser() {}
82
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();
92 }
93
94 int WebMClusterParser::Parse(const uint8* buf, int size) {
95   audio_.Reset();
96   video_.Reset();
97   ResetTextTracks();
98
99   int result = parser_.Parse(buf, size);
100
101   if (result < 0) {
102     cluster_ended_ = false;
103     return result;
104   }
105
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_);
114     }
115
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();
120
121     last_block_timecode_ = -1;
122     cluster_timecode_ = -1;
123   }
124
125   return result;
126 }
127
128 WebMClusterParser::TextTrackIterator
129 WebMClusterParser::CreateTextTrackIterator() const {
130   return TextTrackIterator(text_track_map_);
131 }
132
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;
147   }
148
149   return this;
150 }
151
152 bool WebMClusterParser::OnListEnd(int id) {
153   if (id != kWebMIdBlockGroup)
154     return true;
155
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;
160   }
161
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;
175 }
176
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;
197   }
198   if (*dst != -1)
199     return false;
200   *dst = val;
201   return true;
202 }
203
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;
210
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;
216   }
217
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;
222
223   if (lacing) {
224     MEDIA_LOG(log_cb_) << "Lacing " << lacing << " is not supported yet.";
225     return false;
226   }
227
228   // Sign extend negative timecode offsets.
229   if (timecode & 0x8000)
230     timecode |= ~0xffff;
231
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);
237 }
238
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);
243
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;
249       }
250       block_data_.reset(new uint8[size]);
251       memcpy(block_data_.get(), data, size);
252       block_data_size_ = size;
253       return true;
254
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;
265       }
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;
275     }
276
277     default:
278       return true;
279   }
280 }
281
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;
293   }
294
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;
301   }
302
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;
307   }
308
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;
328   }
329
330   last_block_timecode_ = timecode;
331
332   base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
333       (cluster_timecode_ + timecode) * timecode_multiplier_);
334
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);
341
342   scoped_refptr<StreamParserBuffer> buffer =
343       StreamParserBuffer::CopyFrom(data, size, additional, additional_size,
344                                    is_keyframe);
345
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());
357   }
358
359   buffer->set_timestamp(timestamp);
360   if (cluster_start_time_ == kNoTimestamp())
361     cluster_start_time_ = timestamp;
362
363   if (block_duration >= 0) {
364     buffer->set_duration(base::TimeDelta::FromMicroseconds(
365         block_duration * timecode_multiplier_));
366   }
367
368   if (discard_padding != 0) {
369     buffer->set_discard_padding(base::TimeDelta::FromMicroseconds(
370                                     discard_padding / 1000));
371   }
372
373   return track->AddBuffer(buffer);
374 }
375
376 WebMClusterParser::Track::Track(int track_num, bool is_video)
377     : track_num_(track_num),
378       is_video_(is_video) {
379 }
380
381 WebMClusterParser::Track::~Track() {}
382
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();
390
391   buffers_.push_back(buffer);
392   return true;
393 }
394
395 void WebMClusterParser::Track::Reset() {
396   buffers_.clear();
397 }
398
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;
404
405   // Make sure the block is big enough for the minimal keyframe header size.
406   if (size < 7)
407     return false;
408
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;
413
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;
418
419   return true;
420 }
421
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();
427   }
428 }
429
430 WebMClusterParser::Track*
431 WebMClusterParser::FindTextTrack(int track_num) {
432   const TextTrackMap::iterator it = text_track_map_.find(track_num);
433
434   if (it == text_track_map_.end())
435     return NULL;
436
437   return &it->second;
438 }
439
440 }  // namespace media