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