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 "media/base/data_buffer.h"
11 #include "media/base/decrypt_config.h"
12 #include "media/webm/webm_constants.h"
16 // Generates a 16 byte CTR counter block. The CTR counter block format is a
17 // CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV.
18 // |iv_size| is the size of |iv| in btyes. Returns a string of
19 // kDecryptionKeySize bytes.
20 static std::string
GenerateCounterBlock(const uint8
* iv
, int iv_size
) {
21 std::string
counter_block(reinterpret_cast<const char*>(iv
), iv_size
);
22 counter_block
.append(DecryptConfig::kDecryptionKeySize
- iv_size
, 0);
26 WebMClusterParser::WebMClusterParser(
27 int64 timecode_scale
, int audio_track_num
, int video_track_num
,
28 const std::set
<int64
>& ignored_tracks
,
29 const std::string
& audio_encryption_key_id
,
30 const std::string
& video_encryption_key_id
,
32 : timecode_multiplier_(timecode_scale
/ 1000.0),
33 ignored_tracks_(ignored_tracks
),
34 audio_encryption_key_id_(audio_encryption_key_id
),
35 video_encryption_key_id_(video_encryption_key_id
),
36 parser_(kWebMIdCluster
, this),
37 last_block_timecode_(-1),
40 cluster_timecode_(-1),
41 cluster_start_time_(kNoTimestamp()),
42 cluster_ended_(false),
43 audio_(audio_track_num
, false),
44 video_(video_track_num
, true),
48 WebMClusterParser::~WebMClusterParser() {}
50 void WebMClusterParser::Reset() {
51 last_block_timecode_
= -1;
52 cluster_timecode_
= -1;
53 cluster_start_time_
= kNoTimestamp();
54 cluster_ended_
= false;
60 int WebMClusterParser::Parse(const uint8
* buf
, int size
) {
64 int result
= parser_
.Parse(buf
, size
);
67 cluster_ended_
= false;
71 cluster_ended_
= parser_
.IsParsingComplete();
73 // If there were no buffers in this cluster, set the cluster start time to
74 // be the |cluster_timecode_|.
75 if (cluster_start_time_
== kNoTimestamp()) {
76 DCHECK_GT(cluster_timecode_
, -1);
77 cluster_start_time_
= base::TimeDelta::FromMicroseconds(
78 cluster_timecode_
* timecode_multiplier_
);
81 // Reset the parser if we're done parsing so that
82 // it is ready to accept another cluster on the next
86 last_block_timecode_
= -1;
87 cluster_timecode_
= -1;
93 WebMParserClient
* WebMClusterParser::OnListStart(int id
) {
94 if (id
== kWebMIdCluster
) {
95 cluster_timecode_
= -1;
96 cluster_start_time_
= kNoTimestamp();
97 } else if (id
== kWebMIdBlockGroup
) {
99 block_data_size_
= -1;
100 block_duration_
= -1;
106 bool WebMClusterParser::OnListEnd(int id
) {
107 if (id
!= kWebMIdBlockGroup
)
110 // Make sure the BlockGroup actually had a Block.
111 if (block_data_size_
== -1) {
112 MEDIA_LOG(log_cb_
) << "Block missing from BlockGroup.";
116 bool result
= ParseBlock(false, block_data_
.get(), block_data_size_
,
119 block_data_size_
= -1;
120 block_duration_
= -1;
124 bool WebMClusterParser::OnUInt(int id
, int64 val
) {
125 if (id
== kWebMIdTimecode
) {
126 if (cluster_timecode_
!= -1)
129 cluster_timecode_
= val
;
130 } else if (id
== kWebMIdBlockDuration
) {
131 if (block_duration_
!= -1)
133 block_duration_
= val
;
139 bool WebMClusterParser::ParseBlock(bool is_simple_block
, const uint8
* buf
,
140 int size
, int duration
) {
144 // Return an error if the trackNum > 127. We just aren't
145 // going to support large track numbers right now.
146 if (!(buf
[0] & 0x80)) {
147 MEDIA_LOG(log_cb_
) << "TrackNumber over 127 not supported";
151 int track_num
= buf
[0] & 0x7f;
152 int timecode
= buf
[1] << 8 | buf
[2];
153 int flags
= buf
[3] & 0xff;
154 int lacing
= (flags
>> 1) & 0x3;
157 MEDIA_LOG(log_cb_
) << "Lacing " << lacing
<< " is not supported yet.";
161 // Sign extend negative timecode offsets.
162 if (timecode
& 0x8000)
163 timecode
|= (-1 << 16);
165 const uint8
* frame_data
= buf
+ 4;
166 int frame_size
= size
- (frame_data
- buf
);
167 return OnBlock(is_simple_block
, track_num
, timecode
, duration
, flags
,
168 frame_data
, frame_size
);
171 bool WebMClusterParser::OnBinary(int id
, const uint8
* data
, int size
) {
172 if (id
== kWebMIdSimpleBlock
)
173 return ParseBlock(true, data
, size
, -1);
175 if (id
!= kWebMIdBlock
)
178 if (block_data_
.get()) {
179 MEDIA_LOG(log_cb_
) << "More than 1 Block in a BlockGroup is not supported.";
183 block_data_
.reset(new uint8
[size
]);
184 memcpy(block_data_
.get(), data
, size
);
185 block_data_size_
= size
;
189 bool WebMClusterParser::OnBlock(bool is_simple_block
, int track_num
,
193 const uint8
* data
, int size
) {
195 if (cluster_timecode_
== -1) {
196 MEDIA_LOG(log_cb_
) << "Got a block before cluster timecode.";
201 MEDIA_LOG(log_cb_
) << "Got a block with negative timecode offset "
206 if (last_block_timecode_
!= -1 && timecode
< last_block_timecode_
) {
208 << "Got a block with a timecode before the previous block.";
213 std::string encryption_key_id
;
214 if (track_num
== audio_
.track_num()) {
216 encryption_key_id
= audio_encryption_key_id_
;
217 } else if (track_num
== video_
.track_num()) {
219 encryption_key_id
= video_encryption_key_id_
;
220 } else if (ignored_tracks_
.find(track_num
) != ignored_tracks_
.end()) {
223 MEDIA_LOG(log_cb_
) << "Unexpected track number " << track_num
;
227 last_block_timecode_
= timecode
;
229 base::TimeDelta timestamp
= base::TimeDelta::FromMicroseconds(
230 (cluster_timecode_
+ timecode
) * timecode_multiplier_
);
232 // The first bit of the flags is set when a SimpleBlock contains only
233 // keyframes. If this is a Block, then inspection of the payload is
234 // necessary to determine whether it contains a keyframe or not.
235 // http://www.matroska.org/technical/specs/index.html
237 is_simple_block
? (flags
& 0x80) != 0 : track
->IsKeyframe(data
, size
);
239 scoped_refptr
<StreamParserBuffer
> buffer
=
240 StreamParserBuffer::CopyFrom(data
, size
, is_keyframe
);
242 // Every encrypted Block has a signal byte and IV prepended to it. Current
243 // encrypted WebM request for comments specification is here
244 // http://wiki.webmproject.org/encryption/webm-encryption-rfc
245 if (!encryption_key_id
.empty()) {
246 DCHECK_EQ(kWebMSignalByteSize
, 1);
247 if (size
< kWebMSignalByteSize
) {
249 << "Got a block from an encrypted stream with no data.";
252 uint8 signal_byte
= data
[0];
253 int data_offset
= sizeof(signal_byte
);
255 // Setting the DecryptConfig object of the buffer while leaving the
256 // initialization vector empty will tell the decryptor that the frame is
258 std::string counter_block
;
260 if (signal_byte
& kWebMFlagEncryptedFrame
) {
261 if (size
< kWebMSignalByteSize
+ kWebMIvSize
) {
262 MEDIA_LOG(log_cb_
) << "Got an encrypted block with not enough data "
266 counter_block
= GenerateCounterBlock(data
+ data_offset
, kWebMIvSize
);
267 data_offset
+= kWebMIvSize
;
270 // TODO(fgalligan): Revisit if DecryptConfig needs to be set on unencrypted
271 // frames after the CDM API is finalized.
272 // Unencrypted frames of potentially encrypted streams currently set
274 buffer
->SetDecryptConfig(scoped_ptr
<DecryptConfig
>(new DecryptConfig(
278 std::vector
<SubsampleEntry
>())));
281 buffer
->SetTimestamp(timestamp
);
282 if (cluster_start_time_
== kNoTimestamp())
283 cluster_start_time_
= timestamp
;
285 if (block_duration
>= 0) {
286 buffer
->SetDuration(base::TimeDelta::FromMicroseconds(
287 block_duration
* timecode_multiplier_
));
290 return track
->AddBuffer(buffer
);
293 WebMClusterParser::Track::Track(int track_num
, bool is_video
)
294 : track_num_(track_num
),
295 is_video_(is_video
) {
298 WebMClusterParser::Track::~Track() {}
300 bool WebMClusterParser::Track::AddBuffer(
301 const scoped_refptr
<StreamParserBuffer
>& buffer
) {
302 DVLOG(2) << "AddBuffer() : " << track_num_
303 << " ts " << buffer
->GetTimestamp().InSecondsF()
304 << " dur " << buffer
->GetDuration().InSecondsF()
305 << " kf " << buffer
->IsKeyframe()
306 << " size " << buffer
->GetDataSize();
308 buffers_
.push_back(buffer
);
312 void WebMClusterParser::Track::Reset() {
316 bool WebMClusterParser::Track::IsKeyframe(const uint8
* data
, int size
) const {
317 // For now, assume that all blocks are keyframes for datatypes other than
318 // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
322 // Make sure the block is big enough for the minimal keyframe header size.
326 // The LSb of the first byte must be a 0 for a keyframe.
327 // http://tools.ietf.org/html/rfc6386 Section 19.1
328 if ((data
[0] & 0x01) != 0)
331 // Verify VP8 keyframe startcode.
332 // http://tools.ietf.org/html/rfc6386 Section 19.1
333 if (data
[3] != 0x9d || data
[4] != 0x01 || data
[5] != 0x2a)