Commit f221f7d3 authored by annacc@chromium.org's avatar annacc@chromium.org

Implement support for 2 source ids (1 for audio and 1 for video).

This patch allows data to be appended to 2 different sources (1 audio and 1 video).  Also makes sure that appended data matches the mimetype provided in AddId().

BUG=122909
TEST=ChunkDemuxerTest.*


Review URL: https://chromiumcodereview.appspot.com/10545066

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@141679 0039d316-1c4b-4281-b951-d872f2087c98
parent b44642be
...@@ -415,9 +415,7 @@ ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) ...@@ -415,9 +415,7 @@ ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client)
: state_(WAITING_FOR_INIT), : state_(WAITING_FOR_INIT),
host_(NULL), host_(NULL),
client_(client), client_(client),
buffered_bytes_(0), buffered_bytes_(0) {
has_audio_(false),
has_video_(false) {
DCHECK(client); DCHECK(client);
} }
...@@ -518,6 +516,10 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, ...@@ -518,6 +516,10 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
const std::string& type, const std::string& type,
std::vector<std::string>& codecs) { std::vector<std::string>& codecs) {
DCHECK_GT(codecs.size(), 0u); DCHECK_GT(codecs.size(), 0u);
base::AutoLock auto_lock(lock_);
if (state_ != WAITING_FOR_INIT && state_ != INITIALIZING)
return kReachedIdLimit;
bool has_audio = false; bool has_audio = false;
bool has_video = false; bool has_video = false;
...@@ -525,55 +527,59 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, ...@@ -525,55 +527,59 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
if (!IsSupported(type, codecs, &factory_function, &has_audio, &has_video)) if (!IsSupported(type, codecs, &factory_function, &has_audio, &has_video))
return kNotSupported; return kNotSupported;
// TODO(acolwell): Support for more than one ID if ((has_audio && !source_id_audio_.empty()) ||
// will be added as part of http://crbug.com/122909 (has_video && !source_id_video_.empty()))
if (!source_id_.empty() ||
(has_audio && has_audio_) ||
(has_video && has_video_))
return kReachedIdLimit; return kReachedIdLimit;
source_id_ = id;
StreamParser::NewBuffersCB audio_cb; StreamParser::NewBuffersCB audio_cb;
StreamParser::NewBuffersCB video_cb; StreamParser::NewBuffersCB video_cb;
if (has_audio) { if (has_audio) {
has_audio_ = true; source_id_audio_ = id;
audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers, audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers,
base::Unretained(this)); base::Unretained(this));
} }
if (has_video) { if (has_video) {
has_video_ = true; source_id_video_ = id;
video_cb = base::Bind(&ChunkDemuxer::OnVideoBuffers, video_cb = base::Bind(&ChunkDemuxer::OnVideoBuffers,
base::Unretained(this)); base::Unretained(this));
} }
stream_parser_.reset(factory_function()); scoped_ptr<StreamParser> stream_parser(factory_function());
CHECK(stream_parser_.get()); CHECK(stream_parser.get());
stream_parser_->Init( stream_parser->Init(
base::Bind(&ChunkDemuxer::OnStreamParserInitDone, this), base::Bind(&ChunkDemuxer::OnStreamParserInitDone, this),
base::Bind(&ChunkDemuxer::OnNewConfigs, base::Unretained(this)), base::Bind(&ChunkDemuxer::OnNewConfigs, base::Unretained(this),
has_audio, has_video),
audio_cb, audio_cb,
video_cb, video_cb,
base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this))); base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this)));
stream_parser_map_[id] = stream_parser.release();
return kOk; return kOk;
} }
void ChunkDemuxer::RemoveId(const std::string& id) { void ChunkDemuxer::RemoveId(const std::string& id) {
CHECK(!source_id_.empty()); CHECK_GT(stream_parser_map_.count(id), 0u);
CHECK_EQ(source_id_, id); base::AutoLock auto_lock(lock_);
source_id_ = "";
has_audio_ = false; delete stream_parser_map_[id];
has_video_ = false; stream_parser_map_.erase(id);
if (source_id_audio_ == id && audio_)
audio_->Shutdown();
if (source_id_video_ == id && video_)
video_->Shutdown();
} }
bool ChunkDemuxer::GetBufferedRanges(const std::string& id, bool ChunkDemuxer::GetBufferedRanges(const std::string& id,
Ranges* ranges_out) const { Ranges* ranges_out) const {
DCHECK(!id.empty()); DCHECK(!id.empty());
DCHECK_EQ(source_id_, id); DCHECK_GT(stream_parser_map_.count(id), 0u);
DCHECK(ranges_out); DCHECK(ranges_out);
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
...@@ -587,16 +593,6 @@ bool ChunkDemuxer::AppendData(const std::string& id, ...@@ -587,16 +593,6 @@ bool ChunkDemuxer::AppendData(const std::string& id,
size_t length) { size_t length) {
DVLOG(1) << "AppendData(" << id << ", " << length << ")"; DVLOG(1) << "AppendData(" << id << ", " << length << ")";
// TODO(acolwell): Remove when http://webk.it/83788 fix lands.
if (source_id_.empty()) {
std::vector<std::string> codecs(2);
codecs[0] = "vp8";
codecs[1] = "vorbis";
AddId(id, "video/webm", codecs);
}
DCHECK(!source_id_.empty());
DCHECK_EQ(source_id_, id);
DCHECK(!id.empty()); DCHECK(!id.empty());
DCHECK(data); DCHECK(data);
DCHECK_GT(length, 0u); DCHECK_GT(length, 0u);
...@@ -612,7 +608,8 @@ bool ChunkDemuxer::AppendData(const std::string& id, ...@@ -612,7 +608,8 @@ bool ChunkDemuxer::AppendData(const std::string& id,
switch (state_) { switch (state_) {
case INITIALIZING: case INITIALIZING:
if (!stream_parser_->Parse(data, length)) { DCHECK_GT(stream_parser_map_.count(id), 0u);
if (!stream_parser_map_[id]->Parse(data, length)) {
DCHECK_EQ(state_, INITIALIZING); DCHECK_EQ(state_, INITIALIZING);
ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
return true; return true;
...@@ -620,7 +617,8 @@ bool ChunkDemuxer::AppendData(const std::string& id, ...@@ -620,7 +617,8 @@ bool ChunkDemuxer::AppendData(const std::string& id,
break; break;
case INITIALIZED: { case INITIALIZED: {
if (!stream_parser_->Parse(data, length)) { DCHECK_GT(stream_parser_map_.count(id), 0u);
if (!stream_parser_map_[id]->Parse(data, length)) {
ReportError_Locked(PIPELINE_ERROR_DECODE); ReportError_Locked(PIPELINE_ERROR_DECODE);
return true; return true;
} }
...@@ -657,9 +655,9 @@ bool ChunkDemuxer::AppendData(const std::string& id, ...@@ -657,9 +655,9 @@ bool ChunkDemuxer::AppendData(const std::string& id,
void ChunkDemuxer::Abort(const std::string& id) { void ChunkDemuxer::Abort(const std::string& id) {
DVLOG(1) << "Abort(" << id << ")"; DVLOG(1) << "Abort(" << id << ")";
DCHECK(!id.empty()); DCHECK(!id.empty());
DCHECK_EQ(source_id_, id); DCHECK_GT(stream_parser_map_.count(id), 0u);
stream_parser_->Flush(); stream_parser_map_[id]->Flush();
} }
bool ChunkDemuxer::EndOfStream(PipelineStatus status) { bool ChunkDemuxer::EndOfStream(PipelineStatus status) {
...@@ -710,7 +708,11 @@ void ChunkDemuxer::Shutdown() { ...@@ -710,7 +708,11 @@ void ChunkDemuxer::Shutdown() {
if (video_) if (video_)
video_->Shutdown(); video_->Shutdown();
stream_parser_.reset(); for (StreamParserMap::iterator it = stream_parser_map_.begin();
it != stream_parser_map_.end(); ++it) {
delete it->second;
}
stream_parser_map_.clear();
ChangeState_Locked(SHUTDOWN); ChangeState_Locked(SHUTDOWN);
} }
...@@ -730,6 +732,11 @@ void ChunkDemuxer::ChangeState_Locked(State new_state) { ...@@ -730,6 +732,11 @@ void ChunkDemuxer::ChangeState_Locked(State new_state) {
ChunkDemuxer::~ChunkDemuxer() { ChunkDemuxer::~ChunkDemuxer() {
DCHECK_NE(state_, INITIALIZED); DCHECK_NE(state_, INITIALIZED);
for (StreamParserMap::iterator it = stream_parser_map_.begin();
it != stream_parser_map_.end(); ++it) {
delete it->second;
}
stream_parser_map_.clear();
} }
void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { void ChunkDemuxer::ReportError_Locked(PipelineStatus error) {
...@@ -794,7 +801,14 @@ void ChunkDemuxer::OnStreamParserInitDone(bool success, ...@@ -794,7 +801,14 @@ void ChunkDemuxer::OnStreamParserInitDone(bool success,
return; return;
} }
duration_ = duration; if (duration > duration_)
duration_ = duration;
// Wait until all streams have initialized.
if ((!source_id_audio_.empty() && !audio_) ||
(!source_id_video_.empty() && !video_))
return;
host_->SetDuration(duration_); host_->SetDuration(duration_);
ChangeState_Locked(INITIALIZED); ChangeState_Locked(INITIALIZED);
...@@ -803,9 +817,11 @@ void ChunkDemuxer::OnStreamParserInitDone(bool success, ...@@ -803,9 +817,11 @@ void ChunkDemuxer::OnStreamParserInitDone(bool success,
cb.Run(PIPELINE_OK); cb.Run(PIPELINE_OK);
} }
bool ChunkDemuxer::OnNewConfigs(const AudioDecoderConfig& audio_config, bool ChunkDemuxer::OnNewConfigs(bool has_audio, bool has_video,
const AudioDecoderConfig& audio_config,
const VideoDecoderConfig& video_config) { const VideoDecoderConfig& video_config) {
DVLOG(1) << "OnNewConfigs(" << audio_config.IsValidConfig() DVLOG(1) << "OnNewConfigs(" << has_audio << ", " << has_video
<< ", " << audio_config.IsValidConfig()
<< ", " << video_config.IsValidConfig() << ")"; << ", " << video_config.IsValidConfig() << ")";
CHECK(audio_config.IsValidConfig() || video_config.IsValidConfig()); CHECK(audio_config.IsValidConfig() || video_config.IsValidConfig());
lock_.AssertAcquired(); lock_.AssertAcquired();
...@@ -813,13 +829,13 @@ bool ChunkDemuxer::OnNewConfigs(const AudioDecoderConfig& audio_config, ...@@ -813,13 +829,13 @@ bool ChunkDemuxer::OnNewConfigs(const AudioDecoderConfig& audio_config,
// Signal an error if we get configuration info for stream types that weren't // Signal an error if we get configuration info for stream types that weren't
// specified in AddId() or more configs after a stream is initialized. // specified in AddId() or more configs after a stream is initialized.
// Only allow a single audio config for now. // Only allow a single audio config for now.
if (audio_config.IsValidConfig() && !has_audio_) { if (has_audio != audio_config.IsValidConfig()) {
DVLOG(1) << "OnNewConfigs() : Got unexpected audio config."; DVLOG(1) << "OnNewConfigs() : Got unexpected audio config.";
return false; return false;
} }
// Only allow a single video config for now. // Only allow a single video config for now.
if (video_config.IsValidConfig() && !has_video_) { if (has_video != video_config.IsValidConfig()) {
DVLOG(1) << "OnNewConfigs() : Got unexpected video config."; DVLOG(1) << "OnNewConfigs() : Got unexpected video config.";
return false; return false;
} }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#ifndef MEDIA_FILTERS_CHUNK_DEMUXER_H_ #ifndef MEDIA_FILTERS_CHUNK_DEMUXER_H_
#define MEDIA_FILTERS_CHUNK_DEMUXER_H_ #define MEDIA_FILTERS_CHUNK_DEMUXER_H_
#include <list> #include <map>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
...@@ -112,7 +112,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { ...@@ -112,7 +112,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// StreamParser callbacks. // StreamParser callbacks.
void OnStreamParserInitDone(bool success, base::TimeDelta duration); void OnStreamParserInitDone(bool success, base::TimeDelta duration);
bool OnNewConfigs(const AudioDecoderConfig& audio_config, bool OnNewConfigs(bool has_audio, bool has_video,
const AudioDecoderConfig& audio_config,
const VideoDecoderConfig& video_config); const VideoDecoderConfig& video_config);
bool OnAudioBuffers(const StreamParser::BufferQueue& buffers); bool OnAudioBuffers(const StreamParser::BufferQueue& buffers);
bool OnVideoBuffers(const StreamParser::BufferQueue& buffers); bool OnVideoBuffers(const StreamParser::BufferQueue& buffers);
...@@ -133,15 +134,14 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { ...@@ -133,15 +134,14 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
base::TimeDelta duration_; base::TimeDelta duration_;
scoped_ptr<StreamParser> stream_parser_; typedef std::map<std::string, StreamParser*> StreamParserMap;
StreamParserMap stream_parser_map_;
// TODO(annacc): Remove these when fixing: http://crbug.com/122909 // Used to ensure that (1) config data matches the type and codec provided in
// Used to ensure config data matches the type and codec provided in AddId(). // AddId(), (2) only 1 audio and 1 video sources are added, and (3) ids may be
bool has_audio_; // removed with RemoveID() but can not be re-added (yet).
bool has_video_; std::string source_id_audio_;
std::string source_id_video_;
// TODO(acolwell): Remove this when fixing http://crbug.com/122909
std::string source_id_;
DISALLOW_COPY_AND_ASSIGN(ChunkDemuxer); DISALLOW_COPY_AND_ASSIGN(ChunkDemuxer);
}; };
......
This diff is collapsed.
...@@ -21,10 +21,13 @@ static const char* kSourceId = "SourceId"; ...@@ -21,10 +21,13 @@ static const char* kSourceId = "SourceId";
// Media Source API. // Media Source API.
class MockMediaSource : public ChunkDemuxerClient { class MockMediaSource : public ChunkDemuxerClient {
public: public:
MockMediaSource(const std::string& filename, int initial_append_size) MockMediaSource(const std::string& filename, int initial_append_size,
bool has_audio, bool has_video)
: url_(GetTestDataURL(filename)), : url_(GetTestDataURL(filename)),
current_position_(0), current_position_(0),
initial_append_size_(initial_append_size) { initial_append_size_(initial_append_size),
has_audio_(has_audio),
has_video_(has_video) {
file_data_ = ReadTestDataFile(filename); file_data_ = ReadTestDataFile(filename);
DCHECK_GT(initial_append_size_, 0); DCHECK_GT(initial_append_size_, 0);
...@@ -77,9 +80,13 @@ class MockMediaSource : public ChunkDemuxerClient { ...@@ -77,9 +80,13 @@ class MockMediaSource : public ChunkDemuxerClient {
virtual void DemuxerOpened(ChunkDemuxer* demuxer) { virtual void DemuxerOpened(ChunkDemuxer* demuxer) {
chunk_demuxer_ = demuxer; chunk_demuxer_ = demuxer;
std::vector<std::string> codecs(2); std::vector<std::string> codecs;
codecs[0] = "vp8"; if (has_audio_)
codecs[1] = "vorbis"; codecs.push_back("vorbis");
if (has_video_)
codecs.push_back("vp8");
chunk_demuxer_->AddId(kSourceId, "video/webm", codecs); chunk_demuxer_->AddId(kSourceId, "video/webm", codecs);
AppendData(initial_append_size_); AppendData(initial_append_size_);
} }
...@@ -103,6 +110,8 @@ class MockMediaSource : public ChunkDemuxerClient { ...@@ -103,6 +110,8 @@ class MockMediaSource : public ChunkDemuxerClient {
scoped_refptr<DecoderBuffer> file_data_; scoped_refptr<DecoderBuffer> file_data_;
int current_position_; int current_position_;
int initial_append_size_; int initial_append_size_;
bool has_audio_;
bool has_video_;
scoped_refptr<ChunkDemuxer> chunk_demuxer_; scoped_refptr<ChunkDemuxer> chunk_demuxer_;
AesDecryptor* decryptor_; AesDecryptor* decryptor_;
}; };
...@@ -132,8 +141,10 @@ class PipelineIntegrationTest ...@@ -132,8 +141,10 @@ class PipelineIntegrationTest
base::TimeDelta start_seek_time, base::TimeDelta start_seek_time,
base::TimeDelta seek_time, base::TimeDelta seek_time,
int seek_file_position, int seek_file_position,
int seek_append_size) { int seek_append_size,
MockMediaSource source(filename, initial_append_size); bool has_audio,
bool has_video) {
MockMediaSource source(filename, initial_append_size, has_audio, has_video);
StartPipelineWithMediaSource(source); StartPipelineWithMediaSource(source);
if (pipeline_status_ != PIPELINE_OK) if (pipeline_status_ != PIPELINE_OK)
...@@ -176,7 +187,7 @@ TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) { ...@@ -176,7 +187,7 @@ TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) {
} }
TEST_F(PipelineIntegrationTest, EncryptedPlayback) { TEST_F(PipelineIntegrationTest, EncryptedPlayback) {
MockMediaSource source("bear-320x240-encrypted.webm", 219726); MockMediaSource source("bear-320x240-encrypted.webm", 219726, true, true);
StartPipelineWithMediaSource(source); StartPipelineWithMediaSource(source);
source.EndOfStream(); source.EndOfStream();
...@@ -238,7 +249,7 @@ TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) { ...@@ -238,7 +249,7 @@ TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) {
ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", 8192, ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", 8192,
base::TimeDelta::FromMilliseconds(464), base::TimeDelta::FromMilliseconds(464),
base::TimeDelta::FromMilliseconds(617), base::TimeDelta::FromMilliseconds(617),
0x10CA, 19730)); 0x10CA, 19730, true, false));
} }
// Verify video decoder & renderer can handle aborted demuxer reads. // Verify video decoder & renderer can handle aborted demuxer reads.
...@@ -246,7 +257,7 @@ TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { ...@@ -246,7 +257,7 @@ TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) {
ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768, ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768,
base::TimeDelta::FromMilliseconds(200), base::TimeDelta::FromMilliseconds(200),
base::TimeDelta::FromMilliseconds(1668), base::TimeDelta::FromMilliseconds(1668),
0x1C896, 65536)); 0x1C896, 65536, false, true));
} }
} // namespace media } // namespace media
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment