summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-10 23:26:07 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-10 23:26:07 +0000
commitc851cd6fb95e44c4208e0db3cc86822ce8b09144 (patch)
treef3d1ca6a36fc2d73823f4692f0978ba82a03cb91 /media
parent40c4cbb72decbe9b5c66c6753dddeb3b683a2562 (diff)
downloadchromium_src-c851cd6fb95e44c4208e0db3cc86822ce8b09144.zip
chromium_src-c851cd6fb95e44c4208e0db3cc86822ce8b09144.tar.gz
chromium_src-c851cd6fb95e44c4208e0db3cc86822ce8b09144.tar.bz2
Move WebM specific code from ChunkDemuxer to WebMStreamParser.
BUG=108329 TEST=Existing ChunkDemuxer unittests TBR=acolwell@chromium.org Review URL: http://codereview.chromium.org/9170002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@117113 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/audio_decoder_config.cc45
-rw-r--r--media/base/audio_decoder_config.h8
-rw-r--r--media/base/stream_parser.cc17
-rw-r--r--media/base/stream_parser.h86
-rw-r--r--media/base/video_decoder_config.cc38
-rw-r--r--media/base/video_decoder_config.h8
-rw-r--r--media/ffmpeg/ffmpeg_common.cc8
-rw-r--r--media/filters/chunk_demuxer.cc402
-rw-r--r--media/filters/chunk_demuxer.h61
-rw-r--r--media/filters/ffmpeg_audio_decoder_unittest.cc5
-rw-r--r--media/filters/ffmpeg_video_decoder_unittest.cc4
-rw-r--r--media/media.gyp4
-rw-r--r--media/webm/webm_stream_parser.cc350
-rw-r--r--media/webm/webm_stream_parser.h67
14 files changed, 727 insertions, 376 deletions
diff --git a/media/base/audio_decoder_config.cc b/media/base/audio_decoder_config.cc
index ece264b..38e0136 100644
--- a/media/base/audio_decoder_config.cc
+++ b/media/base/audio_decoder_config.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -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);
+ extra_data, extra_data_size, true);
}
// Helper enum used only for histogramming samples-per-second. Put
@@ -63,21 +63,26 @@ void AudioDecoderConfig::Initialize(AudioCodec codec,
ChannelLayout channel_layout,
int samples_per_second,
const uint8* extra_data,
- size_t extra_data_size) {
+ size_t extra_data_size,
+ bool record_stats) {
CHECK((extra_data_size != 0) == (extra_data != NULL));
- 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);
+ 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);
+ }
}
codec_ = codec;
@@ -105,6 +110,16 @@ 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 168a941..90f51c1 100644
--- a/media/base/audio_decoder_config.h
+++ b/media/base/audio_decoder_config.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -52,7 +52,11 @@ 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);
+ const uint8* extra_data, size_t extra_data_size,
+ bool record_stats);
+
+ // Deep copies |audio_config|.
+ void CopyFrom(const AudioDecoderConfig& audio_config);
// 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
new file mode 100644
index 0000000..255cc6f
--- /dev/null
+++ b/media/base/stream_parser.cc
@@ -0,0 +1,17 @@
+// 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
new file mode 100644
index 0000000..8397fad1
--- /dev/null
+++ b/media/base/stream_parser.h
@@ -0,0 +1,86 @@
+// 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 <deque>
+
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "base/time.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class AudioDecoderConfig;
+class Buffer;
+class VideoDecoderConfig;
+
+// Provides callback methods for StreamParser to report information
+// about the media stream.
+class MEDIA_EXPORT StreamParserHost {
+ public:
+ typedef std::deque<scoped_refptr<Buffer> > 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 MEDIA_EXPORT 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<void(bool, base::TimeDelta)> 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 07bf9a5..4966dfb 100644
--- a/media/base/video_decoder_config.cc
+++ b/media/base/video_decoder_config.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -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);
+ extra_data, extra_data_size, true);
}
VideoDecoderConfig::~VideoDecoderConfig() {}
@@ -74,16 +74,19 @@ void VideoDecoderConfig::Initialize(VideoCodec codec,
int aspect_ratio_numerator,
int aspect_ratio_denominator,
const uint8* extra_data,
- size_t extra_data_size) {
+ size_t extra_data_size,
+ bool record_stats) {
CHECK((extra_data_size != 0) == (extra_data != NULL));
- 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);
+ 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);
+ }
codec_ = codec;
profile_ = profile;
@@ -120,6 +123,21 @@ 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 28b1106..96dd677 100644
--- a/media/base/video_decoder_config.h
+++ b/media/base/video_decoder_config.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -82,7 +82,11 @@ 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);
+ const uint8* extra_data, size_t extra_data_size,
+ bool record_stats);
+
+ // Deep copies |video_config|.
+ void CopyFrom(const VideoDecoderConfig& video_config);
// 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 4f3dd0f..ca7efa1 100644
--- a/media/ffmpeg/ffmpeg_common.cc
+++ b/media/ffmpeg/ffmpeg_common.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -190,7 +190,8 @@ void AVCodecContextToAudioDecoderConfig(
channel_layout,
samples_per_second,
codec_context->extradata,
- codec_context->extradata_size);
+ codec_context->extradata_size,
+ true);
}
void AudioDecoderConfigToAVCodecContext(const AudioDecoderConfig& config,
@@ -258,7 +259,8 @@ void AVStreamToVideoDecoderConfig(
aspect_ratio.num,
aspect_ratio.den,
stream->codec->extradata,
- stream->codec->extradata_size);
+ stream->codec->extradata_size,
+ true);
}
void VideoDecoderConfigToAVCodecContext(
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 79f6178..ba3224b 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -2,10 +2,6 @@
// 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"
@@ -14,47 +10,11 @@
#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/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"
+#include "media/webm/webm_stream_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);
@@ -65,7 +25,8 @@ class ChunkDemuxerStream : public DemuxerStream {
typedef std::deque<scoped_refptr<Buffer> > BufferQueue;
typedef std::deque<ReadCallback> ReadCBQueue;
- ChunkDemuxerStream(Type type, AVStream* stream);
+ explicit ChunkDemuxerStream(const AudioDecoderConfig& audio_config);
+ explicit ChunkDemuxerStream(const VideoDecoderConfig& video_config);
virtual ~ChunkDemuxerStream();
void Flush();
@@ -87,7 +48,6 @@ class ChunkDemuxerStream : public DemuxerStream {
private:
Type type_;
- AVStream* av_stream_;
AudioDecoderConfig audio_config_;
VideoDecoderConfig video_config_;
@@ -105,23 +65,27 @@ class ChunkDemuxerStream : public DemuxerStream {
DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream);
};
-ChunkDemuxerStream::ChunkDemuxerStream(Type type, AVStream* stream)
- : type_(type),
- av_stream_(stream),
+ChunkDemuxerStream::ChunkDemuxerStream(const AudioDecoderConfig& audio_config)
+ : type_(AUDIO),
shutdown_called_(false),
received_end_of_stream_(false),
last_buffer_timestamp_(kNoTimestamp) {
- if (type_ == AUDIO) {
- AVCodecContextToAudioDecoderConfig(stream->codec, &audio_config_);
- } else if (type_ == VIDEO) {
- AVStreamToVideoDecoderConfig(stream, &video_config_);
- }
+ 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);
}
ChunkDemuxerStream::~ChunkDemuxerStream() {}
void ChunkDemuxerStream::Flush() {
- VLOG(1) << "Flush()";
+ DVLOG(1) << "Flush()";
base::AutoLock auto_lock(lock_);
buffers_.clear();
received_end_of_stream_ = false;
@@ -289,7 +253,6 @@ 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);
@@ -297,28 +260,21 @@ 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) {
- VLOG(1) << "Init()";
+ DVLOG(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);
@@ -332,13 +288,13 @@ void ChunkDemuxer::set_host(DemuxerHost* host) {
}
void ChunkDemuxer::Stop(const base::Closure& callback) {
- VLOG(1) << "Stop()";
+ DVLOG(1) << "Stop()";
Shutdown();
callback.Run();
}
void ChunkDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
- VLOG(1) << "Seek(" << time.InSecondsF() << ")";
+ DVLOG(1) << "Seek(" << time.InSecondsF() << ")";
PipelineStatus status = PIPELINE_ERROR_INVALID_STATE;
{
@@ -346,7 +302,7 @@ void ChunkDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
if (state_ == INITIALIZED || state_ == ENDED) {
if (seek_waits_for_data_) {
- VLOG(1) << "Seek() : waiting for more data to arrive.";
+ DVLOG(1) << "Seek() : waiting for more data to arrive.";
seek_cb_ = cb;
return;
}
@@ -393,13 +349,13 @@ scoped_refptr<DemuxerStream> ChunkDemuxer::GetStream(
}
base::TimeDelta ChunkDemuxer::GetStartTime() const {
- VLOG(1) << "GetStartTime()";
+ DVLOG(1) << "GetStartTime()";
// TODO(acolwell) : Fix this so it uses the time on the first packet.
return base::TimeDelta();
}
void ChunkDemuxer::FlushData() {
- VLOG(1) << "FlushData()";
+ DVLOG(1) << "FlushData()";
base::AutoLock auto_lock(lock_);
DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN);
@@ -413,14 +369,14 @@ void ChunkDemuxer::FlushData() {
video_->Flush();
byte_queue_.Reset();
- cluster_parser_->Reset();
+ stream_parser_->Flush();
seek_waits_for_data_ = true;
ChangeState_Locked(INITIALIZED);
}
bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
- VLOG(1) << "AppendData(" << length << ")";
+ DVLOG(1) << "AppendData(" << length << ")";
if (!data || length == 0u)
return false;
@@ -438,40 +394,37 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
int cur_size = 0;
int bytes_parsed = 0;
int result = -1;
- bool can_complete_seek = false;
+
+ // 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_;
byte_queue_.Peek(&cur, &cur_size);
do {
switch(state_) {
case INITIALIZING:
- result = ParseInfoAndTracks_Locked(cur, cur_size);
+ result = stream_parser_->Parse(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: {
- bool buffers_added = false;
- result = ParseCluster_Locked(cur, cur_size, &buffers_added);
+ result = stream_parser_->Parse(cur, cur_size);
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:
- VLOG(1) << "AppendData(): called in unexpected state " << state_;
+ DVLOG(1) << "AppendData(): called in unexpected state " << state_;
return false;
}
@@ -484,11 +437,11 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
byte_queue_.Pop(bytes_parsed);
- if (can_complete_seek && seek_waits_for_data_) {
- seek_waits_for_data_ = false;
-
- if (!seek_cb_.is_null())
- std::swap(cb, seek_cb_);
+ // 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_);
}
base::TimeDelta tmp;
@@ -523,7 +476,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
}
void ChunkDemuxer::EndOfStream(PipelineStatus status) {
- VLOG(1) << "EndOfStream(" << status << ")";
+ DVLOG(1) << "EndOfStream(" << status << ")";
base::AutoLock auto_lock(lock_);
DCHECK_NE(state_, WAITING_FOR_INIT);
DCHECK_NE(state_, ENDED);
@@ -560,7 +513,7 @@ bool ChunkDemuxer::HasEnded() {
}
void ChunkDemuxer::Shutdown() {
- VLOG(1) << "Shutdown()";
+ DVLOG(1) << "Shutdown()";
PipelineStatusCB cb;
{
base::AutoLock auto_lock(lock_);
@@ -576,6 +529,8 @@ void ChunkDemuxer::Shutdown() {
if (video_.get())
video_->Shutdown();
+ stream_parser_.reset();
+
ChangeState_Locked(SHUTDOWN);
}
@@ -590,207 +545,6 @@ void ChunkDemuxer::ChangeState_Locked(State new_state) {
state_ = new_state;
}
-int ChunkDemuxer::ParseInfoAndTracks_Locked(const uint8* data, int size) {
- lock_.AssertAcquired();
- DCHECK(data);
- DCHECK_GT(size, 0);
-
- DCHECK_EQ(state_, INITIALIZING);
-
- 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;
- }
-
- 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;
- 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()));
-
- format_context_ = CreateFormatContext(data, bytes_parsed);
- if (!format_context_ || !SetupStreams())
- return -1;
-
- ChangeState_Locked(INITIALIZED);
- 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::SetupStreams() {
- 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_.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;
-}
-
-int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size,
- bool* buffers_added) {
- lock_.AssertAcquired();
- 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()) {
- // 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;
-}
-
void ChunkDemuxer::ReportError_Locked(PipelineStatus error) {
lock_.AssertAcquired();
DCHECK_NE(error, PIPELINE_OK);
@@ -822,4 +576,68 @@ void ChunkDemuxer::ReportError_Locked(PipelineStatus error) {
}
}
+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;
+ }
+
+ duration_ = duration;
+
+ ChangeState_Locked(INITIALIZED);
+ PipelineStatusCB cb;
+ std::swap(cb, init_cb_);
+ cb.Run(PIPELINE_OK);
+}
+
+bool ChunkDemuxer::OnNewAudioConfig(const AudioDecoderConfig& config) {
+ lock_.AssertAcquired();
+ // Only allow a single audio config for now.
+ if (audio_.get())
+ return false;
+
+ audio_ = new ChunkDemuxerStream(config);
+ return true;
+}
+
+bool ChunkDemuxer::OnNewVideoConfig(const VideoDecoderConfig& config) {
+ lock_.AssertAcquired();
+ // Only allow a single video config for now.
+ if (video_.get())
+ return false;
+
+ video_ = new ChunkDemuxerStream(config);
+ return true;
+}
+
+
+bool ChunkDemuxer::OnAudioBuffers(const BufferQueue& buffers) {
+ if (!audio_.get())
+ return false;
+
+ if (!audio_->CanAddBuffers(buffers))
+ return false;
+
+ audio_->AddBuffers(buffers);
+ seek_waits_for_data_ = false;
+
+ return true;
+}
+
+bool ChunkDemuxer::OnVideoBuffers(const BufferQueue& buffers) {
+ if (!video_.get())
+ return false;
+
+ if (!video_->CanAddBuffers(buffers))
+ return false;
+
+ video_->AddBuffers(buffers);
+ seek_waits_for_data_ = false;
+
+ return true;
+}
+
} // namespace media
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 3313c11..9ce5845 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -10,9 +10,7 @@
#include "base/synchronization/lock.h"
#include "media/base/byte_queue.h"
#include "media/base/demuxer.h"
-#include "media/webm/webm_cluster_parser.h"
-
-struct AVFormatContext;
+#include "media/base/stream_parser.h"
namespace media {
@@ -20,9 +18,9 @@ class ChunkDemuxerClient;
class ChunkDemuxerStream;
class FFmpegURLProtocol;
-// Demuxer implementation that allows chunks of WebM media data to be passed
+// Demuxer implementation that allows chunks of media data to be passed
// from JavaScript to the media stack.
-class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
+class MEDIA_EXPORT ChunkDemuxer : public Demuxer, public StreamParserHost {
public:
explicit ChunkDemuxer(ChunkDemuxerClient* client);
virtual ~ChunkDemuxer();
@@ -64,40 +62,18 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
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_;
@@ -108,22 +84,11 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
scoped_refptr<ChunkDemuxerStream> audio_;
scoped_refptr<ChunkDemuxerStream> video_;
- // Backing buffer for |url_protocol_|.
- scoped_array<uint8> url_protocol_buffer_;
-
- // Protocol used by |format_context_|. It must outlive the context object.
- scoped_ptr<FFmpegURLProtocol> 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<WebMClusterParser> cluster_parser_;
+ scoped_ptr<StreamParser> stream_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 f8ba102..12e2ce6 100644
--- a/media/filters/ffmpeg_audio_decoder_unittest.cc
+++ b/media/filters/ffmpeg_audio_decoder_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -59,7 +59,8 @@ class FFmpegAudioDecoderTest : public testing::Test {
CHANNEL_LAYOUT_STEREO,
44100,
vorbis_extradata_.get(),
- vorbis_extradata_size_);
+ vorbis_extradata_size_,
+ true);
}
virtual ~FFmpegAudioDecoderTest() {}
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc
index b072993..d9c1fd4 100644
--- a/media/filters/ffmpeg_video_decoder_unittest.cc
+++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -62,7 +62,7 @@ class FFmpegVideoDecoderTest : public testing::Test {
kVideoFormat, kCodedSize, kVisibleRect,
kFrameRate.num, kFrameRate.den,
kAspectRatio.num, kAspectRatio.den,
- NULL, 0);
+ NULL, 0, true);
}
virtual ~FFmpegVideoDecoderTest() {}
diff --git a/media/media.gyp b/media/media.gyp
index d218f32..00b04b8 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -161,6 +161,8 @@
'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',
@@ -253,6 +255,8 @@
'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
new file mode 100644
index 0000000..45c57f3
--- /dev/null
+++ b/media/webm/webm_stream_parser.cc
@@ -0,0 +1,350 @@
+// 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<uint8> url_protocol_buffer_;
+
+ // Protocol used by |format_context_|. It must outlive the context object.
+ scoped_ptr<FFmpegURLProtocol> 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
new file mode 100644
index 0000000..9b45c44
--- /dev/null
+++ b/media/webm/webm_stream_parser.h
@@ -0,0 +1,67 @@
+// 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<WebMClusterParser> cluster_parser_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebMStreamParser);
+};
+
+} // namespace media
+
+#endif // MEDIA_WEBM_WEBM_STREAM_PARSER_H_