Index: media/webm/webm_parser.cc |
=================================================================== |
--- media/webm/webm_parser.cc (revision 114032) |
+++ media/webm/webm_parser.cc (working copy) |
@@ -20,7 +20,6 @@ |
static const int kMaxLevelDepth = 6; |
enum ElementType { |
- UNKNOWN, |
LIST, |
UINT, |
FLOAT, |
@@ -31,13 +30,13 @@ |
}; |
struct ElementIdInfo { |
+ int level_; |
ElementType type_; |
int id_; |
}; |
struct ListElementInfo { |
int id_; |
- int level_; |
const ElementIdInfo* id_info_; |
int id_info_size_; |
}; |
@@ -48,83 +47,84 @@ |
// marked as SKIP because they are valid, but we don't care about them |
// right now. |
static const ElementIdInfo kClusterIds[] = { |
- {UINT, kWebMIdTimecode}, |
- {SBLOCK, kWebMIdSimpleBlock}, |
- {LIST, kWebMIdBlockGroup}, |
+ {2, UINT, kWebMIdTimecode}, |
+ {2, SBLOCK, kWebMIdSimpleBlock}, |
+ {2, LIST, kWebMIdBlockGroup}, |
}; |
-static const ElementIdInfo kSegmentIds[] = { |
- {SKIP, kWebMIdSeekHead}, // TODO(acolwell): add SeekHead info |
- {LIST, kWebMIdInfo}, |
- {LIST, kWebMIdCluster}, |
- {LIST, kWebMIdTracks}, |
- {SKIP, kWebMIdCues}, // TODO(acolwell): add CUES info |
-}; |
- |
static const ElementIdInfo kInfoIds[] = { |
- {SKIP, kWebMIdSegmentUID}, |
- {UINT, kWebMIdTimecodeScale}, |
- {FLOAT, kWebMIdDuration}, |
- {SKIP, kWebMIdDateUTC}, |
- {SKIP, kWebMIdTitle}, |
- {SKIP, kWebMIdMuxingApp}, |
- {SKIP, kWebMIdWritingApp}, |
+ {2, SKIP, kWebMIdSegmentUID}, |
+ {2, UINT, kWebMIdTimecodeScale}, |
+ {2, FLOAT, kWebMIdDuration}, |
+ {2, SKIP, kWebMIdDateUTC}, |
+ {2, SKIP, kWebMIdTitle}, |
+ {2, SKIP, kWebMIdMuxingApp}, |
+ {2, SKIP, kWebMIdWritingApp}, |
}; |
static const ElementIdInfo kTracksIds[] = { |
- {LIST, kWebMIdTrackEntry}, |
+ {2, LIST, kWebMIdTrackEntry}, |
}; |
static const ElementIdInfo kTrackEntryIds[] = { |
- {UINT, kWebMIdTrackNumber}, |
- {SKIP, kWebMIdTrackUID}, |
- {UINT, kWebMIdTrackType}, |
- {SKIP, kWebMIdFlagEnabled}, |
- {SKIP, kWebMIdFlagDefault}, |
- {SKIP, kWebMIdFlagForced}, |
- {UINT, kWebMIdFlagLacing}, |
- {UINT, kWebMIdDefaultDuration}, |
- {SKIP, kWebMIdName}, |
- {SKIP, kWebMIdLanguage}, |
- {STRING, kWebMIdCodecID}, |
- {BINARY, kWebMIdCodecPrivate}, |
- {SKIP, kWebMIdCodecName}, |
- {LIST, kWebMIdVideo}, |
- {LIST, kWebMIdAudio}, |
+ {3, UINT, kWebMIdTrackNumber}, |
+ {3, SKIP, kWebMIdTrackUID}, |
+ {3, UINT, kWebMIdTrackType}, |
+ {3, SKIP, kWebMIdFlagEnabled}, |
+ {3, SKIP, kWebMIdFlagDefault}, |
+ {3, SKIP, kWebMIdFlagForced}, |
+ {3, UINT, kWebMIdFlagLacing}, |
+ {3, UINT, kWebMIdDefaultDuration}, |
+ {3, SKIP, kWebMIdName}, |
+ {3, SKIP, kWebMIdLanguage}, |
+ {3, STRING, kWebMIdCodecID}, |
+ {3, BINARY, kWebMIdCodecPrivate}, |
+ {3, SKIP, kWebMIdCodecName}, |
+ {3, LIST, kWebMIdVideo}, |
+ {3, LIST, kWebMIdAudio}, |
}; |
static const ElementIdInfo kVideoIds[] = { |
- {SKIP, kWebMIdFlagInterlaced}, |
- {SKIP, kWebMIdStereoMode}, |
- {UINT, kWebMIdPixelWidth}, |
- {UINT, kWebMIdPixelHeight}, |
- {SKIP, kWebMIdPixelCropBottom}, |
- {SKIP, kWebMIdPixelCropTop}, |
- {SKIP, kWebMIdPixelCropLeft}, |
- {SKIP, kWebMIdPixelCropRight}, |
- {SKIP, kWebMIdDisplayWidth}, |
- {SKIP, kWebMIdDisplayHeight}, |
- {SKIP, kWebMIdDisplayUnit}, |
- {SKIP, kWebMIdAspectRatioType}, |
+ {4, SKIP, kWebMIdFlagInterlaced}, |
+ {4, SKIP, kWebMIdStereoMode}, |
+ {4, UINT, kWebMIdPixelWidth}, |
+ {4, UINT, kWebMIdPixelHeight}, |
+ {4, SKIP, kWebMIdPixelCropBottom}, |
+ {4, SKIP, kWebMIdPixelCropTop}, |
+ {4, SKIP, kWebMIdPixelCropLeft}, |
+ {4, SKIP, kWebMIdPixelCropRight}, |
+ {4, SKIP, kWebMIdDisplayWidth}, |
+ {4, SKIP, kWebMIdDisplayHeight}, |
+ {4, SKIP, kWebMIdDisplayUnit}, |
+ {4, SKIP, kWebMIdAspectRatioType}, |
}; |
static const ElementIdInfo kAudioIds[] = { |
- {SKIP, kWebMIdSamplingFrequency}, |
- {SKIP, kWebMIdOutputSamplingFrequency}, |
- {UINT, kWebMIdChannels}, |
- {SKIP, kWebMIdBitDepth}, |
+ {4, SKIP, kWebMIdSamplingFrequency}, |
+ {4, SKIP, kWebMIdOutputSamplingFrequency}, |
+ {4, UINT, kWebMIdChannels}, |
+ {4, SKIP, kWebMIdBitDepth}, |
}; |
+static const ElementIdInfo kClustersOnly[] = { |
+ {1, LIST, kWebMIdCluster}, |
+}; |
+ |
static const ListElementInfo kListElementInfo[] = { |
- { kWebMIdCluster, 1, kClusterIds, sizeof(kClusterIds) }, |
- { kWebMIdSegment, 0, kSegmentIds, sizeof(kSegmentIds) }, |
- { kWebMIdInfo, 1, kInfoIds, sizeof(kInfoIds) }, |
- { kWebMIdTracks, 1, kTracksIds, sizeof(kTracksIds) }, |
- { kWebMIdTrackEntry, 2, kTrackEntryIds, sizeof(kTrackEntryIds) }, |
- { kWebMIdVideo, 3, kVideoIds, sizeof(kVideoIds) }, |
- { kWebMIdAudio, 3, kAudioIds, sizeof(kAudioIds) }, |
+ { kWebMIdCluster, kClusterIds, sizeof(kClusterIds) }, |
+ { kWebMIdInfo, kInfoIds, sizeof(kInfoIds) }, |
+ { kWebMIdTracks, kTracksIds, sizeof(kTracksIds) }, |
+ { kWebMIdTrackEntry, kTrackEntryIds, sizeof(kTrackEntryIds) }, |
+ { kWebMIdVideo, kVideoIds, sizeof(kVideoIds) }, |
+ { kWebMIdAudio, kAudioIds, sizeof(kAudioIds) }, |
}; |
+// Number of elements in kListElementInfo. |
+const int kListElementInfoCount = |
+ sizeof(kListElementInfo) / sizeof(ListElementInfo); |
+ |
+WebMParserClient::~WebMParserClient() {} |
+ |
// Parses an element header id or size field. These fields are variable length |
// encoded. The first byte indicates how many bytes the field occupies. |
// |buf| - The buffer to parse. |
@@ -206,27 +206,22 @@ |
return num_id_bytes + num_size_bytes; |
} |
-// Finds ElementType for a specific ID. |
-static ElementType FindIdType(int id, |
- const ElementIdInfo* id_info, |
- int id_info_size) { |
- |
- // Check for global element IDs that can be anywhere. |
- if (id == kWebMIdVoid || id == kWebMIdCRC32) |
- return SKIP; |
- |
+// Finds ElementIdInfo for a specific ID. |
+static const ElementIdInfo* FindIdInfo(int id, |
+ const ElementIdInfo* id_info, |
+ int id_info_size) { |
int count = id_info_size / sizeof(*id_info); |
for (int i = 0; i < count; ++i) { |
if (id == id_info[i].id_) |
- return id_info[i].type_; |
+ return &id_info[i]; |
} |
- return UNKNOWN; |
+ return NULL; |
} |
// Finds ListElementInfo for a specific ID. |
static const ListElementInfo* FindListInfo(int id) { |
- for (size_t i = 0; i < arraysize(kListElementInfo); ++i) { |
+ for (int i = 0; i < kListElementInfoCount; ++i) { |
if (id == kListElementInfo[i].id_) |
return &kListElementInfo[i]; |
} |
@@ -242,7 +237,7 @@ |
// Return an error if the trackNum > 127. We just aren't |
// going to support large track numbers right now. |
if ((buf[0] & 0x80) != 0x80) { |
- DVLOG(1) << "TrackNumber over 127 not supported"; |
+ VLOG(1) << "TrackNumber over 127 not supported"; |
return -1; |
} |
@@ -252,7 +247,7 @@ |
int lacing = (flags >> 1) & 0x3; |
if (lacing != 0) { |
- DVLOG(1) << "Lacing " << lacing << " not supported yet."; |
+ VLOG(1) << "Lacing " << lacing << " not supported yet."; |
return -1; |
} |
@@ -270,7 +265,40 @@ |
return size; |
} |
+static int ParseElements(const ElementIdInfo* id_info, |
+ int id_info_size, |
+ const uint8* buf, int size, int level, |
+ WebMParserClient* client); |
+static int ParseElementList(const uint8* buf, int size, |
+ int id, int level, |
+ WebMParserClient* client) { |
+ const ListElementInfo* list_info = FindListInfo(id); |
+ |
+ if (!list_info) { |
+ VLOG(1) << "Failed to find list info for ID " << std::hex << id; |
+ return -1; |
+ } |
+ |
+ if (!client->OnListStart(id)) |
+ return -1; |
+ |
+ int result = ParseElements(list_info->id_info_, |
+ list_info->id_info_size_, |
+ buf, size, |
+ level + 1, |
+ client); |
+ |
+ if (result <= 0) |
+ return result; |
+ |
+ if (!client->OnListEnd(id)) |
+ return -1; |
+ |
+ DCHECK_EQ(result, size); |
+ return result; |
+} |
+ |
static int ParseUInt(const uint8* buf, int size, int id, |
WebMParserClient* client) { |
if ((size <= 0) || (size > 8)) |
@@ -326,285 +354,142 @@ |
return size; |
} |
-static int ParseNonListElement(ElementType type, int id, int64 element_size, |
- const uint8* buf, int size, |
- WebMParserClient* client) { |
- DCHECK_GE(size, element_size); |
+static int ParseElements(const ElementIdInfo* id_info, |
+ int id_info_size, |
+ const uint8* buf, int size, int level, |
+ WebMParserClient* client) { |
+ DCHECK_GE(id_info_size, 0); |
+ DCHECK_GE(size, 0); |
+ DCHECK_GE(level, 0); |
- int result = -1; |
- switch(type) { |
- case SBLOCK: |
- result = ParseSimpleBlock(buf, element_size, client); |
- break; |
- case LIST: |
- NOTIMPLEMENTED(); |
- result = -1; |
- break; |
- case UINT: |
- result = ParseUInt(buf, element_size, id, client); |
- break; |
- case FLOAT: |
- result = ParseFloat(buf, element_size, id, client); |
- break; |
- case BINARY: |
- if (client->OnBinary(id, buf, element_size)) { |
- result = element_size; |
- } else { |
- result = -1; |
- } |
- break; |
- case STRING: |
- if (client->OnString(id, |
- std::string(reinterpret_cast<const char*>(buf), |
- element_size))) { |
- result = element_size; |
- } else { |
- result = -1; |
- } |
- break; |
- case SKIP: |
- result = element_size; |
- break; |
- default: |
- DVLOG(1) << "Unhandled ID type " << type; |
- return -1; |
- }; |
+ const uint8* cur = buf; |
+ int cur_size = size; |
+ int used = 0; |
- DCHECK_LE(result, size); |
- return result; |
-} |
+ if (level > kMaxLevelDepth) |
+ return -1; |
-WebMParserClient::WebMParserClient() {} |
-WebMParserClient::~WebMParserClient() {} |
+ while (cur_size > 0) { |
+ int id = 0; |
+ int64 element_size = 0; |
+ int result = WebMParseElementHeader(cur, cur_size, &id, &element_size); |
-WebMListParser::WebMListParser(int id) |
- : state_(NEED_LIST_HEADER), |
- root_id_(id) { |
- const ListElementInfo* list_info = FindListInfo(id); |
+ if (result <= 0) |
+ return result; |
- DCHECK(list_info); |
+ cur += result; |
+ cur_size -= result; |
+ used += result; |
- root_level_ = list_info->level_; |
-} |
+ // Check to see if the element is larger than the remaining data. |
+ if (element_size > cur_size) |
+ return 0; |
-WebMListParser::~WebMListParser() {} |
+ const ElementIdInfo* info = FindIdInfo(id, id_info, id_info_size); |
-void WebMListParser::Reset() { |
- ChangeState(NEED_LIST_HEADER); |
- list_state_stack_.clear(); |
-} |
+ if (info == NULL) { |
+ VLOG(1) << "No info for ID " << std::hex << id; |
-int WebMListParser::Parse(const uint8* buf, int size, |
- WebMParserClient* client) { |
- DCHECK(buf); |
- DCHECK(client); |
+ // TODO(acolwell): Change this to return -1 after the API has solidified. |
+ // We don't want to allow elements we don't recognize. |
+ cur += element_size; |
+ cur_size -= element_size; |
+ used += element_size; |
+ continue; |
+ } |
- if (size < 0 || state_ == PARSE_ERROR || state_ == DONE_PARSING_LIST) |
- return -1; |
+ if (info->level_ != level) { |
+ VLOG(1) << "ID " << std::hex << id << std::dec << " at level " |
+ << level << " instead of " << info->level_; |
+ return -1; |
+ } |
- if (size == 0) |
- return 0; |
- |
- const uint8* cur = buf; |
- int cur_size = size; |
- int bytes_parsed = 0; |
- |
- while (cur_size > 0 && state_ != PARSE_ERROR && state_ != DONE_PARSING_LIST) { |
- int element_id = 0; |
- int64 element_size = 0; |
- int result = WebMParseElementHeader(cur, cur_size, &element_id, |
- &element_size); |
- |
- if (result < 0) |
- return result; |
- |
- if (result == 0) |
- return bytes_parsed; |
- |
- switch(state_) { |
- case NEED_LIST_HEADER: { |
- if (element_id != root_id_) { |
- ChangeState(PARSE_ERROR); |
+ switch(info->type_) { |
+ case SBLOCK: |
+ if (ParseSimpleBlock(cur, element_size, client) <= 0) |
return -1; |
- } |
- |
- // TODO(acolwell): Add support for lists of unknown size. |
- if (element_size == kWebMUnknownSize) { |
- ChangeState(PARSE_ERROR); |
+ break; |
+ case LIST: |
+ if (ParseElementList(cur, element_size, id, level, client) < 0) |
return -1; |
- } |
- |
- ChangeState(INSIDE_LIST); |
- if (!OnListStart(root_id_, element_size, client)) |
+ break; |
+ case UINT: |
+ if (ParseUInt(cur, element_size, id, client) <= 0) |
return -1; |
- |
break; |
- } |
- |
- case INSIDE_LIST: { |
- int header_size = result; |
- const uint8* element_data = cur + header_size; |
- int element_data_size = cur_size - header_size; |
- |
- if (element_size < element_data_size) |
- element_data_size = element_size; |
- |
- result = ParseListElement(header_size, element_id, element_size, |
- element_data, element_data_size, client); |
- |
- DCHECK_LE(result, header_size + element_data_size); |
- if (result < 0) { |
- ChangeState(PARSE_ERROR); |
+ case FLOAT: |
+ if (ParseFloat(cur, element_size, id, client) <= 0) |
return -1; |
- } |
- |
- if (result == 0) |
- return bytes_parsed; |
- |
break; |
- } |
- case DONE_PARSING_LIST: |
- case PARSE_ERROR: |
- // Shouldn't be able to get here. |
- NOTIMPLEMENTED(); |
+ case BINARY: |
+ if (!client->OnBinary(id, cur, element_size)) |
+ return -1; |
break; |
- } |
+ case STRING: |
+ if (!client->OnString(id, |
+ std::string(reinterpret_cast<const char*>(cur), |
+ element_size))) |
+ return -1; |
+ break; |
+ case SKIP: |
+ // Do nothing. |
+ break; |
+ default: |
+ VLOG(1) << "Unhandled id type " << info->type_; |
+ return -1; |
+ }; |
- cur += result; |
- cur_size -= result; |
- bytes_parsed += result; |
+ cur += element_size; |
+ cur_size -= element_size; |
+ used += element_size; |
} |
- return (state_ == PARSE_ERROR) ? -1 : bytes_parsed; |
+ return used; |
} |
-bool WebMListParser::IsParsingComplete() const { |
- return state_ == DONE_PARSING_LIST; |
-} |
+// Parses a single list element that matches |id|. This method fails if the |
+// buffer points to an element that does not match |id|. |
+int WebMParseListElement(const uint8* buf, int size, int id, |
+ int level, WebMParserClient* client) { |
+ if (size < 0) |
+ return -1; |
-void WebMListParser::ChangeState(State new_state) { |
- state_ = new_state; |
-} |
+ if (size == 0) |
+ return 0; |
-int WebMListParser::ParseListElement(int header_size, |
- int id, int64 element_size, |
- const uint8* data, int size, |
- WebMParserClient* client) { |
- DCHECK_GT(list_state_stack_.size(), 0u); |
+ const uint8* cur = buf; |
+ int cur_size = size; |
+ int bytes_parsed = 0; |
+ int element_id = 0; |
+ int64 element_size = 0; |
+ int result = WebMParseElementHeader(cur, cur_size, &element_id, |
+ &element_size); |
- ListState& list_state = list_state_stack_.back(); |
- DCHECK(list_state.element_info_); |
+ if (result <= 0) |
+ return result; |
- const ListElementInfo* element_info = list_state.element_info_; |
- ElementType id_type = |
- FindIdType(id, element_info->id_info_, element_info->id_info_size_); |
+ cur += result; |
+ cur_size -= result; |
+ bytes_parsed += result; |
- // Unexpected ID. |
- if (id_type == UNKNOWN) { |
- DVLOG(1) << "No ElementType info for ID 0x" << std::hex << id; |
+ if (element_id != id) |
return -1; |
- } |
- // Make sure the whole element can fit inside the current list. |
- int64 total_element_size = header_size + element_size; |
- if (list_state.size_ != kWebMUnknownSize && |
- list_state.size_ < list_state.bytes_parsed_ + total_element_size) { |
- return -1; |
- } |
- |
- if (id_type == LIST) { |
- list_state.bytes_parsed_ += header_size; |
- |
- if (!OnListStart(id, element_size, client)) |
- return -1; |
- return header_size; |
- } |
- |
- // Make sure we have the entire element before trying to parse a non-list |
- // element. |
- if (size < element_size) |
+ if (element_size > cur_size) |
return 0; |
- int bytes_parsed = ParseNonListElement(id_type, id, element_size, |
- data, size, client); |
- DCHECK_LE(bytes_parsed, size); |
+ if (element_size > 0) { |
+ result = ParseElementList(cur, element_size, element_id, level, client); |
- // Return if an error occurred or we need more data. |
- // Note: bytes_parsed is 0 for a successful parse of a size 0 element. We |
- // need to check the element_size to disambiguate the "need more data" case |
- // from a successful parse. |
- if (bytes_parsed < 0 || (bytes_parsed == 0 && element_size != 0)) |
- return bytes_parsed; |
+ if (result <= 0) |
+ return result; |
- int result = header_size + bytes_parsed; |
- list_state.bytes_parsed_ += result; |
- |
- // See if we have reached the end of the current list. |
- if (list_state.bytes_parsed_ == list_state.size_) { |
- if (!OnListEnd(client)) |
- return -1; |
+ cur += result; |
+ cur_size -= result; |
+ bytes_parsed += result; |
} |
- return result; |
+ return bytes_parsed; |
} |
-bool WebMListParser::OnListStart(int id, int64 size, WebMParserClient* client) { |
- ListState list_state = { id, size, 0, FindListInfo(id)}; |
- |
- if (!list_state.element_info_) |
- return false; |
- |
- int current_level = root_level_ + list_state_stack_.size() - 1; |
- if (current_level + 1 != list_state.element_info_->level_) |
- return false; |
- |
- if (!list_state_stack_.empty()) { |
- |
- // Make sure the new list doesn't go past the end of the current list. |
- ListState current_list = list_state_stack_.back(); |
- if (current_list.size_ != kWebMUnknownSize && |
- current_list.size_ < current_list.bytes_parsed_ + size) |
- return false; |
- } |
- |
- if (!client->OnListStart(id)) |
- return false; |
- |
- list_state_stack_.push_back(list_state); |
- |
- if (size == 0) { |
- return OnListEnd(client); |
- } |
- |
- return true; |
-} |
- |
-bool WebMListParser::OnListEnd(WebMParserClient* client) { |
- int lists_ended = 0; |
- for (; !list_state_stack_.empty(); ++lists_ended) { |
- const ListState& list_state = list_state_stack_.back(); |
- |
- if (list_state.bytes_parsed_ != list_state.size_) |
- break; |
- |
- if (!client->OnListEnd(list_state.id_)) |
- return false; |
- |
- int64 bytes_parsed = list_state.bytes_parsed_; |
- list_state_stack_.pop_back(); |
- |
- if (!list_state_stack_.empty()) { |
- // Update the bytes_parsed_ for the parent element. |
- list_state_stack_.back().bytes_parsed_ += bytes_parsed; |
- } |
- } |
- |
- DCHECK_GE(lists_ended, 1); |
- |
- if (list_state_stack_.empty()) |
- ChangeState(DONE_PARSING_LIST); |
- |
- return true; |
-} |
- |
} // namespace media |