Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(34)

Side by Side Diff: media/webm/webm_parser.cc

Issue 7203002: Adding ChunkDemuxer implementation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed CR comments & split code out into separate files. Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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.
4
5 #include "media/webm/webm_parser.h"
6
7 // This file contains code to parse WebM file elements. It was created
8 // from information in the Matroska spec.
9 // http://www.matroska.org/technical/specs/index.html
10
11 #include <iomanip>
12
13 #include "base/logging.h"
14 #include "media/webm/webm_constants.h"
15
16 namespace media {
17
18 // Maximum depth of WebM elements. Some WebM elements are lists of
19 // other elements. This limits the number levels of recursion allowed.
20 static const int kMaxLevelDepth = 6;
21
22 enum ElementType {
23 LIST,
24 UINT,
25 FLOAT,
26 BINARY,
27 STRING,
28 SBLOCK,
29 SKIP,
30 };
31
32 struct ElementIdInfo {
33 int level_;
34 ElementType type_;
35 int id_;
36 };
37
38 struct ListElementInfo {
39 int id_;
40 const ElementIdInfo* id_info_;
41 int id_info_size_;
42 };
43
44 // The following are tables indicating what IDs are valid sub-elements
45 // of particular elements. If an element is encountered that doesn't
46 // appear in the list, a parsing error is signalled. Some elements are
47 // marked as SKIP because they are valid, but we don't care about them
48 // right now.
49 static const ElementIdInfo kClusterIds[] = {
50 {2, UINT, kWebMIdTimecode},
51 {2, SBLOCK, kWebMIdSimpleBlock},
52 {2, LIST, kWebMIdBlockGroup},
53 };
54
55 static const ElementIdInfo kInfoIds[] = {
56 {2, SKIP, kWebMIdSegmentUID},
57 {2, UINT, kWebMIdTimecodeScale},
58 {2, FLOAT, kWebMIdDuration},
59 {2, SKIP, kWebMIdDateUTC},
60 {2, SKIP, kWebMIdTitle},
61 {2, SKIP, kWebMIdMuxingApp},
62 {2, SKIP, kWebMIdWritingApp},
63 };
64
65 static const ElementIdInfo kTracksIds[] = {
66 {2, LIST, kWebMIdTrackEntry},
67 };
68
69 static const ElementIdInfo kTrackEntryIds[] = {
70 {3, UINT, kWebMIdTrackNumber},
71 {3, SKIP, kWebMIdTrackUID},
72 {3, UINT, kWebMIdTrackType},
73 {3, SKIP, kWebMIdFlagEnabled},
74 {3, SKIP, kWebMIdFlagDefault},
75 {3, SKIP, kWebMIdFlagForced},
76 {3, UINT, kWebMIdFlagLacing},
77 {3, UINT, kWebMIdDefaultDuration},
78 {3, SKIP, kWebMIdName},
79 {3, SKIP, kWebMIdLanguage},
80 {3, STRING, kWebMIdCodecID},
81 {3, BINARY, kWebMIdCodecPrivate},
82 {3, SKIP, kWebMIdCodecName},
83 {3, LIST, kWebMIdVideo},
84 {3, LIST, kWebMIdAudio},
85 };
86
87 static const ElementIdInfo kVideoIds[] = {
88 {4, SKIP, kWebMIdFlagInterlaced},
89 {4, SKIP, kWebMIdStereoMode},
90 {4, UINT, kWebMIdPixelWidth},
91 {4, UINT, kWebMIdPixelHeight},
92 {4, SKIP, kWebMIdPixelCropBottom},
93 {4, SKIP, kWebMIdPixelCropTop},
94 {4, SKIP, kWebMIdPixelCropLeft},
95 {4, SKIP, kWebMIdPixelCropRight},
96 {4, SKIP, kWebMIdDisplayWidth},
97 {4, SKIP, kWebMIdDisplayHeight},
98 {4, SKIP, kWebMIdDisplayUnit},
99 {4, SKIP, kWebMIdAspectRatioType},
100 };
101
102 static const ElementIdInfo kAudioIds[] = {
103 {4, SKIP, kWebMIdSamplingFrequency},
104 {4, SKIP, kWebMIdOutputSamplingFrequency},
105 {4, UINT, kWebMIdChannels},
106 {4, SKIP, kWebMIdBitDepth},
107 };
108
109 static const ElementIdInfo kClustersOnly[] = {
110 {1, LIST, kWebMIdCluster},
111 };
112
113 static const ListElementInfo kListElementInfo[] = {
114 { kWebMIdCluster, kClusterIds, sizeof(kClusterIds) },
115 { kWebMIdInfo, kInfoIds, sizeof(kInfoIds) },
116 { kWebMIdTracks, kTracksIds, sizeof(kTracksIds) },
117 { kWebMIdTrackEntry, kTrackEntryIds, sizeof(kTrackEntryIds) },
118 { kWebMIdVideo, kVideoIds, sizeof(kVideoIds) },
119 { kWebMIdAudio, kAudioIds, sizeof(kAudioIds) },
120 };
121
122 // Number of elements in kListElementInfo.
123 const int kListElementInfoCount =
124 sizeof(kListElementInfo) / sizeof(ListElementInfo);
125
126 WebMParserClient::~WebMParserClient() {}
127
128 // Parses an element header id or size field. These fields are variable length
129 // encoded. The first byte indicates how many bytes the field occupies.
130 // |buf| - The buffer to parse.
131 // |size| - The number of bytes in |buf|
132 // |max_bytes| - The maximum number of bytes the field can be. ID fields
133 // set this to 4 & element size fields set this to 8. If the
134 // first byte indicates a larger field size than this it is a
135 // parser error.
136 // |mask_first_byte| - For element size fields the field length encoding bits
137 // need to be masked off. This parameter is true for
138 // element size fields and is false for ID field values.
139 //
140 // Returns: The number of bytes parsed on success. -1 on error.
141 static int ParseWebMElementHeaderField(const uint8* buf, int size,
142 int max_bytes, bool mask_first_byte,
143 int64* num) {
144 DCHECK(buf);
145 DCHECK(num);
146
147 if (size <= 0)
148 return -1;
149
150 int mask = 0x80;
151 uint8 ch = buf[0];
152 int extra_bytes = -1;
153 for (int i = 0; i < max_bytes; ++i) {
154 if ((ch & mask) == mask) {
155 *num = mask_first_byte ? ch & ~mask : ch;
156 extra_bytes = i;
157 break;
158 }
159 mask >>= 1;
160 }
161
162 if ((extra_bytes == -1) || ((1 + extra_bytes) > size))
163 return -1;
164
165 int bytes_used = 1;
166
167 for (int i = 0; i < extra_bytes; ++i)
168 *num = (*num << 8) | (0xff & buf[bytes_used++]);
169
170 return bytes_used;
171 }
172
173 // Parses an element header & returns the ID and element size.
174 //
175 // Returns: The number of bytes parsed on success. -1 on error.
176 // |*id| contains the element ID on success & undefined on error.
177 // |*element_size| contains the element size on success & undefined on error.
178 static int ParseWebMElementHeader(const uint8* buf, int size,
179 int* id, int64* element_size) {
180 DCHECK(buf);
181 DCHECK_GE(size, 0);
182 DCHECK(id);
183 DCHECK(element_size);
184
185 if (size == 0)
186 return 0;
187
188 int64 tmp;
189 int num_id_bytes = ParseWebMElementHeaderField(buf, size, 4, false, &tmp);
190
191 if (num_id_bytes <= 0)
192 return num_id_bytes;
193
194 *id = static_cast<int>(tmp);
195
196 int num_size_bytes = ParseWebMElementHeaderField(buf + num_id_bytes,
197 size - num_id_bytes,
198 8, true, &tmp);
199
200 if (num_size_bytes <= 0)
201 return num_size_bytes;
202
203 *element_size = tmp;
204 return num_id_bytes + num_size_bytes;
205 }
206
207 // Finds ElementIdInfo for a specific ID.
208 static const ElementIdInfo* FindIdInfo(int id,
209 const ElementIdInfo* id_info,
210 int id_info_size) {
211 int count = id_info_size / sizeof(*id_info);
212 for (int i = 0; i < count; ++i) {
213 if (id == id_info[i].id_)
214 return &id_info[i];
215 }
216
217 return NULL;
218 }
219
220 // Finds ListElementInfo for a specific ID.
221 static const ListElementInfo* FindListInfo(int id) {
222 for (int i = 0; i < kListElementInfoCount; ++i) {
223 if (id == kListElementInfo[i].id_)
224 return &kListElementInfo[i];
225 }
226
227 return NULL;
228 }
229
230 static int ParseSimpleBlock(const uint8* buf, int size,
231 WebMParserClient* client) {
232 if (size < 4)
233 return -1;
234
235 // Return an error if the trackNum > 127. We just aren't
236 // going to support large track numbers right now.
237 if ((buf[0] & 0x80) != 0x80) {
238 VLOG(1) << "TrackNumber over 127 not supported";
239 return -1;
240 }
241
242 int track_num = buf[0] & 0x7f;
243 int timecode = buf[1] << 8 | buf[2];
244 int flags = buf[3] & 0xff;
245 int lacing = (flags >> 1) & 0x3;
246
247 if (lacing != 0) {
248 VLOG(1) << "Lacing " << lacing << " not supported yet.";
249 return -1;
250 }
251
252 const uint8* frame_data = buf + 4;
253 int frame_size = size - (frame_data - buf);
254 if (!client->OnSimpleBlock(track_num, timecode, flags,
255 frame_data, frame_size)) {
256 return -1;
257 }
258
259 return size;
260 }
261
262 static int ParseElements(const ElementIdInfo* id_info,
263 int id_info_size,
264 const uint8* buf, int size, int level,
265 WebMParserClient* client);
266
267 static int ParseElementList(const uint8* buf, int size,
268 int id, int level,
269 WebMParserClient* client) {
270 const ListElementInfo* list_info = FindListInfo(id);
271
272 if (!list_info) {
273 VLOG(1) << "Failed to find list info for ID " << std::hex << id;
274 return -1;
275 }
276
277 if (!client->OnListStart(id))
278 return -1;
279
280 int res = ParseElements(list_info->id_info_,
281 list_info->id_info_size_,
282 buf, size,
283 level + 1,
284 client);
285
286 if (res < 0)
287 return -1;
288
289 if (!client->OnListEnd(id))
290 return -1;
291
292 DCHECK_EQ(res, size);
293 return res;
294 }
295
296 static int ParseUInt(const uint8* buf, int size, int id,
297 WebMParserClient* client) {
298 if ((size <= 0) || (size > 8))
299 return -1;
300
301 // Read in the big-endian integer.
302 int64 value = 0;
303 for (int i = 0; i < size; ++i)
304 value = (value << 8) | buf[i];
305
306 if (!client->OnUInt(id, value))
307 return -1;
308
309 return size;
310 }
311
312 static int ParseFloat(const uint8* buf, int size, int id,
313 WebMParserClient* client) {
314
315 if ((size != 4) && (size != 8))
316 return -1;
317
318 double value = -1;
319
320 // Read the bytes from big-endian form into a native endian integer.
321 int64 tmp = 0;
322 for (int i = 0; i < size; ++i)
323 tmp = (tmp << 8) | buf[i];
324
325 // Use a union to convert the integer bit pattern into a floating point
326 // number.
327 if (size == 4) {
328 union {
329 int32 src;
330 float dst;
331 } tmp2;
332 tmp2.src = static_cast<int32>(tmp);
333 value = tmp2.dst;
334 } else if (size == 8) {
335 union {
336 int64 src;
337 double dst;
338 } tmp2;
339 tmp2.src = tmp;
340 value = tmp2.dst;
341 } else {
342 return -1;
343 }
344
345 if (!client->OnFloat(id, value))
346 return -1;
347
348 return size;
349 }
350
351 static int ParseElements(const ElementIdInfo* id_info,
352 int id_info_size,
353 const uint8* buf, int size, int level,
354 WebMParserClient* client) {
355 DCHECK_GE(id_info_size, 0);
356 DCHECK_GE(size, 0);
357 DCHECK_GE(level, 0);
358
359 const uint8* cur = buf;
360 int cur_size = size;
361 int used = 0;
362
363 if (level > kMaxLevelDepth)
364 return -1;
365
366 while (cur_size > 0) {
367 int id;
368 int64 element_size;
369 int res = ParseWebMElementHeader(cur, cur_size, &id, &element_size);
370
371 if (res < 0)
372 return res;
373
374 if (res == 0)
375 break;
376
377 cur += res;
378 cur_size -= res;
379 used += res;
380
381 // Check to see if the element is larger than the remaining data.
382 if (element_size > cur_size)
383 return -1;
384
385 const ElementIdInfo* info = FindIdInfo(id, id_info, id_info_size);
386
387 if (info == NULL) {
388 VLOG(1) << "No info for ID " << std::hex << id;
389
390 // TODO(acolwell): Change this to return -1 after the API has solidified.
391 // We don't want to allow elements we don't recognize.
392 cur += element_size;
393 cur_size -= element_size;
394 used += element_size;
395 continue;
396 }
397
398 if (info->level_ != level) {
399 VLOG(1) << "ID " << std::hex << id << std::dec << " at level "
400 << level << " instead of " << info->level_;
401 return -1;
402 }
403
404 switch(info->type_) {
405 case SBLOCK:
406 if (ParseSimpleBlock(cur, element_size, client) <= 0)
407 return -1;
408 break;
409 case LIST:
410 if (ParseElementList(cur, element_size, id, level, client) < 0)
411 return -1;
412 break;
413 case UINT:
414 if (ParseUInt(cur, element_size, id, client) <= 0)
415 return -1;
416 break;
417 case FLOAT:
418 if (ParseFloat(cur, element_size, id, client) <= 0)
419 return -1;
420 break;
421 case BINARY:
422 if (!client->OnBinary(id, cur, element_size))
423 return -1;
424 break;
425 case STRING:
426 if (!client->OnString(id,
427 std::string(reinterpret_cast<const char*>(cur),
428 element_size)))
429 return -1;
430 break;
431 case SKIP:
432 // Do nothing.
433 break;
434 default:
435 VLOG(1) << "Unhandled id type " << info->type_;
436 return -1;
437 };
438
439 cur += element_size;
440 cur_size -= element_size;
441 used += element_size;
442 }
443
444 return used;
445 }
446
447 // Parses a single list element that matches |id|. This method fails if the
448 // buffer points to an element that does not match |id|.
449 int WebMParseListElement(const uint8* buf, int size, int id,
450 int level, WebMParserClient* client) {
451 if (size == 0)
452 return -1;
453
454 const uint8* cur = buf;
455 int cur_size = size;
456
457 int element_id = 0;
458 int64 element_size = 0;
459 int res = ParseWebMElementHeader(cur, cur_size, &element_id, &element_size);
460
461 if (res <= 0)
462 return res;
463
464 cur += res;
465 cur_size -= res;
466
467 if (element_id != id || element_size > cur_size)
468 return -1;
469
470 res = ParseElementList(cur, element_size, element_id, level, client);
471
472 if (res < 0)
473 return -1;
474
475 cur += res;
476 cur_size -= res;
477
478 return size - cur_size;
479 }
480
481 // Parses a WebM INFO element.
482 int ParseWebMInfo(const uint8* buf, int size, WebMParserClient* client) {
scherkus (not reviewing) 2011/06/24 18:27:37 are these three methods supposed to be in the .h o
acolwell GONE FROM CHROMIUM 2011/06/27 23:48:25 They are defunct. Removed.
483 return WebMParseListElement(buf, size, kWebMIdInfo, 1, client);
484 }
485
486 // Parses a WebM TRACKS element.
487 int ParseWebMTracks(const uint8* buf, int size, WebMParserClient* client) {
488 return WebMParseListElement(buf, size, kWebMIdTracks, 1, client);
489 }
490
491 // Parses one or more WebM CLUSTER elements.
492 int ParseWebMClusters(const uint8* buf, int size, WebMParserClient* client) {
493 return ParseElements(kClustersOnly, sizeof(kClustersOnly),
494 buf, size, 1, client);
495 }
496
497 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698