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"
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/filters/webvtt_util.h"
14 #include "media/webm/webm_constants.h"
15 #include "media/webm/webm_crypto_helpers.h"
16 #include "media/webm/webm_webvtt_parser.h"
20 WebMClusterParser::TextTrackIterator::TextTrackIterator(
21 const TextTrackMap
& text_track_map
) :
22 iterator_(text_track_map
.begin()),
23 iterator_end_(text_track_map
.end()) {
26 WebMClusterParser::TextTrackIterator::TextTrackIterator(
27 const TextTrackIterator
& rhs
) :
28 iterator_(rhs
.iterator_
),
29 iterator_end_(rhs
.iterator_end_
) {
32 WebMClusterParser::TextTrackIterator::~TextTrackIterator() {
35 bool WebMClusterParser::TextTrackIterator::operator()(
37 const BufferQueue
** buffers
) {
38 if (iterator_
== iterator_end_
) {
45 *track_num
= iterator_
->first
;
46 *buffers
= &iterator_
->second
.buffers();
52 WebMClusterParser::WebMClusterParser(
53 int64 timecode_scale
, int audio_track_num
, int video_track_num
,
54 const WebMTracksParser::TextTracks
& text_tracks
,
55 const std::set
<int64
>& ignored_tracks
,
56 const std::string
& audio_encryption_key_id
,
57 const std::string
& video_encryption_key_id
,
59 : timecode_multiplier_(timecode_scale
/ 1000.0),
60 ignored_tracks_(ignored_tracks
),
61 audio_encryption_key_id_(audio_encryption_key_id
),
62 video_encryption_key_id_(video_encryption_key_id
),
63 parser_(kWebMIdCluster
, this),
64 last_block_timecode_(-1),
68 block_additional_data_size_(-1),
70 cluster_timecode_(-1),
71 cluster_start_time_(kNoTimestamp()),
72 cluster_ended_(false),
73 audio_(audio_track_num
, false),
74 video_(video_track_num
, true),
76 for (WebMTracksParser::TextTracks::const_iterator it
= text_tracks
.begin();
77 it
!= text_tracks
.end();
79 text_track_map_
.insert(std::make_pair(it
->first
, Track(it
->first
, false)));
83 WebMClusterParser::~WebMClusterParser() {}
85 void WebMClusterParser::Reset() {
86 last_block_timecode_
= -1;
87 cluster_timecode_
= -1;
88 cluster_start_time_
= kNoTimestamp();
89 cluster_ended_
= false;
96 int WebMClusterParser::Parse(const uint8
* buf
, int size
) {
101 int result
= parser_
.Parse(buf
, size
);
104 cluster_ended_
= false;
108 cluster_ended_
= parser_
.IsParsingComplete();
109 if (cluster_ended_
) {
110 // If there were no buffers in this cluster, set the cluster start time to
111 // be the |cluster_timecode_|.
112 if (cluster_start_time_
== kNoTimestamp()) {
113 // If the cluster did not even have a |cluster_timecode_|, signal parse
115 if (cluster_timecode_
< 0)
118 cluster_start_time_
= base::TimeDelta::FromMicroseconds(
119 cluster_timecode_
* timecode_multiplier_
);
122 // Reset the parser if we're done parsing so that
123 // it is ready to accept another cluster on the next
127 last_block_timecode_
= -1;
128 cluster_timecode_
= -1;
134 WebMClusterParser::TextTrackIterator
135 WebMClusterParser::CreateTextTrackIterator() const {
136 return TextTrackIterator(text_track_map_
);
139 WebMParserClient
* WebMClusterParser::OnListStart(int id
) {
140 if (id
== kWebMIdCluster
) {
141 cluster_timecode_
= -1;
142 cluster_start_time_
= kNoTimestamp();
143 } else if (id
== kWebMIdBlockGroup
) {
145 block_data_size_
= -1;
146 block_duration_
= -1;
147 discard_padding_
= -1;
148 discard_padding_set_
= false;
149 } else if (id
== kWebMIdBlockAdditions
) {
151 block_additional_data_
.reset();
152 block_additional_data_size_
= -1;
158 bool WebMClusterParser::OnListEnd(int id
) {
159 if (id
!= kWebMIdBlockGroup
)
162 // Make sure the BlockGroup actually had a Block.
163 if (block_data_size_
== -1) {
164 MEDIA_LOG(log_cb_
) << "Block missing from BlockGroup.";
168 bool result
= ParseBlock(false, block_data_
.get(), block_data_size_
,
169 block_additional_data_
.get(),
170 block_additional_data_size_
, block_duration_
,
171 discard_padding_set_
? discard_padding_
: 0);
173 block_data_size_
= -1;
174 block_duration_
= -1;
176 block_additional_data_
.reset();
177 block_additional_data_size_
= -1;
178 discard_padding_
= -1;
179 discard_padding_set_
= false;
183 bool WebMClusterParser::OnUInt(int id
, int64 val
) {
186 case kWebMIdTimecode
:
187 dst
= &cluster_timecode_
;
189 case kWebMIdBlockDuration
:
190 dst
= &block_duration_
;
192 case kWebMIdBlockAddID
:
193 dst
= &block_add_id_
;
195 case kWebMIdDiscardPadding
:
196 if (discard_padding_set_
)
198 discard_padding_set_
= true;
199 discard_padding_
= val
;
210 bool WebMClusterParser::ParseBlock(bool is_simple_block
, const uint8
* buf
,
211 int size
, const uint8
* additional
,
212 int additional_size
, int duration
,
213 int64 discard_padding
) {
217 // Return an error if the trackNum > 127. We just aren't
218 // going to support large track numbers right now.
219 if (!(buf
[0] & 0x80)) {
220 MEDIA_LOG(log_cb_
) << "TrackNumber over 127 not supported";
224 int track_num
= buf
[0] & 0x7f;
225 int timecode
= buf
[1] << 8 | buf
[2];
226 int flags
= buf
[3] & 0xff;
227 int lacing
= (flags
>> 1) & 0x3;
230 MEDIA_LOG(log_cb_
) << "Lacing " << lacing
<< " is not supported yet.";
234 // Sign extend negative timecode offsets.
235 if (timecode
& 0x8000)
238 const uint8
* frame_data
= buf
+ 4;
239 int frame_size
= size
- (frame_data
- buf
);
240 return OnBlock(is_simple_block
, track_num
, timecode
, duration
, flags
,
241 frame_data
, frame_size
, additional
, additional_size
,
245 bool WebMClusterParser::OnBinary(int id
, const uint8
* data
, int size
) {
247 case kWebMIdSimpleBlock
:
248 return ParseBlock(true, data
, size
, NULL
, -1, -1, 0);
252 MEDIA_LOG(log_cb_
) << "More than 1 Block in a BlockGroup is not "
256 block_data_
.reset(new uint8
[size
]);
257 memcpy(block_data_
.get(), data
, size
);
258 block_data_size_
= size
;
261 case kWebMIdBlockAdditional
: {
262 uint64 block_add_id
= base::HostToNet64(block_add_id_
);
263 if (block_additional_data_
) {
264 // TODO(vigneshv): Technically, more than 1 BlockAdditional is allowed
265 // as per matroska spec. But for now we don't have a use case to
266 // support parsing of such files. Take a look at this again when such a
268 MEDIA_LOG(log_cb_
) << "More than 1 BlockAdditional in a BlockGroup is "
272 // First 8 bytes of side_data in DecoderBuffer is the BlockAddID
273 // element's value in Big Endian format. This is done to mimic ffmpeg
274 // demuxer's behavior.
275 block_additional_data_size_
= size
+ sizeof(block_add_id
);
276 block_additional_data_
.reset(new uint8
[block_additional_data_size_
]);
277 memcpy(block_additional_data_
.get(), &block_add_id
,
278 sizeof(block_add_id
));
279 memcpy(block_additional_data_
.get() + 8, data
, size
);
288 bool WebMClusterParser::OnBlock(bool is_simple_block
, int track_num
,
292 const uint8
* data
, int size
,
293 const uint8
* additional
, int additional_size
,
294 int64 discard_padding
) {
296 if (cluster_timecode_
== -1) {
297 MEDIA_LOG(log_cb_
) << "Got a block before cluster timecode.";
301 // TODO(acolwell): Should relative negative timecode offsets be rejected? Or
302 // only when the absolute timecode is negative? See http://crbug.com/271794
304 MEDIA_LOG(log_cb_
) << "Got a block with negative timecode offset "
309 if (last_block_timecode_
!= -1 && timecode
< last_block_timecode_
) {
311 << "Got a block with a timecode before the previous block.";
316 bool is_text
= false;
317 std::string encryption_key_id
;
318 if (track_num
== audio_
.track_num()) {
320 encryption_key_id
= audio_encryption_key_id_
;
321 } else if (track_num
== video_
.track_num()) {
323 encryption_key_id
= video_encryption_key_id_
;
324 } else if (ignored_tracks_
.find(track_num
) != ignored_tracks_
.end()) {
326 } else if (Track
* const text_track
= FindTextTrack(track_num
)) {
327 if (is_simple_block
) // BlockGroup is required for WebVTT cues
329 if (block_duration
< 0) // not specified
334 MEDIA_LOG(log_cb_
) << "Unexpected track number " << track_num
;
338 last_block_timecode_
= timecode
;
340 base::TimeDelta timestamp
= base::TimeDelta::FromMicroseconds(
341 (cluster_timecode_
+ timecode
) * timecode_multiplier_
);
343 scoped_refptr
<StreamParserBuffer
> buffer
;
345 // The first bit of the flags is set when a SimpleBlock contains only
346 // keyframes. If this is a Block, then inspection of the payload is
347 // necessary to determine whether it contains a keyframe or not.
348 // http://www.matroska.org/technical/specs/index.html
350 is_simple_block
? (flags
& 0x80) != 0 : track
->IsKeyframe(data
, size
);
352 // Every encrypted Block has a signal byte and IV prepended to it. Current
353 // encrypted WebM request for comments specification is here
354 // http://wiki.webmproject.org/encryption/webm-encryption-rfc
355 scoped_ptr
<DecryptConfig
> decrypt_config
;
357 if (!encryption_key_id
.empty() &&
358 !WebMCreateDecryptConfig(
360 reinterpret_cast<const uint8
*>(encryption_key_id
.data()),
361 encryption_key_id
.size(),
362 &decrypt_config
, &data_offset
)) {
366 buffer
= StreamParserBuffer::CopyFrom(
367 data
+ data_offset
, size
- data_offset
,
368 additional
, additional_size
,
372 buffer
->set_decrypt_config(decrypt_config
.Pass());
374 std::string id
, settings
, content
;
375 WebMWebVTTParser::Parse(data
, size
, &id
, &settings
, &content
);
377 std::vector
<uint8
> side_data
;
378 MakeSideData(id
.begin(), id
.end(),
379 settings
.begin(), settings
.end(),
382 buffer
= StreamParserBuffer::CopyFrom(
383 reinterpret_cast<const uint8
*>(content
.data()),
390 buffer
->set_timestamp(timestamp
);
391 if (cluster_start_time_
== kNoTimestamp())
392 cluster_start_time_
= timestamp
;
394 if (block_duration
>= 0) {
395 buffer
->set_duration(base::TimeDelta::FromMicroseconds(
396 block_duration
* timecode_multiplier_
));
399 if (discard_padding
!= 0) {
400 buffer
->set_discard_padding(base::TimeDelta::FromMicroseconds(
401 discard_padding
/ 1000));
404 return track
->AddBuffer(buffer
);
407 WebMClusterParser::Track::Track(int track_num
, bool is_video
)
408 : track_num_(track_num
),
409 is_video_(is_video
) {
412 WebMClusterParser::Track::~Track() {}
414 bool WebMClusterParser::Track::AddBuffer(
415 const scoped_refptr
<StreamParserBuffer
>& buffer
) {
416 DVLOG(2) << "AddBuffer() : " << track_num_
417 << " ts " << buffer
->timestamp().InSecondsF()
418 << " dur " << buffer
->duration().InSecondsF()
419 << " kf " << buffer
->IsKeyframe()
420 << " size " << buffer
->data_size();
422 buffers_
.push_back(buffer
);
426 void WebMClusterParser::Track::Reset() {
430 bool WebMClusterParser::Track::IsKeyframe(const uint8
* data
, int size
) const {
431 // For now, assume that all blocks are keyframes for datatypes other than
432 // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
436 // Make sure the block is big enough for the minimal keyframe header size.
440 // The LSb of the first byte must be a 0 for a keyframe.
441 // http://tools.ietf.org/html/rfc6386 Section 19.1
442 if ((data
[0] & 0x01) != 0)
445 // Verify VP8 keyframe startcode.
446 // http://tools.ietf.org/html/rfc6386 Section 19.1
447 if (data
[3] != 0x9d || data
[4] != 0x01 || data
[5] != 0x2a)
453 void WebMClusterParser::ResetTextTracks() {
454 for (TextTrackMap::iterator it
= text_track_map_
.begin();
455 it
!= text_track_map_
.end();
461 WebMClusterParser::Track
*
462 WebMClusterParser::FindTextTrack(int track_num
) {
463 const TextTrackMap::iterator it
= text_track_map_
.find(track_num
);
465 if (it
== text_track_map_
.end())