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),
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),
73 for (WebMTracksParser::TextTracks::const_iterator it
= text_tracks
.begin();
74 it
!= text_tracks
.end();
76 text_track_map_
.insert(std::make_pair(it
->first
, Track(it
->first
, false)));
80 WebMClusterParser::~WebMClusterParser() {}
82 void WebMClusterParser::Reset() {
83 last_block_timecode_
= -1;
84 cluster_timecode_
= -1;
85 cluster_start_time_
= kNoTimestamp();
86 cluster_ended_
= false;
93 int WebMClusterParser::Parse(const uint8
* buf
, int size
) {
98 int result
= parser_
.Parse(buf
, size
);
101 cluster_ended_
= false;
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_
);
115 // Reset the parser if we're done parsing so that
116 // it is ready to accept another cluster on the next
120 last_block_timecode_
= -1;
121 cluster_timecode_
= -1;
127 WebMClusterParser::TextTrackIterator
128 WebMClusterParser::CreateTextTrackIterator() const {
129 return TextTrackIterator(text_track_map_
);
132 WebMParserClient
* WebMClusterParser::OnListStart(int id
) {
133 if (id
== kWebMIdCluster
) {
134 cluster_timecode_
= -1;
135 cluster_start_time_
= kNoTimestamp();
136 } else if (id
== kWebMIdBlockGroup
) {
138 block_data_size_
= -1;
139 block_duration_
= -1;
140 } else if (id
== kWebMIdBlockAdditions
) {
142 block_additional_data_
.reset();
143 block_additional_data_size_
= -1;
149 bool WebMClusterParser::OnListEnd(int id
) {
150 if (id
!= kWebMIdBlockGroup
)
153 // Make sure the BlockGroup actually had a Block.
154 if (block_data_size_
== -1) {
155 MEDIA_LOG(log_cb_
) << "Block missing from BlockGroup.";
159 bool result
= ParseBlock(false, block_data_
.get(), block_data_size_
,
160 block_additional_data_
.get(),
161 block_additional_data_size_
, block_duration_
);
163 block_data_size_
= -1;
164 block_duration_
= -1;
166 block_additional_data_
.reset();
167 block_additional_data_size_
= -1;
171 bool WebMClusterParser::OnUInt(int id
, int64 val
) {
174 case kWebMIdTimecode
:
175 dst
= &cluster_timecode_
;
177 case kWebMIdBlockDuration
:
178 dst
= &block_duration_
;
180 case kWebMIdBlockAddID
:
181 dst
= &block_add_id_
;
192 bool WebMClusterParser::ParseBlock(bool is_simple_block
, const uint8
* buf
,
193 int size
, const uint8
* additional
,
194 int additional_size
, int duration
) {
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";
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;
211 MEDIA_LOG(log_cb_
) << "Lacing " << lacing
<< " is not supported yet.";
215 // Sign extend negative timecode offsets.
216 if (timecode
& 0x8000)
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
);
225 bool WebMClusterParser::OnBinary(int id
, const uint8
* data
, int size
) {
227 case kWebMIdSimpleBlock
:
228 return ParseBlock(true, data
, size
, NULL
, -1, -1);
232 MEDIA_LOG(log_cb_
) << "More than 1 Block in a BlockGroup is not "
236 block_data_
.reset(new uint8
[size
]);
237 memcpy(block_data_
.get(), data
, size
);
238 block_data_size_
= size
;
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
248 MEDIA_LOG(log_cb_
) << "More than 1 BlockAdditional in a BlockGroup is "
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
);
268 bool WebMClusterParser::OnBlock(bool is_simple_block
, int track_num
,
272 const uint8
* data
, int size
,
273 const uint8
* additional
, int additional_size
) {
275 if (cluster_timecode_
== -1) {
276 MEDIA_LOG(log_cb_
) << "Got a block before cluster timecode.";
280 // TODO(acolwell): Should relative negative timecode offsets be rejected? Or
281 // only when the absolute timecode is negative? See http://crbug.com/271794
283 MEDIA_LOG(log_cb_
) << "Got a block with negative timecode offset "
288 if (last_block_timecode_
!= -1 && timecode
< last_block_timecode_
) {
290 << "Got a block with a timecode before the previous block.";
295 std::string encryption_key_id
;
296 if (track_num
== audio_
.track_num()) {
298 encryption_key_id
= audio_encryption_key_id_
;
299 } else if (track_num
== video_
.track_num()) {
301 encryption_key_id
= video_encryption_key_id_
;
302 } else if (ignored_tracks_
.find(track_num
) != ignored_tracks_
.end()) {
304 } else if (Track
* const text_track
= FindTextTrack(track_num
)) {
305 if (is_simple_block
) // BlockGroup is required for WebVTT cues
307 if (block_duration
< 0) // not specified
311 MEDIA_LOG(log_cb_
) << "Unexpected track number " << track_num
;
315 last_block_timecode_
= timecode
;
317 base::TimeDelta timestamp
= base::TimeDelta::FromMicroseconds(
318 (cluster_timecode_
+ timecode
) * timecode_multiplier_
);
320 // The first bit of the flags is set when a SimpleBlock contains only
321 // keyframes. If this is a Block, then inspection of the payload is
322 // necessary to determine whether it contains a keyframe or not.
323 // http://www.matroska.org/technical/specs/index.html
325 is_simple_block
? (flags
& 0x80) != 0 : track
->IsKeyframe(data
, size
);
327 scoped_refptr
<StreamParserBuffer
> buffer
=
328 StreamParserBuffer::CopyFrom(data
, size
, additional
, additional_size
,
331 // Every encrypted Block has a signal byte and IV prepended to it. Current
332 // encrypted WebM request for comments specification is here
333 // http://wiki.webmproject.org/encryption/webm-encryption-rfc
334 if (!encryption_key_id
.empty()) {
335 scoped_ptr
<DecryptConfig
> config(WebMCreateDecryptConfig(
337 reinterpret_cast<const uint8
*>(encryption_key_id
.data()),
338 encryption_key_id
.size()));
341 buffer
->set_decrypt_config(config
.Pass());
344 buffer
->set_timestamp(timestamp
);
345 if (cluster_start_time_
== kNoTimestamp())
346 cluster_start_time_
= timestamp
;
348 if (block_duration
>= 0) {
349 buffer
->set_duration(base::TimeDelta::FromMicroseconds(
350 block_duration
* timecode_multiplier_
));
353 return track
->AddBuffer(buffer
);
356 WebMClusterParser::Track::Track(int track_num
, bool is_video
)
357 : track_num_(track_num
),
358 is_video_(is_video
) {
361 WebMClusterParser::Track::~Track() {}
363 bool WebMClusterParser::Track::AddBuffer(
364 const scoped_refptr
<StreamParserBuffer
>& buffer
) {
365 DVLOG(2) << "AddBuffer() : " << track_num_
366 << " ts " << buffer
->timestamp().InSecondsF()
367 << " dur " << buffer
->duration().InSecondsF()
368 << " kf " << buffer
->IsKeyframe()
369 << " size " << buffer
->data_size();
371 buffers_
.push_back(buffer
);
375 void WebMClusterParser::Track::Reset() {
379 bool WebMClusterParser::Track::IsKeyframe(const uint8
* data
, int size
) const {
380 // For now, assume that all blocks are keyframes for datatypes other than
381 // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
385 // Make sure the block is big enough for the minimal keyframe header size.
389 // The LSb of the first byte must be a 0 for a keyframe.
390 // http://tools.ietf.org/html/rfc6386 Section 19.1
391 if ((data
[0] & 0x01) != 0)
394 // Verify VP8 keyframe startcode.
395 // http://tools.ietf.org/html/rfc6386 Section 19.1
396 if (data
[3] != 0x9d || data
[4] != 0x01 || data
[5] != 0x2a)
402 void WebMClusterParser::ResetTextTracks() {
403 for (TextTrackMap::iterator it
= text_track_map_
.begin();
404 it
!= text_track_map_
.end();
410 WebMClusterParser::Track
*
411 WebMClusterParser::FindTextTrack(int track_num
) {
412 const TextTrackMap::iterator it
= text_track_map_
.find(track_num
);
414 if (it
== text_track_map_
.end())