summaryrefslogtreecommitdiffstats
path: root/chromecast/media
diff options
context:
space:
mode:
authorgunsch <gunsch@chromium.org>2014-11-20 13:31:07 -0800
committerCommit bot <commit-bot@chromium.org>2014-11-20 21:31:35 +0000
commitbd08add63918abae4ef8fb10b515bef9ce153d14 (patch)
tree9bea26ffeb5b0a13aaf8d925b536b1139640632c /chromecast/media
parent34cf1b98e64108d50969638dc39a1b1bc92ee8f7 (diff)
downloadchromium_src-bd08add63918abae4ef8fb10b515bef9ce153d14.zip
chromium_src-bd08add63918abae4ef8fb10b515bef9ce153d14.tar.gz
chromium_src-bd08add63918abae4ef8fb10b515bef9ce153d14.tar.bz2
Chromecast: adds a media pipeline feeding data to CMA device backends.
The pipeline state transitions are based closely on the interface of ::media::Renderer. The pipeline interfaces themselves are intended to be used both renderer- and browser-side, with future renderer-side implementations using the CMA IPC code for data feeding. R=lcwu@chromium.org,damienv@chromium.org,servolk@chromium.org BUG=408189 Review URL: https://codereview.chromium.org/741863002 Cr-Commit-Position: refs/heads/master@{#305081}
Diffstat (limited to 'chromecast/media')
-rw-r--r--chromecast/media/cma/base/buffering_defs.cc14
-rw-r--r--chromecast/media/cma/base/buffering_defs.h19
-rw-r--r--chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc14
-rw-r--r--chromecast/media/cma/pipeline/audio_pipeline.cc17
-rw-r--r--chromecast/media/cma/pipeline/audio_pipeline.h34
-rw-r--r--chromecast/media/cma/pipeline/audio_pipeline_impl.cc157
-rw-r--r--chromecast/media/cma/pipeline/audio_pipeline_impl.h79
-rw-r--r--chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc211
-rw-r--r--chromecast/media/cma/pipeline/av_pipeline_client.cc17
-rw-r--r--chromecast/media/cma/pipeline/av_pipeline_client.h35
-rw-r--r--chromecast/media/cma/pipeline/av_pipeline_impl.cc382
-rw-r--r--chromecast/media/cma/pipeline/av_pipeline_impl.h172
-rw-r--r--chromecast/media/cma/pipeline/decrypt_util.cc128
-rw-r--r--chromecast/media/cma/pipeline/decrypt_util.h31
-rw-r--r--chromecast/media/cma/pipeline/load_type.h20
-rw-r--r--chromecast/media/cma/pipeline/media_pipeline.h69
-rw-r--r--chromecast/media/cma/pipeline/media_pipeline_client.cc17
-rw-r--r--chromecast/media/cma/pipeline/media_pipeline_client.h37
-rw-r--r--chromecast/media/cma/pipeline/media_pipeline_impl.cc366
-rw-r--r--chromecast/media/cma/pipeline/media_pipeline_impl.h112
-rw-r--r--chromecast/media/cma/pipeline/video_pipeline.cc17
-rw-r--r--chromecast/media/cma/pipeline/video_pipeline.h28
-rw-r--r--chromecast/media/cma/pipeline/video_pipeline_client.cc17
-rw-r--r--chromecast/media/cma/pipeline/video_pipeline_client.h35
-rw-r--r--chromecast/media/cma/pipeline/video_pipeline_impl.cc174
-rw-r--r--chromecast/media/cma/pipeline/video_pipeline_impl.h82
-rw-r--r--chromecast/media/media.gyp52
27 files changed, 2329 insertions, 7 deletions
diff --git a/chromecast/media/cma/base/buffering_defs.cc b/chromecast/media/cma/base/buffering_defs.cc
new file mode 100644
index 0000000..3afd0f8
--- /dev/null
+++ b/chromecast/media/cma/base/buffering_defs.cc
@@ -0,0 +1,14 @@
+// Copyright 2014 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 "chromecast/media/cma/base/buffering_defs.h"
+
+namespace chromecast {
+namespace media {
+
+const size_t kAppAudioBufferSize = 64 * 1024;
+const size_t kAppVideoBufferSize = 4 * 1024 * 1024;
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/base/buffering_defs.h b/chromecast/media/cma/base/buffering_defs.h
new file mode 100644
index 0000000..a56a9a6
--- /dev/null
+++ b/chromecast/media/cma/base/buffering_defs.h
@@ -0,0 +1,19 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_BASE_BUFFERING_DEFS_H_
+#define CHROMECAST_MEDIA_CMA_BASE_BUFFERING_DEFS_H_
+
+#include "base/basictypes.h"
+
+namespace chromecast {
+namespace media {
+
+extern const size_t kAppAudioBufferSize;
+extern const size_t kAppVideoBufferSize;
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_BUFFERING_DEFS_H_
diff --git a/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc b/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc
index bee63eb..d0573ab 100644
--- a/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc
+++ b/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc
@@ -44,12 +44,12 @@ class DummyDemuxerStream : public ::media::DemuxerStream {
virtual ~DummyDemuxerStream();
// ::media::DemuxerStream implementation.
- virtual void Read(const ReadCB& read_cb) override;
- virtual ::media::AudioDecoderConfig audio_decoder_config() override;
- virtual ::media::VideoDecoderConfig video_decoder_config() override;
- virtual Type type() override;
- virtual bool SupportsConfigChanges() override;
- virtual ::media::VideoRotation video_rotation() override;
+ void Read(const ReadCB& read_cb) override;
+ ::media::AudioDecoderConfig audio_decoder_config() override;
+ ::media::VideoDecoderConfig video_decoder_config() override;
+ Type type() const override;
+ bool SupportsConfigChanges() override;
+ ::media::VideoRotation video_rotation() override;
bool has_pending_read() const {
return has_pending_read_;
@@ -127,7 +127,7 @@ void DummyDemuxerStream::Read(const ReadCB& read_cb) {
false);
}
-::media::DemuxerStream::Type DummyDemuxerStream::type() {
+::media::DemuxerStream::Type DummyDemuxerStream::type() const {
return VIDEO;
}
diff --git a/chromecast/media/cma/pipeline/audio_pipeline.cc b/chromecast/media/cma/pipeline/audio_pipeline.cc
new file mode 100644
index 0000000..7d98e09
--- /dev/null
+++ b/chromecast/media/cma/pipeline/audio_pipeline.cc
@@ -0,0 +1,17 @@
+// Copyright 2014 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 "chromecast/media/cma/pipeline/audio_pipeline.h"
+
+namespace chromecast {
+namespace media {
+
+AudioPipeline::AudioPipeline() {
+}
+
+AudioPipeline::~AudioPipeline() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/audio_pipeline.h b/chromecast/media/cma/pipeline/audio_pipeline.h
new file mode 100644
index 0000000..d7284a1
--- /dev/null
+++ b/chromecast/media/cma/pipeline/audio_pipeline.h
@@ -0,0 +1,34 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_PIPELINE_AUDIO_PIPELINE_H_
+#define CHROMECAST_MEDIA_CMA_PIPELINE_AUDIO_PIPELINE_H_
+
+#include "base/macros.h"
+#include "chromecast/media/cma/pipeline/av_pipeline_client.h"
+
+namespace chromecast {
+namespace media {
+
+class AudioPipeline {
+ public:
+ AudioPipeline();
+ virtual ~AudioPipeline();
+
+ // Set the audio client.
+ virtual void SetClient(const AvPipelineClient& client) = 0;
+
+ // Set the stream volume.
+ // - A value of 1.0 is the neutral value.
+ // - |volume|=0.0 mutes the stream.
+ virtual void SetVolume(float volume) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AudioPipeline);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_PIPELINE_AUDIO_PIPELINE_H_
diff --git a/chromecast/media/cma/pipeline/audio_pipeline_impl.cc b/chromecast/media/cma/pipeline/audio_pipeline_impl.cc
new file mode 100644
index 0000000..404031f
--- /dev/null
+++ b/chromecast/media/cma/pipeline/audio_pipeline_impl.cc
@@ -0,0 +1,157 @@
+// Copyright 2014 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 "chromecast/media/cma/pipeline/audio_pipeline_impl.h"
+
+#include "base/bind.h"
+#include "chromecast/media/cma/backend/audio_pipeline_device.h"
+#include "chromecast/media/cma/base/buffering_defs.h"
+#include "chromecast/media/cma/base/cma_logging.h"
+#include "chromecast/media/cma/base/coded_frame_provider.h"
+#include "chromecast/media/cma/pipeline/av_pipeline_impl.h"
+#include "media/base/audio_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+const size_t kMaxAudioFrameSize = 32 * 1024;
+}
+
+AudioPipelineImpl::AudioPipelineImpl(AudioPipelineDevice* audio_device)
+ : audio_device_(audio_device),
+ weak_factory_(this) {
+ av_pipeline_impl_.reset(new AvPipelineImpl(
+ audio_device_,
+ base::Bind(&AudioPipelineImpl::OnUpdateConfig, base::Unretained(this))));
+ weak_this_ = weak_factory_.GetWeakPtr();
+}
+
+AudioPipelineImpl::~AudioPipelineImpl() {
+}
+
+void AudioPipelineImpl::SetCodedFrameProvider(
+ scoped_ptr<CodedFrameProvider> frame_provider) {
+ av_pipeline_impl_->SetCodedFrameProvider(
+ frame_provider.Pass(), kAppAudioBufferSize, kMaxAudioFrameSize);
+}
+
+void AudioPipelineImpl::SetClient(const AvPipelineClient& client) {
+ audio_client_ = client;
+ av_pipeline_impl_->SetClient(client);
+}
+
+bool AudioPipelineImpl::StartPlayingFrom(
+ base::TimeDelta time,
+ const scoped_refptr<BufferingState>& buffering_state) {
+ CMALOG(kLogControl) << "AudioPipelineImpl::StartPlayingFrom t0="
+ << time.InMilliseconds();
+
+ // Reset the pipeline statistics.
+ previous_stats_ = ::media::PipelineStatistics();
+
+ // Start playing.
+ if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError)
+ return false;
+ DCHECK_EQ(av_pipeline_impl_->GetState(), AvPipelineImpl::kFlushed);
+
+ if (!av_pipeline_impl_->StartPlayingFrom(time, buffering_state)) {
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kError);
+ return false;
+ }
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kPlaying);
+
+ return true;
+}
+
+void AudioPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) {
+ CMALOG(kLogControl) << "AudioPipelineImpl::Flush";
+ if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError) {
+ status_cb.Run(::media::PIPELINE_ERROR_ABORT);
+ return;
+ }
+ DCHECK_EQ(av_pipeline_impl_->GetState(), AvPipelineImpl::kPlaying);
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushing);
+ av_pipeline_impl_->Flush(
+ base::Bind(&AudioPipelineImpl::OnFlushDone, weak_this_, status_cb));
+}
+
+void AudioPipelineImpl::OnFlushDone(
+ const ::media::PipelineStatusCB& status_cb) {
+ CMALOG(kLogControl) << "AudioPipelineImpl::OnFlushDone";
+ if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError) {
+ status_cb.Run(::media::PIPELINE_ERROR_ABORT);
+ return;
+ }
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushed);
+ status_cb.Run(::media::PIPELINE_OK);
+}
+
+void AudioPipelineImpl::Stop() {
+ CMALOG(kLogControl) << "AudioPipelineImpl::Stop";
+ av_pipeline_impl_->Stop();
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kStopped);
+}
+
+void AudioPipelineImpl::SetCdm(BrowserCdmCast* media_keys) {
+ av_pipeline_impl_->SetCdm(media_keys);
+}
+
+void AudioPipelineImpl::Initialize(
+ const ::media::AudioDecoderConfig& audio_config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb) {
+ CMALOG(kLogControl) << "AudioPipelineImpl::Initialize "
+ << audio_config.AsHumanReadableString();
+ if (frame_provider)
+ SetCodedFrameProvider(frame_provider.Pass());
+
+ if (!audio_device_->SetConfig(audio_config) ||
+ !av_pipeline_impl_->Initialize()) {
+ status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED);
+ return;
+ }
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushed);
+ status_cb.Run(::media::PIPELINE_OK);
+}
+
+void AudioPipelineImpl::SetVolume(float volume) {
+ audio_device_->SetStreamVolumeMultiplier(volume);
+}
+
+void AudioPipelineImpl::OnUpdateConfig(
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config) {
+ if (audio_config.IsValidConfig()) {
+ CMALOG(kLogControl) << "AudioPipelineImpl::OnUpdateConfig "
+ << audio_config.AsHumanReadableString();
+
+ bool success = audio_device_->SetConfig(audio_config);
+ if (!success && !audio_client_.playback_error_cb.is_null())
+ audio_client_.playback_error_cb.Run(::media::PIPELINE_ERROR_DECODE);
+ }
+}
+
+void AudioPipelineImpl::UpdateStatistics() {
+ if (audio_client_.statistics_cb.is_null())
+ return;
+
+ MediaComponentDevice::Statistics device_stats;
+ if (!audio_device_->GetStatistics(&device_stats))
+ return;
+
+ ::media::PipelineStatistics current_stats;
+ current_stats.audio_bytes_decoded = device_stats.decoded_bytes;
+
+ ::media::PipelineStatistics delta_stats;
+ delta_stats.audio_bytes_decoded =
+ current_stats.audio_bytes_decoded - previous_stats_.audio_bytes_decoded;
+
+ previous_stats_ = current_stats;
+
+ audio_client_.statistics_cb.Run(delta_stats);
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/audio_pipeline_impl.h b/chromecast/media/cma/pipeline/audio_pipeline_impl.h
new file mode 100644
index 0000000..275ef0c
--- /dev/null
+++ b/chromecast/media/cma/pipeline/audio_pipeline_impl.h
@@ -0,0 +1,79 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_BASE_AUDIO_PIPELINE_IMPL_H_
+#define CHROMECAST_MEDIA_CMA_BASE_AUDIO_PIPELINE_IMPL_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "chromecast/media/cma/pipeline/audio_pipeline.h"
+#include "chromecast/media/cma/pipeline/av_pipeline_client.h"
+
+namespace media {
+class AudioDecoderConfig;
+class VideoDecoderConfig;
+}
+
+namespace chromecast {
+namespace media {
+class AudioPipelineDevice;
+class AvPipelineImpl;
+class BrowserCdmCast;
+class BufferingState;
+class CodedFrameProvider;
+
+class AudioPipelineImpl : public AudioPipeline {
+ public:
+ // |buffering_controller| can be NULL.
+ explicit AudioPipelineImpl(AudioPipelineDevice* audio_device);
+ ~AudioPipelineImpl() override;
+
+ // Input port of the pipeline.
+ void SetCodedFrameProvider(scoped_ptr<CodedFrameProvider> frame_provider);
+
+ // Provide the CDM to use to decrypt samples.
+ void SetCdm(BrowserCdmCast* media_keys);
+
+ // Functions to control the state of the audio pipeline.
+ void Initialize(
+ const ::media::AudioDecoderConfig& config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb);
+ bool StartPlayingFrom(base::TimeDelta time,
+ const scoped_refptr<BufferingState>& buffering_state);
+ void Flush(const ::media::PipelineStatusCB& status_cb);
+ void Stop();
+
+ // Update the playback statistics for this audio stream.
+ void UpdateStatistics();
+
+ // AudioPipeline implementation.
+ void SetClient(const AvPipelineClient& client) override;
+ void SetVolume(float volume) override;
+
+ private:
+ void OnFlushDone(const ::media::PipelineStatusCB& status_cb);
+ void OnUpdateConfig(const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config);
+
+ AudioPipelineDevice* audio_device_;
+
+ scoped_ptr<AvPipelineImpl> av_pipeline_impl_;
+ AvPipelineClient audio_client_;
+
+ ::media::PipelineStatistics previous_stats_;
+
+ base::WeakPtrFactory<AudioPipelineImpl> weak_factory_;
+ base::WeakPtr<AudioPipelineImpl> weak_this_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioPipelineImpl);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_AUDIO_PIPELINE_IMPL_H_
diff --git a/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc b/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc
new file mode 100644
index 0000000..86bb29e
--- /dev/null
+++ b/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright 2014 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 <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/time/time.h"
+#include "chromecast/media/cma/backend/audio_pipeline_device.h"
+#include "chromecast/media/cma/backend/media_clock_device.h"
+#include "chromecast/media/cma/backend/media_pipeline_device.h"
+#include "chromecast/media/cma/backend/media_pipeline_device_fake.h"
+#include "chromecast/media/cma/base/buffering_controller.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "chromecast/media/cma/pipeline/audio_pipeline_impl.h"
+#include "chromecast/media/cma/pipeline/av_pipeline_client.h"
+#include "chromecast/media/cma/pipeline/media_pipeline_impl.h"
+#include "chromecast/media/cma/pipeline/video_pipeline_client.h"
+#include "chromecast/media/cma/pipeline/video_pipeline_impl.h"
+#include "chromecast/media/cma/test/frame_generator_for_test.h"
+#include "chromecast/media/cma/test/mock_frame_provider.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/buffers.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/video_decoder_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+
+class AudioVideoPipelineImplTest : public testing::Test {
+ public:
+ AudioVideoPipelineImplTest();
+ virtual ~AudioVideoPipelineImplTest();
+
+ void Initialize(const base::Closure& done_cb,
+ ::media::PipelineStatus status,
+ bool is_audio);
+ void StartPlaying(const base::Closure& done_cb,
+ ::media::PipelineStatus status);
+
+ void Flush(const base::Closure& done_cb, ::media::PipelineStatus status);
+ void Stop(const base::Closure& done_cb, ::media::PipelineStatus status);
+
+ void OnEos();
+
+ base::Closure task_after_eos_cb_;
+
+ private:
+ scoped_ptr<MediaPipelineImpl> media_pipeline_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioVideoPipelineImplTest);
+};
+
+AudioVideoPipelineImplTest::AudioVideoPipelineImplTest()
+ : media_pipeline_(new MediaPipelineImpl()) {
+ scoped_ptr<MediaPipelineDevice> media_pipeline_device(
+ new MediaPipelineDeviceFake());
+ media_pipeline_->Initialize(kLoadTypeURL, media_pipeline_device.Pass());
+ media_pipeline_->SetPlaybackRate(1.0);
+}
+
+AudioVideoPipelineImplTest::~AudioVideoPipelineImplTest() {
+}
+
+void AudioVideoPipelineImplTest::Initialize(
+ const base::Closure& done_cb,
+ ::media::PipelineStatus status,
+ bool is_audio) {
+ if (is_audio) {
+ AvPipelineClient client;
+ client.eos_cb =
+ base::Bind(&AudioVideoPipelineImplTest::OnEos, base::Unretained(this));
+ media_pipeline_->GetAudioPipeline()->SetClient(client);
+ } else {
+ VideoPipelineClient client;
+ client.av_pipeline_client.eos_cb =
+ base::Bind(&AudioVideoPipelineImplTest::OnEos, base::Unretained(this));
+ media_pipeline_->GetVideoPipeline()->SetClient(client);
+ }
+
+ ::media::AudioDecoderConfig audio_config(
+ ::media::kCodecMP3,
+ ::media::kSampleFormatS16,
+ ::media::CHANNEL_LAYOUT_STEREO,
+ 44100,
+ NULL, 0, false);
+ ::media::VideoDecoderConfig video_config(
+ ::media::kCodecH264,
+ ::media::H264PROFILE_MAIN,
+ ::media::VideoFrame::I420,
+ gfx::Size(640, 480),
+ gfx::Rect(0, 0, 640, 480),
+ gfx::Size(640, 480),
+ NULL, 0, false);
+
+ // Frame generation on the producer side.
+ std::vector<FrameGeneratorForTest::FrameSpec> frame_specs;
+ frame_specs.resize(100);
+ for (size_t k = 0; k < frame_specs.size() - 1; k++) {
+ frame_specs[k].has_config = (k == 0);
+ frame_specs[k].timestamp = base::TimeDelta::FromMilliseconds(40) * k;
+ frame_specs[k].size = 512;
+ frame_specs[k].has_decrypt_config = false;
+ }
+ frame_specs[frame_specs.size() - 1].is_eos = true;
+
+ scoped_ptr<FrameGeneratorForTest> frame_generator_provider(
+ new FrameGeneratorForTest(frame_specs));
+ bool provider_delayed_pattern[] = { true, false };
+ scoped_ptr<MockFrameProvider> frame_provider(new MockFrameProvider());
+ frame_provider->Configure(
+ std::vector<bool>(
+ provider_delayed_pattern,
+ provider_delayed_pattern + arraysize(provider_delayed_pattern)),
+ frame_generator_provider.Pass());
+
+ ::media::PipelineStatusCB next_task =
+ base::Bind(&AudioVideoPipelineImplTest::StartPlaying,
+ base::Unretained(this),
+ done_cb);
+
+ scoped_ptr<CodedFrameProvider> frame_provider_base(frame_provider.release());
+ base::Closure task = is_audio ?
+ base::Bind(&MediaPipeline::InitializeAudio,
+ base::Unretained(media_pipeline_.get()),
+ audio_config,
+ base::Passed(&frame_provider_base),
+ next_task) :
+ base::Bind(&MediaPipeline::InitializeVideo,
+ base::Unretained(media_pipeline_.get()),
+ video_config,
+ base::Passed(&frame_provider_base),
+ next_task);
+
+ base::MessageLoopProxy::current()->PostTask(FROM_HERE, task);
+}
+
+void AudioVideoPipelineImplTest::StartPlaying(
+ const base::Closure& done_cb, ::media::PipelineStatus status) {
+ base::TimeDelta start_time = base::TimeDelta::FromMilliseconds(0);
+
+ media_pipeline_->StartPlayingFrom(start_time);
+ if (!done_cb.is_null())
+ done_cb.Run();
+}
+
+void AudioVideoPipelineImplTest::OnEos() {
+ task_after_eos_cb_.Run();
+}
+
+void AudioVideoPipelineImplTest::Flush(
+ const base::Closure& done_cb, ::media::PipelineStatus status) {
+ ::media::PipelineStatusCB next_task =
+ base::Bind(&AudioVideoPipelineImplTest::Stop, base::Unretained(this),
+ done_cb);
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MediaPipeline::Flush,
+ base::Unretained(media_pipeline_.get()),
+ next_task));
+}
+
+void AudioVideoPipelineImplTest::Stop(
+ const base::Closure& done_cb, ::media::PipelineStatus status) {
+ media_pipeline_->Stop();
+ if (!done_cb.is_null())
+ done_cb.Run();
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+
+TEST_F(AudioVideoPipelineImplTest, AudioFullCycleInitToStop) {
+ bool is_audio = true;
+ task_after_eos_cb_ = base::Bind(
+ &AudioVideoPipelineImplTest::Flush, base::Unretained(this),
+ base::Closure(), ::media::PIPELINE_OK);
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&AudioVideoPipelineImplTest::Initialize,
+ base::Unretained(this),
+ base::Closure(),
+ ::media::PIPELINE_OK, is_audio));
+ message_loop->Run();
+};
+
+TEST_F(AudioVideoPipelineImplTest, VideoFullCycleInitToStop) {
+ bool is_audio = false;
+ task_after_eos_cb_ = base::Bind(
+ &AudioVideoPipelineImplTest::Flush, base::Unretained(this),
+ base::Closure(), ::media::PIPELINE_OK);
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&AudioVideoPipelineImplTest::Initialize,
+ base::Unretained(this),
+ base::Closure(),
+ ::media::PIPELINE_OK, is_audio));
+ message_loop->Run();
+};
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/av_pipeline_client.cc b/chromecast/media/cma/pipeline/av_pipeline_client.cc
new file mode 100644
index 0000000..1845c6c
--- /dev/null
+++ b/chromecast/media/cma/pipeline/av_pipeline_client.cc
@@ -0,0 +1,17 @@
+// Copyright 2014 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 "chromecast/media/cma/pipeline/av_pipeline_client.h"
+
+namespace chromecast {
+namespace media {
+
+AvPipelineClient::AvPipelineClient() {
+}
+
+AvPipelineClient::~AvPipelineClient() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/av_pipeline_client.h b/chromecast/media/cma/pipeline/av_pipeline_client.h
new file mode 100644
index 0000000..6928716
--- /dev/null
+++ b/chromecast/media/cma/pipeline/av_pipeline_client.h
@@ -0,0 +1,35 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_PIPELINE_AV_PIPELINE_CLIENT_H_
+#define CHROMECAST_MEDIA_CMA_PIPELINE_AV_PIPELINE_CLIENT_H_
+
+#include "base/callback.h"
+#include "base/time/time.h"
+#include "media/base/pipeline_status.h"
+
+namespace chromecast {
+namespace media {
+
+struct AvPipelineClient {
+ typedef base::Callback<void(
+ base::TimeDelta, base::TimeDelta, base::TimeTicks)> TimeUpdateCB;
+
+ AvPipelineClient();
+ ~AvPipelineClient();
+
+ // End of stream notification.
+ base::Closure eos_cb;
+
+ // Asynchronous playback error notification.
+ ::media::PipelineStatusCB playback_error_cb;
+
+ // Callback used to report the playback statistics.
+ ::media::StatisticsCB statistics_cb;
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_PIPELINE_AV_PIPELINE_CLIENT_H_
diff --git a/chromecast/media/cma/pipeline/av_pipeline_impl.cc b/chromecast/media/cma/pipeline/av_pipeline_impl.cc
new file mode 100644
index 0000000..dd80a0a5
--- /dev/null
+++ b/chromecast/media/cma/pipeline/av_pipeline_impl.cc
@@ -0,0 +1,382 @@
+// Copyright 2014 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 "chromecast/media/cma/pipeline/av_pipeline_impl.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/strings/string_number_conversions.h"
+#include "chromecast/media/base/decrypt_context.h"
+#include "chromecast/media/cdm/browser_cdm_cast.h"
+#include "chromecast/media/cma/backend/media_clock_device.h"
+#include "chromecast/media/cma/backend/media_component_device.h"
+#include "chromecast/media/cma/base/buffering_frame_provider.h"
+#include "chromecast/media/cma/base/buffering_state.h"
+#include "chromecast/media/cma/base/cma_logging.h"
+#include "chromecast/media/cma/base/coded_frame_provider.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "chromecast/media/cma/pipeline/decrypt_util.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/decrypt_config.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+const int kNoCallbackId = -1;
+
+} // namespace
+
+AvPipelineImpl::AvPipelineImpl(
+ MediaComponentDevice* media_component_device,
+ const UpdateConfigCB& update_config_cb)
+ : update_config_cb_(update_config_cb),
+ media_component_device_(media_component_device),
+ state_(kUninitialized),
+ buffered_time_(::media::kNoTimestamp()),
+ playable_buffered_time_(::media::kNoTimestamp()),
+ enable_feeding_(false),
+ pending_read_(false),
+ pending_push_(false),
+ enable_time_update_(false),
+ pending_time_update_task_(false),
+ media_keys_(NULL),
+ media_keys_callback_id_(kNoCallbackId),
+ weak_factory_(this) {
+ DCHECK(media_component_device);
+ weak_this_ = weak_factory_.GetWeakPtr();
+ thread_checker_.DetachFromThread();
+}
+
+AvPipelineImpl::~AvPipelineImpl() {
+ // If there are weak pointers in the wild, they must be invalidated
+ // on the right thread.
+ DCHECK(thread_checker_.CalledOnValidThread());
+ media_component_device_->SetClient(MediaComponentDevice::Client());
+
+ if (media_keys_ && media_keys_callback_id_ != kNoCallbackId)
+ media_keys_->UnregisterPlayer(media_keys_callback_id_);
+}
+
+void AvPipelineImpl::TransitionToState(State state) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ state_ = state;
+}
+
+void AvPipelineImpl::SetCodedFrameProvider(
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ size_t max_buffer_size,
+ size_t max_frame_size) {
+ DCHECK_EQ(state_, kUninitialized);
+ DCHECK(frame_provider);
+
+ // Wrap the incoming frame provider to add some buffering capabilities.
+ frame_provider_.reset(
+ new BufferingFrameProvider(
+ frame_provider.Pass(),
+ max_buffer_size,
+ max_frame_size,
+ base::Bind(&AvPipelineImpl::OnFrameBuffered, weak_this_)));
+}
+
+void AvPipelineImpl::SetClient(const AvPipelineClient& client) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(state_, kUninitialized);
+ client_ = client;
+}
+
+bool AvPipelineImpl::Initialize() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(state_, kUninitialized);
+
+ MediaComponentDevice::Client client;
+ client.eos_cb = base::Bind(&AvPipelineImpl::OnEos, weak_this_);
+ media_component_device_->SetClient(client);
+ if (!media_component_device_->SetState(MediaComponentDevice::kStateIdle))
+ return false;
+
+ return true;
+}
+
+bool AvPipelineImpl::StartPlayingFrom(
+ base::TimeDelta time,
+ const scoped_refptr<BufferingState>& buffering_state) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(state_, kFlushed);
+
+ // Media time where rendering should start
+ // and switch to a state where the audio device accepts incoming buffers.
+ if (!media_component_device_->SetStartPts(time) ||
+ !media_component_device_->SetState(MediaComponentDevice::kStatePaused)) {
+ return false;
+ }
+
+ // Buffering related initialization.
+ DCHECK(frame_provider_);
+ buffering_state_ = buffering_state;
+ if (buffering_state_.get())
+ buffering_state_->SetMediaTime(time);
+
+ if (!media_component_device_->SetState(MediaComponentDevice::kStateRunning))
+ return false;
+
+ // Start feeding the pipeline.
+ enable_feeding_ = true;
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_));
+
+ return true;
+}
+
+void AvPipelineImpl::Flush(const base::Closure& done_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(state_, kFlushing);
+ DCHECK_EQ(
+ media_component_device_->GetState(), MediaComponentDevice::kStateRunning);
+ // Note: returning to idle state aborts any pending frame push.
+ media_component_device_->SetState(MediaComponentDevice::kStateIdle);
+ pending_push_ = false;
+
+ // Break the feeding loop.
+ enable_feeding_ = false;
+
+ // Remove any pending buffer.
+ pending_buffer_ = scoped_refptr<DecoderBufferBase>();
+
+ // Finally, remove any frames left in the frame provider.
+ pending_read_ = false;
+ buffered_time_ = ::media::kNoTimestamp();
+ playable_buffered_time_ = ::media::kNoTimestamp();
+ non_playable_frames_.clear();
+ frame_provider_->Flush(done_cb);
+}
+
+void AvPipelineImpl::Stop() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Stop can be called from any state.
+ if (state_ == kUninitialized || state_ == kStopped)
+ return;
+
+ // Stop feeding the pipeline.
+ enable_feeding_ = false;
+
+ // Release hardware resources on Stop.
+ if (media_component_device_->GetState() ==
+ MediaComponentDevice::kStatePaused ||
+ media_component_device_->GetState() ==
+ MediaComponentDevice::kStateRunning) {
+ media_component_device_->SetState(MediaComponentDevice::kStateIdle);
+ }
+ if (media_component_device_->GetState() == MediaComponentDevice::kStateIdle) {
+ media_component_device_->SetState(
+ MediaComponentDevice::kStateUninitialized);
+ }
+}
+
+void AvPipelineImpl::SetCdm(BrowserCdmCast* media_keys) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(media_keys);
+
+ if (media_keys_ && media_keys_callback_id_ != kNoCallbackId)
+ media_keys_->UnregisterPlayer(media_keys_callback_id_);
+
+ media_keys_ = media_keys;
+ media_keys_callback_id_ = media_keys_->RegisterPlayer(
+ base::Bind(&AvPipelineImpl::OnCdmStateChanged, weak_this_),
+ base::Bind(&AvPipelineImpl::OnCdmDestroyed, weak_this_));
+}
+
+void AvPipelineImpl::OnEos() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ CMALOG(kLogControl) << __FUNCTION__;
+ if (state_ != kPlaying)
+ return;
+
+ if (!client_.eos_cb.is_null())
+ client_.eos_cb.Run();
+}
+
+void AvPipelineImpl::FetchBufferIfNeeded() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!enable_feeding_)
+ return;
+
+ if (pending_read_ || pending_buffer_.get())
+ return;
+
+ pending_read_ = true;
+ frame_provider_->Read(
+ base::Bind(&AvPipelineImpl::OnNewFrame, weak_this_));
+}
+
+void AvPipelineImpl::OnNewFrame(
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ pending_read_ = false;
+
+ if (audio_config.IsValidConfig() || video_config.IsValidConfig())
+ update_config_cb_.Run(audio_config, video_config);
+
+ pending_buffer_ = buffer;
+ ProcessPendingBuffer();
+
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_));
+}
+
+void AvPipelineImpl::ProcessPendingBuffer() {
+ if (!enable_feeding_)
+ return;
+
+ // Initiate a read if there isn't already one.
+ if (!pending_buffer_.get() && !pending_read_) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_));
+ return;
+ }
+
+ if (!pending_buffer_.get() || pending_push_)
+ return;
+
+ // Break the feeding loop when the end of stream is reached.
+ if (pending_buffer_->end_of_stream()) {
+ CMALOG(kLogControl) << __FUNCTION__ << ": EOS reached, stopped feeding";
+ enable_feeding_ = false;
+ }
+
+ scoped_refptr<DecryptContext> decrypt_context;
+ if (!pending_buffer_->end_of_stream() &&
+ pending_buffer_->decrypt_config()) {
+ // Verify that CDM has the key ID.
+ // Should not send the frame if the key ID is not available yet.
+ std::string key_id(pending_buffer_->decrypt_config()->key_id());
+ if (!media_keys_) {
+ CMALOG(kLogControl) << "No CDM for frame: pts="
+ << pending_buffer_->timestamp().InMilliseconds();
+ return;
+ }
+ decrypt_context = media_keys_->GetDecryptContext(key_id);
+ if (!decrypt_context.get()) {
+ CMALOG(kLogControl) << "frame(pts="
+ << pending_buffer_->timestamp().InMilliseconds()
+ << "): waiting for key id "
+ << base::HexEncode(&key_id[0], key_id.size());
+ return;
+ }
+
+ // If we do have the clear key, decrypt the pending buffer
+ // and reset the decryption context (not needed anymore).
+ crypto::SymmetricKey* key = decrypt_context->GetKey();
+ if (key != NULL) {
+ pending_buffer_ = DecryptDecoderBuffer(pending_buffer_, key);
+ decrypt_context = scoped_refptr<DecryptContext>();
+ }
+ }
+
+ MediaComponentDevice::FrameStatus status = media_component_device_->PushFrame(
+ decrypt_context,
+ pending_buffer_,
+ base::Bind(&AvPipelineImpl::OnFramePushed, weak_this_));
+ pending_buffer_ = scoped_refptr<DecoderBufferBase>();
+
+ pending_push_ = (status == MediaComponentDevice::kFramePending);
+ if (!pending_push_)
+ OnFramePushed(status);
+}
+
+void AvPipelineImpl::OnFramePushed(MediaComponentDevice::FrameStatus status) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ pending_push_ = false;
+ if (status == MediaComponentDevice::kFrameFailed) {
+ LOG(WARNING) << "AvPipelineImpl: PushFrame failed";
+ enable_feeding_ = false;
+ state_ = kError;
+ return;
+ }
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AvPipelineImpl::ProcessPendingBuffer, weak_this_));
+}
+
+void AvPipelineImpl::OnCdmStateChanged() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Update the buffering state if needed.
+ if (buffering_state_.get())
+ UpdatePlayableFrames();
+
+ // Process the pending buffer in case the CDM now has the frame key id.
+ ProcessPendingBuffer();
+}
+
+void AvPipelineImpl::OnCdmDestroyed() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ media_keys_ = NULL;
+}
+
+void AvPipelineImpl::OnFrameBuffered(
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ bool is_at_max_capacity) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!buffering_state_.get())
+ return;
+
+ if (!buffer->end_of_stream() &&
+ (buffered_time_ == ::media::kNoTimestamp() ||
+ buffered_time_ < buffer->timestamp())) {
+ buffered_time_ = buffer->timestamp();
+ }
+
+ if (is_at_max_capacity)
+ buffering_state_->NotifyMaxCapacity(buffered_time_);
+
+ // No need to update the list of playable frames,
+ // if we are already blocking on a frame.
+ bool update_playable_frames = non_playable_frames_.empty();
+ non_playable_frames_.push_back(buffer);
+ if (update_playable_frames)
+ UpdatePlayableFrames();
+}
+
+void AvPipelineImpl::UpdatePlayableFrames() {
+ while (!non_playable_frames_.empty()) {
+ const scoped_refptr<DecoderBufferBase>& non_playable_frame =
+ non_playable_frames_.front();
+
+ if (non_playable_frame->end_of_stream()) {
+ buffering_state_->NotifyEos();
+ } else {
+ const ::media::DecryptConfig* decrypt_config =
+ non_playable_frame->decrypt_config();
+ if (decrypt_config &&
+ !(media_keys_ &&
+ media_keys_->GetDecryptContext(decrypt_config->key_id()).get())) {
+ // The frame is still not playable. All the following are thus not
+ // playable.
+ break;
+ }
+
+ if (playable_buffered_time_ == ::media::kNoTimestamp() ||
+ playable_buffered_time_ < non_playable_frame->timestamp()) {
+ playable_buffered_time_ = non_playable_frame->timestamp();
+ buffering_state_->SetBufferedTime(playable_buffered_time_);
+ }
+ }
+
+ // The frame is playable: remove it from the list of non playable frames.
+ non_playable_frames_.pop_front();
+ }
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/av_pipeline_impl.h b/chromecast/media/cma/pipeline/av_pipeline_impl.h
new file mode 100644
index 0000000..4b8856f
--- /dev/null
+++ b/chromecast/media/cma/pipeline/av_pipeline_impl.h
@@ -0,0 +1,172 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_BASE_AV_PIPELINE_IMPL_H_
+#define CHROMECAST_MEDIA_CMA_BASE_AV_PIPELINE_IMPL_H_
+
+#include <list>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "chromecast/media/cma/backend/media_component_device.h"
+#include "chromecast/media/cma/pipeline/av_pipeline_client.h"
+
+namespace media {
+class AudioDecoderConfig;
+class VideoDecoderConfig;
+}
+
+namespace chromecast {
+namespace media {
+class BrowserCdmCast;
+class BufferingFrameProvider;
+class BufferingState;
+class CodedFrameProvider;
+class DecoderBufferBase;
+class MediaComponentDevice;
+
+class AvPipelineImpl {
+ public:
+ // Pipeline states.
+ enum State {
+ kUninitialized,
+ kPlaying,
+ kFlushing,
+ kFlushed,
+ kStopped,
+ kError,
+ };
+
+ typedef base::Callback<
+ void(const ::media::AudioDecoderConfig&,
+ const ::media::VideoDecoderConfig&)> UpdateConfigCB;
+
+ AvPipelineImpl(
+ MediaComponentDevice* media_component_device,
+ const UpdateConfigCB& update_config_cb);
+ ~AvPipelineImpl();
+
+ // Setting the frame provider or the client must be done in the
+ // |kUninitialized| state.
+ void SetCodedFrameProvider(scoped_ptr<CodedFrameProvider> frame_provider,
+ size_t max_buffer_size,
+ size_t max_frame_size);
+ void SetClient(const AvPipelineClient& client);
+
+ // Initialize the pipeline.
+ bool Initialize();
+
+ // Setup the pipeline and ensure samples are available for the given media
+ // time, then start rendering samples.
+ bool StartPlayingFrom(base::TimeDelta time,
+ const scoped_refptr<BufferingState>& buffering_state);
+
+ // Flush any remaining samples in the pipeline.
+ // Invoke |done_cb| when flush is completed.
+ void Flush(const base::Closure& done_cb);
+
+ // Tear down the pipeline and release the hardware resources.
+ void Stop();
+
+ State GetState() const { return state_; }
+ void TransitionToState(State state);
+
+ void SetCdm(BrowserCdmCast* media_keys);
+
+ private:
+ // Callback invoked when the CDM state has changed in a way that might
+ // impact media playback.
+ void OnCdmStateChange();
+
+ // Callback invoked when playback has reached the end of stream.
+ void OnEos();
+
+ // Feed the pipeline, getting the frames from |frame_provider_|.
+ void FetchBufferIfNeeded();
+
+ // Callback invoked when receiving a new frame from |frame_provider_|.
+ void OnNewFrame(const scoped_refptr<DecoderBufferBase>& buffer,
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config);
+
+ // Process a pending buffer.
+ void ProcessPendingBuffer();
+
+ void OnFramePushed(MediaComponentDevice::FrameStatus status);
+
+ // Callbacks:
+ // - when BrowserCdm updated its state.
+ // - when BrowserCdm has been destroyed.
+ void OnCdmStateChanged();
+ void OnCdmDestroyed();
+
+ // Callback invoked when a frame has been buffered by |frame_provider_|
+ // which is a BufferingFrameProvider.
+ void OnFrameBuffered(const scoped_refptr<DecoderBufferBase>& buffer,
+ bool is_at_max_capacity);
+ void UpdatePlayableFrames();
+
+ base::ThreadChecker thread_checker_;
+
+ UpdateConfigCB update_config_cb_;
+
+ AvPipelineClient client_;
+
+ // Backends.
+ MediaComponentDevice* media_component_device_;
+
+ // AV pipeline state.
+ State state_;
+
+ // Buffering state.
+ // Can be NULL if there is no buffering strategy.
+ scoped_refptr<BufferingState> buffering_state_;
+
+ // |buffered_time_| is the maximum timestamp of buffered frames.
+ // |playable_buffered_time_| is the maximum timestamp of buffered and
+ // playable frames (i.e. the key id is available for those frames).
+ base::TimeDelta buffered_time_;
+ base::TimeDelta playable_buffered_time_;
+
+ // List of frames buffered but not playable right away due to a missing
+ // key id.
+ std::list<scoped_refptr<DecoderBufferBase> > non_playable_frames_;
+
+ // Buffer provider.
+ scoped_ptr<BufferingFrameProvider> frame_provider_;
+
+ // Indicate whether the frame fetching process is active.
+ bool enable_feeding_;
+
+ // Indicate whether there is a pending buffer read.
+ bool pending_read_;
+
+ // Pending buffer.
+ scoped_refptr<DecoderBufferBase> pending_buffer_;
+
+ // Indicate if there is a frame being pushed to the audio device.
+ bool pending_push_;
+
+ // The media time is retrieved at regular intervals.
+ // Indicate whether time update is enabled.
+ bool enable_time_update_;
+ bool pending_time_update_task_;
+
+ // Decryption keys, if available.
+ BrowserCdmCast* media_keys_;
+ int media_keys_callback_id_;
+
+ base::WeakPtrFactory<AvPipelineImpl> weak_factory_;
+ base::WeakPtr<AvPipelineImpl> weak_this_;
+
+ DISALLOW_COPY_AND_ASSIGN(AvPipelineImpl);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_AV_PIPELINE_IMPL_H_
diff --git a/chromecast/media/cma/pipeline/decrypt_util.cc b/chromecast/media/cma/pipeline/decrypt_util.cc
new file mode 100644
index 0000000..04d5a47
--- /dev/null
+++ b/chromecast/media/cma/pipeline/decrypt_util.cc
@@ -0,0 +1,128 @@
+// Copyright 2014 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 "chromecast/media/cma/pipeline/decrypt_util.h"
+
+#include <openssl/aes.h>
+#include <string>
+
+#include "base/logging.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "crypto/symmetric_key.h"
+#include "media/base/decrypt_config.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+class DecoderBufferClear : public DecoderBufferBase {
+ public:
+ explicit DecoderBufferClear(const scoped_refptr<DecoderBufferBase>& buffer);
+
+ // DecoderBufferBase implementation.
+ base::TimeDelta timestamp() const override;
+ const uint8* data() const override;
+ uint8* writable_data() const override;
+ int data_size() const override;
+ const ::media::DecryptConfig* decrypt_config() const override;
+ bool end_of_stream() const override;
+
+ private:
+ virtual ~DecoderBufferClear();
+
+ scoped_refptr<DecoderBufferBase> const buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(DecoderBufferClear);
+};
+
+DecoderBufferClear::DecoderBufferClear(
+ const scoped_refptr<DecoderBufferBase>& buffer)
+ : buffer_(buffer) {
+}
+
+DecoderBufferClear::~DecoderBufferClear() {
+}
+
+base::TimeDelta DecoderBufferClear::timestamp() const {
+ return buffer_->timestamp();
+}
+
+const uint8* DecoderBufferClear::data() const {
+ return buffer_->data();
+}
+
+uint8* DecoderBufferClear::writable_data() const {
+ return buffer_->writable_data();
+}
+
+int DecoderBufferClear::data_size() const {
+ return buffer_->data_size();
+}
+
+const ::media::DecryptConfig* DecoderBufferClear::decrypt_config() const {
+ // Buffer is clear so no decryption info.
+ return NULL;
+}
+
+bool DecoderBufferClear::end_of_stream() const {
+ return buffer_->end_of_stream();
+}
+
+} // namespace
+
+scoped_refptr<DecoderBufferBase> DecryptDecoderBuffer(
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ crypto::SymmetricKey* key) {
+ if (buffer->end_of_stream())
+ return buffer;
+
+ const ::media::DecryptConfig* decrypt_config = buffer->decrypt_config();
+ if (!decrypt_config || decrypt_config->iv().size() == 0)
+ return buffer;
+
+ // Get the key.
+ std::string raw_key;
+ if (!key->GetRawKey(&raw_key)) {
+ LOG(ERROR) << "Failed to get the underlying AES key";
+ return buffer;
+ }
+ DCHECK_EQ(static_cast<int>(raw_key.length()), AES_BLOCK_SIZE);
+ const uint8* key_u8 = reinterpret_cast<const uint8*>(raw_key.data());
+ AES_KEY aes_key;
+ if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) {
+ LOG(ERROR) << "Failed to set the AES key";
+ return buffer;
+ }
+
+ // Get the IV.
+ uint8 aes_iv[AES_BLOCK_SIZE];
+ DCHECK_EQ(static_cast<int>(decrypt_config->iv().length()),
+ AES_BLOCK_SIZE);
+ memcpy(aes_iv, decrypt_config->iv().data(), AES_BLOCK_SIZE);
+
+ // Decryption state.
+ unsigned int encrypted_byte_offset = 0;
+ uint8 ecount_buf[AES_BLOCK_SIZE];
+
+ // Perform the decryption.
+ const std::vector< ::media::SubsampleEntry>& subsamples =
+ decrypt_config->subsamples();
+ uint8* data = buffer->writable_data();
+ uint32 offset = 0;
+ for (size_t k = 0; k < subsamples.size(); k++) {
+ offset += subsamples[k].clear_bytes;
+ uint32 cypher_bytes = subsamples[k].cypher_bytes;
+ CHECK_LE(offset + cypher_bytes, buffer->data_size());
+ AES_ctr128_encrypt(
+ data + offset, data + offset, cypher_bytes, &aes_key,
+ aes_iv, ecount_buf, &encrypted_byte_offset);
+ offset += cypher_bytes;
+ }
+
+ return scoped_refptr<DecoderBufferBase>(new DecoderBufferClear(buffer));
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/decrypt_util.h b/chromecast/media/cma/pipeline/decrypt_util.h
new file mode 100644
index 0000000..5a59afc
--- /dev/null
+++ b/chromecast/media/cma/pipeline/decrypt_util.h
@@ -0,0 +1,31 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_PIPELINE_DECRYPT_UTIL_H_
+#define CHROMECAST_MEDIA_CMA_PIPELINE_DECRYPT_UTIL_H_
+
+#include "base/memory/ref_counted.h"
+
+namespace crypto {
+class SymmetricKey;
+}
+
+namespace chromecast {
+namespace media {
+
+class DecoderBufferBase;
+
+// Create a new buffer which corresponds to the clear version of |buffer|.
+// Note: the memory area corresponding to the ES data of the new buffer
+// is the same as the ES data of |buffer| (for efficiency).
+// After the function is called, |buffer| is left in a inconsistent state
+// in the sense it has some decryption info but the ES data is now in clear.
+scoped_refptr<DecoderBufferBase> DecryptDecoderBuffer(
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ crypto::SymmetricKey* key);
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_PIPELINE_DECRYPT_UTIL_H_
diff --git a/chromecast/media/cma/pipeline/load_type.h b/chromecast/media/cma/pipeline/load_type.h
new file mode 100644
index 0000000..569b2ef
--- /dev/null
+++ b/chromecast/media/cma/pipeline/load_type.h
@@ -0,0 +1,20 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_PIPELINE_LOAD_TYPE_H_
+#define CHROMECAST_MEDIA_CMA_PIPELINE_LOAD_TYPE_H_
+
+namespace chromecast {
+namespace media {
+
+enum LoadType {
+ kLoadTypeURL,
+ kLoadTypeMediaSource,
+ kLoadTypeMediaStream,
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_PIPELINE_LOAD_TYPE_H_
diff --git a/chromecast/media/cma/pipeline/media_pipeline.h b/chromecast/media/cma/pipeline/media_pipeline.h
new file mode 100644
index 0000000..2b82c4f
--- /dev/null
+++ b/chromecast/media/cma/pipeline/media_pipeline.h
@@ -0,0 +1,69 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_H_
+#define CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_H_
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "media/base/pipeline_status.h"
+
+namespace media {
+class AudioDecoderConfig;
+class BrowserCdm;
+class VideoDecoderConfig;
+}
+
+namespace chromecast {
+namespace media {
+class AudioPipeline;
+class CodedFrameProvider;
+struct MediaPipelineClient;
+class VideoPipeline;
+
+class MediaPipeline {
+ public:
+ MediaPipeline() {}
+ virtual ~MediaPipeline() {}
+
+ // Set the media pipeline client.
+ virtual void SetClient(const MediaPipelineClient& client) = 0;
+
+ // Set the CDM to use for decryption.
+ // The CDM is refered by its id.
+ virtual void SetCdm(int cdm_id) = 0;
+
+ // Return the audio/video pipeline owned by the MediaPipeline.
+ virtual AudioPipeline* GetAudioPipeline() const = 0;
+ virtual VideoPipeline* GetVideoPipeline() const = 0;
+
+ // Create an audio/video pipeline.
+ // MediaPipeline owns the resulting audio/video pipeline.
+ // Only one audio and one video pipeline can be created.
+ virtual void InitializeAudio(
+ const ::media::AudioDecoderConfig& config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb) = 0;
+ virtual void InitializeVideo(
+ const ::media::VideoDecoderConfig& config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb) = 0;
+
+ // Control the media pipeline state machine.
+ virtual void StartPlayingFrom(base::TimeDelta time) = 0;
+ virtual void Flush(const ::media::PipelineStatusCB& status_cb) = 0;
+ virtual void Stop() = 0;
+
+ // Set the playback rate.
+ virtual void SetPlaybackRate(float playback_rate) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MediaPipeline);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_H_
diff --git a/chromecast/media/cma/pipeline/media_pipeline_client.cc b/chromecast/media/cma/pipeline/media_pipeline_client.cc
new file mode 100644
index 0000000..8dbfc8d
--- /dev/null
+++ b/chromecast/media/cma/pipeline/media_pipeline_client.cc
@@ -0,0 +1,17 @@
+// Copyright 2014 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 "chromecast/media/cma/pipeline/media_pipeline_client.h"
+
+namespace chromecast {
+namespace media {
+
+MediaPipelineClient::MediaPipelineClient() {
+}
+
+MediaPipelineClient::~MediaPipelineClient() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/media_pipeline_client.h b/chromecast/media/cma/pipeline/media_pipeline_client.h
new file mode 100644
index 0000000..692263c
--- /dev/null
+++ b/chromecast/media/cma/pipeline/media_pipeline_client.h
@@ -0,0 +1,37 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_CLIENT_H_
+#define CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_CLIENT_H_
+
+#include "base/callback.h"
+#include "base/time/time.h"
+#include "media/base/buffering_state.h"
+#include "media/base/pipeline_status.h"
+
+namespace chromecast {
+namespace media {
+
+struct MediaPipelineClient {
+ typedef base::Callback<void(
+ base::TimeDelta, base::TimeDelta, base::TimeTicks)> TimeUpdateCB;
+
+ MediaPipelineClient();
+ ~MediaPipelineClient();
+
+ // Callback used to report a playback error as a ::media::PipelineStatus.
+ ::media::PipelineStatusCB error_cb;
+
+ // Callback used to report the latest playback time,
+ // as well as the maximum time available for rendering.
+ TimeUpdateCB time_update_cb;
+
+ // Callback used to report the buffering status.
+ ::media::BufferingStateCB buffering_state_cb;
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_CLIENT_H_
diff --git a/chromecast/media/cma/pipeline/media_pipeline_impl.cc b/chromecast/media/cma/pipeline/media_pipeline_impl.cc
new file mode 100644
index 0000000..2a89df5
--- /dev/null
+++ b/chromecast/media/cma/pipeline/media_pipeline_impl.cc
@@ -0,0 +1,366 @@
+// Copyright 2014 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 "chromecast/media/cma/pipeline/media_pipeline_impl.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/time/time.h"
+#include "chromecast/media/cdm/browser_cdm_cast.h"
+#include "chromecast/media/cma/backend/media_clock_device.h"
+#include "chromecast/media/cma/backend/media_pipeline_device.h"
+#include "chromecast/media/cma/base/buffering_controller.h"
+#include "chromecast/media/cma/base/buffering_state.h"
+#include "chromecast/media/cma/base/cma_logging.h"
+#include "chromecast/media/cma/base/coded_frame_provider.h"
+#include "chromecast/media/cma/pipeline/audio_pipeline_impl.h"
+#include "chromecast/media/cma/pipeline/video_pipeline_impl.h"
+#include "media/base/buffers.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+// Buffering parameters when load_type is kLoadTypeUrl.
+const base::TimeDelta kLowBufferThresholdURL(
+ base::TimeDelta::FromMilliseconds(2000));
+const base::TimeDelta kHighBufferThresholdURL(
+ base::TimeDelta::FromMilliseconds(6000));
+
+// Buffering parameters when load_type is kLoadTypeMediaSource.
+const base::TimeDelta kLowBufferThresholdMediaSource(
+ base::TimeDelta::FromMilliseconds(0));
+const base::TimeDelta kHighBufferThresholdMediaSource(
+ base::TimeDelta::FromMilliseconds(300));
+
+// Interval between two updates of the media time.
+const base::TimeDelta kTimeUpdateInterval(
+ base::TimeDelta::FromMilliseconds(250));
+
+// Interval between two updates of the statistics is equal to:
+// kTimeUpdateInterval * kStatisticsUpdatePeriod.
+const int kStatisticsUpdatePeriod = 4;
+
+} // namespace
+
+MediaPipelineImpl::MediaPipelineImpl()
+ : has_audio_(false),
+ has_video_(false),
+ target_playback_rate_(0.0),
+ enable_time_update_(false),
+ pending_time_update_task_(false),
+ statistics_rolling_counter_(0),
+ weak_factory_(this) {
+ CMALOG(kLogControl) << __FUNCTION__;
+ weak_this_ = weak_factory_.GetWeakPtr();
+ thread_checker_.DetachFromThread();
+}
+
+MediaPipelineImpl::~MediaPipelineImpl() {
+ CMALOG(kLogControl) << __FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void MediaPipelineImpl::Initialize(
+ LoadType load_type,
+ scoped_ptr<MediaPipelineDevice> media_pipeline_device) {
+ CMALOG(kLogControl) << __FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ media_pipeline_device_.reset(media_pipeline_device.release());
+ clock_device_ = media_pipeline_device_->GetMediaClockDevice();
+
+ if (load_type == kLoadTypeURL || load_type == kLoadTypeMediaSource) {
+ base::TimeDelta low_threshold(kLowBufferThresholdURL);
+ base::TimeDelta high_threshold(kHighBufferThresholdURL);
+ if (load_type == kLoadTypeMediaSource) {
+ low_threshold = kLowBufferThresholdMediaSource;
+ high_threshold = kHighBufferThresholdMediaSource;
+ }
+ scoped_refptr<BufferingConfig> buffering_config(
+ new BufferingConfig(low_threshold, high_threshold));
+ buffering_controller_.reset(new BufferingController(
+ buffering_config,
+ base::Bind(&MediaPipelineImpl::OnBufferingNotification, weak_this_)));
+ }
+
+ audio_pipeline_.reset(new AudioPipelineImpl(
+ media_pipeline_device_->GetAudioPipelineDevice()));
+
+ video_pipeline_.reset(new VideoPipelineImpl(
+ media_pipeline_device_->GetVideoPipelineDevice()));
+}
+
+void MediaPipelineImpl::SetClient(const MediaPipelineClient& client) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!client.error_cb.is_null());
+ DCHECK(!client.time_update_cb.is_null());
+ DCHECK(!client.buffering_state_cb.is_null());
+ client_ = client;
+}
+
+void MediaPipelineImpl::SetCdm(int cdm_id) {
+ CMALOG(kLogControl) << __FUNCTION__ << " cdm_id=" << cdm_id;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ NOTIMPLEMENTED();
+ // TODO(gunsch): SetCdm(int) is not implemented.
+ // One possibility would be a GetCdmByIdCB that's passed in.
+}
+
+void MediaPipelineImpl::SetCdm(::media::BrowserCdm* media_keys) {
+ CMALOG(kLogControl) << __FUNCTION__;
+ audio_pipeline_->SetCdm(static_cast<BrowserCdmCast*>(media_keys));
+ video_pipeline_->SetCdm(static_cast<BrowserCdmCast*>(media_keys));
+}
+
+AudioPipeline* MediaPipelineImpl::GetAudioPipeline() const {
+ return audio_pipeline_.get();
+}
+
+VideoPipeline* MediaPipelineImpl::GetVideoPipeline() const {
+ return video_pipeline_.get();
+}
+
+void MediaPipelineImpl::InitializeAudio(
+ const ::media::AudioDecoderConfig& config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!has_audio_);
+ if (clock_device_->GetState() == MediaClockDevice::kStateUninitialized &&
+ !clock_device_->SetState(MediaClockDevice::kStateIdle)) {
+ status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED);
+ return;
+ }
+ has_audio_ = true;
+ audio_pipeline_->Initialize(config, frame_provider.Pass(), status_cb);
+}
+
+void MediaPipelineImpl::InitializeVideo(
+ const ::media::VideoDecoderConfig& config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!has_video_);
+ if (clock_device_->GetState() == MediaClockDevice::kStateUninitialized &&
+ !clock_device_->SetState(MediaClockDevice::kStateIdle)) {
+ status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED);
+ return;
+ }
+ has_video_ = true;
+ video_pipeline_->Initialize(config, frame_provider.Pass(), status_cb);
+}
+
+void MediaPipelineImpl::StartPlayingFrom(base::TimeDelta time) {
+ CMALOG(kLogControl) << __FUNCTION__ << " t0=" << time.InMilliseconds();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(has_audio_ || has_video_);
+ DCHECK(!pending_callbacks_);
+
+ // Reset the start of the timeline.
+ DCHECK_EQ(clock_device_->GetState(), MediaClockDevice::kStateIdle);
+ clock_device_->ResetTimeline(time);
+
+ // Start the clock. If the playback rate is 0, then the clock is started
+ // but does not increase.
+ if (!clock_device_->SetState(MediaClockDevice::kStateRunning)) {
+ OnError(::media::PIPELINE_ERROR_ABORT);
+ return;
+ }
+
+ // Enable time updates.
+ enable_time_update_ = true;
+ statistics_rolling_counter_ = 0;
+ if (!pending_time_update_task_) {
+ pending_time_update_task_ = true;
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_));
+ }
+
+ // Setup the audio and video pipeline for the new timeline.
+ if (has_audio_) {
+ scoped_refptr<BufferingState> buffering_state;
+ if (buffering_controller_)
+ buffering_state = buffering_controller_->AddStream();
+ if (!audio_pipeline_->StartPlayingFrom(time, buffering_state)) {
+ OnError(::media::PIPELINE_ERROR_ABORT);
+ return;
+ }
+ }
+ if (has_video_) {
+ scoped_refptr<BufferingState> buffering_state;
+ if (buffering_controller_)
+ buffering_state = buffering_controller_->AddStream();
+ if (!video_pipeline_->StartPlayingFrom(time, buffering_state)) {
+ OnError(::media::PIPELINE_ERROR_ABORT);
+ return;
+ }
+ }
+}
+
+void MediaPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) {
+ CMALOG(kLogControl) << __FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(has_audio_ || has_video_);
+ DCHECK(!pending_callbacks_);
+
+ // No need to update media time anymore.
+ enable_time_update_ = false;
+
+ buffering_controller_->Reset();
+
+ // The clock should return to idle.
+ if (!clock_device_->SetState(MediaClockDevice::kStateIdle)) {
+ status_cb.Run(::media::PIPELINE_ERROR_ABORT);
+ return;
+ }
+
+ // Flush both the audio and video pipeline.
+ ::media::SerialRunner::Queue bound_fns;
+ if (has_audio_) {
+ bound_fns.Push(base::Bind(
+ &AudioPipelineImpl::Flush,
+ base::Unretained(audio_pipeline_.get())));
+ }
+ if (has_video_) {
+ bound_fns.Push(base::Bind(
+ &VideoPipelineImpl::Flush,
+ base::Unretained(video_pipeline_.get())));
+ }
+ ::media::PipelineStatusCB transition_cb =
+ base::Bind(&MediaPipelineImpl::StateTransition, weak_this_, status_cb);
+ pending_callbacks_ =
+ ::media::SerialRunner::Run(bound_fns, transition_cb);
+}
+
+void MediaPipelineImpl::Stop() {
+ CMALOG(kLogControl) << __FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(has_audio_ || has_video_);
+ DCHECK(!pending_callbacks_);
+
+ // No need to update media time anymore.
+ enable_time_update_ = false;
+
+ // Release hardware resources on Stop.
+ // Note: Stop can be called from any state.
+ if (clock_device_->GetState() == MediaClockDevice::kStateRunning)
+ clock_device_->SetState(MediaClockDevice::kStateIdle);
+ if (clock_device_->GetState() == MediaClockDevice::kStateIdle)
+ clock_device_->SetState(MediaClockDevice::kStateUninitialized);
+
+ // Stop both the audio and video pipeline.
+ if (has_audio_)
+ audio_pipeline_->Stop();
+ if (has_video_)
+ video_pipeline_->Stop();
+}
+
+void MediaPipelineImpl::SetPlaybackRate(float rate) {
+ CMALOG(kLogControl) << __FUNCTION__ << " rate=" << rate;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ target_playback_rate_ = rate;
+ if (!buffering_controller_ || !buffering_controller_->IsBuffering())
+ media_pipeline_device_->GetMediaClockDevice()->SetRate(rate);
+}
+
+AudioPipelineImpl* MediaPipelineImpl::GetAudioPipelineImpl() const {
+ return audio_pipeline_.get();
+}
+
+VideoPipelineImpl* MediaPipelineImpl::GetVideoPipelineImpl() const {
+ return video_pipeline_.get();
+}
+
+void MediaPipelineImpl::StateTransition(
+ const ::media::PipelineStatusCB& status_cb,
+ ::media::PipelineStatus status) {
+ pending_callbacks_.reset();
+ status_cb.Run(status);
+}
+
+void MediaPipelineImpl::OnBufferingNotification(bool is_buffering) {
+ CMALOG(kLogControl) << __FUNCTION__ << " is_buffering=" << is_buffering;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(buffering_controller_);
+
+ if (!client_.buffering_state_cb.is_null()) {
+ ::media::BufferingState buffering_state = is_buffering ?
+ ::media::BUFFERING_HAVE_NOTHING : ::media::BUFFERING_HAVE_ENOUGH;
+ client_.buffering_state_cb.Run(buffering_state);
+ }
+
+ if (media_pipeline_device_->GetMediaClockDevice()->GetState() ==
+ MediaClockDevice::kStateUninitialized) {
+ return;
+ }
+
+ if (is_buffering) {
+ // Do not consume data in a rebuffering phase.
+ media_pipeline_device_->GetMediaClockDevice()->SetRate(0.0);
+ } else {
+ media_pipeline_device_->GetMediaClockDevice()->SetRate(
+ target_playback_rate_);
+ }
+}
+
+void MediaPipelineImpl::UpdateMediaTime() {
+ pending_time_update_task_ = false;
+ if (!enable_time_update_)
+ return;
+
+ if (statistics_rolling_counter_ == 0) {
+ audio_pipeline_->UpdateStatistics();
+ video_pipeline_->UpdateStatistics();
+ }
+ statistics_rolling_counter_ =
+ (statistics_rolling_counter_ + 1) % kStatisticsUpdatePeriod;
+
+ base::TimeDelta media_time(clock_device_->GetTime());
+ if (media_time == ::media::kNoTimestamp()) {
+ pending_time_update_task_ = true;
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_),
+ kTimeUpdateInterval);
+ return;
+ }
+ base::TimeTicks stc = base::TimeTicks::Now();
+
+ base::TimeDelta max_rendering_time = media_time;
+ if (buffering_controller_) {
+ buffering_controller_->SetMediaTime(media_time);
+
+ if (media_time != last_media_time_) {
+ max_rendering_time = buffering_controller_->GetMaxRenderingTime();
+ if (max_rendering_time == ::media::kNoTimestamp())
+ max_rendering_time = media_time;
+ }
+ }
+
+ last_media_time_ = media_time;
+ if (!client_.time_update_cb.is_null())
+ client_.time_update_cb.Run(media_time, max_rendering_time, stc);
+
+ pending_time_update_task_ = true;
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_),
+ kTimeUpdateInterval);
+}
+
+void MediaPipelineImpl::OnError(::media::PipelineStatus error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_NE(error, ::media::PIPELINE_OK) << "PIPELINE_OK is not an error!";
+ if (!client_.error_cb.is_null())
+ client_.error_cb.Run(error);
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/media_pipeline_impl.h b/chromecast/media/cma/pipeline/media_pipeline_impl.h
new file mode 100644
index 0000000..0b7a8e7
--- /dev/null
+++ b/chromecast/media/cma/pipeline/media_pipeline_impl.h
@@ -0,0 +1,112 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_IMPL_H_
+#define CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_IMPL_H_
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "chromecast/media/cma/pipeline/load_type.h"
+#include "chromecast/media/cma/pipeline/media_pipeline.h"
+#include "chromecast/media/cma/pipeline/media_pipeline_client.h"
+#include "media/base/serial_runner.h"
+
+namespace chromecast {
+namespace media {
+class AudioPipelineImpl;
+class BufferingController;
+class MediaClockDevice;
+class MediaPipelineDevice;
+class VideoPipelineImpl;
+
+class MediaPipelineImpl : public MediaPipeline {
+ public:
+ MediaPipelineImpl();
+ ~MediaPipelineImpl() override;
+
+ // Initialize the media pipeline: the pipeline is configured based on
+ // |load_type|.
+ void Initialize(LoadType load_type,
+ scoped_ptr<MediaPipelineDevice> media_pipeline_device);
+
+ // MediaPipeline implementation.
+ void SetClient(const MediaPipelineClient& client) override;
+ void SetCdm(int cdm_id) override;
+ AudioPipeline* GetAudioPipeline() const override;
+ VideoPipeline* GetVideoPipeline() const override;
+ void InitializeAudio(
+ const ::media::AudioDecoderConfig& config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb) override;
+ void InitializeVideo(
+ const ::media::VideoDecoderConfig& config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb) override;
+ void StartPlayingFrom(base::TimeDelta time) override;
+ void Flush(const ::media::PipelineStatusCB& status_cb) override;
+ void Stop() override;
+ void SetPlaybackRate(float playback_rate) override;
+
+ AudioPipelineImpl* GetAudioPipelineImpl() const;
+ VideoPipelineImpl* GetVideoPipelineImpl() const;
+
+ void SetCdm(::media::BrowserCdm* cdm);
+
+ private:
+ void StateTransition(const ::media::PipelineStatusCB& status_cb,
+ ::media::PipelineStatus status);
+
+ // Invoked to notify about a change of buffering state.
+ void OnBufferingNotification(bool is_buffering);
+
+ void UpdateMediaTime();
+
+ void OnError(::media::PipelineStatus error);
+
+ base::ThreadChecker thread_checker_;
+
+ MediaPipelineClient client_;
+
+ scoped_ptr<BufferingController> buffering_controller_;
+
+ // Interface with the underlying hardware media pipeline.
+ scoped_ptr<MediaPipelineDevice> media_pipeline_device_;
+ MediaClockDevice* clock_device_;
+
+ bool has_audio_;
+ bool has_video_;
+ scoped_ptr<AudioPipelineImpl> audio_pipeline_;
+ scoped_ptr<VideoPipelineImpl> video_pipeline_;
+ scoped_ptr< ::media::SerialRunner> pending_callbacks_;
+
+ // Playback rate set by the upper layer.
+ float target_playback_rate_;
+
+ // Indicate a possible re-buffering phase.
+ bool is_buffering_;
+
+ // The media time is retrieved at regular intervals.
+ // Indicate whether time update is enabled.
+ bool enable_time_update_;
+ bool pending_time_update_task_;
+ base::TimeDelta last_media_time_;
+
+ // Used to make the statistics update period a multiplier of the time update
+ // period.
+ int statistics_rolling_counter_;
+
+ base::WeakPtrFactory<MediaPipelineImpl> weak_factory_;
+ base::WeakPtr<MediaPipelineImpl> weak_this_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaPipelineImpl);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_IMPL_H_
diff --git a/chromecast/media/cma/pipeline/video_pipeline.cc b/chromecast/media/cma/pipeline/video_pipeline.cc
new file mode 100644
index 0000000..6b428b5
--- /dev/null
+++ b/chromecast/media/cma/pipeline/video_pipeline.cc
@@ -0,0 +1,17 @@
+// Copyright 2014 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 "chromecast/media/cma/pipeline/video_pipeline.h"
+
+namespace chromecast {
+namespace media {
+
+VideoPipeline::VideoPipeline() {
+}
+
+VideoPipeline::~VideoPipeline() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/video_pipeline.h b/chromecast/media/cma/pipeline/video_pipeline.h
new file mode 100644
index 0000000..68a9b8c
--- /dev/null
+++ b/chromecast/media/cma/pipeline/video_pipeline.h
@@ -0,0 +1,28 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_H_
+#define CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_H_
+
+#include "base/macros.h"
+
+namespace chromecast {
+namespace media {
+struct VideoPipelineClient;
+
+class VideoPipeline {
+ public:
+ VideoPipeline();
+ virtual ~VideoPipeline();
+
+ virtual void SetClient(const VideoPipelineClient& client) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VideoPipeline);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_H_
diff --git a/chromecast/media/cma/pipeline/video_pipeline_client.cc b/chromecast/media/cma/pipeline/video_pipeline_client.cc
new file mode 100644
index 0000000..bb7388e
--- /dev/null
+++ b/chromecast/media/cma/pipeline/video_pipeline_client.cc
@@ -0,0 +1,17 @@
+// Copyright 2014 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 "chromecast/media/cma/pipeline/video_pipeline_client.h"
+
+namespace chromecast {
+namespace media {
+
+VideoPipelineClient::VideoPipelineClient() {
+}
+
+VideoPipelineClient::~VideoPipelineClient() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/video_pipeline_client.h b/chromecast/media/cma/pipeline/video_pipeline_client.h
new file mode 100644
index 0000000..105b8a2
--- /dev/null
+++ b/chromecast/media/cma/pipeline/video_pipeline_client.h
@@ -0,0 +1,35 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_CLIENT_H_
+#define CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_CLIENT_H_
+
+#include "base/callback.h"
+#include "chromecast/media/cma/pipeline/av_pipeline_client.h"
+
+namespace gfx {
+class Size;
+}
+
+namespace chromecast {
+namespace media {
+
+struct VideoPipelineClient {
+ typedef base::Callback<void(
+ const gfx::Size& natural_size)> NaturalSizeChangedCB;
+
+ VideoPipelineClient();
+ ~VideoPipelineClient();
+
+ // All the default callbacks.
+ AvPipelineClient av_pipeline_client;
+
+ // Video resolution change notification.
+ NaturalSizeChangedCB natural_size_changed_cb;
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_CLIENT_H_
diff --git a/chromecast/media/cma/pipeline/video_pipeline_impl.cc b/chromecast/media/cma/pipeline/video_pipeline_impl.cc
new file mode 100644
index 0000000..d3ae9be
--- /dev/null
+++ b/chromecast/media/cma/pipeline/video_pipeline_impl.cc
@@ -0,0 +1,174 @@
+// Copyright 2014 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 "chromecast/media/cma/pipeline/video_pipeline_impl.h"
+
+#include "base/bind.h"
+#include "chromecast/media/cma/backend/video_pipeline_device.h"
+#include "chromecast/media/cma/base/buffering_defs.h"
+#include "chromecast/media/cma/base/cma_logging.h"
+#include "chromecast/media/cma/base/coded_frame_provider.h"
+#include "chromecast/media/cma/pipeline/av_pipeline_impl.h"
+#include "media/base/video_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+const size_t kMaxVideoFrameSize = 1024 * 1024;
+}
+
+VideoPipelineImpl::VideoPipelineImpl(VideoPipelineDevice* video_device)
+ : video_device_(video_device),
+ weak_factory_(this) {
+ weak_this_ = weak_factory_.GetWeakPtr();
+ av_pipeline_impl_.reset(new AvPipelineImpl(
+ video_device_,
+ base::Bind(&VideoPipelineImpl::OnUpdateConfig, base::Unretained(this))));
+}
+
+VideoPipelineImpl::~VideoPipelineImpl() {
+}
+
+void VideoPipelineImpl::SetCodedFrameProvider(
+ scoped_ptr<CodedFrameProvider> frame_provider) {
+ av_pipeline_impl_->SetCodedFrameProvider(
+ frame_provider.Pass(), kAppVideoBufferSize, kMaxVideoFrameSize);
+}
+
+bool VideoPipelineImpl::StartPlayingFrom(
+ base::TimeDelta time,
+ const scoped_refptr<BufferingState>& buffering_state) {
+ CMALOG(kLogControl) << "VideoPipelineImpl::StartPlayingFrom t0="
+ << time.InMilliseconds();
+
+ // Reset the pipeline statistics.
+ previous_stats_ = ::media::PipelineStatistics();
+
+ // Start playing.
+ if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError)
+ return false;
+ DCHECK_EQ(av_pipeline_impl_->GetState(), AvPipelineImpl::kFlushed);
+
+ if (!av_pipeline_impl_->StartPlayingFrom(time, buffering_state)) {
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kError);
+ return false;
+ }
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kPlaying);
+
+ return true;
+}
+
+void VideoPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) {
+ CMALOG(kLogControl) << "VideoPipelineImpl::Flush";
+ if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError) {
+ status_cb.Run(::media::PIPELINE_ERROR_ABORT);
+ return;
+ }
+ DCHECK_EQ(av_pipeline_impl_->GetState(), AvPipelineImpl::kPlaying);
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushing);
+ av_pipeline_impl_->Flush(
+ base::Bind(&VideoPipelineImpl::OnFlushDone, weak_this_, status_cb));
+}
+
+void VideoPipelineImpl::OnFlushDone(
+ const ::media::PipelineStatusCB& status_cb) {
+ CMALOG(kLogControl) << "VideoPipelineImpl::OnFlushDone";
+ if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError) {
+ status_cb.Run(::media::PIPELINE_ERROR_ABORT);
+ return;
+ }
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushed);
+ status_cb.Run(::media::PIPELINE_OK);
+}
+
+void VideoPipelineImpl::Stop() {
+ CMALOG(kLogControl) << "VideoPipelineImpl::Stop";
+ av_pipeline_impl_->Stop();
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kStopped);
+}
+
+void VideoPipelineImpl::SetCdm(BrowserCdmCast* media_keys) {
+ av_pipeline_impl_->SetCdm(media_keys);
+}
+
+void VideoPipelineImpl::SetClient(const VideoPipelineClient& client) {
+ video_client_ = client;
+ av_pipeline_impl_->SetClient(client.av_pipeline_client);
+}
+
+void VideoPipelineImpl::Initialize(
+ const ::media::VideoDecoderConfig& video_config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb) {
+ CMALOG(kLogControl) << "VideoPipelineImpl::Initialize "
+ << video_config.AsHumanReadableString();
+ VideoPipelineDevice::VideoClient client;
+ client.natural_size_changed_cb =
+ base::Bind(&VideoPipelineImpl::OnNaturalSizeChanged, weak_this_);
+ video_device_->SetVideoClient(client);
+ if (frame_provider)
+ SetCodedFrameProvider(frame_provider.Pass());
+
+ if (!video_device_->SetConfig(video_config) ||
+ !av_pipeline_impl_->Initialize()) {
+ status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED);
+ return;
+ }
+ av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushed);
+ status_cb.Run(::media::PIPELINE_OK);
+}
+
+void VideoPipelineImpl::OnUpdateConfig(
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config) {
+ if (video_config.IsValidConfig()) {
+ CMALOG(kLogControl) << "VideoPipelineImpl::OnUpdateConfig "
+ << video_config.AsHumanReadableString();
+
+ bool success = video_device_->SetConfig(video_config);
+ if (!success &&
+ !video_client_.av_pipeline_client.playback_error_cb.is_null()) {
+ video_client_.av_pipeline_client.playback_error_cb.Run(
+ ::media::PIPELINE_ERROR_DECODE);
+ }
+ }
+}
+
+void VideoPipelineImpl::OnNaturalSizeChanged(const gfx::Size& size) {
+ if (av_pipeline_impl_->GetState() != AvPipelineImpl::kPlaying)
+ return;
+
+ if (!video_client_.natural_size_changed_cb.is_null())
+ video_client_.natural_size_changed_cb.Run(size);
+}
+
+void VideoPipelineImpl::UpdateStatistics() {
+ if (video_client_.av_pipeline_client.statistics_cb.is_null())
+ return;
+
+ MediaComponentDevice::Statistics device_stats;
+ if (!video_device_->GetStatistics(&device_stats))
+ return;
+
+ ::media::PipelineStatistics current_stats;
+ current_stats.video_bytes_decoded = device_stats.decoded_bytes;
+ current_stats.video_frames_decoded = device_stats.decoded_samples;
+ current_stats.video_frames_dropped = device_stats.dropped_samples;
+
+ ::media::PipelineStatistics delta_stats;
+ delta_stats.video_bytes_decoded =
+ current_stats.video_bytes_decoded - previous_stats_.video_bytes_decoded;
+ delta_stats.video_frames_decoded =
+ current_stats.video_frames_decoded - previous_stats_.video_frames_decoded;
+ delta_stats.video_frames_dropped =
+ current_stats.video_frames_dropped - previous_stats_.video_frames_dropped;
+
+ previous_stats_ = current_stats;
+
+ video_client_.av_pipeline_client.statistics_cb.Run(delta_stats);
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/video_pipeline_impl.h b/chromecast/media/cma/pipeline/video_pipeline_impl.h
new file mode 100644
index 0000000..a393559
--- /dev/null
+++ b/chromecast/media/cma/pipeline/video_pipeline_impl.h
@@ -0,0 +1,82 @@
+// Copyright 2014 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 CHROMECAST_MEDIA_CMA_BASE_VIDEO_PIPELINE_IMPL_H_
+#define CHROMECAST_MEDIA_CMA_BASE_VIDEO_PIPELINE_IMPL_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "chromecast/media/cma/pipeline/video_pipeline.h"
+#include "chromecast/media/cma/pipeline/video_pipeline_client.h"
+
+namespace gfx {
+class Size;
+}
+
+namespace media {
+class AudioDecoderConfig;
+class VideoDecoderConfig;
+}
+
+namespace chromecast {
+namespace media {
+class AvPipelineImpl;
+class BrowserCdmCast;
+class BufferingState;
+class CodedFrameProvider;
+class VideoPipelineDevice;
+
+class VideoPipelineImpl : public VideoPipeline {
+ public:
+ // |buffering_controller| can be NULL.
+ explicit VideoPipelineImpl(VideoPipelineDevice* video_device);
+ ~VideoPipelineImpl() override;
+
+ // Input port of the pipeline.
+ void SetCodedFrameProvider(scoped_ptr<CodedFrameProvider> frame_provider);
+
+ // Provide the CDM to use to decrypt samples.
+ void SetCdm(BrowserCdmCast* media_keys);
+
+ // Functions to control the state of the audio pipeline.
+ void Initialize(
+ const ::media::VideoDecoderConfig& config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb);
+ bool StartPlayingFrom(base::TimeDelta time,
+ const scoped_refptr<BufferingState>& buffering_state);
+ void Flush(const ::media::PipelineStatusCB& status_cb);
+ void Stop();
+
+ // Update the playback statistics for this video stream.
+ void UpdateStatistics();
+
+ // VideoPipeline implementation.
+ void SetClient(const VideoPipelineClient& client) override;
+
+ private:
+ void OnFlushDone(const ::media::PipelineStatusCB& status_cb);
+ void OnUpdateConfig(const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config);
+ void OnNaturalSizeChanged(const gfx::Size& size);
+
+ VideoPipelineDevice* video_device_;
+
+ scoped_ptr<AvPipelineImpl> av_pipeline_impl_;
+ VideoPipelineClient video_client_;
+
+ ::media::PipelineStatistics previous_stats_;
+
+ base::WeakPtrFactory<VideoPipelineImpl> weak_factory_;
+ base::WeakPtr<VideoPipelineImpl> weak_this_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoPipelineImpl);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_VIDEO_PIPELINE_IMPL_H_
diff --git a/chromecast/media/media.gyp b/chromecast/media/media.gyp
index b5bd621..8c09410 100644
--- a/chromecast/media/media.gyp
+++ b/chromecast/media/media.gyp
@@ -64,6 +64,8 @@
'cma/base/balanced_media_task_runner_factory.h',
'cma/base/buffering_controller.cc',
'cma/base/buffering_controller.h',
+ 'cma/base/buffering_defs.cc',
+ 'cma/base/buffering_defs.h',
'cma/base/buffering_frame_provider.cc',
'cma/base/buffering_frame_provider.h',
'cma/base/buffering_state.cc',
@@ -158,6 +160,54 @@
],
},
{
+ 'target_name': 'cma_pipeline',
+ 'type': '<(component)',
+ 'dependencies': [
+ 'cma_backend',
+ 'cma_base',
+ 'media_base',
+ 'media_cdm',
+ '../../base/base.gyp:base',
+ '../../crypto/crypto.gyp:crypto',
+ '../../media/media.gyp:media',
+ ],
+ 'conditions': [
+ ['chromecast_branding=="Chrome"', {
+ 'dependencies': [
+ '../internal/cast_system.gyp:openssl',
+ ],
+ }, {
+ 'dependencies': [
+ '../../third_party/boringssl/boringssl.gyp:boringssl',
+ ],
+ }],
+ ],
+ 'sources': [
+ 'cma/pipeline/audio_pipeline.cc',
+ 'cma/pipeline/audio_pipeline.h',
+ 'cma/pipeline/audio_pipeline_impl.cc',
+ 'cma/pipeline/audio_pipeline_impl.h',
+ 'cma/pipeline/av_pipeline_client.cc',
+ 'cma/pipeline/av_pipeline_client.h',
+ 'cma/pipeline/av_pipeline_impl.cc',
+ 'cma/pipeline/av_pipeline_impl.h',
+ 'cma/pipeline/decrypt_util.cc',
+ 'cma/pipeline/decrypt_util.h',
+ 'cma/pipeline/load_type.h',
+ 'cma/pipeline/media_pipeline.h',
+ 'cma/pipeline/media_pipeline_client.cc',
+ 'cma/pipeline/media_pipeline_client.h',
+ 'cma/pipeline/media_pipeline_impl.cc',
+ 'cma/pipeline/media_pipeline_impl.h',
+ 'cma/pipeline/video_pipeline.cc',
+ 'cma/pipeline/video_pipeline.h',
+ 'cma/pipeline/video_pipeline_client.cc',
+ 'cma/pipeline/video_pipeline_client.h',
+ 'cma/pipeline/video_pipeline_impl.cc',
+ 'cma/pipeline/video_pipeline_impl.h',
+ ],
+ },
+ {
'target_name': 'cma_filters',
'type': '<(component)',
'dependencies': [
@@ -179,6 +229,7 @@
'cma_filters',
'cma_ipc',
'cma_ipc_streamer',
+ 'cma_pipeline',
'media_cdm',
],
},
@@ -205,6 +256,7 @@
'cma/ipc/media_message_fifo_unittest.cc',
'cma/ipc/media_message_unittest.cc',
'cma/ipc_streamer/av_streamer_unittest.cc',
+ 'cma/pipeline/audio_video_pipeline_impl_unittest.cc',
'cma/test/frame_generator_for_test.cc',
'cma/test/frame_generator_for_test.h',
'cma/test/frame_segmenter_for_test.cc',