summaryrefslogtreecommitdiffstats
path: root/media/filters
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/filters
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/filters')
-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
4 files changed, 345 insertions, 127 deletions
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() {}