OLD | NEW |
---|---|
(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 | |
OLD | NEW |