From 4b4116d2aca7ea0b1756044b8e26fe01e59ddf5a Mon Sep 17 00:00:00 2001 From: "acolwell@chromium.org" Date: Tue, 10 Jan 2012 00:29:40 +0000 Subject: Revert 116957 - Move WebM specific code from ChunkDemuxer to WebMStreamParser. Created StreamParser interface to allow ChunkDemuxer to support formats other than WebM. BUG=108329 TEST=Existing ChunkDemuxer unittests Reverting because windows shared build broke. Review URL: http://codereview.chromium.org/9018019 TBR=acolwell@chromium.org Review URL: http://codereview.chromium.org/9149018 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@116973 0039d316-1c4b-4281-b951-d872f2087c98 --- media/base/audio_decoder_config.cc | 45 +-- media/base/audio_decoder_config.h | 8 +- media/base/stream_parser.cc | 17 -- media/base/stream_parser.h | 85 ------ media/base/video_decoder_config.cc | 38 +-- media/base/video_decoder_config.h | 8 +- media/ffmpeg/ffmpeg_common.cc | 8 +- media/filters/chunk_demuxer.cc | 402 ++++++++++++++++++------- media/filters/chunk_demuxer.h | 61 +++- media/filters/ffmpeg_audio_decoder_unittest.cc | 5 +- media/filters/ffmpeg_video_decoder_unittest.cc | 4 +- media/media.gyp | 6 +- media/webm/webm_stream_parser.cc | 350 --------------------- media/webm/webm_stream_parser.h | 67 ----- 14 files changed, 378 insertions(+), 726 deletions(-) delete mode 100644 media/base/stream_parser.cc delete mode 100644 media/base/stream_parser.h delete mode 100644 media/webm/webm_stream_parser.cc delete mode 100644 media/webm/webm_stream_parser.h (limited to 'media') diff --git a/media/base/audio_decoder_config.cc b/media/base/audio_decoder_config.cc index 38e0136..ece264b 100644 --- a/media/base/audio_decoder_config.cc +++ b/media/base/audio_decoder_config.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. @@ -25,7 +25,7 @@ AudioDecoderConfig::AudioDecoderConfig(AudioCodec codec, const uint8* extra_data, size_t extra_data_size) { Initialize(codec, bits_per_channel, channel_layout, samples_per_second, - extra_data, extra_data_size, true); + extra_data, extra_data_size); } // Helper enum used only for histogramming samples-per-second. Put @@ -63,26 +63,21 @@ void AudioDecoderConfig::Initialize(AudioCodec codec, ChannelLayout channel_layout, int samples_per_second, const uint8* extra_data, - size_t extra_data_size, - bool record_stats) { + size_t extra_data_size) { CHECK((extra_data_size != 0) == (extra_data != NULL)); - if (record_stats) { - UMA_HISTOGRAM_ENUMERATION("Media.AudioCodec", codec, kAudioCodecMax + 1); - // Fake enum histogram to get exact integral buckets. Expect to never see - // any values over 32 and even that is huge. - UMA_HISTOGRAM_ENUMERATION("Media.AudioBitsPerChannel", bits_per_channel, - 40); - UMA_HISTOGRAM_ENUMERATION( - "Media.AudioChannelLayout", channel_layout, CHANNEL_LAYOUT_MAX); - AudioSamplesPerSecond asps = AsAudioSamplesPerSecond(samples_per_second); - if (asps != kUnexpected) { - UMA_HISTOGRAM_ENUMERATION("Media.AudioSamplesPerSecond", asps, - kUnexpected); - } else { - UMA_HISTOGRAM_COUNTS( - "Media.AudioSamplesPerSecondUnexpected", samples_per_second); - } + UMA_HISTOGRAM_ENUMERATION("Media.AudioCodec", codec, kAudioCodecMax + 1); + // Fake enum histogram to get exact integral buckets. Expect to never see + // any values over 32 and even that is huge. + UMA_HISTOGRAM_ENUMERATION("Media.AudioBitsPerChannel", bits_per_channel, 40); + UMA_HISTOGRAM_ENUMERATION( + "Media.AudioChannelLayout", channel_layout, CHANNEL_LAYOUT_MAX); + AudioSamplesPerSecond asps = AsAudioSamplesPerSecond(samples_per_second); + if (asps != kUnexpected) { + UMA_HISTOGRAM_ENUMERATION("Media.AudioSamplesPerSecond", asps, kUnexpected); + } else { + UMA_HISTOGRAM_COUNTS( + "Media.AudioSamplesPerSecondUnexpected", samples_per_second); } codec_ = codec; @@ -110,16 +105,6 @@ bool AudioDecoderConfig::IsValidConfig() const { samples_per_second_ <= limits::kMaxSampleRate; } -void AudioDecoderConfig::CopyFrom(const AudioDecoderConfig& audio_config) { - Initialize(audio_config.codec(), - audio_config.bits_per_channel(), - audio_config.channel_layout(), - audio_config.samples_per_second(), - audio_config.extra_data(), - audio_config.extra_data_size(), - false); -} - AudioCodec AudioDecoderConfig::codec() const { return codec_; } diff --git a/media/base/audio_decoder_config.h b/media/base/audio_decoder_config.h index 90f51c1..168a941 100644 --- a/media/base/audio_decoder_config.h +++ b/media/base/audio_decoder_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. @@ -52,11 +52,7 @@ class MEDIA_EXPORT AudioDecoderConfig { // Resets the internal state of this object. void Initialize(AudioCodec codec, int bits_per_channel, ChannelLayout channel_layout, int samples_per_second, - const uint8* extra_data, size_t extra_data_size, - bool record_stats); - - // Deep copies |audio_config|. - void CopyFrom(const AudioDecoderConfig& audio_config); + const uint8* extra_data, size_t extra_data_size); // Returns true if this object has appropriate configuration values, false // otherwise. diff --git a/media/base/stream_parser.cc b/media/base/stream_parser.cc deleted file mode 100644 index 255cc6f..0000000 --- a/media/base/stream_parser.cc +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2012 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/base/stream_parser.h" - -namespace media { - -StreamParserHost::StreamParserHost() {} - -StreamParserHost::~StreamParserHost() {} - -StreamParser::StreamParser() {} - -StreamParser::~StreamParser() {} - -} // namespace media diff --git a/media/base/stream_parser.h b/media/base/stream_parser.h deleted file mode 100644 index 71baf130..0000000 --- a/media/base/stream_parser.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef MEDIA_BASE_STREAM_PARSER_H_ -#define MEDIA_BASE_STREAM_PARSER_H_ - -#include - -#include "base/callback_forward.h" -#include "base/memory/ref_counted.h" -#include "base/time.h" - -namespace media { - -class AudioDecoderConfig; -class Buffer; -class VideoDecoderConfig; - -// Provides callback methods for StreamParser to report information -// about the media stream. -class StreamParserHost { - public: - typedef std::deque > BufferQueue; - - StreamParserHost(); - virtual ~StreamParserHost(); - - // A new audio decoder configuration was encountered. All audio buffers - // after this call will be associated with this configuration. - virtual bool OnNewAudioConfig(const AudioDecoderConfig& config) = 0; - - // A new video decoder configuration was encountered. All video buffers - // after this call will be associated with this configuration. - virtual bool OnNewVideoConfig(const VideoDecoderConfig& config) = 0; - - // New audio buffers have been received. - virtual bool OnAudioBuffers(const BufferQueue& buffers) = 0; - - // New video buffers have been received. - virtual bool OnVideoBuffers(const BufferQueue& buffers) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(StreamParserHost); -}; - -// Abstract interface for parsing media byte streams. -class StreamParser { - public: - StreamParser(); - virtual ~StreamParser(); - - // Indicates completion of parser initialization. - // First parameter - Indicates initialization success. Set to true if - // initialization was successful. False if an error - // occurred. - // Second parameter - Indicates the stream duration. Only contains a valid - // value if the first parameter is true. - typedef base::Callback InitCB; - - // Initialize the parser with necessary callbacks. Must be called before any - // data is passed to Parse(). |init_cb| will be called once enough data has - // been parsed to determine the initial stream configurations, presentation - // start time, and duration. - virtual void Init(const InitCB& init_cb, StreamParserHost* host) = 0; - - // Called when a seek occurs. This flushes the current parser state - // and puts the parser in a state where it can receive data for the new seek - // point. - virtual void Flush() = 0; - - // Called when there is new data to parse. - // - // Returns < 0 if the parse fails. - // Returns 0 if more data is needed. - // Returning > 0 indicates success & the number of bytes parsed. - virtual int Parse(const uint8* buf, int size) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(StreamParser); -}; - -} // namespace media - -#endif // MEDIA_BASE_STREAM_PARSER_H_ diff --git a/media/base/video_decoder_config.cc b/media/base/video_decoder_config.cc index 4966dfb..07bf9a5 100644 --- a/media/base/video_decoder_config.cc +++ b/media/base/video_decoder_config.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. @@ -37,7 +37,7 @@ VideoDecoderConfig::VideoDecoderConfig(VideoCodec codec, Initialize(codec, profile, format, coded_size, visible_rect, frame_rate_numerator, frame_rate_denominator, aspect_ratio_numerator, aspect_ratio_denominator, - extra_data, extra_data_size, true); + extra_data, extra_data_size); } VideoDecoderConfig::~VideoDecoderConfig() {} @@ -74,19 +74,16 @@ void VideoDecoderConfig::Initialize(VideoCodec codec, int aspect_ratio_numerator, int aspect_ratio_denominator, const uint8* extra_data, - size_t extra_data_size, - bool record_stats) { + size_t extra_data_size) { CHECK((extra_data_size != 0) == (extra_data != NULL)); - if (record_stats) { - UMA_HISTOGRAM_ENUMERATION("Media.VideoCodec", codec, kVideoCodecMax + 1); - UMA_HISTOGRAM_ENUMERATION("Media.VideoCodecProfile", profile, - VIDEO_CODEC_PROFILE_MAX + 1); - UMA_HISTOGRAM_COUNTS_10000("Media.VideoCodedWidth", coded_size.width()); - UmaHistogramAspectRatio("Media.VideoCodedAspectRatio", coded_size); - UMA_HISTOGRAM_COUNTS_10000("Media.VideoVisibleWidth", visible_rect.width()); - UmaHistogramAspectRatio("Media.VideoVisibleAspectRatio", visible_rect); - } + UMA_HISTOGRAM_ENUMERATION("Media.VideoCodec", codec, kVideoCodecMax + 1); + UMA_HISTOGRAM_ENUMERATION("Media.VideoCodecProfile", profile, + VIDEO_CODEC_PROFILE_MAX + 1); + UMA_HISTOGRAM_COUNTS_10000("Media.VideoCodedWidth", coded_size.width()); + UmaHistogramAspectRatio("Media.VideoCodedAspectRatio", coded_size); + UMA_HISTOGRAM_COUNTS_10000("Media.VideoVisibleWidth", visible_rect.width()); + UmaHistogramAspectRatio("Media.VideoVisibleAspectRatio", visible_rect); codec_ = codec; profile_ = profile; @@ -123,21 +120,6 @@ void VideoDecoderConfig::Initialize(VideoCodec codec, natural_size_.SetSize(width & ~1, height); } -void VideoDecoderConfig::CopyFrom(const VideoDecoderConfig& video_config) { - Initialize(video_config.codec(), - video_config.profile(), - video_config.format(), - video_config.coded_size(), - video_config.visible_rect(), - video_config.frame_rate_numerator(), - video_config.frame_rate_denominator(), - video_config.aspect_ratio_numerator(), - video_config.aspect_ratio_denominator(), - video_config.extra_data(), - video_config.extra_data_size(), - false); -} - bool VideoDecoderConfig::IsValidConfig() const { return codec_ != kUnknownVideoCodec && format_ != VideoFrame::INVALID && diff --git a/media/base/video_decoder_config.h b/media/base/video_decoder_config.h index 96dd677..28b1106 100644 --- a/media/base/video_decoder_config.h +++ b/media/base/video_decoder_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. @@ -82,11 +82,7 @@ class MEDIA_EXPORT VideoDecoderConfig { const gfx::Rect& visible_rect, int frame_rate_numerator, int frame_rate_denominator, int aspect_ratio_numerator, int aspect_ratio_denominator, - const uint8* extra_data, size_t extra_data_size, - bool record_stats); - - // Deep copies |video_config|. - void CopyFrom(const VideoDecoderConfig& video_config); + const uint8* extra_data, size_t extra_data_size); // Returns true if this object has appropriate configuration values, false // otherwise. diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc index ca7efa1..4f3dd0f 100644 --- a/media/ffmpeg/ffmpeg_common.cc +++ b/media/ffmpeg/ffmpeg_common.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. @@ -190,8 +190,7 @@ void AVCodecContextToAudioDecoderConfig( channel_layout, samples_per_second, codec_context->extradata, - codec_context->extradata_size, - true); + codec_context->extradata_size); } void AudioDecoderConfigToAVCodecContext(const AudioDecoderConfig& config, @@ -259,8 +258,7 @@ void AVStreamToVideoDecoderConfig( aspect_ratio.num, aspect_ratio.den, stream->codec->extradata, - stream->codec->extradata_size, - true); + stream->codec->extradata_size); } void VideoDecoderConfigToAVCodecContext( diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 50d73aa..64fad53 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -1,7 +1,11 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. +// Implements a Demuxer that can switch among different data sources mid-stream. +// Uses FFmpegDemuxer under the covers, so see the caveats at the top of +// ffmpeg_demuxer.h. + #include "media/filters/chunk_demuxer.h" #include "base/bind.h" @@ -10,11 +14,47 @@ #include "media/base/audio_decoder_config.h" #include "media/base/data_buffer.h" #include "media/base/video_decoder_config.h" +#include "media/ffmpeg/ffmpeg_common.h" #include "media/filters/chunk_demuxer_client.h" -#include "media/webm/webm_stream_parser.h" +#include "media/filters/ffmpeg_glue.h" +#include "media/filters/in_memory_url_protocol.h" +#include "media/webm/webm_cluster_parser.h" +#include "media/webm/webm_constants.h" +#include "media/webm/webm_info_parser.h" +#include "media/webm/webm_tracks_parser.h" namespace media { +// WebM File Header. This is prepended to the INFO & TRACKS +// data passed to Init() before handing it to FFmpeg. Essentially +// we are making the INFO & TRACKS data look like a small WebM +// file so we can use FFmpeg to initialize the AVFormatContext. +// +// TODO(acolwell): Remove this when we construct AudioDecoderConfig and +// VideoDecoderConfig without requiring an AVStream object. +static const uint8 kWebMHeader[] = { + 0x1A, 0x45, 0xDF, 0xA3, 0x9F, // EBML (size = 0x1f) + 0x42, 0x86, 0x81, 0x01, // EBMLVersion = 1 + 0x42, 0xF7, 0x81, 0x01, // EBMLReadVersion = 1 + 0x42, 0xF2, 0x81, 0x04, // EBMLMaxIDLength = 4 + 0x42, 0xF3, 0x81, 0x08, // EBMLMaxSizeLength = 8 + 0x42, 0x82, 0x84, 0x77, 0x65, 0x62, 0x6D, // DocType = "webm" + 0x42, 0x87, 0x81, 0x02, // DocTypeVersion = 2 + 0x42, 0x85, 0x81, 0x02, // DocTypeReadVersion = 2 + // EBML end + 0x18, 0x53, 0x80, 0x67, // Segment + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segment(size = 0) + // INFO goes here. +}; + +// Offset of the segment size field in kWebMHeader. Used to update +// the segment size field before handing the buffer to FFmpeg. +static const int kSegmentSizeOffset = sizeof(kWebMHeader) - 8; + +static const uint8 kEmptyCluster[] = { + 0x1F, 0x43, 0xB6, 0x75, 0x80 // CLUSTER (size = 0) +}; + // Create an "end of stream" buffer. static Buffer* CreateEOSBuffer() { return new DataBuffer(0, 0); } @@ -23,8 +63,7 @@ class ChunkDemuxerStream : public DemuxerStream { typedef std::deque > BufferQueue; typedef std::deque ReadCBQueue; - explicit ChunkDemuxerStream(const AudioDecoderConfig& audio_config); - explicit ChunkDemuxerStream(const VideoDecoderConfig& video_config); + ChunkDemuxerStream(Type type, AVStream* stream); virtual ~ChunkDemuxerStream(); void Flush(); @@ -46,6 +85,7 @@ class ChunkDemuxerStream : public DemuxerStream { private: Type type_; + AVStream* av_stream_; AudioDecoderConfig audio_config_; VideoDecoderConfig video_config_; @@ -63,27 +103,23 @@ class ChunkDemuxerStream : public DemuxerStream { DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); }; -ChunkDemuxerStream::ChunkDemuxerStream(const AudioDecoderConfig& audio_config) - : type_(AUDIO), +ChunkDemuxerStream::ChunkDemuxerStream(Type type, AVStream* stream) + : type_(type), + av_stream_(stream), shutdown_called_(false), received_end_of_stream_(false), last_buffer_timestamp_(kNoTimestamp) { - audio_config_.CopyFrom(audio_config); -} - - -ChunkDemuxerStream::ChunkDemuxerStream(const VideoDecoderConfig& video_config) - : type_(VIDEO), - shutdown_called_(false), - received_end_of_stream_(false), - last_buffer_timestamp_(kNoTimestamp) { - video_config_.CopyFrom(video_config); + if (type_ == AUDIO) { + AVCodecContextToAudioDecoderConfig(stream->codec, &audio_config_); + } else if (type_ == VIDEO) { + AVStreamToVideoDecoderConfig(stream, &video_config_); + } } ChunkDemuxerStream::~ChunkDemuxerStream() {} void ChunkDemuxerStream::Flush() { - DVLOG(1) << "Flush()"; + VLOG(1) << "Flush()"; base::AutoLock auto_lock(lock_); buffers_.clear(); received_end_of_stream_ = false; @@ -251,6 +287,7 @@ const VideoDecoderConfig& ChunkDemuxerStream::video_decoder_config() { ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) : state_(WAITING_FOR_INIT), client_(client), + format_context_(NULL), buffered_bytes_(0), seek_waits_for_data_(true) { DCHECK(client); @@ -258,21 +295,28 @@ ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) ChunkDemuxer::~ChunkDemuxer() { DCHECK_NE(state_, INITIALIZED); + + if (!format_context_) + return; + + DestroyAVFormatContext(format_context_); + format_context_ = NULL; + + if (url_protocol_.get()) { + FFmpegGlue::GetInstance()->RemoveProtocol(url_protocol_.get()); + url_protocol_.reset(); + url_protocol_buffer_.reset(); + } } void ChunkDemuxer::Init(const PipelineStatusCB& cb) { - DVLOG(1) << "Init()"; + VLOG(1) << "Init()"; { base::AutoLock auto_lock(lock_); DCHECK_EQ(state_, WAITING_FOR_INIT); ChangeState_Locked(INITIALIZING); init_cb_ = cb; - stream_parser_.reset(new WebMStreamParser()); - - stream_parser_->Init( - base::Bind(&ChunkDemuxer::OnStreamParserInitDone, this), - this); } client_->DemuxerOpened(this); @@ -286,13 +330,13 @@ void ChunkDemuxer::set_host(DemuxerHost* host) { } void ChunkDemuxer::Stop(const base::Closure& callback) { - DVLOG(1) << "Stop()"; + VLOG(1) << "Stop()"; Shutdown(); callback.Run(); } void ChunkDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { - DVLOG(1) << "Seek(" << time.InSecondsF() << ")"; + VLOG(1) << "Seek(" << time.InSecondsF() << ")"; PipelineStatus status = PIPELINE_ERROR_INVALID_STATE; { @@ -300,7 +344,7 @@ void ChunkDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { if (state_ == INITIALIZED || state_ == ENDED) { if (seek_waits_for_data_) { - DVLOG(1) << "Seek() : waiting for more data to arrive."; + VLOG(1) << "Seek() : waiting for more data to arrive."; seek_cb_ = cb; return; } @@ -347,13 +391,13 @@ scoped_refptr ChunkDemuxer::GetStream( } base::TimeDelta ChunkDemuxer::GetStartTime() const { - DVLOG(1) << "GetStartTime()"; + VLOG(1) << "GetStartTime()"; // TODO(acolwell) : Fix this so it uses the time on the first packet. return base::TimeDelta(); } void ChunkDemuxer::FlushData() { - DVLOG(1) << "FlushData()"; + VLOG(1) << "FlushData()"; base::AutoLock auto_lock(lock_); DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN); @@ -367,14 +411,14 @@ void ChunkDemuxer::FlushData() { video_->Flush(); byte_queue_.Reset(); - stream_parser_->Flush(); + cluster_parser_->Reset(); seek_waits_for_data_ = true; ChangeState_Locked(INITIALIZED); } bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { - DVLOG(1) << "AppendData(" << length << ")"; + VLOG(1) << "AppendData(" << length << ")"; if (!data || length == 0u) return false; @@ -392,37 +436,40 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { int cur_size = 0; int bytes_parsed = 0; int result = -1; - - // Capture |seek_waits_for_data_| state before we start parsing. - // Its state can be changed by OnAudioBuffers() or OnVideoBuffers() - // calls during the parse. - bool old_seek_waits_for_data = seek_waits_for_data_; + bool can_complete_seek = false; byte_queue_.Peek(&cur, &cur_size); do { switch(state_) { case INITIALIZING: - result = stream_parser_->Parse(cur, cur_size); + result = ParseInfoAndTracks_Locked(cur, cur_size); if (result < 0) { + VLOG(1) << "AppendData(): parsing info & tracks failed"; ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); return true; } break; case INITIALIZED: { - result = stream_parser_->Parse(cur, cur_size); + bool buffers_added = false; + result = ParseCluster_Locked(cur, cur_size, &buffers_added); if (result < 0) { + VLOG(1) << "AppendData(): parsing data failed"; ReportError_Locked(PIPELINE_ERROR_DECODE); return true; } + + // We can complete the seek if we have successfully parsed + // some data and buffers were added to one of the DemuxerStreams. + can_complete_seek |= (result > 0 && buffers_added); } break; case WAITING_FOR_INIT: case ENDED: case PARSE_ERROR: case SHUTDOWN: - DVLOG(1) << "AppendData(): called in unexpected state " << state_; + VLOG(1) << "AppendData(): called in unexpected state " << state_; return false; } @@ -435,11 +482,11 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { byte_queue_.Pop(bytes_parsed); - // Check to see if parsing triggered seek_waits_for_data_ to go from true to - // false. This indicates we have parsed enough data to complete the seek. - if (old_seek_waits_for_data && !seek_waits_for_data_ && - !seek_cb_.is_null()) { - std::swap(cb, seek_cb_); + if (can_complete_seek && seek_waits_for_data_) { + seek_waits_for_data_ = false; + + if (!seek_cb_.is_null()) + std::swap(cb, seek_cb_); } base::TimeDelta tmp; @@ -474,7 +521,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { } void ChunkDemuxer::EndOfStream(PipelineStatus status) { - DVLOG(1) << "EndOfStream(" << status << ")"; + VLOG(1) << "EndOfStream(" << status << ")"; base::AutoLock auto_lock(lock_); DCHECK_NE(state_, WAITING_FOR_INIT); DCHECK_NE(state_, ENDED); @@ -511,7 +558,7 @@ bool ChunkDemuxer::HasEnded() { } void ChunkDemuxer::Shutdown() { - DVLOG(1) << "Shutdown()"; + VLOG(1) << "Shutdown()"; PipelineStatusCB cb; { base::AutoLock auto_lock(lock_); @@ -541,99 +588,236 @@ void ChunkDemuxer::ChangeState_Locked(State new_state) { state_ = new_state; } -void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { +int ChunkDemuxer::ParseInfoAndTracks_Locked(const uint8* data, int size) { lock_.AssertAcquired(); - DCHECK_NE(error, PIPELINE_OK); + DCHECK(data); + DCHECK_GT(size, 0); - ChangeState_Locked(PARSE_ERROR); + DCHECK_EQ(state_, INITIALIZING); - PipelineStatusCB cb; + const uint8* cur = data; + int cur_size = size; + int bytes_parsed = 0; + + int id; + int64 element_size; + int result = WebMParseElementHeader(cur, cur_size, &id, &element_size); + + if (result <= 0) + return result; + + switch (id) { + case kWebMIdEBML : + case kWebMIdSeekHead : + case kWebMIdVoid : + case kWebMIdCRC32 : + case kWebMIdCues : + if (cur_size < (result + element_size)) { + // We don't have the whole element yet. Signal we need more data. + return 0; + } + // Skip the element. + return result + element_size; + break; + case kWebMIdSegment : + // Just consume the segment header. + return result; + break; + case kWebMIdInfo : + // We've found the element we are looking for. + break; + default: + VLOG(1) << "Unexpected ID 0x" << std::hex << id; + return -1; + } - if (!init_cb_.is_null()) { - std::swap(cb, init_cb_); - } else { - if (!seek_cb_.is_null()) - std::swap(cb, seek_cb_); + WebMInfoParser info_parser; + result = info_parser.Parse(cur, cur_size); - if (audio_.get()) - audio_->Shutdown(); + if (result <= 0) + return result; - if (video_.get()) - video_->Shutdown(); - } + cur += result; + cur_size -= result; + bytes_parsed += result; - { - base::AutoUnlock auto_unlock(lock_); - if (cb.is_null()) { - host()->OnDemuxerError(error); - return; - } - cb.Run(error); - } -} + WebMTracksParser tracks_parser(info_parser.timecode_scale()); + result = tracks_parser.Parse(cur, cur_size); -void ChunkDemuxer::OnStreamParserInitDone(bool success, - base::TimeDelta duration) { - lock_.AssertAcquired(); - DCHECK_EQ(state_, INITIALIZING); - if (!success || (!audio_.get() && !video_.get())) { - ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); - return; - } + if (result <= 0) + return result; + + bytes_parsed += result; + + double mult = info_parser.timecode_scale() / 1000.0; + duration_ = base::TimeDelta::FromMicroseconds(info_parser.duration() * mult); + + cluster_parser_.reset(new WebMClusterParser( + info_parser.timecode_scale(), + tracks_parser.audio_track_num(), + tracks_parser.audio_default_duration(), + tracks_parser.video_track_num(), + tracks_parser.video_default_duration())); - duration_ = duration; + format_context_ = CreateFormatContext(data, bytes_parsed); + if (!format_context_ || !SetupStreams()) + return -1; ChangeState_Locked(INITIALIZED); - PipelineStatusCB cb; - std::swap(cb, init_cb_); - cb.Run(PIPELINE_OK); + init_cb_.Run(PIPELINE_OK); + init_cb_.Reset(); + return bytes_parsed; +} + +AVFormatContext* ChunkDemuxer::CreateFormatContext(const uint8* data, + int size) { + DCHECK(!url_protocol_.get()); + DCHECK(!url_protocol_buffer_.get()); + + int segment_size = size + sizeof(kEmptyCluster); + int buf_size = sizeof(kWebMHeader) + segment_size; + url_protocol_buffer_.reset(new uint8[buf_size]); + uint8* buf = url_protocol_buffer_.get(); + memcpy(buf, kWebMHeader, sizeof(kWebMHeader)); + memcpy(buf + sizeof(kWebMHeader), data, size); + memcpy(buf + sizeof(kWebMHeader) + size, kEmptyCluster, + sizeof(kEmptyCluster)); + + // Update the segment size in the buffer. + int64 tmp = (segment_size & GG_LONGLONG(0x00FFFFFFFFFFFFFF)) | + GG_LONGLONG(0x0100000000000000); + for (int i = 0; i < 8; i++) { + buf[kSegmentSizeOffset + i] = (tmp >> (8 * (7 - i))) & 0xff; + } + + url_protocol_.reset(new InMemoryUrlProtocol(buf, buf_size, true)); + std::string key = FFmpegGlue::GetInstance()->AddProtocol(url_protocol_.get()); + + // Open FFmpeg AVFormatContext. + AVFormatContext* context = NULL; + int result = av_open_input_file(&context, key.c_str(), NULL, 0, NULL); + + if (result < 0) + return NULL; + + return context; } -bool ChunkDemuxer::OnNewAudioConfig(const AudioDecoderConfig& config) { - lock_.AssertAcquired(); - // Only allow a single audio config for now. - if (audio_.get()) +bool ChunkDemuxer::SetupStreams() { + int result = av_find_stream_info(format_context_); + + if (result < 0) return false; - audio_ = new ChunkDemuxerStream(config); - return true; + bool no_supported_streams = true; + for (size_t i = 0; i < format_context_->nb_streams; ++i) { + AVStream* stream = format_context_->streams[i]; + AVCodecContext* codec_context = stream->codec; + AVMediaType codec_type = codec_context->codec_type; + + if (codec_type == AVMEDIA_TYPE_AUDIO && + stream->codec->codec_id == CODEC_ID_VORBIS && + !audio_.get()) { + audio_ = new ChunkDemuxerStream(DemuxerStream::AUDIO, stream); + no_supported_streams = false; + continue; + } + + if (codec_type == AVMEDIA_TYPE_VIDEO && + stream->codec->codec_id == CODEC_ID_VP8 && + !video_.get()) { + video_ = new ChunkDemuxerStream(DemuxerStream::VIDEO, stream); + no_supported_streams = false; + continue; + } + } + + return !no_supported_streams; } -bool ChunkDemuxer::OnNewVideoConfig(const VideoDecoderConfig& config) { +int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size, + bool* buffers_added) { lock_.AssertAcquired(); - // Only allow a single video config for now. - if (video_.get()) - return false; + if (!cluster_parser_.get()) + return -1; - video_ = new ChunkDemuxerStream(config); - return true; -} + int id; + int64 element_size; + int result = WebMParseElementHeader(data, size, &id, &element_size); + if (result <= 0) + return result; -bool ChunkDemuxer::OnAudioBuffers(const BufferQueue& buffers) { - if (!audio_.get()) - return false; + if (id == kWebMIdCues) { + if (size < (result + element_size)) { + // We don't have the whole element yet. Signal we need more data. + return 0; + } + // Skip the element. + return result + element_size; + } - if (!audio_->CanAddBuffers(buffers)) - return false; + int bytes_parsed = cluster_parser_->Parse(data, size); - audio_->AddBuffers(buffers); - seek_waits_for_data_ = false; + if (bytes_parsed <= 0) + return bytes_parsed; - return true; + 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. + if ((audio_.get() && + !audio_->CanAddBuffers(cluster_parser_->audio_buffers())) || + (video_.get() && + !video_->CanAddBuffers(cluster_parser_->video_buffers()))) { + return -1; + } + + if (audio_.get()) + audio_->AddBuffers(cluster_parser_->audio_buffers()); + + if (video_.get()) + video_->AddBuffers(cluster_parser_->video_buffers()); + + *buffers_added = true; + } + + // TODO(acolwell) : make this more representative of what is actually + // buffered. + buffered_bytes_ += bytes_parsed; + + return bytes_parsed; } -bool ChunkDemuxer::OnVideoBuffers(const BufferQueue& buffers) { - if (!video_.get()) - return false; +void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { + lock_.AssertAcquired(); + DCHECK_NE(error, PIPELINE_OK); - if (!video_->CanAddBuffers(buffers)) - return false; + ChangeState_Locked(PARSE_ERROR); - video_->AddBuffers(buffers); - seek_waits_for_data_ = false; + PipelineStatusCB cb; - return true; + if (!init_cb_.is_null()) { + std::swap(cb, init_cb_); + } else { + if (!seek_cb_.is_null()) + std::swap(cb, seek_cb_); + + if (audio_.get()) + audio_->Shutdown(); + + if (video_.get()) + video_->Shutdown(); + } + + { + base::AutoUnlock auto_unlock(lock_); + if (cb.is_null()) { + host()->OnDemuxerError(error); + return; + } + cb.Run(error); + } } } // namespace media diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index 9ce5845..3313c11 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. @@ -10,7 +10,9 @@ #include "base/synchronization/lock.h" #include "media/base/byte_queue.h" #include "media/base/demuxer.h" -#include "media/base/stream_parser.h" +#include "media/webm/webm_cluster_parser.h" + +struct AVFormatContext; namespace media { @@ -18,9 +20,9 @@ class ChunkDemuxerClient; class ChunkDemuxerStream; class FFmpegURLProtocol; -// Demuxer implementation that allows chunks of media data to be passed +// Demuxer implementation that allows chunks of WebM media data to be passed // from JavaScript to the media stack. -class MEDIA_EXPORT ChunkDemuxer : public Demuxer, public StreamParserHost { +class MEDIA_EXPORT ChunkDemuxer : public Demuxer { public: explicit ChunkDemuxer(ChunkDemuxerClient* client); virtual ~ChunkDemuxer(); @@ -62,18 +64,40 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer, public StreamParserHost { void ChangeState_Locked(State new_state); + // Parses a buffer that contains an INFO & TRACKS element. This method handles + // calling & clearing |init_cb_| before it returns. + // + // Returns -1 if the parse fails. + // Returns 0 if more data is needed. + // Returns the number of bytes parsed on success. + int ParseInfoAndTracks_Locked(const uint8* data, int size); + + // Generates an AVFormatContext for the INFO & TRACKS elements contained + // in |data|. Returns NULL if parsing |data| fails. + AVFormatContext* CreateFormatContext(const uint8* data, int size); + + // Sets up |audio_| & |video_| DemuxerStreams based on the data in + // |format_context_|. Returns false if no valid audio or video stream were + // found. + bool SetupStreams(); + + // Parse a cluster and add the buffers to the appropriate DemuxerStream. This + // 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 0 if more data is needed. + // Returns the number of bytes parsed on success. + 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 // data. void ReportError_Locked(PipelineStatus error); - void OnStreamParserInitDone(bool success, base::TimeDelta duration); - - // StreamParserHost implementation. - virtual bool OnNewAudioConfig(const AudioDecoderConfig& config) OVERRIDE; - virtual bool OnNewVideoConfig(const VideoDecoderConfig& config) OVERRIDE; - virtual bool OnAudioBuffers(const BufferQueue& buffer) OVERRIDE; - virtual bool OnVideoBuffers(const BufferQueue& buffer) OVERRIDE; - base::Lock lock_; State state_; @@ -84,11 +108,22 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer, public StreamParserHost { scoped_refptr audio_; scoped_refptr video_; + // Backing buffer for |url_protocol_|. + scoped_array url_protocol_buffer_; + + // Protocol used by |format_context_|. It must outlive the context object. + scoped_ptr url_protocol_; + + // FFmpeg format context for this demuxer. It is created by + // av_open_input_file() during demuxer initialization and cleaned up with + // DestroyAVFormatContext() in the destructor. + AVFormatContext* format_context_; + int64 buffered_bytes_; base::TimeDelta duration_; - scoped_ptr stream_parser_; + scoped_ptr cluster_parser_; // Should a Seek() call wait for more data before calling the // callback. diff --git a/media/filters/ffmpeg_audio_decoder_unittest.cc b/media/filters/ffmpeg_audio_decoder_unittest.cc index 12e2ce6..f8ba102 100644 --- a/media/filters/ffmpeg_audio_decoder_unittest.cc +++ b/media/filters/ffmpeg_audio_decoder_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. @@ -59,8 +59,7 @@ class FFmpegAudioDecoderTest : public testing::Test { CHANNEL_LAYOUT_STEREO, 44100, vorbis_extradata_.get(), - vorbis_extradata_size_, - true); + vorbis_extradata_size_); } virtual ~FFmpegAudioDecoderTest() {} diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc index d9c1fd4..b072993 100644 --- a/media/filters/ffmpeg_video_decoder_unittest.cc +++ b/media/filters/ffmpeg_video_decoder_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. @@ -62,7 +62,7 @@ class FFmpegVideoDecoderTest : public testing::Test { kVideoFormat, kCodedSize, kVisibleRect, kFrameRate.num, kFrameRate.den, kAspectRatio.num, kAspectRatio.den, - NULL, 0, true); + NULL, 0); } virtual ~FFmpegVideoDecoderTest() {} diff --git a/media/media.gyp b/media/media.gyp index e57c56f..b28fdac 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -1,4 +1,4 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# 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. @@ -161,8 +161,6 @@ 'base/seekable_buffer.h', 'base/state_matrix.cc', 'base/state_matrix.h', - 'base/stream_parser.cc', - 'base/stream_parser.h', 'base/video_decoder_config.cc', 'base/video_decoder_config.h', 'base/video_frame.cc', @@ -255,8 +253,6 @@ 'webm/webm_info_parser.h', 'webm/webm_parser.cc', 'webm/webm_parser.h', - 'webm/webm_stream_parser.cc', - 'webm/webm_stream_parser.h', 'webm/webm_tracks_parser.cc', 'webm/webm_tracks_parser.h', ], diff --git a/media/webm/webm_stream_parser.cc b/media/webm/webm_stream_parser.cc deleted file mode 100644 index 45c57f3..0000000 --- a/media/webm/webm_stream_parser.cc +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright (c) 2012 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/webm_stream_parser.h" - -#include "base/callback.h" -#include "base/logging.h" -#include "media/ffmpeg/ffmpeg_common.h" -#include "media/filters/ffmpeg_glue.h" -#include "media/filters/in_memory_url_protocol.h" -#include "media/webm/webm_cluster_parser.h" -#include "media/webm/webm_constants.h" -#include "media/webm/webm_info_parser.h" -#include "media/webm/webm_tracks_parser.h" - -namespace media { - -// Helper class that uses FFmpeg to create AudioDecoderConfig & -// VideoDecoderConfig objects. -// -// This dependency on FFmpeg can be removed once we update WebMTracksParser -// to parse the necessary data to construct AudioDecoderConfig & -// VideoDecoderConfig objects. http://crbug.com/108756 -class FFmpegConfigHelper { - public: - FFmpegConfigHelper(); - ~FFmpegConfigHelper(); - - bool Parse(const uint8* data, int size); - - const AudioDecoderConfig& audio_config() const; - const VideoDecoderConfig& video_config() const; - - private: - AVFormatContext* CreateFormatContext(const uint8* data, int size); - bool SetupStreamConfigs(); - - AudioDecoderConfig audio_config_; - VideoDecoderConfig video_config_; - - // Backing buffer for |url_protocol_|. - scoped_array url_protocol_buffer_; - - // Protocol used by |format_context_|. It must outlive the context object. - scoped_ptr url_protocol_; - - // FFmpeg format context for this demuxer. It is created by - // av_open_input_file() during demuxer initialization and cleaned up with - // DestroyAVFormatContext() in the destructor. - AVFormatContext* format_context_; - - static const uint8 kWebMHeader[]; - static const int kSegmentSizeOffset; - static const uint8 kEmptyCluster[]; - - DISALLOW_COPY_AND_ASSIGN(FFmpegConfigHelper); -}; - -// WebM File Header. This is prepended to the INFO & TRACKS -// data passed to Init() before handing it to FFmpeg. Essentially -// we are making the INFO & TRACKS data look like a small WebM -// file so we can use FFmpeg to initialize the AVFormatContext. -const uint8 FFmpegConfigHelper::kWebMHeader[] = { - 0x1A, 0x45, 0xDF, 0xA3, 0x9F, // EBML (size = 0x1f) - 0x42, 0x86, 0x81, 0x01, // EBMLVersion = 1 - 0x42, 0xF7, 0x81, 0x01, // EBMLReadVersion = 1 - 0x42, 0xF2, 0x81, 0x04, // EBMLMaxIDLength = 4 - 0x42, 0xF3, 0x81, 0x08, // EBMLMaxSizeLength = 8 - 0x42, 0x82, 0x84, 0x77, 0x65, 0x62, 0x6D, // DocType = "webm" - 0x42, 0x87, 0x81, 0x02, // DocTypeVersion = 2 - 0x42, 0x85, 0x81, 0x02, // DocTypeReadVersion = 2 - // EBML end - 0x18, 0x53, 0x80, 0x67, // Segment - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segment(size = 0) - // INFO goes here. -}; - -// Offset of the segment size field in kWebMHeader. Used to update -// the segment size field before handing the buffer to FFmpeg. -const int FFmpegConfigHelper::kSegmentSizeOffset = sizeof(kWebMHeader) - 8; - -const uint8 FFmpegConfigHelper::kEmptyCluster[] = { - 0x1F, 0x43, 0xB6, 0x75, 0x80 // CLUSTER (size = 0) -}; - -FFmpegConfigHelper::FFmpegConfigHelper() : format_context_(NULL) {} - -FFmpegConfigHelper::~FFmpegConfigHelper() { - if (!format_context_) - return; - - DestroyAVFormatContext(format_context_); - format_context_ = NULL; - - if (url_protocol_.get()) { - FFmpegGlue::GetInstance()->RemoveProtocol(url_protocol_.get()); - url_protocol_.reset(); - url_protocol_buffer_.reset(); - } -} - -bool FFmpegConfigHelper::Parse(const uint8* data, int size) { - format_context_ = CreateFormatContext(data, size); - return format_context_ && SetupStreamConfigs(); -} - -const AudioDecoderConfig& FFmpegConfigHelper::audio_config() const { - return audio_config_; -} - -const VideoDecoderConfig& FFmpegConfigHelper::video_config() const { - return video_config_; -} - -AVFormatContext* FFmpegConfigHelper::CreateFormatContext(const uint8* data, - int size) { - DCHECK(!url_protocol_.get()); - DCHECK(!url_protocol_buffer_.get()); - - int segment_size = size + sizeof(kEmptyCluster); - int buf_size = sizeof(kWebMHeader) + segment_size; - url_protocol_buffer_.reset(new uint8[buf_size]); - uint8* buf = url_protocol_buffer_.get(); - memcpy(buf, kWebMHeader, sizeof(kWebMHeader)); - memcpy(buf + sizeof(kWebMHeader), data, size); - memcpy(buf + sizeof(kWebMHeader) + size, kEmptyCluster, - sizeof(kEmptyCluster)); - - // Update the segment size in the buffer. - int64 tmp = (segment_size & GG_LONGLONG(0x00FFFFFFFFFFFFFF)) | - GG_LONGLONG(0x0100000000000000); - for (int i = 0; i < 8; i++) { - buf[kSegmentSizeOffset + i] = (tmp >> (8 * (7 - i))) & 0xff; - } - - url_protocol_.reset(new InMemoryUrlProtocol(buf, buf_size, true)); - std::string key = FFmpegGlue::GetInstance()->AddProtocol(url_protocol_.get()); - - // Open FFmpeg AVFormatContext. - AVFormatContext* context = NULL; - int result = av_open_input_file(&context, key.c_str(), NULL, 0, NULL); - - if (result < 0) - return NULL; - - return context; -} - -bool FFmpegConfigHelper::SetupStreamConfigs() { - int result = av_find_stream_info(format_context_); - - if (result < 0) - return false; - - bool no_supported_streams = true; - for (size_t i = 0; i < format_context_->nb_streams; ++i) { - AVStream* stream = format_context_->streams[i]; - AVCodecContext* codec_context = stream->codec; - AVMediaType codec_type = codec_context->codec_type; - - if (codec_type == AVMEDIA_TYPE_AUDIO && - stream->codec->codec_id == CODEC_ID_VORBIS && - !audio_config_.IsValidConfig()) { - AVCodecContextToAudioDecoderConfig(stream->codec, &audio_config_); - no_supported_streams = false; - continue; - } - - if (codec_type == AVMEDIA_TYPE_VIDEO && - stream->codec->codec_id == CODEC_ID_VP8 && - !video_config_.IsValidConfig()) { - AVStreamToVideoDecoderConfig(stream, &video_config_); - no_supported_streams = false; - continue; - } - } - - return !no_supported_streams; -} - -WebMStreamParser::WebMStreamParser() - : state_(WAITING_FOR_INIT), - host_(NULL) { -} - -WebMStreamParser::~WebMStreamParser() {} - -void WebMStreamParser::Init(const InitCB& init_cb, StreamParserHost* host) { - DCHECK_EQ(state_, WAITING_FOR_INIT); - DCHECK(init_cb_.is_null()); - DCHECK(!host_); - DCHECK(!init_cb.is_null()); - DCHECK(host); - - ChangeState(PARSING_HEADERS); - init_cb_ = init_cb; - host_ = host; -} - -void WebMStreamParser::Flush() { - DCHECK_NE(state_, WAITING_FOR_INIT); - - if (state_ != PARSING_CLUSTERS) - return; - - cluster_parser_->Reset(); -} - -int WebMStreamParser::Parse(const uint8* buf, int size) { - DCHECK_NE(state_, WAITING_FOR_INIT); - - if (state_ == PARSING_HEADERS) - return ParseInfoAndTracks(buf, size); - - if (state_ == PARSING_CLUSTERS) - return ParseCluster(buf, size); - - return -1; -} - -void WebMStreamParser::ChangeState(State new_state) { - state_ = new_state; -} - -int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) { - DCHECK(data); - DCHECK_GT(size, 0); - - const uint8* cur = data; - int cur_size = size; - int bytes_parsed = 0; - - int id; - int64 element_size; - int result = WebMParseElementHeader(cur, cur_size, &id, &element_size); - - if (result <= 0) - return result; - - switch (id) { - case kWebMIdEBML: - case kWebMIdSeekHead: - case kWebMIdVoid: - case kWebMIdCRC32: - case kWebMIdCues: - if (cur_size < (result + element_size)) { - // We don't have the whole element yet. Signal we need more data. - return 0; - } - // Skip the element. - return result + element_size; - break; - case kWebMIdSegment: - // Just consume the segment header. - return result; - break; - case kWebMIdInfo: - // We've found the element we are looking for. - break; - default: - DVLOG(1) << "Unexpected ID 0x" << std::hex << id; - return -1; - } - - WebMInfoParser info_parser; - result = info_parser.Parse(cur, cur_size); - - if (result <= 0) - return result; - - cur += result; - cur_size -= result; - bytes_parsed += result; - - WebMTracksParser tracks_parser(info_parser.timecode_scale()); - result = tracks_parser.Parse(cur, cur_size); - - if (result <= 0) - return result; - - bytes_parsed += result; - - double mult = info_parser.timecode_scale() / 1000.0; - base::TimeDelta duration = - base::TimeDelta::FromMicroseconds(info_parser.duration() * mult); - - FFmpegConfigHelper config_helper; - - if (!config_helper.Parse(data, bytes_parsed)) - return -1; - - if (config_helper.audio_config().IsValidConfig()) - host_->OnNewAudioConfig(config_helper.audio_config()); - - if (config_helper.video_config().IsValidConfig()) - host_->OnNewVideoConfig(config_helper.video_config()); - - cluster_parser_.reset(new WebMClusterParser( - info_parser.timecode_scale(), - tracks_parser.audio_track_num(), - tracks_parser.audio_default_duration(), - tracks_parser.video_track_num(), - tracks_parser.video_default_duration())); - - ChangeState(PARSING_CLUSTERS); - init_cb_.Run(true, duration); - - return bytes_parsed; -} - -int WebMStreamParser::ParseCluster(const uint8* data, int size) { - if (!cluster_parser_.get()) - return -1; - - int id; - int64 element_size; - int result = WebMParseElementHeader(data, size, &id, &element_size); - - if (result <= 0) - return result; - - if (id == kWebMIdCues) { - if (size < (result + element_size)) { - // We don't have the whole element yet. Signal we need more data. - return 0; - } - // Skip the element. - return result + element_size; - } - - int bytes_parsed = cluster_parser_->Parse(data, size); - - if (bytes_parsed <= 0) - return bytes_parsed; - - if (cluster_parser_->audio_buffers().empty() && - cluster_parser_->video_buffers().empty()) - return bytes_parsed; - - if (!host_->OnAudioBuffers(cluster_parser_->audio_buffers())) - return -1; - - if (!host_->OnVideoBuffers(cluster_parser_->video_buffers())) - return -1; - - return bytes_parsed; -} - -} // namespace media diff --git a/media/webm/webm_stream_parser.h b/media/webm/webm_stream_parser.h deleted file mode 100644 index 9b45c44..0000000 --- a/media/webm/webm_stream_parser.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef MEDIA_WEBM_WEBM_STREAM_PARSER_H_ -#define MEDIA_WEBM_WEBM_STREAM_PARSER_H_ - -#include "base/callback_forward.h" -#include "base/memory/ref_counted.h" -#include "media/base/audio_decoder_config.h" -#include "media/base/buffers.h" -#include "media/base/stream_parser.h" -#include "media/base/video_decoder_config.h" -#include "media/webm/webm_cluster_parser.h" - -namespace media { - -class WebMStreamParser : public StreamParser { - public: - WebMStreamParser(); - virtual ~WebMStreamParser(); - - // StreamParser implementation. - virtual void Init(const InitCB& init_cb, StreamParserHost* host) OVERRIDE; - virtual void Flush() OVERRIDE; - virtual int Parse(const uint8* buf, int size) OVERRIDE; - - private: - enum State { - WAITING_FOR_INIT, - PARSING_HEADERS, - PARSING_CLUSTERS - }; - - void ChangeState(State new_state); - - // Parses WebM Header, Info, Tracks elements. It also skips other level 1 - // elements that are not used right now. Once the Info & Tracks elements have - // been parsed, this method will transition the parser from PARSING_HEADERS to - // PARSING_CLUSTERS. - // - // Returns < 0 if the parse fails. - // Returns 0 if more data is needed. - // Returning > 0 indicates success & the number of bytes parsed. - int ParseInfoAndTracks(const uint8* data, int size); - - // Incrementally parses WebM cluster elements. This method also skips - // CUES elements if they are encountered since we currently don't use the - // data in these elements. - // - // Returns < 0 if the parse fails. - // Returns 0 if more data is needed. - // Returning > 0 indicates success & the number of bytes parsed. - int ParseCluster(const uint8* data, int size); - - State state_; - InitCB init_cb_; - StreamParserHost* host_; - - scoped_ptr cluster_parser_; - - DISALLOW_COPY_AND_ASSIGN(WebMStreamParser); -}; - -} // namespace media - -#endif // MEDIA_WEBM_WEBM_STREAM_PARSER_H_ -- cgit v1.1