Commit 31bc2039 authored by acolwell@chromium.org's avatar acolwell@chromium.org

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
parent 8453535e
...@@ -130,7 +130,7 @@ void ChunkDemuxerStream::Flush() { ...@@ -130,7 +130,7 @@ void ChunkDemuxerStream::Flush() {
bool ChunkDemuxerStream::CanAddBuffers(const BufferQueue& buffers) const { bool ChunkDemuxerStream::CanAddBuffers(const BufferQueue& buffers) const {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
// If we haven't seen any buffers yet than anything can be added. // If we haven't seen any buffers yet, then anything can be added.
if (last_buffer_timestamp_ == kNoTimestamp) if (last_buffer_timestamp_ == kNoTimestamp)
return true; return true;
...@@ -434,7 +434,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { ...@@ -434,7 +434,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
int cur_size = 0; int cur_size = 0;
int bytes_parsed = 0; int bytes_parsed = 0;
int result = -1; int result = -1;
bool parsed_a_cluster = false; bool can_complete_seek = false;
byte_queue_.Peek(&cur, &cur_size); byte_queue_.Peek(&cur, &cur_size);
...@@ -449,16 +449,19 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { ...@@ -449,16 +449,19 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
} }
break; break;
case INITIALIZED: case INITIALIZED: {
result = ParseCluster_Locked(cur, cur_size); bool buffers_added = false;
result = ParseCluster_Locked(cur, cur_size, &buffers_added);
if (result < 0) { if (result < 0) {
VLOG(1) << "AppendData(): parsing data failed"; VLOG(1) << "AppendData(): parsing data failed";
ReportError_Locked(PIPELINE_ERROR_DECODE); ReportError_Locked(PIPELINE_ERROR_DECODE);
return true; return true;
} }
parsed_a_cluster = (result > 0); // We can complete the seek if we have successfully parsed
break; // some data and buffers were added to one of the DemuxerStreams.
can_complete_seek |= (result > 0 && buffers_added);
} break;
case WAITING_FOR_INIT: case WAITING_FOR_INIT:
case ENDED: case ENDED:
...@@ -477,7 +480,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { ...@@ -477,7 +480,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
byte_queue_.Pop(bytes_parsed); byte_queue_.Pop(bytes_parsed);
if (parsed_a_cluster && seek_waits_for_data_) { if (can_complete_seek && seek_waits_for_data_) {
seek_waits_for_data_ = false; seek_waits_for_data_ = false;
if (!seek_cb_.is_null()) if (!seek_cb_.is_null())
...@@ -730,7 +733,8 @@ bool ChunkDemuxer::SetupStreams() { ...@@ -730,7 +733,8 @@ bool ChunkDemuxer::SetupStreams() {
return !no_supported_streams; return !no_supported_streams;
} }
int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) { int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size,
bool* buffers_added) {
lock_.AssertAcquired(); lock_.AssertAcquired();
if (!cluster_parser_.get()) if (!cluster_parser_.get())
return -1; return -1;
...@@ -749,9 +753,6 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) { ...@@ -749,9 +753,6 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) {
} }
// Skip the element. // Skip the element.
return result + element_size; return result + element_size;
} else if (id != kWebMIdCluster) {
VLOG(1) << "Unexpected ID 0x" << std::hex << id;
return -1;
} }
int bytes_parsed = cluster_parser_->Parse(data, size); int bytes_parsed = cluster_parser_->Parse(data, size);
...@@ -759,7 +760,9 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) { ...@@ -759,7 +760,9 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) {
if (bytes_parsed <= 0) if (bytes_parsed <= 0)
return bytes_parsed; return bytes_parsed;
// Make sure we can add the buffers to both streams before we actutally if (!cluster_parser_->audio_buffers().empty() ||
!cluster_parser_->video_buffers().empty()) {
// Make sure we can add the buffers to both streams before we actually
// add them. This allows us to accept all of the data or none of it. // add them. This allows us to accept all of the data or none of it.
if ((audio_.get() && if ((audio_.get() &&
!audio_->CanAddBuffers(cluster_parser_->audio_buffers())) || !audio_->CanAddBuffers(cluster_parser_->audio_buffers())) ||
...@@ -774,6 +777,9 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) { ...@@ -774,6 +777,9 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) {
if (video_.get()) if (video_.get())
video_->AddBuffers(cluster_parser_->video_buffers()); video_->AddBuffers(cluster_parser_->video_buffers());
*buffers_added = true;
}
// TODO(acolwell) : make this more representative of what is actually // TODO(acolwell) : make this more representative of what is actually
// buffered. // buffered.
buffered_bytes_ += bytes_parsed; buffered_bytes_ += bytes_parsed;
......
...@@ -81,13 +81,18 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { ...@@ -81,13 +81,18 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// found. // found.
bool SetupStreams(); bool SetupStreams();
// Parse a cluster add add the buffers to the appropriate DemxuerStream. // Parse a cluster and add the buffers to the appropriate DemuxerStream. This
// |data| is expected to point to the beginning of a cluster element. // method also skips over CUES elements if it happens to encounter them.
//
// |data| is expected to point to the beginning of an element.
//
// |buffers_added| - Indicates whether Buffers were added to DemuxerStreams
// during the call. This is only valid if the return value > 0.
// //
// Returns -1 if the parse fails. // Returns -1 if the parse fails.
// Returns 0 if more data is needed. // Returns 0 if more data is needed.
// Returns the number of bytes parsed on success. // Returns the number of bytes parsed on success.
int ParseCluster_Locked(const uint8* data, int size); int ParseCluster_Locked(const uint8* data, int size, bool* buffers_added);
// Reports an error and puts the demuxer in a state where it won't accept more // Reports an error and puts the demuxer in a state where it won't accept more
// data. // data.
......
...@@ -621,6 +621,7 @@ ...@@ -621,6 +621,7 @@
'video/capture/video_capture_device_unittest.cc', 'video/capture/video_capture_device_unittest.cc',
'webm/cluster_builder.cc', 'webm/cluster_builder.cc',
'webm/cluster_builder.h', 'webm/cluster_builder.h',
'webm/webm_parser_unittest.cc',
], ],
'conditions': [ 'conditions': [
['os_posix==1 and OS!="mac"', { ['os_posix==1 and OS!="mac"', {
......
...@@ -26,6 +26,7 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale, ...@@ -26,6 +26,7 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale,
audio_default_duration_(audio_default_duration), audio_default_duration_(audio_default_duration),
video_track_num_(video_track_num), video_track_num_(video_track_num),
video_default_duration_(video_default_duration), video_default_duration_(video_default_duration),
parser_(kWebMIdCluster),
last_block_timecode_(-1), last_block_timecode_(-1),
cluster_timecode_(-1) { cluster_timecode_(-1) {
} }
...@@ -33,12 +34,25 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale, ...@@ -33,12 +34,25 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale,
WebMClusterParser::~WebMClusterParser() {} WebMClusterParser::~WebMClusterParser() {}
int WebMClusterParser::Parse(const uint8* buf, int size) { int WebMClusterParser::Parse(const uint8* buf, int size) {
last_block_timecode_ = -1;
cluster_timecode_ = -1;
audio_buffers_.clear(); audio_buffers_.clear();
video_buffers_.clear(); video_buffers_.clear();
return WebMParseListElement(buf, size, kWebMIdCluster, 1, this); int result = parser_.Parse(buf, size, this);
if (result <= 0)
return result;
if (parser_.IsParsingComplete()) {
// Reset the parser if we're done parsing so that
// it is ready to accept another cluster on the next
// call.
parser_.Reset();
last_block_timecode_ = -1;
cluster_timecode_ = -1;
}
return result;
} }
bool WebMClusterParser::OnListStart(int id) { bool WebMClusterParser::OnListStart(int id) {
...@@ -67,17 +81,17 @@ bool WebMClusterParser::OnUInt(int id, int64 val) { ...@@ -67,17 +81,17 @@ bool WebMClusterParser::OnUInt(int id, int64 val) {
} }
bool WebMClusterParser::OnFloat(int id, double val) { bool WebMClusterParser::OnFloat(int id, double val) {
VLOG(1) << "Unexpected float element with ID " << std::hex << id; DVLOG(1) << "Unexpected float element with ID " << std::hex << id;
return false; return false;
} }
bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) { bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) {
VLOG(1) << "Unexpected binary element with ID " << std::hex << id; DVLOG(1) << "Unexpected binary element with ID " << std::hex << id;
return false; return false;
} }
bool WebMClusterParser::OnString(int id, const std::string& str) { bool WebMClusterParser::OnString(int id, const std::string& str) {
VLOG(1) << "Unexpected string element with ID " << std::hex << id; DVLOG(1) << "Unexpected string element with ID " << std::hex << id;
return false; return false;
} }
...@@ -85,17 +99,17 @@ bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode, ...@@ -85,17 +99,17 @@ bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode,
int flags, int flags,
const uint8* data, int size) { const uint8* data, int size) {
if (cluster_timecode_ == -1) { if (cluster_timecode_ == -1) {
VLOG(1) << "Got SimpleBlock before cluster timecode."; DVLOG(1) << "Got SimpleBlock before cluster timecode.";
return false; return false;
} }
if (timecode < 0) { if (timecode < 0) {
VLOG(1) << "Got SimpleBlock with negative timecode offset " << timecode; DVLOG(1) << "Got SimpleBlock with negative timecode offset " << timecode;
return false; return false;
} }
if (last_block_timecode_ != -1 && timecode < last_block_timecode_) { if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
VLOG(1) << "Got SimpleBlock with a timecode before the previous block."; DVLOG(1) << "Got SimpleBlock with a timecode before the previous block.";
return false; return false;
} }
...@@ -115,13 +129,13 @@ bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode, ...@@ -115,13 +129,13 @@ bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode,
buffer->SetDuration(video_default_duration_); buffer->SetDuration(video_default_duration_);
queue = &video_buffers_; queue = &video_buffers_;
} else { } else {
VLOG(1) << "Unexpected track number " << track_num; DVLOG(1) << "Unexpected track number " << track_num;
return false; return false;
} }
if (!queue->empty() && if (!queue->empty() &&
buffer->GetTimestamp() == queue->back()->GetTimestamp()) { buffer->GetTimestamp() == queue->back()->GetTimestamp()) {
VLOG(1) << "Got SimpleBlock timecode is not strictly monotonically " DVLOG(1) << "Got SimpleBlock timecode is not strictly monotonically "
<< "increasing for track " << track_num; << "increasing for track " << track_num;
return false; return false;
} }
......
...@@ -53,6 +53,8 @@ class WebMClusterParser : public WebMParserClient { ...@@ -53,6 +53,8 @@ class WebMClusterParser : public WebMParserClient {
int video_track_num_; int video_track_num_;
base::TimeDelta video_default_duration_; base::TimeDelta video_default_duration_;
WebMListParser parser_;
int64 last_block_timecode_; int64 last_block_timecode_;
int64 cluster_timecode_; int64 cluster_timecode_;
......
...@@ -64,6 +64,8 @@ const int kWebMIdVideo = 0xE0; ...@@ -64,6 +64,8 @@ const int kWebMIdVideo = 0xE0;
const int kWebMIdVoid = 0xEC; const int kWebMIdVoid = 0xEC;
const int kWebMIdWritingApp = 0x5741; const int kWebMIdWritingApp = 0x5741;
const int64 kWebMUnknownSize = GG_LONGLONG(0x00FFFFFFFFFFFFFF);
// Default timecode scale if the TimecodeScale element is // Default timecode scale if the TimecodeScale element is
// not specified in the INFO element. // not specified in the INFO element.
const int kWebMDefaultTimecodeScale = 1000000; const int kWebMDefaultTimecodeScale = 1000000;
......
...@@ -17,7 +17,17 @@ WebMInfoParser::WebMInfoParser() ...@@ -17,7 +17,17 @@ WebMInfoParser::WebMInfoParser()
WebMInfoParser::~WebMInfoParser() {} WebMInfoParser::~WebMInfoParser() {}
int WebMInfoParser::Parse(const uint8* buf, int size) { int WebMInfoParser::Parse(const uint8* buf, int size) {
return WebMParseListElement(buf, size, kWebMIdInfo, 1, this); timecode_scale_ = -1;
duration_ = -1;
WebMListParser parser(kWebMIdInfo);
int result = parser.Parse(buf, size, this);
if (result <= 0)
return result;
// For now we do all or nothing parsing.
return parser.IsParsingComplete() ? result : 0;
} }
bool WebMInfoParser::OnListStart(int id) { return true; } bool WebMInfoParser::OnListStart(int id) { return true; }
...@@ -36,7 +46,7 @@ bool WebMInfoParser::OnUInt(int id, int64 val) { ...@@ -36,7 +46,7 @@ bool WebMInfoParser::OnUInt(int id, int64 val) {
return true; return true;
if (timecode_scale_ != -1) { if (timecode_scale_ != -1) {
VLOG(1) << "Multiple values for id " << std::hex << id << " specified"; DVLOG(1) << "Multiple values for id " << std::hex << id << " specified";
return false; return false;
} }
...@@ -46,12 +56,12 @@ bool WebMInfoParser::OnUInt(int id, int64 val) { ...@@ -46,12 +56,12 @@ bool WebMInfoParser::OnUInt(int id, int64 val) {
bool WebMInfoParser::OnFloat(int id, double val) { bool WebMInfoParser::OnFloat(int id, double val) {
if (id != kWebMIdDuration) { if (id != kWebMIdDuration) {
VLOG(1) << "Unexpected float for id" << std::hex << id; DVLOG(1) << "Unexpected float for id" << std::hex << id;
return false; return false;
} }
if (duration_ != -1) { if (duration_ != -1) {
VLOG(1) << "Multiple values for duration."; DVLOG(1) << "Multiple values for duration.";
return false; return false;
} }
......
...@@ -20,6 +20,7 @@ namespace media { ...@@ -20,6 +20,7 @@ namespace media {
static const int kMaxLevelDepth = 6; static const int kMaxLevelDepth = 6;
enum ElementType { enum ElementType {
UNKNOWN,
LIST, LIST,
UINT, UINT,
FLOAT, FLOAT,
...@@ -30,13 +31,13 @@ enum ElementType { ...@@ -30,13 +31,13 @@ enum ElementType {
}; };
struct ElementIdInfo { struct ElementIdInfo {
int level_;
ElementType type_; ElementType type_;
int id_; int id_;
}; };
struct ListElementInfo { struct ListElementInfo {
int id_; int id_;
int level_;
const ElementIdInfo* id_info_; const ElementIdInfo* id_info_;
int id_info_size_; int id_info_size_;
}; };
...@@ -47,84 +48,83 @@ struct ListElementInfo { ...@@ -47,84 +48,83 @@ struct ListElementInfo {
// marked as SKIP because they are valid, but we don't care about them // marked as SKIP because they are valid, but we don't care about them
// right now. // right now.
static const ElementIdInfo kClusterIds[] = { static const ElementIdInfo kClusterIds[] = {
{2, UINT, kWebMIdTimecode}, {UINT, kWebMIdTimecode},
{2, SBLOCK, kWebMIdSimpleBlock}, {SBLOCK, kWebMIdSimpleBlock},
{2, LIST, kWebMIdBlockGroup}, {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[] = { static const ElementIdInfo kInfoIds[] = {
{2, SKIP, kWebMIdSegmentUID}, {SKIP, kWebMIdSegmentUID},
{2, UINT, kWebMIdTimecodeScale}, {UINT, kWebMIdTimecodeScale},
{2, FLOAT, kWebMIdDuration}, {FLOAT, kWebMIdDuration},
{2, SKIP, kWebMIdDateUTC}, {SKIP, kWebMIdDateUTC},
{2, SKIP, kWebMIdTitle}, {SKIP, kWebMIdTitle},
{2, SKIP, kWebMIdMuxingApp}, {SKIP, kWebMIdMuxingApp},
{2, SKIP, kWebMIdWritingApp}, {SKIP, kWebMIdWritingApp},
}; };
static const ElementIdInfo kTracksIds[] = { static const ElementIdInfo kTracksIds[] = {
{2, LIST, kWebMIdTrackEntry}, {LIST, kWebMIdTrackEntry},
}; };
static const ElementIdInfo kTrackEntryIds[] = { static const ElementIdInfo kTrackEntryIds[] = {
{3, UINT, kWebMIdTrackNumber}, {UINT, kWebMIdTrackNumber},
{3, SKIP, kWebMIdTrackUID}, {SKIP, kWebMIdTrackUID},
{3, UINT, kWebMIdTrackType}, {UINT, kWebMIdTrackType},
{3, SKIP, kWebMIdFlagEnabled}, {SKIP, kWebMIdFlagEnabled},
{3, SKIP, kWebMIdFlagDefault}, {SKIP, kWebMIdFlagDefault},
{3, SKIP, kWebMIdFlagForced}, {SKIP, kWebMIdFlagForced},
{3, UINT, kWebMIdFlagLacing}, {UINT, kWebMIdFlagLacing},
{3, UINT, kWebMIdDefaultDuration}, {UINT, kWebMIdDefaultDuration},
{3, SKIP, kWebMIdName}, {SKIP, kWebMIdName},
{3, SKIP, kWebMIdLanguage}, {SKIP, kWebMIdLanguage},
{3, STRING, kWebMIdCodecID}, {STRING, kWebMIdCodecID},
{3, BINARY, kWebMIdCodecPrivate}, {BINARY, kWebMIdCodecPrivate},
{3, SKIP, kWebMIdCodecName}, {SKIP, kWebMIdCodecName},
{3, LIST, kWebMIdVideo}, {LIST, kWebMIdVideo},
{3, LIST, kWebMIdAudio}, {LIST, kWebMIdAudio},
}; };
static const ElementIdInfo kVideoIds[] = { static const ElementIdInfo kVideoIds[] = {
{4, SKIP, kWebMIdFlagInterlaced}, {SKIP, kWebMIdFlagInterlaced},
{4, SKIP, kWebMIdStereoMode}, {SKIP, kWebMIdStereoMode},
{4, UINT, kWebMIdPixelWidth}, {UINT, kWebMIdPixelWidth},
{4, UINT, kWebMIdPixelHeight}, {UINT, kWebMIdPixelHeight},
{4, SKIP, kWebMIdPixelCropBottom}, {SKIP, kWebMIdPixelCropBottom},
{4, SKIP, kWebMIdPixelCropTop}, {SKIP, kWebMIdPixelCropTop},
{4, SKIP, kWebMIdPixelCropLeft}, {SKIP, kWebMIdPixelCropLeft},
{4, SKIP, kWebMIdPixelCropRight}, {SKIP, kWebMIdPixelCropRight},
{4, SKIP, kWebMIdDisplayWidth}, {SKIP, kWebMIdDisplayWidth},
{4, SKIP, kWebMIdDisplayHeight}, {SKIP, kWebMIdDisplayHeight},
{4, SKIP, kWebMIdDisplayUnit}, {SKIP, kWebMIdDisplayUnit},
{4, SKIP, kWebMIdAspectRatioType}, {SKIP, kWebMIdAspectRatioType},
}; };
static const ElementIdInfo kAudioIds[] = { static const ElementIdInfo kAudioIds[] = {
{4, SKIP, kWebMIdSamplingFrequency}, {SKIP, kWebMIdSamplingFrequency},
{4, SKIP, kWebMIdOutputSamplingFrequency}, {SKIP, kWebMIdOutputSamplingFrequency},
{4, UINT, kWebMIdChannels}, {UINT, kWebMIdChannels},
{4, SKIP, kWebMIdBitDepth}, {SKIP, kWebMIdBitDepth},
};
static const ElementIdInfo kClustersOnly[] = {
{1, LIST, kWebMIdCluster},
}; };
static const ListElementInfo kListElementInfo[] = { static const ListElementInfo kListElementInfo[] = {
{ kWebMIdCluster, kClusterIds, sizeof(kClusterIds) }, { kWebMIdCluster, 1, kClusterIds, sizeof(kClusterIds) },
{ kWebMIdInfo, kInfoIds, sizeof(kInfoIds) }, { kWebMIdSegment, 0, kSegmentIds, sizeof(kSegmentIds) },
{ kWebMIdTracks, kTracksIds, sizeof(kTracksIds) }, { kWebMIdInfo, 1, kInfoIds, sizeof(kInfoIds) },
{ kWebMIdTrackEntry, kTrackEntryIds, sizeof(kTrackEntryIds) }, { kWebMIdTracks, 1, kTracksIds, sizeof(kTracksIds) },
{ kWebMIdVideo, kVideoIds, sizeof(kVideoIds) }, { kWebMIdTrackEntry, 2, kTrackEntryIds, sizeof(kTrackEntryIds) },
{ kWebMIdAudio, kAudioIds, sizeof(kAudioIds) }, { 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 // Parses an element header id or size field. These fields are variable length
// encoded. The first byte indicates how many bytes the field occupies. // encoded. The first byte indicates how many bytes the field occupies.
// |buf| - The buffer to parse. // |buf| - The buffer to parse.
...@@ -206,22 +206,27 @@ int WebMParseElementHeader(const uint8* buf, int size, ...@@ -206,22 +206,27 @@ int WebMParseElementHeader(const uint8* buf, int size,
return num_id_bytes + num_size_bytes; return num_id_bytes + num_size_bytes;
} }
// Finds ElementIdInfo for a specific ID. // Finds ElementType for a specific ID.
static const ElementIdInfo* FindIdInfo(int id, static ElementType FindIdType(int id,
const ElementIdInfo* id_info, const ElementIdInfo* id_info,
int id_info_size) { 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); int count = id_info_size / sizeof(*id_info);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
if (id == id_info[i].id_) 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. // Finds ListElementInfo for a specific ID.
static const ListElementInfo* FindListInfo(int 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_) if (id == kListElementInfo[i].id_)
return &kListElementInfo[i]; return &kListElementInfo[i];
} }
...@@ -237,7 +242,7 @@ static int ParseSimpleBlock(const uint8* buf, int size, ...@@ -237,7 +242,7 @@ static int ParseSimpleBlock(const uint8* buf, int size,
// Return an error if the trackNum > 127. We just aren't // Return an error if the trackNum > 127. We just aren't
// going to support large track numbers right now. // going to support large track numbers right now.
if ((buf[0] & 0x80) != 0x80) { if ((buf[0] & 0x80) != 0x80) {
VLOG(1) << "TrackNumber over 127 not supported"; DVLOG(1) << "TrackNumber over 127 not supported";
return -1; return -1;
} }
...@@ -247,7 +252,7 @@ static int ParseSimpleBlock(const uint8* buf, int size, ...@@ -247,7 +252,7 @@ static int ParseSimpleBlock(const uint8* buf, int size,
int lacing = (flags >> 1) & 0x3; int lacing = (flags >> 1) & 0x3;
if (lacing != 0) { if (lacing != 0) {
VLOG(1) << "Lacing " << lacing << " not supported yet."; DVLOG(1) << "Lacing " << lacing << " not supported yet.";
return -1; return -1;
} }
...@@ -265,39 +270,6 @@ static int ParseSimpleBlock(const uint8* buf, int size, ...@@ -265,39 +270,6 @@ static int ParseSimpleBlock(const uint8* buf, int size,
return 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, static int ParseUInt(const uint8* buf, int size, int id,
WebMParserClient* client) { WebMParserClient* client) {
...@@ -354,104 +326,80 @@ static int ParseFloat(const uint8* buf, int size, int id, ...@@ -354,104 +326,80 @@ static int ParseFloat(const uint8* buf, int size, int id,
return size; return size;
} }
static int ParseElements(const ElementIdInfo* id_info, static int ParseNonListElement(ElementType type, int id, int64 element_size,
int id_info_size, const uint8* buf, int size,
const uint8* buf, int size, int level,
WebMParserClient* client) { WebMParserClient* client) {
DCHECK_GE(id_info_size, 0); DCHECK_GE(size, element_size);
DCHECK_GE(size, 0);
DCHECK_GE(level, 0);
const uint8* cur = buf;
int cur_size = size;
int used = 0;
if (level > kMaxLevelDepth)
return -1;
while (cur_size > 0) {
int id = 0;
int64 element_size = 0;
int result = WebMParseElementHeader(cur, cur_size, &id, &element_size);
if (result <= 0)
return result;
cur += result; int result = -1;
cur_size -= result; switch(type) {
used += result;
// Check to see if the element is larger than the remaining data.
if (element_size > cur_size)
return 0;
const ElementIdInfo* info = FindIdInfo(id, id_info, id_info_size);
if (info == NULL) {
VLOG(1) << "No info for ID " << std::hex << id;
// 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 (info->level_ != level) {
VLOG(1) << "ID " << std::hex << id << std::dec << " at level "
<< level << " instead of " << info->level_;
return -1;
}
switch(info->type_) {
case SBLOCK: case SBLOCK:
if (ParseSimpleBlock(cur, element_size, client) <= 0) result = ParseSimpleBlock(buf, element_size, client);
return -1;
break; break;
case LIST: case LIST:
if (ParseElementList(cur, element_size, id, level, client) < 0) NOTIMPLEMENTED();
return -1; result = -1;
break; break;
case UINT: case UINT:
if (ParseUInt(cur, element_size, id, client) <= 0) result = ParseUInt(buf, element_size, id, client);
return -1;
break; break;
case FLOAT: case FLOAT:
if (ParseFloat(cur, element_size, id, client) <= 0) result = ParseFloat(buf, element_size, id, client);
return -1;
break; break;
case BINARY: case BINARY:
if (!client->OnBinary(id, cur, element_size)) if (client->OnBinary(id, buf, element_size)) {
return -1; result = element_size;
} else {
result = -1;
}
break; break;
case STRING: case STRING:
if (!client->OnString(id, if (client->OnString(id,
std::string(reinterpret_cast<const char*>(cur), std::string(reinterpret_cast<const char*>(buf),
element_size))) element_size))) {
return -1; result = element_size;
} else {
result = -1;
}
break; break;
case SKIP: case SKIP:
// Do nothing. result = element_size;
break; break;
default: default:
VLOG(1) << "Unhandled id type " << info->type_; DVLOG(1) << "Unhandled ID type " << type;
return -1; return -1;
}; };
cur += element_size; DCHECK_LE(result, size);
cur_size -= element_size; return result;
used += element_size; }
}
WebMParserClient::WebMParserClient() {}
WebMParserClient::~WebMParserClient() {}
WebMListParser::WebMListParser(int id)
: state_(NEED_LIST_HEADER),
root_id_(id) {
const ListElementInfo* list_info = FindListInfo(id);
return used; DCHECK(list_info);
root_level_ = list_info->level_;
} }
// Parses a single list element that matches |id|. This method fails if the WebMListParser::~WebMListParser() {}
// buffer points to an element that does not match |id|.
int WebMParseListElement(const uint8* buf, int size, int id, void WebMListParser::Reset() {
int level, WebMParserClient* client) { ChangeState(NEED_LIST_HEADER);
if (size < 0) list_state_stack_.clear();
}
int WebMListParser::Parse(const uint8* buf, int size,
WebMParserClient* client) {
DCHECK(buf);
DCHECK(client);
if (size < 0 || state_ == PARSE_ERROR || state_ == DONE_PARSING_LIST)
return -1; return -1;
if (size == 0) if (size == 0)
...@@ -460,36 +408,203 @@ int WebMParseListElement(const uint8* buf, int size, int id, ...@@ -460,36 +408,203 @@ int WebMParseListElement(const uint8* buf, int size, int id,
const uint8* cur = buf; const uint8* cur = buf;
int cur_size = size; int cur_size = size;
int bytes_parsed = 0; int bytes_parsed = 0;
while (cur_size > 0 && state_ != PARSE_ERROR && state_ != DONE_PARSING_LIST) {
int element_id = 0; int element_id = 0;
int64 element_size = 0; int64 element_size = 0;
int result = WebMParseElementHeader(cur, cur_size, &element_id, int result = WebMParseElementHeader(cur, cur_size, &element_id,
&element_size); &element_size);
if (result <= 0) if (result < 0)
return result; return result;
if (result == 0)
return bytes_parsed;
switch(state_) {
case NEED_LIST_HEADER: {
if (element_id != root_id_) {
ChangeState(PARSE_ERROR);
return -1;
}
// TODO(acolwell): Add support for lists of unknown size.
if (element_size == kWebMUnknownSize) {
ChangeState(PARSE_ERROR);
return -1;
}
ChangeState(INSIDE_LIST);
if (!OnListStart(root_id_, element_size, client))
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);
return -1;
}
if (result == 0)
return bytes_parsed;
break;
}
case DONE_PARSING_LIST:
case PARSE_ERROR:
// Shouldn't be able to get here.
NOTIMPLEMENTED();
break;
}
cur += result; cur += result;
cur_size -= result; cur_size -= result;
bytes_parsed += result; bytes_parsed += result;
}
if (element_id != id) return (state_ == PARSE_ERROR) ? -1 : bytes_parsed;
}
bool WebMListParser::IsParsingComplete() const {
return state_ == DONE_PARSING_LIST;
}
void WebMListParser::ChangeState(State new_state) {
state_ = new_state;
}
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);
ListState& list_state = list_state_stack_.back();
DCHECK(list_state.element_info_);
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; return -1;
}
if (element_size > cur_size) // 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)
return 0; return 0;
if (element_size > 0) { int bytes_parsed = ParseNonListElement(id_type, id, element_size,
result = ParseElementList(cur, element_size, element_id, level, client); data, size, client);
DCHECK_LE(bytes_parsed, size);
// 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;
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;
}
if (result <= 0)
return result; return result;
}
cur += result; bool WebMListParser::OnListStart(int id, int64 size, WebMParserClient* client) {
cur_size -= result; ListState list_state = { id, size, 0, FindListInfo(id)};
bytes_parsed += result;
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 } // namespace media
...@@ -6,8 +6,10 @@ ...@@ -6,8 +6,10 @@
#define MEDIA_WEBM_WEBM_PARSER_H_ #define MEDIA_WEBM_WEBM_PARSER_H_
#include <string> #include <string>
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "media/base/media_export.h"
namespace media { namespace media {
...@@ -21,7 +23,7 @@ namespace media { ...@@ -21,7 +23,7 @@ namespace media {
// indicates whether the parsed data is valid. If false is returned // indicates whether the parsed data is valid. If false is returned
// then the parse is immediately terminated and an error is reported by the // then the parse is immediately terminated and an error is reported by the
// parser. // parser.
class WebMParserClient { class MEDIA_EXPORT WebMParserClient {
public: public:
virtual ~WebMParserClient(); virtual ~WebMParserClient();
...@@ -34,6 +36,106 @@ class WebMParserClient { ...@@ -34,6 +36,106 @@ class WebMParserClient {
virtual bool OnSimpleBlock(int track_num, int timecode, virtual bool OnSimpleBlock(int track_num, int timecode,
int flags, int flags,
const uint8* data, int size) = 0; const uint8* data, int size) = 0;
protected:
WebMParserClient();
DISALLOW_COPY_AND_ASSIGN(WebMParserClient);
};
struct ListElementInfo;
// Parses a WebM list element and all of its children. This
// class supports incremental parsing of the list so Parse()
// can be called multiple times with pieces of the list.
// IsParsingComplete() will return true once the entire list has
// been parsed.
class MEDIA_EXPORT WebMListParser {
public:
// |id| - Element ID of the list we intend to parse.
explicit WebMListParser(int id);
~WebMListParser();
// Resets the state of the parser so it can start parsing a new list.
void Reset();
// Parses list data contained in |buf|.
// |client| Called as different elements in the list are parsed.
//
// Returns < 0 if the parse fails.
// Returns 0 if more data is needed.
// Returning > 0 indicates success & the number of bytes parsed.
int Parse(const uint8* buf, int size, WebMParserClient* client);
// Returns true if the entire list has been parsed.
bool IsParsingComplete() const;
private:
enum State {
NEED_LIST_HEADER,
INSIDE_LIST,
DONE_PARSING_LIST,
PARSE_ERROR,
};
struct ListState {
int id_;
int size_;
int bytes_parsed_;
const ListElementInfo* element_info_;
};
void ChangeState(State new_state);
// Parses a single element in the current list.
//
// |header_size| - The size of the element header
// |id| - The ID of the element being parsed.
// |element_size| - The size of the element body.
// |data| - Pointer to the element contents.
// |size| - Number of bytes in |data|
// |client| - Client to pass the parsed data to.
//
// Returns < 0 if the parse fails.
// Returns 0 if more data is needed.
// Returning > 0 indicates success & the number of bytes parsed.
int ParseListElement(int header_size,
int id, int64 element_size,
const uint8* data, int size,
WebMParserClient* client);
// Called when starting to parse a new list.
//
// |id| - The ID of the new list.
// |size| - The size of the new list.
// |client| - The client object to notify that a new list is being parsed.
//
// Returns true if this list can be started in the current context. False
// if starting this list causes some sort of parse error.
bool OnListStart(int id, int64 size, WebMParserClient* client);
// Called when the end of the current list has been reached. This may also
// signal the end of the current list's ancestors if the current list happens
// to be at the end of its parent.
//
// |client| - The client to notify about lists ending.
//
// Returns true if no errors occurred while ending this list(s).
bool OnListEnd(WebMParserClient* client);
State state_;
// Element ID passed to the constructor.
int root_id_;
// Element level for |root_id_|. Used to verify that elements appear at
// the correct level.
int root_level_;
// Stack of state for all the lists currently being parsed. Lists are
// added and removed from this stack as they are parsed.
std::vector<ListState> list_state_stack_;
DISALLOW_COPY_AND_ASSIGN(WebMListParser);
}; };
// Parses an element header & returns the ID and element size. // Parses an element header & returns the ID and element size.
...@@ -47,15 +149,6 @@ class WebMParserClient { ...@@ -47,15 +149,6 @@ class WebMParserClient {
int WebMParseElementHeader(const uint8* buf, int size, int WebMParseElementHeader(const uint8* buf, int size,
int* id, int64* element_size); int* id, int64* element_size);
// Parses a single list element that matches |id|. This method fails if the
// buffer points to an element that does not match |id|.
//
// Returns -1 if the parse fails.
// Returns 0 if more data is needed.
// Returns the number of bytes parsed on success.
int WebMParseListElement(const uint8* buf, int size, int id,
int level, WebMParserClient* client);
} // namespace media } // namespace media
#endif // MEDIA_WEBM_WEBM_PARSER_H_ #endif // MEDIA_WEBM_WEBM_PARSER_H_
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/webm/cluster_builder.h"
#include "media/webm/webm_constants.h"
#include "media/webm/webm_parser.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::InSequence;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::_;
namespace media {
class MockWebMParserClient : public WebMParserClient {
public:
virtual ~MockWebMParserClient() {}
// WebMParserClient methods.
MOCK_METHOD1(OnListStart, bool(int));
MOCK_METHOD1(OnListEnd, bool(int));
MOCK_METHOD2(OnUInt, bool(int, int64));
MOCK_METHOD2(OnFloat, bool(int, double));
MOCK_METHOD3(OnBinary, bool(int, const uint8*, int));
MOCK_METHOD2(OnString, bool(int, const std::string&));
MOCK_METHOD5(OnSimpleBlock, bool(int, int, int, const uint8*, int));
};
class WebMParserTest : public testing::Test {
protected:
StrictMock<MockWebMParserClient> client_;
};
struct SimpleBlockInfo {
int track_num;
int timestamp;
};
static void AddSimpleBlock(ClusterBuilder* cb, int track_num,
int64 timecode) {
uint8 data[] = { 0x00 };
cb->AddSimpleBlock(track_num, timecode, 0, data, sizeof(data));
}
static Cluster* CreateCluster(int timecode,
const SimpleBlockInfo* block_info,
int block_count) {
ClusterBuilder cb;
cb.SetClusterTimecode(0);
for (int i = 0; i < block_count; i++)
AddSimpleBlock(&cb, block_info[i].track_num, block_info[i].timestamp);
return cb.Finish();
}
static void CreateClusterExpectations(int timecode,
const SimpleBlockInfo* block_info,
int block_count,
MockWebMParserClient* client) {
InSequence s;
EXPECT_CALL(*client, OnListStart(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(*client, OnUInt(kWebMIdTimecode, 0)).WillOnce(Return(true));
for (int i = 0; i < block_count; i++) {
EXPECT_CALL(*client, OnSimpleBlock(block_info[i].track_num,
block_info[i].timestamp,
_, _, _))
.WillOnce(Return(true));
}
EXPECT_CALL(*client, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
}
TEST_F(WebMParserTest, EmptyCluster) {
const uint8 kEmptyCluster[] = {
0x1F, 0x43, 0xB6, 0x75, 0x80 // CLUSTER (size = 0)
};
int size = sizeof(kEmptyCluster);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
WebMListParser parser(kWebMIdCluster);
int result = parser.Parse(kEmptyCluster, size, &client_);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, EmptyClusterInSegment) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x85, // SEGMENT (size = 5)
0x1F, 0x43, 0xB6, 0x75, 0x80, // CLUSTER (size = 0)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
// Test the case where a non-list child element has a size
// that is beyond the end of the parent.
TEST_F(WebMParserTest, ChildNonListLargerThanParent) {
const uint8 kBuffer[] = {
0x1F, 0x43, 0xB6, 0x75, 0x81, // CLUSTER (size = 1)
0xE7, 0x81, 0x01, // Timecode (size=1, value=1)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(true));
WebMListParser parser(kWebMIdCluster);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
// Test the case where a list child element has a size
// that is beyond the end of the parent.
TEST_F(WebMParserTest, ChildListLargerThanParent) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x85, // SEGMENT (size = 5)
0x1F, 0x43, 0xB6, 0x75, 0x81, 0x11 // CLUSTER (size = 1)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
// Expecting to parse a Cluster, but get a Segment.
TEST_F(WebMParserTest, ListIdDoesNotMatch) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x80, // SEGMENT (size = 0)
};
int size = sizeof(kBuffer);
WebMListParser parser(kWebMIdCluster);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, InvalidElementInList) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x82, // SEGMENT (size = 2)
0xAE, 0x80, // TrackEntry (size = 0)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, VoidAndCRC32InList) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x99, // SEGMENT (size = 25)
0xEC, 0x83, 0x00, 0x00, 0x00, // Void (size = 3)
0xBF, 0x83, 0x00, 0x00, 0x00, // CRC32 (size = 3)
0x1F, 0x43, 0xB6, 0x75, 0x8A, // CLUSTER (size = 10)
0xEC, 0x83, 0x00, 0x00, 0x00, // Void (size = 3)
0xBF, 0x83, 0x00, 0x00, 0x00, // CRC32 (size = 3)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, ParseListElementWithSingleCall) {
const SimpleBlockInfo kBlockInfo[] = {
{ 0, 1 },
{ 1, 2 },
{ 0, 3 },
{ 0, 4 },
{ 1, 4 },
};
int block_count = arraysize(kBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
CreateClusterExpectations(0, kBlockInfo, block_count, &client_);
WebMListParser parser(kWebMIdCluster);
int result = parser.Parse(cluster->data(), cluster->size(), &client_);
EXPECT_EQ(cluster->size(), result);
EXPECT_TRUE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, ParseListElementWithMultipleCalls) {
const SimpleBlockInfo kBlockInfo[] = {
{ 0, 1 },
{ 1, 2 },
{ 0, 3 },
{ 0, 4 },
{ 1, 4 },
};
int block_count = arraysize(kBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
CreateClusterExpectations(0, kBlockInfo, block_count, &client_);
const uint8* data = cluster->data();
int size = cluster->size();
int default_parse_size = 3;
WebMListParser parser(kWebMIdCluster);
int parse_size = std::min(default_parse_size, size);
while (size > 0) {
int result = parser.Parse(data, parse_size, &client_);
EXPECT_GE(result, 0);
EXPECT_LE(result, parse_size);
if (result == 0) {
// The parser needs more data so increase the parse_size a little.
EXPECT_FALSE(parser.IsParsingComplete());
parse_size += default_parse_size;
parse_size = std::min(parse_size, size);
continue;
}
parse_size = default_parse_size;
data += result;
size -= result;
EXPECT_EQ((size == 0), parser.IsParsingComplete());
}
EXPECT_TRUE(parser.IsParsingComplete());
}
} // namespace media
...@@ -9,21 +9,39 @@ ...@@ -9,21 +9,39 @@
namespace media { namespace media {
static const base::TimeDelta kNoDuration =
base::TimeDelta::FromMicroseconds(-1);
WebMTracksParser::WebMTracksParser(int64 timecode_scale) WebMTracksParser::WebMTracksParser(int64 timecode_scale)
: timecode_scale_(timecode_scale), : timecode_scale_(timecode_scale),
track_type_(-1), track_type_(-1),
track_num_(-1), track_num_(-1),
track_default_duration_(-1), track_default_duration_(-1),
audio_track_num_(-1), audio_track_num_(-1),
audio_default_duration_(base::TimeDelta::FromMicroseconds(-1)), audio_default_duration_(kNoDuration),
video_track_num_(-1), video_track_num_(-1),
video_default_duration_(base::TimeDelta::FromMicroseconds(-1)) { video_default_duration_(kNoDuration) {
} }
WebMTracksParser::~WebMTracksParser() {} WebMTracksParser::~WebMTracksParser() {}
int WebMTracksParser::Parse(const uint8* buf, int size) { int WebMTracksParser::Parse(const uint8* buf, int size) {
return WebMParseListElement(buf, size, kWebMIdTracks, 1, this); track_type_ =-1;
track_num_ = -1;
track_default_duration_ = -1;
audio_track_num_ = -1;
audio_default_duration_ = kNoDuration;
video_track_num_ = -1;
video_default_duration_ = kNoDuration;
WebMListParser parser(kWebMIdTracks);
int result = parser.Parse(buf, size, this);
if (result <= 0)
return result;
// For now we do all or nothing parsing.
return parser.IsParsingComplete() ? result : 0;
} }
...@@ -40,7 +58,7 @@ bool WebMTracksParser::OnListStart(int id) { ...@@ -40,7 +58,7 @@ bool WebMTracksParser::OnListStart(int id) {
bool WebMTracksParser::OnListEnd(int id) { bool WebMTracksParser::OnListEnd(int id) {
if (id == kWebMIdTrackEntry) { if (id == kWebMIdTrackEntry) {
if (track_type_ == -1 || track_num_ == -1) { if (track_type_ == -1 || track_num_ == -1) {
VLOG(1) << "Missing TrackEntry data" DVLOG(1) << "Missing TrackEntry data"
<< " TrackType " << track_type_ << " TrackType " << track_type_
<< " TrackNum " << track_num_; << " TrackNum " << track_num_;
return false; return false;
...@@ -57,7 +75,7 @@ bool WebMTracksParser::OnListEnd(int id) { ...@@ -57,7 +75,7 @@ bool WebMTracksParser::OnListEnd(int id) {
audio_track_num_ = track_num_; audio_track_num_ = track_num_;
audio_default_duration_ = default_duration; audio_default_duration_ = default_duration;
} else { } else {
VLOG(1) << "Unexpected TrackType " << track_type_; DVLOG(1) << "Unexpected TrackType " << track_type_;
return false; return false;
} }
...@@ -86,7 +104,7 @@ bool WebMTracksParser::OnUInt(int id, int64 val) { ...@@ -86,7 +104,7 @@ bool WebMTracksParser::OnUInt(int id, int64 val) {
} }
if (*dst != -1) { if (*dst != -1) {
VLOG(1) << "Multiple values for id " << std::hex << id << " specified"; DVLOG(1) << "Multiple values for id " << std::hex << id << " specified";
return false; return false;
} }
...@@ -95,7 +113,7 @@ bool WebMTracksParser::OnUInt(int id, int64 val) { ...@@ -95,7 +113,7 @@ bool WebMTracksParser::OnUInt(int id, int64 val) {
} }
bool WebMTracksParser::OnFloat(int id, double val) { bool WebMTracksParser::OnFloat(int id, double val) {
VLOG(1) << "Unexpected float for id" << std::hex << id; DVLOG(1) << "Unexpected float for id" << std::hex << id;
return false; return false;
} }
...@@ -108,7 +126,7 @@ bool WebMTracksParser::OnString(int id, const std::string& str) { ...@@ -108,7 +126,7 @@ bool WebMTracksParser::OnString(int id, const std::string& str) {
return false; return false;
if (str != "A_VORBIS" && str != "V_VP8") { if (str != "A_VORBIS" && str != "V_VP8") {
VLOG(1) << "Unexpected CodecID " << str; DVLOG(1) << "Unexpected CodecID " << str;
return false; return false;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment