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/webm/webm_constants.h"
14 #include "media/webm/webm_crypto_helpers.h"
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()(
35 const BufferQueue
** buffers
) {
36 if (iterator_
== iterator_end_
) {
43 *track_num
= iterator_
->first
;
44 *buffers
= &iterator_
->second
.buffers();
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
,
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),
66 block_additional_data_size_(-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),
74 for (WebMTracksParser::TextTracks::const_iterator it
= text_tracks
.begin();
75 it
!= text_tracks
.end();
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;
94 int WebMClusterParser::Parse(const uint8
* buf
, int size
) {
99 int result
= parser_
.Parse(buf
, size
);
102 cluster_ended_
= false;
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
121 last_block_timecode_
= -1;
122 cluster_timecode_
= -1;
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
) {
139 block_data_size_
= -1;
140 block_duration_
= -1;
141 discard_padding_
= -1;
142 discard_padding_set_
= false;
143 } else if (id
== kWebMIdBlockAdditions
) {
145 block_additional_data_
.reset();
146 block_additional_data_size_
= -1;
152 bool WebMClusterParser::OnListEnd(int id
) {
153 if (id
!= kWebMIdBlockGroup
)
156 // Make sure the BlockGroup actually had a Block.
157 if (block_data_size_
== -1) {
158 MEDIA_LOG(log_cb_
) << "Block missing from BlockGroup.";
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);
167 block_data_size_
= -1;
168 block_duration_
= -1;
170 block_additional_data_
.reset();
171 block_additional_data_size_
= -1;
172 discard_padding_
= -1;
173 discard_padding_set_
= false;
177 bool WebMClusterParser::OnUInt(int id
, int64 val
) {
180 case kWebMIdTimecode
:
181 dst
= &cluster_timecode_
;
183 case kWebMIdBlockDuration
:
184 dst
= &block_duration_
;
186 case kWebMIdBlockAddID
:
187 dst
= &block_add_id_
;
189 case kWebMIdDiscardPadding
:
190 if (discard_padding_set_
)
192 discard_padding_set_
= true;
193 discard_padding_
= val
;
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
) {
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";
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;
224 MEDIA_LOG(log_cb_
) << "Lacing " << lacing
<< " is not supported yet.";
228 // Sign extend negative timecode offsets.
229 if (timecode
& 0x8000)
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
,
239 bool WebMClusterParser::OnBinary(int id
, const uint8
* data
, int size
) {
241 case kWebMIdSimpleBlock
:
242 return ParseBlock(true, data
, size
, NULL
, -1, -1, 0);
246 MEDIA_LOG(log_cb_
) << "More than 1 Block in a BlockGroup is not "
250 block_data_
.reset(new uint8
[size
]);
251 memcpy(block_data_
.get(), data
, size
);
252 block_data_size_
= size
;
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
262 MEDIA_LOG(log_cb_
) << "More than 1 BlockAdditional in a BlockGroup is "
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
);
282 bool WebMClusterParser::OnBlock(bool is_simple_block
, int track_num
,
286 const uint8
* data
, int size
,
287 const uint8
* additional
, int additional_size
,
288 int64 discard_padding
) {
290 if (cluster_timecode_
== -1) {
291 MEDIA_LOG(log_cb_
) << "Got a block before cluster timecode.";
295 // TODO(acolwell): Should relative negative timecode offsets be rejected? Or
296 // only when the absolute timecode is negative? See http://crbug.com/271794
298 MEDIA_LOG(log_cb_
) << "Got a block with negative timecode offset "
303 if (last_block_timecode_
!= -1 && timecode
< last_block_timecode_
) {
305 << "Got a block with a timecode before the previous block.";
310 std::string encryption_key_id
;
311 if (track_num
== audio_
.track_num()) {
313 encryption_key_id
= audio_encryption_key_id_
;
314 } else if (track_num
== video_
.track_num()) {
316 encryption_key_id
= video_encryption_key_id_
;
317 } else if (ignored_tracks_
.find(track_num
) != ignored_tracks_
.end()) {
319 } else if (Track
* const text_track
= FindTextTrack(track_num
)) {
320 if (is_simple_block
) // BlockGroup is required for WebVTT cues
322 if (block_duration
< 0) // not specified
326 MEDIA_LOG(log_cb_
) << "Unexpected track number " << track_num
;
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
340 is_simple_block
? (flags
& 0x80) != 0 : track
->IsKeyframe(data
, size
);
342 scoped_refptr
<StreamParserBuffer
> buffer
=
343 StreamParserBuffer::CopyFrom(data
, size
, additional
, additional_size
,
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(
352 reinterpret_cast<const uint8
*>(encryption_key_id
.data()),
353 encryption_key_id
.size()));
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
);
395 void WebMClusterParser::Track::Reset() {
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.
405 // Make sure the block is big enough for the minimal keyframe header size.
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)
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)
422 void WebMClusterParser::ResetTextTracks() {
423 for (TextTrackMap::iterator it
= text_track_map_
.begin();
424 it
!= text_track_map_
.end();
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())