Fix iOS build for XCode 4.6.
[chromium-blink-merge.git] / media / webm / webm_cluster_parser.cc
blob1506ad2cbd3e95b0a51a363012834ed057c96560
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"
7 #include <vector>
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"
14 namespace media {
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);
23 return counter_block;
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,
31 const LogCB& log_cb)
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),
38 block_data_size_(-1),
39 block_duration_(-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),
45 log_cb_(log_cb) {
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;
55 parser_.Reset();
56 audio_.Reset();
57 video_.Reset();
60 int WebMClusterParser::Parse(const uint8* buf, int size) {
61 audio_.Reset();
62 video_.Reset();
64 int result = parser_.Parse(buf, size);
66 if (result < 0) {
67 cluster_ended_ = false;
68 return result;
71 cluster_ended_ = parser_.IsParsingComplete();
72 if (cluster_ended_) {
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
83 // call.
84 parser_.Reset();
86 last_block_timecode_ = -1;
87 cluster_timecode_ = -1;
90 return result;
93 WebMParserClient* WebMClusterParser::OnListStart(int id) {
94 if (id == kWebMIdCluster) {
95 cluster_timecode_ = -1;
96 cluster_start_time_ = kNoTimestamp();
97 } else if (id == kWebMIdBlockGroup) {
98 block_data_.reset();
99 block_data_size_ = -1;
100 block_duration_ = -1;
103 return this;
106 bool WebMClusterParser::OnListEnd(int id) {
107 if (id != kWebMIdBlockGroup)
108 return true;
110 // Make sure the BlockGroup actually had a Block.
111 if (block_data_size_ == -1) {
112 MEDIA_LOG(log_cb_) << "Block missing from BlockGroup.";
113 return false;
116 bool result = ParseBlock(false, block_data_.get(), block_data_size_,
117 block_duration_);
118 block_data_.reset();
119 block_data_size_ = -1;
120 block_duration_ = -1;
121 return result;
124 bool WebMClusterParser::OnUInt(int id, int64 val) {
125 if (id == kWebMIdTimecode) {
126 if (cluster_timecode_ != -1)
127 return false;
129 cluster_timecode_ = val;
130 } else if (id == kWebMIdBlockDuration) {
131 if (block_duration_ != -1)
132 return false;
133 block_duration_ = val;
136 return true;
139 bool WebMClusterParser::ParseBlock(bool is_simple_block, const uint8* buf,
140 int size, int duration) {
141 if (size < 4)
142 return false;
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";
148 return false;
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;
156 if (lacing) {
157 MEDIA_LOG(log_cb_) << "Lacing " << lacing << " is not supported yet.";
158 return false;
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)
176 return true;
178 if (block_data_.get()) {
179 MEDIA_LOG(log_cb_) << "More than 1 Block in a BlockGroup is not supported.";
180 return false;
183 block_data_.reset(new uint8[size]);
184 memcpy(block_data_.get(), data, size);
185 block_data_size_ = size;
186 return true;
189 bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num,
190 int timecode,
191 int block_duration,
192 int flags,
193 const uint8* data, int size) {
194 DCHECK_GE(size, 0);
195 if (cluster_timecode_ == -1) {
196 MEDIA_LOG(log_cb_) << "Got a block before cluster timecode.";
197 return false;
200 if (timecode < 0) {
201 MEDIA_LOG(log_cb_) << "Got a block with negative timecode offset "
202 << timecode;
203 return false;
206 if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
207 MEDIA_LOG(log_cb_)
208 << "Got a block with a timecode before the previous block.";
209 return false;
212 Track* track = NULL;
213 std::string encryption_key_id;
214 if (track_num == audio_.track_num()) {
215 track = &audio_;
216 encryption_key_id = audio_encryption_key_id_;
217 } else if (track_num == video_.track_num()) {
218 track = &video_;
219 encryption_key_id = video_encryption_key_id_;
220 } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
221 return true;
222 } else {
223 MEDIA_LOG(log_cb_) << "Unexpected track number " << track_num;
224 return false;
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
236 bool is_keyframe =
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) {
248 MEDIA_LOG(log_cb_)
249 << "Got a block from an encrypted stream with no data.";
250 return false;
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
257 // unencrypted.
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 "
263 << size;
264 return false;
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
273 // DecryptConfig.
274 buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig(
275 encryption_key_id,
276 counter_block,
277 data_offset,
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);
309 return true;
312 void WebMClusterParser::Track::Reset() {
313 buffers_.clear();
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.
319 if (!is_video_)
320 return true;
322 // Make sure the block is big enough for the minimal keyframe header size.
323 if (size < 7)
324 return false;
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)
329 return false;
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)
334 return false;
336 return true;
339 } // namespace media