summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-10 00:29:40 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-10 00:29:40 +0000
commit4b4116d2aca7ea0b1756044b8e26fe01e59ddf5a (patch)
tree511ca8d5b1b2140c1f73bd6174440c59f4f2e647 /media
parent97cf0b0418cb481a63de19597d966f85ce6af6c9 (diff)
downloadchromium_src-4b4116d2aca7ea0b1756044b8e26fe01e59ddf5a.zip
chromium_src-4b4116d2aca7ea0b1756044b8e26fe01e59ddf5a.tar.gz
chromium_src-4b4116d2aca7ea0b1756044b8e26fe01e59ddf5a.tar.bz2
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
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.h85
-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.gyp6
-rw-r--r--media/webm/webm_stream_parser.cc350
-rw-r--r--media/webm/webm_stream_parser.h67
14 files changed, 378 insertions, 726 deletions
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 <deque>
-
-#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<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 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 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<scoped_refptr<Buffer> > BufferQueue;
typedef std::deque<ReadCallback> 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<DemuxerStream> 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<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<StreamParser> stream_parser_;
+ scoped_ptr<WebMClusterParser> 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<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
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<WebMClusterParser> cluster_parser_;
-
- DISALLOW_COPY_AND_ASSIGN(WebMStreamParser);
-};
-
-} // namespace media
-
-#endif // MEDIA_WEBM_WEBM_STREAM_PARSER_H_