summaryrefslogtreecommitdiffstats
path: root/media/webm/webm_parser.cc
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-12 17:43:04 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-12 17:43:04 +0000
commit31bc2039ba95532aafee8f028673c2a53d76b24a (patch)
treeb70c4e29d583cf1084675df8323f6375ef1c4f91 /media/webm/webm_parser.cc
parent8453535eedd7e7789fcc315436cead1a030312a4 (diff)
downloadchromium_src-31bc2039ba95532aafee8f028673c2a53d76b24a.zip
chromium_src-31bc2039ba95532aafee8f028673c2a53d76b24a.tar.gz
chromium_src-31bc2039ba95532aafee8f028673c2a53d76b24a.tar.bz2
Adding support for incremental cluster parsing.
BUG=104160 TEST=Covered by ChunkDemuxer unittests. Review URL: http://codereview.chromium.org/8775035 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@114030 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/webm/webm_parser.cc')
-rw-r--r--media/webm/webm_parser.cc519
1 files changed, 317 insertions, 202 deletions
diff --git a/media/webm/webm_parser.cc b/media/webm/webm_parser.cc
index 1e42815..1422308 100644
--- a/media/webm/webm_parser.cc
+++ b/media/webm/webm_parser.cc
@@ -20,6 +20,7 @@ namespace media {
static const int kMaxLevelDepth = 6;
enum ElementType {
+ UNKNOWN,
LIST,
UINT,
FLOAT,
@@ -30,13 +31,13 @@ enum ElementType {
};
struct ElementIdInfo {
- int level_;
ElementType type_;
int id_;
};
struct ListElementInfo {
int id_;
+ int level_;
const ElementIdInfo* id_info_;
int id_info_size_;
};
@@ -47,84 +48,83 @@ struct ListElementInfo {
// marked as SKIP because they are valid, but we don't care about them
// right now.
static const ElementIdInfo kClusterIds[] = {
- {2, UINT, kWebMIdTimecode},
- {2, SBLOCK, kWebMIdSimpleBlock},
- {2, LIST, kWebMIdBlockGroup},
+ {UINT, kWebMIdTimecode},
+ {SBLOCK, kWebMIdSimpleBlock},
+ {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[] = {
- {2, SKIP, kWebMIdSegmentUID},
- {2, UINT, kWebMIdTimecodeScale},
- {2, FLOAT, kWebMIdDuration},
- {2, SKIP, kWebMIdDateUTC},
- {2, SKIP, kWebMIdTitle},
- {2, SKIP, kWebMIdMuxingApp},
- {2, SKIP, kWebMIdWritingApp},
+ {SKIP, kWebMIdSegmentUID},
+ {UINT, kWebMIdTimecodeScale},
+ {FLOAT, kWebMIdDuration},
+ {SKIP, kWebMIdDateUTC},
+ {SKIP, kWebMIdTitle},
+ {SKIP, kWebMIdMuxingApp},
+ {SKIP, kWebMIdWritingApp},
};
static const ElementIdInfo kTracksIds[] = {
- {2, LIST, kWebMIdTrackEntry},
+ {LIST, kWebMIdTrackEntry},
};
static const ElementIdInfo kTrackEntryIds[] = {
- {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},
+ {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},
};
static const ElementIdInfo kVideoIds[] = {
- {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},
+ {SKIP, kWebMIdFlagInterlaced},
+ {SKIP, kWebMIdStereoMode},
+ {UINT, kWebMIdPixelWidth},
+ {UINT, kWebMIdPixelHeight},
+ {SKIP, kWebMIdPixelCropBottom},
+ {SKIP, kWebMIdPixelCropTop},
+ {SKIP, kWebMIdPixelCropLeft},
+ {SKIP, kWebMIdPixelCropRight},
+ {SKIP, kWebMIdDisplayWidth},
+ {SKIP, kWebMIdDisplayHeight},
+ {SKIP, kWebMIdDisplayUnit},
+ {SKIP, kWebMIdAspectRatioType},
};
static const ElementIdInfo kAudioIds[] = {
- {4, SKIP, kWebMIdSamplingFrequency},
- {4, SKIP, kWebMIdOutputSamplingFrequency},
- {4, UINT, kWebMIdChannels},
- {4, SKIP, kWebMIdBitDepth},
-};
-
-static const ElementIdInfo kClustersOnly[] = {
- {1, LIST, kWebMIdCluster},
+ {SKIP, kWebMIdSamplingFrequency},
+ {SKIP, kWebMIdOutputSamplingFrequency},
+ {UINT, kWebMIdChannels},
+ {SKIP, kWebMIdBitDepth},
};
static const ListElementInfo kListElementInfo[] = {
- { kWebMIdCluster, kClusterIds, sizeof(kClusterIds) },
- { kWebMIdInfo, kInfoIds, sizeof(kInfoIds) },
- { kWebMIdTracks, kTracksIds, sizeof(kTracksIds) },
- { kWebMIdTrackEntry, kTrackEntryIds, sizeof(kTrackEntryIds) },
- { kWebMIdVideo, kVideoIds, sizeof(kVideoIds) },
- { kWebMIdAudio, kAudioIds, sizeof(kAudioIds) },
+ { 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) },
};
-// 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,22 +206,27 @@ int WebMParseElementHeader(const uint8* buf, int size,
return num_id_bytes + num_size_bytes;
}
-// Finds ElementIdInfo for a specific ID.
-static const ElementIdInfo* FindIdInfo(int id,
- const ElementIdInfo* id_info,
- int id_info_size) {
+// 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;
+
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];
+ return id_info[i].type_;
}
- return NULL;
+ return UNKNOWN;
}
// Finds ListElementInfo for a specific ID.
static const ListElementInfo* FindListInfo(int id) {
- for (int i = 0; i < kListElementInfoCount; ++i) {
+ for (size_t i = 0; i < arraysize(kListElementInfo); ++i) {
if (id == kListElementInfo[i].id_)
return &kListElementInfo[i];
}
@@ -237,7 +242,7 @@ static int ParseSimpleBlock(const uint8* buf, int size,
// Return an error if the trackNum > 127. We just aren't
// going to support large track numbers right now.
if ((buf[0] & 0x80) != 0x80) {
- VLOG(1) << "TrackNumber over 127 not supported";
+ DVLOG(1) << "TrackNumber over 127 not supported";
return -1;
}
@@ -247,7 +252,7 @@ static int ParseSimpleBlock(const uint8* buf, int size,
int lacing = (flags >> 1) & 0x3;
if (lacing != 0) {
- VLOG(1) << "Lacing " << lacing << " not supported yet.";
+ DVLOG(1) << "Lacing " << lacing << " not supported yet.";
return -1;
}
@@ -265,39 +270,6 @@ static int ParseSimpleBlock(const uint8* buf, int size,
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) {
@@ -354,142 +326,285 @@ static int ParseFloat(const uint8* buf, int size, int id,
return 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);
+static int ParseNonListElement(ElementType type, int id, int64 element_size,
+ const uint8* buf, int size,
+ WebMParserClient* client) {
+ DCHECK_GE(size, element_size);
- const uint8* cur = buf;
- int cur_size = size;
- int used = 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;
+ };
- if (level > kMaxLevelDepth)
- return -1;
+ DCHECK_LE(result, size);
+ return result;
+}
- while (cur_size > 0) {
- int id = 0;
- int64 element_size = 0;
- int result = WebMParseElementHeader(cur, cur_size, &id, &element_size);
+WebMParserClient::WebMParserClient() {}
+WebMParserClient::~WebMParserClient() {}
- if (result <= 0)
- return result;
+WebMListParser::WebMListParser(int id)
+ : state_(NEED_LIST_HEADER),
+ root_id_(id) {
+ const ListElementInfo* list_info = FindListInfo(id);
- cur += result;
- cur_size -= result;
- used += result;
+ DCHECK(list_info);
- // Check to see if the element is larger than the remaining data.
- if (element_size > cur_size)
- return 0;
+ root_level_ = list_info->level_;
+}
- const ElementIdInfo* info = FindIdInfo(id, id_info, id_info_size);
+WebMListParser::~WebMListParser() {}
- if (info == NULL) {
- VLOG(1) << "No info for ID " << std::hex << id;
+void WebMListParser::Reset() {
+ ChangeState(NEED_LIST_HEADER);
+ list_state_stack_.clear();
+}
- // 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;
- }
+int WebMListParser::Parse(const uint8* buf, int size,
+ WebMParserClient* client) {
+ DCHECK(buf);
+ DCHECK(client);
- if (info->level_ != level) {
- VLOG(1) << "ID " << std::hex << id << std::dec << " at level "
- << level << " instead of " << info->level_;
- return -1;
- }
+ if (size < 0 || state_ == PARSE_ERROR || state_ == DONE_PARSING_LIST)
+ return -1;
- switch(info->type_) {
- case SBLOCK:
- if (ParseSimpleBlock(cur, element_size, client) <= 0)
- return -1;
- break;
- case LIST:
- if (ParseElementList(cur, element_size, id, level, client) < 0)
- return -1;
- break;
- case UINT:
- if (ParseUInt(cur, element_size, id, client) <= 0)
+ 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);
return -1;
- break;
- case FLOAT:
- if (ParseFloat(cur, element_size, id, client) <= 0)
+ }
+
+ // TODO(acolwell): Add support for lists of unknown size.
+ if (element_size == kWebMUnknownSize) {
+ ChangeState(PARSE_ERROR);
return -1;
- break;
- case BINARY:
- if (!client->OnBinary(id, cur, element_size))
+ }
+
+ ChangeState(INSIDE_LIST);
+ if (!OnListStart(root_id_, element_size, client))
return -1;
+
break;
- case STRING:
- if (!client->OnString(id,
- std::string(reinterpret_cast<const char*>(cur),
- element_size)))
+ }
+
+ 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);
return -1;
+ }
+
+ if (result == 0)
+ return bytes_parsed;
+
break;
- case SKIP:
- // Do nothing.
+ }
+ case DONE_PARSING_LIST:
+ case PARSE_ERROR:
+ // Shouldn't be able to get here.
+ NOTIMPLEMENTED();
break;
- default:
- VLOG(1) << "Unhandled id type " << info->type_;
- return -1;
- };
-
- cur += element_size;
- cur_size -= element_size;
- used += element_size;
+ }
+
+ cur += result;
+ cur_size -= result;
+ bytes_parsed += result;
}
- return used;
+ return (state_ == PARSE_ERROR) ? -1 : bytes_parsed;
}
-// 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;
+bool WebMListParser::IsParsingComplete() const {
+ return state_ == DONE_PARSING_LIST;
+}
- if (size == 0)
- return 0;
+void WebMListParser::ChangeState(State new_state) {
+ state_ = new_state;
+}
- 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);
+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);
- if (result <= 0)
- return result;
+ ListState& list_state = list_state_stack_.back();
+ DCHECK(list_state.element_info_);
- cur += result;
- cur_size -= result;
- bytes_parsed += result;
+ const ListElementInfo* element_info = list_state.element_info_;
+ ElementType id_type =
+ FindIdType(id, element_info->id_info_, element_info->id_info_size_);
+
+ // Unexpected ID.
+ if (id_type == UNKNOWN) {
+ DVLOG(1) << "No ElementType info for ID 0x" << std::hex << id;
+ return -1;
+ }
- if (element_id != id)
+ // 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;
+ }
- if (element_size > cur_size)
+ // Make sure we have the entire element before trying to parse a non-list
+ // element.
+ if (size < element_size)
return 0;
- if (element_size > 0) {
- result = ParseElementList(cur, element_size, element_id, level, client);
+ int bytes_parsed = ParseNonListElement(id_type, id, element_size,
+ data, size, client);
+ DCHECK_LE(bytes_parsed, size);
- if (result <= 0)
- return result;
+ // 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;
- cur += result;
- cur_size -= result;
- bytes_parsed += 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;
+ }
+
+ return result;
+}
+
+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;
}
- return bytes_parsed;
+ 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