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