diff options
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 32 | ||||
-rw-r--r-- | media/formats/webm/cluster_builder.cc | 11 | ||||
-rw-r--r-- | media/formats/webm/cluster_builder.h | 1 | ||||
-rw-r--r-- | media/formats/webm/webm_stream_parser.cc | 100 |
4 files changed, 96 insertions, 48 deletions
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 294d769..74f2a01 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -673,6 +673,13 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<bool> { scoped_ptr<Cluster> GenerateCluster(int first_audio_timecode, int first_video_timecode, int block_count) { + return GenerateCluster(first_audio_timecode, first_video_timecode, + block_count, false); + } + scoped_ptr<Cluster> GenerateCluster(int first_audio_timecode, + int first_video_timecode, + int block_count, + bool unknown_size) { CHECK_GT(block_count, 0); int size = 10; @@ -722,7 +729,7 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<bool> { kWebMFlagKeyframe, data.get(), size); } - return cb.Finish(); + return unknown_size ? cb.FinishWithUnknownSize() : cb.Finish(); } scoped_ptr<Cluster> GenerateSingleStreamCluster(int timecode, @@ -2893,11 +2900,14 @@ TEST_P(ChunkDemuxerTest, EmitBuffersDuringAbort) { #endif TEST_P(ChunkDemuxerTest, WebMIsParsingMediaSegmentDetection) { - // TODO(wolenetz): Also test 'unknown' sized clusters. - // See http://crbug.com/335676. const uint8 kBuffer[] = { 0x1F, 0x43, 0xB6, 0x75, 0x83, // CLUSTER (size = 3) 0xE7, 0x81, 0x01, // Cluster TIMECODE (value = 1) + + 0x1F, 0x43, 0xB6, 0x75, 0xFF, // CLUSTER (size = unknown; really 3 due to:) + 0xE7, 0x81, 0x02, // Cluster TIMECODE (value = 2) + /* e.g. put some blocks here... */ + 0x1A, 0x45, 0xDF, 0xA3, 0x8A, // EBMLHEADER (size = 10, not fully appended) }; // This array indicates expected return value of IsParsingMediaSegment() @@ -2905,6 +2915,11 @@ TEST_P(ChunkDemuxerTest, WebMIsParsingMediaSegmentDetection) { const bool kExpectedReturnValues[] = { false, false, false, false, true, true, true, false, + + false, false, false, false, true, + true, true, true, + + true, true, true, true, false, }; COMPILE_ASSERT(arraysize(kBuffer) == arraysize(kExpectedReturnValues), @@ -3454,6 +3469,17 @@ TEST_P(ChunkDemuxerTest, SeekCompletesWithoutTextCues) { CheckExpectedBuffers(video_stream, "180 210"); } +TEST_P(ChunkDemuxerTest, ClusterWithUnknownSize) { + ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO)); + + AppendCluster(GenerateCluster(0, 0, 4, true)); + CheckExpectedRanges(kSourceId, "{ [0,46) }"); + + // A new cluster indicates end of the previous cluster with unknown size. + AppendCluster(GenerateCluster(46, 66, 5, true)); + CheckExpectedRanges(kSourceId, "{ [0,115) }"); +} + // Generate two sets of tests: one using FrameProcessor, and one using // LegacyFrameProcessor. INSTANTIATE_TEST_CASE_P(NewFrameProcessor, ChunkDemuxerTest, Values(false)); diff --git a/media/formats/webm/cluster_builder.cc b/media/formats/webm/cluster_builder.cc index bada9e2..1a3b358 100644 --- a/media/formats/webm/cluster_builder.cc +++ b/media/formats/webm/cluster_builder.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "media/base/data_buffer.h" +#include "media/formats/webm/webm_constants.h" namespace media { @@ -180,6 +181,16 @@ scoped_ptr<Cluster> ClusterBuilder::Finish() { return ret.Pass(); } +scoped_ptr<Cluster> ClusterBuilder::FinishWithUnknownSize() { + DCHECK_NE(cluster_timecode_, -1); + + UpdateUInt64(kClusterSizeOffset, kWebMUnknownSize); + + scoped_ptr<Cluster> ret(new Cluster(buffer_.Pass(), bytes_used_)); + Reset(); + return ret.Pass(); +} + void ClusterBuilder::Reset() { buffer_size_ = kInitialBufferSize; buffer_.reset(new uint8[buffer_size_]); diff --git a/media/formats/webm/cluster_builder.h b/media/formats/webm/cluster_builder.h index ac5464a..ab5797c 100644 --- a/media/formats/webm/cluster_builder.h +++ b/media/formats/webm/cluster_builder.h @@ -40,6 +40,7 @@ class ClusterBuilder { int flags, const uint8* data, int size); scoped_ptr<Cluster> Finish(); + scoped_ptr<Cluster> FinishWithUnknownSize(); private: void AddBlockGroupInternal(int track_num, int64 timecode, diff --git a/media/formats/webm/webm_stream_parser.cc b/media/formats/webm/webm_stream_parser.cc index a90a623..3f370da 100644 --- a/media/formats/webm/webm_stream_parser.cc +++ b/media/formats/webm/webm_stream_parser.cc @@ -143,6 +143,7 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) { case kWebMIdCRC32: case kWebMIdCues: case kWebMIdChapters: + // TODO(matthewjheaney): Implement support for chapters. if (cur_size < (result + element_size)) { // We don't have the whole element yet. Signal we need more data. return 0; @@ -242,61 +243,70 @@ int WebMStreamParser::ParseCluster(const uint8* data, int size) { if (!cluster_parser_) return -1; - int id; - int64 element_size; - int result = WebMParseElementHeader(data, size, &id, &element_size); - - if (result <= 0) - return result; - - // TODO(matthewjheaney): implement support for chapters - if (id == kWebMIdCues || id == kWebMIdChapters) { - // TODO(wolenetz): Handle unknown-sized cluster parse completion correctly. - // See http://crbug.com/335676. - if (size < (result + element_size)) { - // We don't have the whole element yet. Signal we need more data. - return 0; + int result = 0; + int bytes_parsed; + bool cluster_ended; + do { + cluster_ended = false; + + // If we are not parsing a cluster then handle the case when the next + // element is not a cluster. + if (!parsing_cluster_) { + int id; + int64 element_size; + bytes_parsed = WebMParseElementHeader(data, size, &id, &element_size); + + if (bytes_parsed < 0) + return bytes_parsed; + + if (bytes_parsed == 0) + return result; + + if (id != kWebMIdCluster) { + ChangeState(kParsingHeaders); + return result; + } } - // Skip the element. - return result + element_size; - } - if (id == kWebMIdEBMLHeader) { - // TODO(wolenetz): Handle unknown-sized cluster parse completion correctly. - // See http://crbug.com/335676. - ChangeState(kParsingHeaders); - return 0; - } + bytes_parsed = cluster_parser_->Parse(data, size); - int bytes_parsed = cluster_parser_->Parse(data, size); + if (bytes_parsed < 0) + return bytes_parsed; - if (bytes_parsed <= 0) - return bytes_parsed; + // If cluster detected, immediately notify new segment if we have not + // already done this. + if (!parsing_cluster_ && bytes_parsed > 0) { + parsing_cluster_ = true; + new_segment_cb_.Run(); + } - // If cluster detected, immediately notify new segment if we have not already - // done this. - if (id == kWebMIdCluster && !parsing_cluster_) { - parsing_cluster_ = true; - new_segment_cb_.Run(); - } + const BufferQueue& audio_buffers = cluster_parser_->GetAudioBuffers(); + const BufferQueue& video_buffers = cluster_parser_->GetVideoBuffers(); + const TextBufferQueueMap& text_map = cluster_parser_->GetTextBuffers(); - const BufferQueue& audio_buffers = cluster_parser_->GetAudioBuffers(); - const BufferQueue& video_buffers = cluster_parser_->GetVideoBuffers(); - const TextBufferQueueMap& text_map = cluster_parser_->GetTextBuffers(); + cluster_ended = cluster_parser_->cluster_ended(); - bool cluster_ended = cluster_parser_->cluster_ended(); + if ((!audio_buffers.empty() || !video_buffers.empty() || + !text_map.empty()) && + !new_buffers_cb_.Run(audio_buffers, video_buffers, text_map)) { + return -1; + } - if ((!audio_buffers.empty() || !video_buffers.empty() || !text_map.empty()) && - !new_buffers_cb_.Run(audio_buffers, video_buffers, text_map)) { - return -1; - } + if (cluster_ended) { + parsing_cluster_ = false; + end_of_segment_cb_.Run(); + } - if (cluster_ended) { - parsing_cluster_ = false; - end_of_segment_cb_.Run(); - } + result += bytes_parsed; + data += bytes_parsed; + size -= bytes_parsed; - return bytes_parsed; + // WebMClusterParser returns 0 and |cluster_ended| is true if previously + // parsing an unknown-size cluster and |data| does not continue that + // cluster. Try parsing again in that case. + } while (size > 0 && (bytes_parsed > 0 || cluster_ended)); + + return result; } void WebMStreamParser::FireNeedKey(const std::string& key_id) { |