OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/webm/webm_parser.h" | 5 #include "media/webm/webm_parser.h" |
6 | 6 |
7 // This file contains code to parse WebM file elements. It was created | 7 // This file contains code to parse WebM file elements. It was created |
8 // from information in the Matroska spec. | 8 // from information in the Matroska spec. |
9 // http://www.matroska.org/technical/specs/index.html | 9 // http://www.matroska.org/technical/specs/index.html |
10 | 10 |
11 #include <iomanip> | 11 #include <iomanip> |
12 | 12 |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "media/webm/webm_constants.h" | 14 #include "media/webm/webm_constants.h" |
15 | 15 |
16 namespace media { | 16 namespace media { |
17 | 17 |
18 // Maximum depth of WebM elements. Some WebM elements are lists of | 18 // Maximum depth of WebM elements. Some WebM elements are lists of |
19 // other elements. This limits the number levels of recursion allowed. | 19 // other elements. This limits the number levels of recursion allowed. |
20 static const int kMaxLevelDepth = 6; | 20 static const int kMaxLevelDepth = 6; |
21 | 21 |
22 enum ElementType { | 22 enum ElementType { |
23 UNKNOWN, | |
24 LIST, | 23 LIST, |
25 UINT, | 24 UINT, |
26 FLOAT, | 25 FLOAT, |
27 BINARY, | 26 BINARY, |
28 STRING, | 27 STRING, |
29 SBLOCK, | 28 SBLOCK, |
30 SKIP, | 29 SKIP, |
31 }; | 30 }; |
32 | 31 |
33 struct ElementIdInfo { | 32 struct ElementIdInfo { |
| 33 int level_; |
34 ElementType type_; | 34 ElementType type_; |
35 int id_; | 35 int id_; |
36 }; | 36 }; |
37 | 37 |
38 struct ListElementInfo { | 38 struct ListElementInfo { |
39 int id_; | 39 int id_; |
40 int level_; | |
41 const ElementIdInfo* id_info_; | 40 const ElementIdInfo* id_info_; |
42 int id_info_size_; | 41 int id_info_size_; |
43 }; | 42 }; |
44 | 43 |
45 // The following are tables indicating what IDs are valid sub-elements | 44 // The following are tables indicating what IDs are valid sub-elements |
46 // of particular elements. If an element is encountered that doesn't | 45 // of particular elements. If an element is encountered that doesn't |
47 // appear in the list, a parsing error is signalled. Some elements are | 46 // appear in the list, a parsing error is signalled. Some elements are |
48 // marked as SKIP because they are valid, but we don't care about them | 47 // marked as SKIP because they are valid, but we don't care about them |
49 // right now. | 48 // right now. |
50 static const ElementIdInfo kClusterIds[] = { | 49 static const ElementIdInfo kClusterIds[] = { |
51 {UINT, kWebMIdTimecode}, | 50 {2, UINT, kWebMIdTimecode}, |
52 {SBLOCK, kWebMIdSimpleBlock}, | 51 {2, SBLOCK, kWebMIdSimpleBlock}, |
53 {LIST, kWebMIdBlockGroup}, | 52 {2, LIST, kWebMIdBlockGroup}, |
54 }; | |
55 | |
56 static const ElementIdInfo kSegmentIds[] = { | |
57 {SKIP, kWebMIdSeekHead}, // TODO(acolwell): add SeekHead info | |
58 {LIST, kWebMIdInfo}, | |
59 {LIST, kWebMIdCluster}, | |
60 {LIST, kWebMIdTracks}, | |
61 {SKIP, kWebMIdCues}, // TODO(acolwell): add CUES info | |
62 }; | 53 }; |
63 | 54 |
64 static const ElementIdInfo kInfoIds[] = { | 55 static const ElementIdInfo kInfoIds[] = { |
65 {SKIP, kWebMIdSegmentUID}, | 56 {2, SKIP, kWebMIdSegmentUID}, |
66 {UINT, kWebMIdTimecodeScale}, | 57 {2, UINT, kWebMIdTimecodeScale}, |
67 {FLOAT, kWebMIdDuration}, | 58 {2, FLOAT, kWebMIdDuration}, |
68 {SKIP, kWebMIdDateUTC}, | 59 {2, SKIP, kWebMIdDateUTC}, |
69 {SKIP, kWebMIdTitle}, | 60 {2, SKIP, kWebMIdTitle}, |
70 {SKIP, kWebMIdMuxingApp}, | 61 {2, SKIP, kWebMIdMuxingApp}, |
71 {SKIP, kWebMIdWritingApp}, | 62 {2, SKIP, kWebMIdWritingApp}, |
72 }; | 63 }; |
73 | 64 |
74 static const ElementIdInfo kTracksIds[] = { | 65 static const ElementIdInfo kTracksIds[] = { |
75 {LIST, kWebMIdTrackEntry}, | 66 {2, LIST, kWebMIdTrackEntry}, |
76 }; | 67 }; |
77 | 68 |
78 static const ElementIdInfo kTrackEntryIds[] = { | 69 static const ElementIdInfo kTrackEntryIds[] = { |
79 {UINT, kWebMIdTrackNumber}, | 70 {3, UINT, kWebMIdTrackNumber}, |
80 {SKIP, kWebMIdTrackUID}, | 71 {3, SKIP, kWebMIdTrackUID}, |
81 {UINT, kWebMIdTrackType}, | 72 {3, UINT, kWebMIdTrackType}, |
82 {SKIP, kWebMIdFlagEnabled}, | 73 {3, SKIP, kWebMIdFlagEnabled}, |
83 {SKIP, kWebMIdFlagDefault}, | 74 {3, SKIP, kWebMIdFlagDefault}, |
84 {SKIP, kWebMIdFlagForced}, | 75 {3, SKIP, kWebMIdFlagForced}, |
85 {UINT, kWebMIdFlagLacing}, | 76 {3, UINT, kWebMIdFlagLacing}, |
86 {UINT, kWebMIdDefaultDuration}, | 77 {3, UINT, kWebMIdDefaultDuration}, |
87 {SKIP, kWebMIdName}, | 78 {3, SKIP, kWebMIdName}, |
88 {SKIP, kWebMIdLanguage}, | 79 {3, SKIP, kWebMIdLanguage}, |
89 {STRING, kWebMIdCodecID}, | 80 {3, STRING, kWebMIdCodecID}, |
90 {BINARY, kWebMIdCodecPrivate}, | 81 {3, BINARY, kWebMIdCodecPrivate}, |
91 {SKIP, kWebMIdCodecName}, | 82 {3, SKIP, kWebMIdCodecName}, |
92 {LIST, kWebMIdVideo}, | 83 {3, LIST, kWebMIdVideo}, |
93 {LIST, kWebMIdAudio}, | 84 {3, LIST, kWebMIdAudio}, |
94 }; | 85 }; |
95 | 86 |
96 static const ElementIdInfo kVideoIds[] = { | 87 static const ElementIdInfo kVideoIds[] = { |
97 {SKIP, kWebMIdFlagInterlaced}, | 88 {4, SKIP, kWebMIdFlagInterlaced}, |
98 {SKIP, kWebMIdStereoMode}, | 89 {4, SKIP, kWebMIdStereoMode}, |
99 {UINT, kWebMIdPixelWidth}, | 90 {4, UINT, kWebMIdPixelWidth}, |
100 {UINT, kWebMIdPixelHeight}, | 91 {4, UINT, kWebMIdPixelHeight}, |
101 {SKIP, kWebMIdPixelCropBottom}, | 92 {4, SKIP, kWebMIdPixelCropBottom}, |
102 {SKIP, kWebMIdPixelCropTop}, | 93 {4, SKIP, kWebMIdPixelCropTop}, |
103 {SKIP, kWebMIdPixelCropLeft}, | 94 {4, SKIP, kWebMIdPixelCropLeft}, |
104 {SKIP, kWebMIdPixelCropRight}, | 95 {4, SKIP, kWebMIdPixelCropRight}, |
105 {SKIP, kWebMIdDisplayWidth}, | 96 {4, SKIP, kWebMIdDisplayWidth}, |
106 {SKIP, kWebMIdDisplayHeight}, | 97 {4, SKIP, kWebMIdDisplayHeight}, |
107 {SKIP, kWebMIdDisplayUnit}, | 98 {4, SKIP, kWebMIdDisplayUnit}, |
108 {SKIP, kWebMIdAspectRatioType}, | 99 {4, SKIP, kWebMIdAspectRatioType}, |
109 }; | 100 }; |
110 | 101 |
111 static const ElementIdInfo kAudioIds[] = { | 102 static const ElementIdInfo kAudioIds[] = { |
112 {SKIP, kWebMIdSamplingFrequency}, | 103 {4, SKIP, kWebMIdSamplingFrequency}, |
113 {SKIP, kWebMIdOutputSamplingFrequency}, | 104 {4, SKIP, kWebMIdOutputSamplingFrequency}, |
114 {UINT, kWebMIdChannels}, | 105 {4, UINT, kWebMIdChannels}, |
115 {SKIP, kWebMIdBitDepth}, | 106 {4, SKIP, kWebMIdBitDepth}, |
| 107 }; |
| 108 |
| 109 static const ElementIdInfo kClustersOnly[] = { |
| 110 {1, LIST, kWebMIdCluster}, |
116 }; | 111 }; |
117 | 112 |
118 static const ListElementInfo kListElementInfo[] = { | 113 static const ListElementInfo kListElementInfo[] = { |
119 { kWebMIdCluster, 1, kClusterIds, sizeof(kClusterIds) }, | 114 { kWebMIdCluster, kClusterIds, sizeof(kClusterIds) }, |
120 { kWebMIdSegment, 0, kSegmentIds, sizeof(kSegmentIds) }, | 115 { kWebMIdInfo, kInfoIds, sizeof(kInfoIds) }, |
121 { kWebMIdInfo, 1, kInfoIds, sizeof(kInfoIds) }, | 116 { kWebMIdTracks, kTracksIds, sizeof(kTracksIds) }, |
122 { kWebMIdTracks, 1, kTracksIds, sizeof(kTracksIds) }, | 117 { kWebMIdTrackEntry, kTrackEntryIds, sizeof(kTrackEntryIds) }, |
123 { kWebMIdTrackEntry, 2, kTrackEntryIds, sizeof(kTrackEntryIds) }, | 118 { kWebMIdVideo, kVideoIds, sizeof(kVideoIds) }, |
124 { kWebMIdVideo, 3, kVideoIds, sizeof(kVideoIds) }, | 119 { kWebMIdAudio, kAudioIds, sizeof(kAudioIds) }, |
125 { kWebMIdAudio, 3, kAudioIds, sizeof(kAudioIds) }, | |
126 }; | 120 }; |
127 | 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 | 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. | 129 // encoded. The first byte indicates how many bytes the field occupies. |
130 // |buf| - The buffer to parse. | 130 // |buf| - The buffer to parse. |
131 // |size| - The number of bytes in |buf| | 131 // |size| - The number of bytes in |buf| |
132 // |max_bytes| - The maximum number of bytes the field can be. ID fields | 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 | 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 | 134 // first byte indicates a larger field size than this it is a |
135 // parser error. | 135 // parser error. |
136 // |mask_first_byte| - For element size fields the field length encoding bits | 136 // |mask_first_byte| - For element size fields the field length encoding bits |
137 // need to be masked off. This parameter is true for | 137 // need to be masked off. This parameter is true for |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
199 size - num_id_bytes, | 199 size - num_id_bytes, |
200 8, true, &tmp); | 200 8, true, &tmp); |
201 | 201 |
202 if (num_size_bytes <= 0) | 202 if (num_size_bytes <= 0) |
203 return num_size_bytes; | 203 return num_size_bytes; |
204 | 204 |
205 *element_size = tmp; | 205 *element_size = tmp; |
206 return num_id_bytes + num_size_bytes; | 206 return num_id_bytes + num_size_bytes; |
207 } | 207 } |
208 | 208 |
209 // Finds ElementType for a specific ID. | 209 // Finds ElementIdInfo for a specific ID. |
210 static ElementType FindIdType(int id, | 210 static const ElementIdInfo* FindIdInfo(int id, |
211 const ElementIdInfo* id_info, | 211 const ElementIdInfo* id_info, |
212 int id_info_size) { | 212 int id_info_size) { |
213 | |
214 // Check for global element IDs that can be anywhere. | |
215 if (id == kWebMIdVoid || id == kWebMIdCRC32) | |
216 return SKIP; | |
217 | |
218 int count = id_info_size / sizeof(*id_info); | 213 int count = id_info_size / sizeof(*id_info); |
219 for (int i = 0; i < count; ++i) { | 214 for (int i = 0; i < count; ++i) { |
220 if (id == id_info[i].id_) | 215 if (id == id_info[i].id_) |
221 return id_info[i].type_; | 216 return &id_info[i]; |
222 } | 217 } |
223 | 218 |
224 return UNKNOWN; | 219 return NULL; |
225 } | 220 } |
226 | 221 |
227 // Finds ListElementInfo for a specific ID. | 222 // Finds ListElementInfo for a specific ID. |
228 static const ListElementInfo* FindListInfo(int id) { | 223 static const ListElementInfo* FindListInfo(int id) { |
229 for (size_t i = 0; i < arraysize(kListElementInfo); ++i) { | 224 for (int i = 0; i < kListElementInfoCount; ++i) { |
230 if (id == kListElementInfo[i].id_) | 225 if (id == kListElementInfo[i].id_) |
231 return &kListElementInfo[i]; | 226 return &kListElementInfo[i]; |
232 } | 227 } |
233 | 228 |
234 return NULL; | 229 return NULL; |
235 } | 230 } |
236 | 231 |
237 static int ParseSimpleBlock(const uint8* buf, int size, | 232 static int ParseSimpleBlock(const uint8* buf, int size, |
238 WebMParserClient* client) { | 233 WebMParserClient* client) { |
239 if (size < 4) | 234 if (size < 4) |
240 return -1; | 235 return -1; |
241 | 236 |
242 // Return an error if the trackNum > 127. We just aren't | 237 // Return an error if the trackNum > 127. We just aren't |
243 // going to support large track numbers right now. | 238 // going to support large track numbers right now. |
244 if ((buf[0] & 0x80) != 0x80) { | 239 if ((buf[0] & 0x80) != 0x80) { |
245 DVLOG(1) << "TrackNumber over 127 not supported"; | 240 VLOG(1) << "TrackNumber over 127 not supported"; |
246 return -1; | 241 return -1; |
247 } | 242 } |
248 | 243 |
249 int track_num = buf[0] & 0x7f; | 244 int track_num = buf[0] & 0x7f; |
250 int timecode = buf[1] << 8 | buf[2]; | 245 int timecode = buf[1] << 8 | buf[2]; |
251 int flags = buf[3] & 0xff; | 246 int flags = buf[3] & 0xff; |
252 int lacing = (flags >> 1) & 0x3; | 247 int lacing = (flags >> 1) & 0x3; |
253 | 248 |
254 if (lacing != 0) { | 249 if (lacing != 0) { |
255 DVLOG(1) << "Lacing " << lacing << " not supported yet."; | 250 VLOG(1) << "Lacing " << lacing << " not supported yet."; |
256 return -1; | 251 return -1; |
257 } | 252 } |
258 | 253 |
259 // Sign extend negative timecode offsets. | 254 // Sign extend negative timecode offsets. |
260 if (timecode & 0x8000) | 255 if (timecode & 0x8000) |
261 timecode |= (-1 << 16); | 256 timecode |= (-1 << 16); |
262 | 257 |
263 const uint8* frame_data = buf + 4; | 258 const uint8* frame_data = buf + 4; |
264 int frame_size = size - (frame_data - buf); | 259 int frame_size = size - (frame_data - buf); |
265 if (!client->OnSimpleBlock(track_num, timecode, flags, | 260 if (!client->OnSimpleBlock(track_num, timecode, flags, |
266 frame_data, frame_size)) { | 261 frame_data, frame_size)) { |
267 return -1; | 262 return -1; |
268 } | 263 } |
269 | 264 |
270 return size; | 265 return size; |
271 } | 266 } |
272 | 267 |
| 268 static int ParseElements(const ElementIdInfo* id_info, |
| 269 int id_info_size, |
| 270 const uint8* buf, int size, int level, |
| 271 WebMParserClient* client); |
| 272 |
| 273 static int ParseElementList(const uint8* buf, int size, |
| 274 int id, int level, |
| 275 WebMParserClient* client) { |
| 276 const ListElementInfo* list_info = FindListInfo(id); |
| 277 |
| 278 if (!list_info) { |
| 279 VLOG(1) << "Failed to find list info for ID " << std::hex << id; |
| 280 return -1; |
| 281 } |
| 282 |
| 283 if (!client->OnListStart(id)) |
| 284 return -1; |
| 285 |
| 286 int result = ParseElements(list_info->id_info_, |
| 287 list_info->id_info_size_, |
| 288 buf, size, |
| 289 level + 1, |
| 290 client); |
| 291 |
| 292 if (result <= 0) |
| 293 return result; |
| 294 |
| 295 if (!client->OnListEnd(id)) |
| 296 return -1; |
| 297 |
| 298 DCHECK_EQ(result, size); |
| 299 return result; |
| 300 } |
273 | 301 |
274 static int ParseUInt(const uint8* buf, int size, int id, | 302 static int ParseUInt(const uint8* buf, int size, int id, |
275 WebMParserClient* client) { | 303 WebMParserClient* client) { |
276 if ((size <= 0) || (size > 8)) | 304 if ((size <= 0) || (size > 8)) |
277 return -1; | 305 return -1; |
278 | 306 |
279 // Read in the big-endian integer. | 307 // Read in the big-endian integer. |
280 int64 value = 0; | 308 int64 value = 0; |
281 for (int i = 0; i < size; ++i) | 309 for (int i = 0; i < size; ++i) |
282 value = (value << 8) | buf[i]; | 310 value = (value << 8) | buf[i]; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
319 } else { | 347 } else { |
320 return -1; | 348 return -1; |
321 } | 349 } |
322 | 350 |
323 if (!client->OnFloat(id, value)) | 351 if (!client->OnFloat(id, value)) |
324 return -1; | 352 return -1; |
325 | 353 |
326 return size; | 354 return size; |
327 } | 355 } |
328 | 356 |
329 static int ParseNonListElement(ElementType type, int id, int64 element_size, | 357 static int ParseElements(const ElementIdInfo* id_info, |
330 const uint8* buf, int size, | 358 int id_info_size, |
331 WebMParserClient* client) { | 359 const uint8* buf, int size, int level, |
332 DCHECK_GE(size, element_size); | 360 WebMParserClient* client) { |
| 361 DCHECK_GE(id_info_size, 0); |
| 362 DCHECK_GE(size, 0); |
| 363 DCHECK_GE(level, 0); |
333 | 364 |
334 int result = -1; | 365 const uint8* cur = buf; |
335 switch(type) { | 366 int cur_size = size; |
336 case SBLOCK: | 367 int used = 0; |
337 result = ParseSimpleBlock(buf, element_size, client); | 368 |
338 break; | 369 if (level > kMaxLevelDepth) |
339 case LIST: | 370 return -1; |
340 NOTIMPLEMENTED(); | 371 |
341 result = -1; | 372 while (cur_size > 0) { |
342 break; | 373 int id = 0; |
343 case UINT: | 374 int64 element_size = 0; |
344 result = ParseUInt(buf, element_size, id, client); | 375 int result = WebMParseElementHeader(cur, cur_size, &id, &element_size); |
345 break; | 376 |
346 case FLOAT: | 377 if (result <= 0) |
347 result = ParseFloat(buf, element_size, id, client); | 378 return result; |
348 break; | 379 |
349 case BINARY: | 380 cur += result; |
350 if (client->OnBinary(id, buf, element_size)) { | 381 cur_size -= result; |
351 result = element_size; | 382 used += result; |
352 } else { | 383 |
353 result = -1; | 384 // Check to see if the element is larger than the remaining data. |
354 } | 385 if (element_size > cur_size) |
355 break; | 386 return 0; |
356 case STRING: | 387 |
357 if (client->OnString(id, | 388 const ElementIdInfo* info = FindIdInfo(id, id_info, id_info_size); |
358 std::string(reinterpret_cast<const char*>(buf), | 389 |
359 element_size))) { | 390 if (info == NULL) { |
360 result = element_size; | 391 VLOG(1) << "No info for ID " << std::hex << id; |
361 } else { | 392 |
362 result = -1; | 393 // TODO(acolwell): Change this to return -1 after the API has solidified. |
363 } | 394 // We don't want to allow elements we don't recognize. |
364 break; | 395 cur += element_size; |
365 case SKIP: | 396 cur_size -= element_size; |
366 result = element_size; | 397 used += element_size; |
367 break; | 398 continue; |
368 default: | 399 } |
369 DVLOG(1) << "Unhandled ID type " << type; | 400 |
| 401 if (info->level_ != level) { |
| 402 VLOG(1) << "ID " << std::hex << id << std::dec << " at level " |
| 403 << level << " instead of " << info->level_; |
370 return -1; | 404 return -1; |
371 }; | 405 } |
372 | 406 |
373 DCHECK_LE(result, size); | 407 switch(info->type_) { |
374 return result; | 408 case SBLOCK: |
| 409 if (ParseSimpleBlock(cur, element_size, client) <= 0) |
| 410 return -1; |
| 411 break; |
| 412 case LIST: |
| 413 if (ParseElementList(cur, element_size, id, level, client) < 0) |
| 414 return -1; |
| 415 break; |
| 416 case UINT: |
| 417 if (ParseUInt(cur, element_size, id, client) <= 0) |
| 418 return -1; |
| 419 break; |
| 420 case FLOAT: |
| 421 if (ParseFloat(cur, element_size, id, client) <= 0) |
| 422 return -1; |
| 423 break; |
| 424 case BINARY: |
| 425 if (!client->OnBinary(id, cur, element_size)) |
| 426 return -1; |
| 427 break; |
| 428 case STRING: |
| 429 if (!client->OnString(id, |
| 430 std::string(reinterpret_cast<const char*>(cur), |
| 431 element_size))) |
| 432 return -1; |
| 433 break; |
| 434 case SKIP: |
| 435 // Do nothing. |
| 436 break; |
| 437 default: |
| 438 VLOG(1) << "Unhandled id type " << info->type_; |
| 439 return -1; |
| 440 }; |
| 441 |
| 442 cur += element_size; |
| 443 cur_size -= element_size; |
| 444 used += element_size; |
| 445 } |
| 446 |
| 447 return used; |
375 } | 448 } |
376 | 449 |
377 WebMParserClient::WebMParserClient() {} | 450 // Parses a single list element that matches |id|. This method fails if the |
378 WebMParserClient::~WebMParserClient() {} | 451 // buffer points to an element that does not match |id|. |
379 | 452 int WebMParseListElement(const uint8* buf, int size, int id, |
380 WebMListParser::WebMListParser(int id) | 453 int level, WebMParserClient* client) { |
381 : state_(NEED_LIST_HEADER), | 454 if (size < 0) |
382 root_id_(id) { | |
383 const ListElementInfo* list_info = FindListInfo(id); | |
384 | |
385 DCHECK(list_info); | |
386 | |
387 root_level_ = list_info->level_; | |
388 } | |
389 | |
390 WebMListParser::~WebMListParser() {} | |
391 | |
392 void WebMListParser::Reset() { | |
393 ChangeState(NEED_LIST_HEADER); | |
394 list_state_stack_.clear(); | |
395 } | |
396 | |
397 int WebMListParser::Parse(const uint8* buf, int size, | |
398 WebMParserClient* client) { | |
399 DCHECK(buf); | |
400 DCHECK(client); | |
401 | |
402 if (size < 0 || state_ == PARSE_ERROR || state_ == DONE_PARSING_LIST) | |
403 return -1; | 455 return -1; |
404 | 456 |
405 if (size == 0) | 457 if (size == 0) |
406 return 0; | 458 return 0; |
407 | 459 |
408 const uint8* cur = buf; | 460 const uint8* cur = buf; |
409 int cur_size = size; | 461 int cur_size = size; |
410 int bytes_parsed = 0; | 462 int bytes_parsed = 0; |
| 463 int element_id = 0; |
| 464 int64 element_size = 0; |
| 465 int result = WebMParseElementHeader(cur, cur_size, &element_id, |
| 466 &element_size); |
411 | 467 |
412 while (cur_size > 0 && state_ != PARSE_ERROR && state_ != DONE_PARSING_LIST) { | 468 if (result <= 0) |
413 int element_id = 0; | 469 return result; |
414 int64 element_size = 0; | |
415 int result = WebMParseElementHeader(cur, cur_size, &element_id, | |
416 &element_size); | |
417 | 470 |
418 if (result < 0) | 471 cur += result; |
| 472 cur_size -= result; |
| 473 bytes_parsed += result; |
| 474 |
| 475 if (element_id != id) |
| 476 return -1; |
| 477 |
| 478 if (element_size > cur_size) |
| 479 return 0; |
| 480 |
| 481 if (element_size > 0) { |
| 482 result = ParseElementList(cur, element_size, element_id, level, client); |
| 483 |
| 484 if (result <= 0) |
419 return result; | 485 return result; |
420 | 486 |
421 if (result == 0) | |
422 return bytes_parsed; | |
423 | |
424 switch(state_) { | |
425 case NEED_LIST_HEADER: { | |
426 if (element_id != root_id_) { | |
427 ChangeState(PARSE_ERROR); | |
428 return -1; | |
429 } | |
430 | |
431 // TODO(acolwell): Add support for lists of unknown size. | |
432 if (element_size == kWebMUnknownSize) { | |
433 ChangeState(PARSE_ERROR); | |
434 return -1; | |
435 } | |
436 | |
437 ChangeState(INSIDE_LIST); | |
438 if (!OnListStart(root_id_, element_size, client)) | |
439 return -1; | |
440 | |
441 break; | |
442 } | |
443 | |
444 case INSIDE_LIST: { | |
445 int header_size = result; | |
446 const uint8* element_data = cur + header_size; | |
447 int element_data_size = cur_size - header_size; | |
448 | |
449 if (element_size < element_data_size) | |
450 element_data_size = element_size; | |
451 | |
452 result = ParseListElement(header_size, element_id, element_size, | |
453 element_data, element_data_size, client); | |
454 | |
455 DCHECK_LE(result, header_size + element_data_size); | |
456 if (result < 0) { | |
457 ChangeState(PARSE_ERROR); | |
458 return -1; | |
459 } | |
460 | |
461 if (result == 0) | |
462 return bytes_parsed; | |
463 | |
464 break; | |
465 } | |
466 case DONE_PARSING_LIST: | |
467 case PARSE_ERROR: | |
468 // Shouldn't be able to get here. | |
469 NOTIMPLEMENTED(); | |
470 break; | |
471 } | |
472 | |
473 cur += result; | 487 cur += result; |
474 cur_size -= result; | 488 cur_size -= result; |
475 bytes_parsed += result; | 489 bytes_parsed += result; |
476 } | 490 } |
477 | 491 |
478 return (state_ == PARSE_ERROR) ? -1 : bytes_parsed; | 492 return bytes_parsed; |
479 } | |
480 | |
481 bool WebMListParser::IsParsingComplete() const { | |
482 return state_ == DONE_PARSING_LIST; | |
483 } | |
484 | |
485 void WebMListParser::ChangeState(State new_state) { | |
486 state_ = new_state; | |
487 } | |
488 | |
489 int WebMListParser::ParseListElement(int header_size, | |
490 int id, int64 element_size, | |
491 const uint8* data, int size, | |
492 WebMParserClient* client) { | |
493 DCHECK_GT(list_state_stack_.size(), 0u); | |
494 | |
495 ListState& list_state = list_state_stack_.back(); | |
496 DCHECK(list_state.element_info_); | |
497 | |
498 const ListElementInfo* element_info = list_state.element_info_; | |
499 ElementType id_type = | |
500 FindIdType(id, element_info->id_info_, element_info->id_info_size_); | |
501 | |
502 // Unexpected ID. | |
503 if (id_type == UNKNOWN) { | |
504 DVLOG(1) << "No ElementType info for ID 0x" << std::hex << id; | |
505 return -1; | |
506 } | |
507 | |
508 // Make sure the whole element can fit inside the current list. | |
509 int64 total_element_size = header_size + element_size; | |
510 if (list_state.size_ != kWebMUnknownSize && | |
511 list_state.size_ < list_state.bytes_parsed_ + total_element_size) { | |
512 return -1; | |
513 } | |
514 | |
515 if (id_type == LIST) { | |
516 list_state.bytes_parsed_ += header_size; | |
517 | |
518 if (!OnListStart(id, element_size, client)) | |
519 return -1; | |
520 return header_size; | |
521 } | |
522 | |
523 // Make sure we have the entire element before trying to parse a non-list | |
524 // element. | |
525 if (size < element_size) | |
526 return 0; | |
527 | |
528 int bytes_parsed = ParseNonListElement(id_type, id, element_size, | |
529 data, size, client); | |
530 DCHECK_LE(bytes_parsed, size); | |
531 | |
532 // Return if an error occurred or we need more data. | |
533 // Note: bytes_parsed is 0 for a successful parse of a size 0 element. We | |
534 // need to check the element_size to disambiguate the "need more data" case | |
535 // from a successful parse. | |
536 if (bytes_parsed < 0 || (bytes_parsed == 0 && element_size != 0)) | |
537 return bytes_parsed; | |
538 | |
539 int result = header_size + bytes_parsed; | |
540 list_state.bytes_parsed_ += result; | |
541 | |
542 // See if we have reached the end of the current list. | |
543 if (list_state.bytes_parsed_ == list_state.size_) { | |
544 if (!OnListEnd(client)) | |
545 return -1; | |
546 } | |
547 | |
548 return result; | |
549 } | |
550 | |
551 bool WebMListParser::OnListStart(int id, int64 size, WebMParserClient* client) { | |
552 ListState list_state = { id, size, 0, FindListInfo(id)}; | |
553 | |
554 if (!list_state.element_info_) | |
555 return false; | |
556 | |
557 int current_level = root_level_ + list_state_stack_.size() - 1; | |
558 if (current_level + 1 != list_state.element_info_->level_) | |
559 return false; | |
560 | |
561 if (!list_state_stack_.empty()) { | |
562 | |
563 // Make sure the new list doesn't go past the end of the current list. | |
564 ListState current_list = list_state_stack_.back(); | |
565 if (current_list.size_ != kWebMUnknownSize && | |
566 current_list.size_ < current_list.bytes_parsed_ + size) | |
567 return false; | |
568 } | |
569 | |
570 if (!client->OnListStart(id)) | |
571 return false; | |
572 | |
573 list_state_stack_.push_back(list_state); | |
574 | |
575 if (size == 0) { | |
576 return OnListEnd(client); | |
577 } | |
578 | |
579 return true; | |
580 } | |
581 | |
582 bool WebMListParser::OnListEnd(WebMParserClient* client) { | |
583 int lists_ended = 0; | |
584 for (; !list_state_stack_.empty(); ++lists_ended) { | |
585 const ListState& list_state = list_state_stack_.back(); | |
586 | |
587 if (list_state.bytes_parsed_ != list_state.size_) | |
588 break; | |
589 | |
590 if (!client->OnListEnd(list_state.id_)) | |
591 return false; | |
592 | |
593 int64 bytes_parsed = list_state.bytes_parsed_; | |
594 list_state_stack_.pop_back(); | |
595 | |
596 if (!list_state_stack_.empty()) { | |
597 // Update the bytes_parsed_ for the parent element. | |
598 list_state_stack_.back().bytes_parsed_ += bytes_parsed; | |
599 } | |
600 } | |
601 | |
602 DCHECK_GE(lists_ended, 1); | |
603 | |
604 if (list_state_stack_.empty()) | |
605 ChangeState(DONE_PARSING_LIST); | |
606 | |
607 return true; | |
608 } | 493 } |
609 | 494 |
610 } // namespace media | 495 } // namespace media |
OLD | NEW |