summaryrefslogtreecommitdiff
path: root/chromium/chromecast/media
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@theqtcompany.com>2014-12-05 15:04:29 +0100
committerAndras Becsi <andras.becsi@theqtcompany.com>2014-12-09 10:49:28 +0100
commitaf6588f8d723931a298c995fa97259bb7f7deb55 (patch)
tree060ca707847ba1735f01af2372e0d5e494dc0366 /chromium/chromecast/media
parent2fff84d821cc7b1c785f6404e0f8091333283e74 (diff)
downloadqtwebengine-chromium-af6588f8d723931a298c995fa97259bb7f7deb55.tar.gz
BASELINE: Update chromium to 40.0.2214.28 and ninja to 1.5.3.
Change-Id: I759465284fd64d59ad120219cbe257f7402c4181 Reviewed-by: Andras Becsi <andras.becsi@theqtcompany.com>
Diffstat (limited to 'chromium/chromecast/media')
-rw-r--r--chromium/chromecast/media/DEPS4
-rw-r--r--chromium/chromecast/media/base/decrypt_context.cc22
-rw-r--r--chromium/chromecast/media/base/decrypt_context.h46
-rw-r--r--chromium/chromecast/media/base/decrypt_context_clearkey.cc27
-rw-r--r--chromium/chromecast/media/base/decrypt_context_clearkey.h33
-rw-r--r--chromium/chromecast/media/base/key_systems_common.cc41
-rw-r--r--chromium/chromecast/media/base/key_systems_common.h35
-rw-r--r--chromium/chromecast/media/base/key_systems_common_simple.cc15
-rw-r--r--chromium/chromecast/media/cma/backend/audio_pipeline_device.cc17
-rw-r--r--chromium/chromecast/media/cma/backend/audio_pipeline_device.h41
-rw-r--r--chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc386
-rw-r--r--chromium/chromecast/media/cma/backend/media_clock_device.cc55
-rw-r--r--chromium/chromecast/media/cma/backend/media_clock_device.h82
-rw-r--r--chromium/chromecast/media/cma/backend/media_component_device.cc67
-rw-r--r--chromium/chromecast/media/cma/backend/media_component_device.h140
-rw-r--r--chromium/chromecast/media/cma/backend/media_pipeline_device.cc17
-rw-r--r--chromium/chromecast/media/cma/backend/media_pipeline_device.h42
-rw-r--r--chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc573
-rw-r--r--chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h40
-rw-r--r--chromium/chromecast/media/cma/backend/media_pipeline_device_fake_factory.cc18
-rw-r--r--chromium/chromecast/media/cma/backend/media_pipeline_device_params.cc18
-rw-r--r--chromium/chromecast/media/cma/backend/media_pipeline_device_params.h31
-rw-r--r--chromium/chromecast/media/cma/backend/video_pipeline_device.cc23
-rw-r--r--chromium/chromecast/media/cma/backend/video_pipeline_device.h63
-rw-r--r--chromium/chromecast/media/cma/base/balanced_media_task_runner_factory.cc252
-rw-r--r--chromium/chromecast/media/cma/base/balanced_media_task_runner_factory.h68
-rw-r--r--chromium/chromecast/media/cma/base/balanced_media_task_runner_unittest.cc263
-rw-r--r--chromium/chromecast/media/cma/base/buffering_controller.cc206
-rw-r--r--chromium/chromecast/media/cma/base/buffering_controller.h109
-rw-r--r--chromium/chromecast/media/cma/base/buffering_controller_unittest.cc133
-rw-r--r--chromium/chromecast/media/cma/base/buffering_frame_provider.cc140
-rw-r--r--chromium/chromecast/media/cma/base/buffering_frame_provider.h117
-rw-r--r--chromium/chromecast/media/cma/base/buffering_frame_provider_unittest.cc187
-rw-r--r--chromium/chromecast/media/cma/base/buffering_state.cc129
-rw-r--r--chromium/chromecast/media/cma/base/buffering_state.h138
-rw-r--r--chromium/chromecast/media/cma/base/cma_logging.h23
-rw-r--r--chromium/chromecast/media/cma/base/coded_frame_provider.cc17
-rw-r--r--chromium/chromecast/media/cma/base/coded_frame_provider.h51
-rw-r--r--chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc45
-rw-r--r--chromium/chromecast/media/cma/base/decoder_buffer_adapter.h45
-rw-r--r--chromium/chromecast/media/cma/base/decoder_buffer_base.cc17
-rw-r--r--chromium/chromecast/media/cma/base/decoder_buffer_base.h57
-rw-r--r--chromium/chromecast/media/cma/base/media_task_runner.cc17
-rw-r--r--chromium/chromecast/media/cma/base/media_task_runner.h44
-rw-r--r--chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc207
-rw-r--r--chromium/chromecast/media/cma/filters/demuxer_stream_adapter.h93
-rw-r--r--chromium/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc345
-rw-r--r--chromium/chromecast/media/cma/ipc/media_memory_chunk.cc14
-rw-r--r--chromium/chromecast/media/cma/ipc/media_memory_chunk.h39
-rw-r--r--chromium/chromecast/media/cma/ipc/media_message.cc198
-rw-r--r--chromium/chromecast/media/cma/ipc/media_message.h165
-rw-r--r--chromium/chromecast/media/cma/ipc/media_message_fifo.cc403
-rw-r--r--chromium/chromecast/media/cma/ipc/media_message_fifo.h208
-rw-r--r--chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc195
-rw-r--r--chromium/chromecast/media/cma/ipc/media_message_type.h15
-rw-r--r--chromium/chromecast/media/cma/ipc/media_message_unittest.cc146
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.cc70
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h27
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.cc200
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.h88
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/av_streamer_unittest.cc253
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/coded_frame_provider_host.cc96
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/coded_frame_provider_host.h61
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.cc177
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.h29
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.cc75
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.h31
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.cc113
-rw-r--r--chromium/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h27
-rw-r--r--chromium/chromecast/media/media.gyp208
70 files changed, 7377 insertions, 0 deletions
diff --git a/chromium/chromecast/media/DEPS b/chromium/chromecast/media/DEPS
new file mode 100644
index 00000000000..1891d1afe50
--- /dev/null
+++ b/chromium/chromecast/media/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+media/base",
+ "+media/cdm",
+]
diff --git a/chromium/chromecast/media/base/decrypt_context.cc b/chromium/chromecast/media/base/decrypt_context.cc
new file mode 100644
index 00000000000..38da3af7c06
--- /dev/null
+++ b/chromium/chromecast/media/base/decrypt_context.cc
@@ -0,0 +1,22 @@
+// 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/base/decrypt_context.h"
+
+namespace chromecast {
+namespace media {
+
+DecryptContext::DecryptContext(CastKeySystem key_system)
+ : key_system_(key_system) {
+}
+
+DecryptContext::~DecryptContext() {
+}
+
+crypto::SymmetricKey* DecryptContext::GetKey() const {
+ return NULL;
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/base/decrypt_context.h b/chromium/chromecast/media/base/decrypt_context.h
new file mode 100644
index 00000000000..5d053c0cafa
--- /dev/null
+++ b/chromium/chromecast/media/base/decrypt_context.h
@@ -0,0 +1,46 @@
+// 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_BASE_DECRYPT_CONTEXT_H_
+#define CHROMECAST_MEDIA_BASE_DECRYPT_CONTEXT_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "chromecast/media/base/key_systems_common.h"
+
+namespace crypto {
+class SymmetricKey;
+}
+
+namespace chromecast {
+namespace media {
+
+// Base class of a decryption context: a decryption context gathers all the
+// information needed to decrypt frames with a given key id.
+// Each CDM should implement this and add fields needed to fully describe a
+// decryption context.
+//
+class DecryptContext : public base::RefCountedThreadSafe<DecryptContext> {
+ public:
+ explicit DecryptContext(CastKeySystem key_system);
+
+ CastKeySystem key_system() const { return key_system_; }
+
+ // Returns the clear key if available, NULL otherwise.
+ virtual crypto::SymmetricKey* GetKey() const;
+
+ protected:
+ friend class base::RefCountedThreadSafe<DecryptContext>;
+ virtual ~DecryptContext();
+
+ private:
+ CastKeySystem key_system_;
+
+ DISALLOW_COPY_AND_ASSIGN(DecryptContext);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_BASE_DECRYPT_CONTEXT_H_ \ No newline at end of file
diff --git a/chromium/chromecast/media/base/decrypt_context_clearkey.cc b/chromium/chromecast/media/base/decrypt_context_clearkey.cc
new file mode 100644
index 00000000000..248cbb459c6
--- /dev/null
+++ b/chromium/chromecast/media/base/decrypt_context_clearkey.cc
@@ -0,0 +1,27 @@
+// 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/base/decrypt_context_clearkey.h"
+
+#include "base/logging.h"
+#include "crypto/symmetric_key.h"
+
+namespace chromecast {
+namespace media {
+
+DecryptContextClearKey::DecryptContextClearKey(crypto::SymmetricKey* key)
+ : DecryptContext(KEY_SYSTEM_CLEAR_KEY),
+ key_(key) {
+ CHECK(key);
+}
+
+DecryptContextClearKey::~DecryptContextClearKey() {
+}
+
+crypto::SymmetricKey* DecryptContextClearKey::GetKey() const {
+ return key_;
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/base/decrypt_context_clearkey.h b/chromium/chromecast/media/base/decrypt_context_clearkey.h
new file mode 100644
index 00000000000..770cea08144
--- /dev/null
+++ b/chromium/chromecast/media/base/decrypt_context_clearkey.h
@@ -0,0 +1,33 @@
+// 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_BASE_DECRYPT_CONTEXT_CLEARKEY_H_
+#define CHROMECAST_MEDIA_BASE_DECRYPT_CONTEXT_CLEARKEY_H_
+
+#include "base/macros.h"
+#include "chromecast/media/base/decrypt_context.h"
+
+namespace chromecast {
+namespace media {
+
+class DecryptContextClearKey : public DecryptContext {
+ public:
+ // Note: DecryptContextClearKey does not take ownership of |key|.
+ explicit DecryptContextClearKey(crypto::SymmetricKey* key);
+
+ // DecryptContext implementation.
+ virtual crypto::SymmetricKey* GetKey() const override;
+
+ private:
+ virtual ~DecryptContextClearKey();
+
+ crypto::SymmetricKey* const key_;
+
+ DISALLOW_COPY_AND_ASSIGN(DecryptContextClearKey);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_BASE_DECRYPT_CONTEXT_CLEARKEY_H_ \ No newline at end of file
diff --git a/chromium/chromecast/media/base/key_systems_common.cc b/chromium/chromecast/media/base/key_systems_common.cc
new file mode 100644
index 00000000000..568aaa8a3e1
--- /dev/null
+++ b/chromium/chromecast/media/base/key_systems_common.cc
@@ -0,0 +1,41 @@
+// 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/base/key_systems_common.h"
+
+#include <cstddef>
+
+#include "media/cdm/key_system_names.h"
+
+#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
+
+namespace chromecast {
+namespace media {
+
+#if defined(PLAYREADY_CDM_AVAILABLE)
+const char kChromecastPlayreadyKeySystem[] = "com.chromecast.playready";
+#endif // defined(PLAYREADY_CDM_AVAILABLE)
+
+CastKeySystem GetKeySystemByName(const std::string& key_system_name) {
+#if defined(WIDEVINE_CDM_AVAILABLE)
+ if (key_system_name.compare(kWidevineKeySystem) == 0) {
+ return KEY_SYSTEM_WIDEVINE;
+ }
+#endif // defined(WIDEVINE_CDM_AVAILABLE)
+
+#if defined(PLAYREADY_CDM_AVAILABLE)
+ if (key_system_name.compare(kChromecastPlayreadyKeySystem) == 0) {
+ return KEY_SYSTEM_PLAYREADY;
+ }
+#endif // defined(PLAYREADY_CDM_AVAILABLE)
+
+ if (key_system_name.compare(::media::kClearKey) == 0) {
+ return KEY_SYSTEM_CLEAR_KEY;
+ }
+
+ return GetPlatformKeySystemByName(key_system_name);
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/base/key_systems_common.h b/chromium/chromecast/media/base/key_systems_common.h
new file mode 100644
index 00000000000..4ab8ea75dec
--- /dev/null
+++ b/chromium/chromecast/media/base/key_systems_common.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_BASE_KEY_SYSTEMS_COMMON_H_
+#define CHROMECAST_MEDIA_BASE_KEY_SYSTEMS_COMMON_H_
+
+#include <string>
+
+namespace chromecast {
+namespace media {
+
+#if defined(PLAYREADY_CDM_AVAILABLE)
+extern const char kChromecastPlayreadyKeySystem[];
+#endif // defined(PLAYREADY_CDM_AVAILABLE)
+
+enum CastKeySystem {
+ KEY_SYSTEM_NONE = 0,
+ KEY_SYSTEM_CLEAR_KEY,
+ KEY_SYSTEM_PLAYREADY,
+ KEY_SYSTEM_WIDEVINE
+};
+
+// Translates a key system string into a CastKeySystem, calling into the
+// platform for known key systems if needed.
+CastKeySystem GetKeySystemByName(const std::string& key_system_name);
+
+// Translates a platform-specific key system string into a CastKeySystem.
+// TODO(gunsch): Remove when prefixed EME is removed.
+CastKeySystem GetPlatformKeySystemByName(const std::string& key_system_name);
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_BASE_KEY_SYSTEMS_COMMON_H_
diff --git a/chromium/chromecast/media/base/key_systems_common_simple.cc b/chromium/chromecast/media/base/key_systems_common_simple.cc
new file mode 100644
index 00000000000..e6dbd028902
--- /dev/null
+++ b/chromium/chromecast/media/base/key_systems_common_simple.cc
@@ -0,0 +1,15 @@
+// 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/base/key_systems_common.h"
+
+namespace chromecast {
+namespace media {
+
+CastKeySystem GetPlatformKeySystemByName(const std::string& key_system_name) {
+ return KEY_SYSTEM_NONE;
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/backend/audio_pipeline_device.cc b/chromium/chromecast/media/cma/backend/audio_pipeline_device.cc
new file mode 100644
index 00000000000..60c6c3092e1
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/audio_pipeline_device.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/backend/audio_pipeline_device.h"
+
+namespace chromecast {
+namespace media {
+
+AudioPipelineDevice::AudioPipelineDevice() {
+}
+
+AudioPipelineDevice::~AudioPipelineDevice() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/backend/audio_pipeline_device.h b/chromium/chromecast/media/cma/backend/audio_pipeline_device.h
new file mode 100644
index 00000000000..3fbf5731dbe
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/audio_pipeline_device.h
@@ -0,0 +1,41 @@
+// 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_BACKEND_AUDIO_PIPELINE_DEVICE_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_PIPELINE_DEVICE_H_
+
+#include "base/macros.h"
+#include "chromecast/media/cma/backend/media_component_device.h"
+
+namespace media {
+class AudioDecoderConfig;
+}
+
+namespace chromecast {
+namespace media {
+class AudioPipelineDeviceClient;
+
+class AudioPipelineDevice : public MediaComponentDevice {
+ public:
+ AudioPipelineDevice();
+ virtual ~AudioPipelineDevice();
+
+ // Provide the audio configuration.
+ // Must be called before switching from |kStateUninitialized| to |kStateIdle|.
+ // Afterwards, this can be invoked any time the configuration changes.
+ // Returns true if the configuration is a supported configuration.
+ virtual bool SetConfig(const ::media::AudioDecoderConfig& config) = 0;
+
+ // Sets the volume multiplier.
+ // The multiplier must be in the range [0.0, 1.0].
+ virtual void SetStreamVolumeMultiplier(float multiplier) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AudioPipelineDevice);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_PIPELINE_DEVICE_H_
diff --git a/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc b/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc
new file mode 100644
index 00000000000..cf1daca9ab0
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc
@@ -0,0 +1,386 @@
+// 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/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/path_service.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "chromecast/media/base/decrypt_context.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_params.h"
+#include "chromecast/media/cma/backend/video_pipeline_device.h"
+#include "chromecast/media/cma/base/decoder_buffer_adapter.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "chromecast/media/cma/test/frame_segmenter_for_test.h"
+#include "chromecast/media/cma/test/media_component_device_feeder_for_test.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 {
+
+namespace {
+
+typedef ScopedVector<MediaComponentDeviceFeederForTest>::iterator
+ ComponentDeviceIterator;
+
+const base::TimeDelta kMonitorLoopDelay = base::TimeDelta::FromMilliseconds(20);
+
+base::FilePath GetTestDataFilePath(const std::string& name) {
+ base::FilePath file_path;
+ CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
+
+ file_path = file_path.Append(FILE_PATH_LITERAL("media"))
+ .Append(FILE_PATH_LITERAL("test")).Append(FILE_PATH_LITERAL("data"))
+ .AppendASCII(name);
+ return file_path;
+}
+
+} // namespace
+
+class AudioVideoPipelineDeviceTest : public testing::Test {
+ public:
+ struct PauseInfo {
+ PauseInfo() {}
+ PauseInfo(base::TimeDelta d, base::TimeDelta l) : delay(d), length(l) {}
+ ~PauseInfo() {}
+
+ base::TimeDelta delay;
+ base::TimeDelta length;
+ };
+
+ AudioVideoPipelineDeviceTest();
+ virtual ~AudioVideoPipelineDeviceTest();
+
+ void ConfigureForFile(std::string filename);
+ void ConfigureForAudioOnly(std::string filename);
+ void ConfigureForVideoOnly(std::string filename, bool raw_h264);
+
+ // Pattern loops, waiting >= pattern[i].delay against media clock between
+ // pauses, then pausing for >= pattern[i].length against MessageLoop
+ // A pause with delay <0 signals to stop sequence and do not loop
+ void SetPausePattern(const std::vector<PauseInfo> pattern);
+
+ // Adds a pause to the end of pause pattern
+ void AddPause(base::TimeDelta delay, base::TimeDelta length);
+
+ void Start();
+
+ private:
+ void Initialize();
+
+ void LoadAudioStream(std::string filename);
+ void LoadVideoStream(std::string filename, bool raw_h264);
+
+ void MonitorLoop();
+
+ void OnPauseCompleted();
+
+ void OnEos(MediaComponentDeviceFeederForTest* device_feeder);
+
+ scoped_ptr<MediaPipelineDevice> media_pipeline_device_;
+ MediaClockDevice* media_clock_device_;
+
+ // Devices to feed
+ ScopedVector<MediaComponentDeviceFeederForTest>
+ component_device_feeders_;
+
+ // Current media time.
+ base::TimeDelta pause_time_;
+
+ // Pause settings
+ std::vector<PauseInfo> pause_pattern_;
+ int pause_pattern_idx_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioVideoPipelineDeviceTest);
+};
+
+AudioVideoPipelineDeviceTest::AudioVideoPipelineDeviceTest()
+ : pause_pattern_() {
+}
+
+AudioVideoPipelineDeviceTest::~AudioVideoPipelineDeviceTest() {
+}
+
+void AudioVideoPipelineDeviceTest::AddPause(base::TimeDelta delay,
+ base::TimeDelta length) {
+ pause_pattern_.push_back(PauseInfo(delay, length));
+}
+
+void AudioVideoPipelineDeviceTest::SetPausePattern(
+ const std::vector<PauseInfo> pattern) {
+ pause_pattern_ = pattern;
+}
+
+void AudioVideoPipelineDeviceTest::ConfigureForAudioOnly(std::string filename) {
+ Initialize();
+ LoadAudioStream(filename);
+}
+
+void AudioVideoPipelineDeviceTest::ConfigureForVideoOnly(std::string filename,
+ bool raw_h264) {
+ Initialize();
+ LoadVideoStream(filename, raw_h264);
+}
+
+void AudioVideoPipelineDeviceTest::ConfigureForFile(std::string filename) {
+ Initialize();
+ LoadVideoStream(filename, false /* raw_h264 */);
+ LoadAudioStream(filename);
+}
+
+void AudioVideoPipelineDeviceTest::LoadAudioStream(std::string filename) {
+ base::FilePath file_path = GetTestDataFilePath(filename);
+ DemuxResult demux_result = FFmpegDemuxForTest(file_path, true /* audio */);
+ BufferList frames = demux_result.frames;
+
+ AudioPipelineDevice* audio_pipeline_device =
+ media_pipeline_device_->GetAudioPipelineDevice();
+
+ bool success = audio_pipeline_device->SetConfig(demux_result.audio_config);
+ ASSERT_TRUE(success);
+
+ VLOG(2) << "Got " << frames.size() << " audio input frames";
+
+ frames.push_back(
+ scoped_refptr<DecoderBufferBase>(
+ new DecoderBufferAdapter(::media::DecoderBuffer::CreateEOSBuffer())));
+
+ MediaComponentDeviceFeederForTest* device_feeder =
+ new MediaComponentDeviceFeederForTest(audio_pipeline_device, frames);
+ device_feeder->Initialize(base::Bind(&AudioVideoPipelineDeviceTest::OnEos,
+ base::Unretained(this),
+ device_feeder));
+ component_device_feeders_.push_back(device_feeder);
+}
+
+void AudioVideoPipelineDeviceTest::LoadVideoStream(std::string filename,
+ bool raw_h264) {
+ BufferList frames;
+ ::media::VideoDecoderConfig video_config;
+
+ if (raw_h264) {
+ base::FilePath file_path = GetTestDataFilePath(filename);
+ base::MemoryMappedFile video_stream;
+ ASSERT_TRUE(video_stream.Initialize(file_path))
+ << "Couldn't open stream file: " << file_path.MaybeAsASCII();
+ frames = H264SegmenterForTest(video_stream.data(), video_stream.length());
+
+ // Use arbitraty sizes.
+ gfx::Size coded_size(320, 240);
+ gfx::Rect visible_rect(0, 0, 320, 240);
+ gfx::Size natural_size(320, 240);
+
+ // TODO(kjoswiak): Either pull data from stream or make caller specify value
+ video_config = ::media::VideoDecoderConfig(
+ ::media::kCodecH264,
+ ::media::H264PROFILE_MAIN,
+ ::media::VideoFrame::I420,
+ coded_size,
+ visible_rect,
+ natural_size,
+ NULL, 0, false);
+ } else {
+ base::FilePath file_path = GetTestDataFilePath(filename);
+ DemuxResult demux_result = FFmpegDemuxForTest(file_path,
+ /*audio*/ false);
+ frames = demux_result.frames;
+ video_config = demux_result.video_config;
+ }
+
+ VideoPipelineDevice* video_pipeline_device =
+ media_pipeline_device_->GetVideoPipelineDevice();
+
+ // Set configuration.
+ bool success = video_pipeline_device->SetConfig(video_config);
+ ASSERT_TRUE(success);
+
+ VLOG(2) << "Got " << frames.size() << " video input frames";
+
+ frames.push_back(
+ scoped_refptr<DecoderBufferBase>(new DecoderBufferAdapter(
+ ::media::DecoderBuffer::CreateEOSBuffer())));
+
+ MediaComponentDeviceFeederForTest* device_feeder =
+ new MediaComponentDeviceFeederForTest(video_pipeline_device, frames);
+ device_feeder->Initialize(base::Bind(&AudioVideoPipelineDeviceTest::OnEos,
+ base::Unretained(this),
+ device_feeder));
+ component_device_feeders_.push_back(device_feeder);
+}
+
+void AudioVideoPipelineDeviceTest::Start() {
+ pause_time_ = base::TimeDelta();
+ pause_pattern_idx_ = 0;
+
+ for (int i = 0; i < component_device_feeders_.size(); i++) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MediaComponentDeviceFeederForTest::Feed,
+ base::Unretained(component_device_feeders_[i])));
+ }
+
+ media_clock_device_->SetState(MediaClockDevice::kStateRunning);
+
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AudioVideoPipelineDeviceTest::MonitorLoop,
+ base::Unretained(this)));
+}
+
+void AudioVideoPipelineDeviceTest::MonitorLoop() {
+ base::TimeDelta media_time = media_clock_device_->GetTime();
+
+ if (!pause_pattern_.empty() &&
+ pause_pattern_[pause_pattern_idx_].delay >= base::TimeDelta() &&
+ media_time >= pause_time_ + pause_pattern_[pause_pattern_idx_].delay) {
+ // Do Pause
+ media_clock_device_->SetRate(0.0);
+ pause_time_ = media_clock_device_->GetTime();
+
+ VLOG(2) << "Pausing at " << pause_time_.InMilliseconds() << "ms for " <<
+ pause_pattern_[pause_pattern_idx_].length.InMilliseconds() << "ms";
+
+ // Wait for pause finish
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AudioVideoPipelineDeviceTest::OnPauseCompleted,
+ base::Unretained(this)),
+ pause_pattern_[pause_pattern_idx_].length);
+ return;
+ }
+
+ // Check state again in a little while
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AudioVideoPipelineDeviceTest::MonitorLoop,
+ base::Unretained(this)),
+ kMonitorLoopDelay);
+}
+
+void AudioVideoPipelineDeviceTest::OnPauseCompleted() {
+ // Make sure the media time didn't move during that time.
+ base::TimeDelta media_time = media_clock_device_->GetTime();
+
+ // TODO(damienv):
+ // Should be:
+ // EXPECT_EQ(media_time, media_time_);
+ // However, some backends, when rendering the first frame while in paused
+ // mode moves the time forward.
+ // This behaviour is not intended.
+ EXPECT_GE(media_time, pause_time_);
+ EXPECT_LE(media_time, pause_time_ + base::TimeDelta::FromMilliseconds(50));
+
+ pause_time_ = media_time;
+ pause_pattern_idx_ = (pause_pattern_idx_ + 1) % pause_pattern_.size();
+
+ VLOG(2) << "Pause complete, restarting media clock";
+
+ // Resume playback and frame feeding.
+ media_clock_device_->SetRate(1.0);
+
+ MonitorLoop();
+}
+
+void AudioVideoPipelineDeviceTest::OnEos(
+ MediaComponentDeviceFeederForTest* device_feeder) {
+ for (ComponentDeviceIterator it = component_device_feeders_.begin();
+ it != component_device_feeders_.end();
+ ++it) {
+ if (*it == device_feeder) {
+ component_device_feeders_.erase(it);
+ break;
+ }
+ }
+
+ // Check if all streams finished
+ if (component_device_feeders_.empty())
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+void AudioVideoPipelineDeviceTest::Initialize() {
+ // Create the media device.
+ MediaPipelineDeviceParams params;
+ media_pipeline_device_.reset(CreateMediaPipelineDevice(params).release());
+ media_clock_device_ = media_pipeline_device_->GetMediaClockDevice();
+
+ // Clock initialization and configuration.
+ bool success =
+ media_clock_device_->SetState(MediaClockDevice::kStateIdle);
+ ASSERT_TRUE(success);
+ success = media_clock_device_->ResetTimeline(base::TimeDelta());
+ ASSERT_TRUE(success);
+ media_clock_device_->SetRate(1.0);
+}
+
+TEST_F(AudioVideoPipelineDeviceTest, Mp3Playback) {
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+
+ ConfigureForAudioOnly("sfx.mp3");
+ Start();
+ message_loop->Run();
+}
+
+TEST_F(AudioVideoPipelineDeviceTest, VorbisPlayback) {
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+
+ ConfigureForAudioOnly("sfx.ogg");
+ Start();
+ message_loop->Run();
+}
+
+TEST_F(AudioVideoPipelineDeviceTest, H264Playback) {
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+
+ ConfigureForVideoOnly("bear.h264", true /* raw_h264 */);
+ Start();
+ message_loop->Run();
+}
+
+TEST_F(AudioVideoPipelineDeviceTest, WebmPlaybackWithPause) {
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+
+ // Setup to pause for 100ms every 500ms
+ AddPause(base::TimeDelta::FromMilliseconds(500),
+ base::TimeDelta::FromMilliseconds(100));
+
+ ConfigureForVideoOnly("bear-640x360.webm", false /* raw_h264 */);
+ Start();
+ message_loop->Run();
+}
+
+TEST_F(AudioVideoPipelineDeviceTest, Vp8Playback) {
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+
+ ConfigureForVideoOnly("bear-vp8a.webm", false /* raw_h264 */);
+ Start();
+ message_loop->Run();
+}
+
+TEST_F(AudioVideoPipelineDeviceTest, WebmPlayback) {
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+
+ ConfigureForFile("bear-640x360.webm");
+ Start();
+ message_loop->Run();
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/backend/media_clock_device.cc b/chromium/chromecast/media/cma/backend/media_clock_device.cc
new file mode 100644
index 00000000000..853e5bafbd0
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_clock_device.cc
@@ -0,0 +1,55 @@
+// 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/backend/media_clock_device.h"
+
+#include "base/logging.h"
+
+namespace chromecast {
+namespace media {
+
+MediaClockDevice::MediaClockDevice() {
+}
+
+MediaClockDevice::~MediaClockDevice() {
+}
+
+// static
+bool MediaClockDevice::IsValidStateTransition(State state1, State state2) {
+ if (state2 == state1)
+ return true;
+
+ // All states can transition to |kStateError|.
+ bool is_transition_valid = (state2 == kStateError);
+
+ // All the other valid FSM transitions.
+ is_transition_valid = is_transition_valid ||
+ (state1 == kStateUninitialized && (state2 == kStateIdle)) ||
+ (state1 == kStateIdle && (state2 == kStateRunning ||
+ state2 == kStateUninitialized)) ||
+ (state1 == kStateRunning && (state2 == kStateIdle)) ||
+ (state1 == kStateError && (state2 == kStateUninitialized));
+
+ return is_transition_valid;
+}
+
+// static
+std::string MediaClockDevice::StateToString(const State& state) {
+ switch (state) {
+ case kStateUninitialized:
+ return "Uninitialized";
+ case kStateIdle:
+ return "Idle";
+ case kStateRunning:
+ return "Running";
+ case kStateError:
+ return "Error";
+ default:
+ NOTREACHED() << "Unknown MediaClockDevice::State: " << state;
+ return "";
+ }
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/backend/media_clock_device.h b/chromium/chromecast/media/cma/backend/media_clock_device.h
new file mode 100644
index 00000000000..b625dd57f82
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_clock_device.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_BACKEND_MEDIA_CLOCK_DEVICE_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_CLOCK_DEVICE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time/time.h"
+
+namespace chromecast {
+namespace media {
+
+// MediaClockDevice -
+//
+// State machine:
+// -------------------
+// | |
+// v |
+// kUninitialized --> kIdle --------- kRunning
+//
+// {any state} --> kError
+//
+// Notes:
+// - Hardware resources are acquired when transitioning from the
+// |kUninitialized| state to the |kIdle| state.
+// - The initial value of the timeline can only be set in the kIdle state.
+//
+class MediaClockDevice
+ : NON_EXPORTED_BASE(public base::NonThreadSafe) {
+ public:
+ enum State {
+ kStateUninitialized,
+ kStateIdle,
+ kStateRunning,
+ kStateError,
+ };
+
+ // Return true if transition from |state1| to |state2| is a valid state
+ // transition.
+ static bool IsValidStateTransition(State state1, State state2);
+
+ // Returns string representation of state (for logging)
+ static std::string StateToString(const State& state);
+
+ MediaClockDevice();
+ virtual ~MediaClockDevice();
+
+ // Return the current state of the media clock.
+ virtual State GetState() const = 0;
+
+ // Changes the state and performs any necessary transitions.
+ // Returns true when successful.
+ virtual bool SetState(State new_state) = 0;
+
+ // Sets the initial value of the timeline.
+ // Can only be invoked in state kStateIdle.
+ // Returns true when successful.
+ virtual bool ResetTimeline(base::TimeDelta time) = 0;
+
+ // Sets the clock rate.
+ // Setting it to 0 means the clock is not progressing and that the renderer
+ // tied to this media clock should pause rendering.
+ // Can only be invoked in states kStateIdle or kStateRunning.
+ virtual bool SetRate(float rate) = 0;
+
+ // Retrieves the media clock time.
+ // Can only be invoked in states kStateIdle or kStateRunning.
+ virtual base::TimeDelta GetTime() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MediaClockDevice);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_CLOCK_DEVICE_H_
diff --git a/chromium/chromecast/media/cma/backend/media_component_device.cc b/chromium/chromecast/media/cma/backend/media_component_device.cc
new file mode 100644
index 00000000000..7331946f47c
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_component_device.cc
@@ -0,0 +1,67 @@
+// 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/backend/media_component_device.h"
+
+#include "base/logging.h"
+
+namespace chromecast {
+namespace media {
+
+MediaComponentDevice::Client::Client() {
+}
+
+MediaComponentDevice::Client::~Client() {
+}
+
+MediaComponentDevice::MediaComponentDevice() {
+}
+
+MediaComponentDevice::~MediaComponentDevice() {
+}
+
+// static
+bool MediaComponentDevice::IsValidStateTransition(State state1, State state2) {
+ if (state2 == state1)
+ return true;
+
+ // All states can transition to |kStateError|.
+ bool is_transition_valid = (state2 == kStateError);
+
+ // All the other valid FSM transitions.
+ is_transition_valid = is_transition_valid ||
+ (state1 == kStateUninitialized && (state2 == kStateIdle)) ||
+ (state1 == kStateIdle && (state2 == kStateRunning ||
+ state2 == kStatePaused ||
+ state2 == kStateUninitialized)) ||
+ (state1 == kStatePaused && (state2 == kStateIdle ||
+ state2 == kStateRunning)) ||
+ (state1 == kStateRunning && (state2 == kStateIdle ||
+ state2 == kStatePaused)) ||
+ (state1 == kStateError && (state2 == kStateUninitialized));
+
+ return is_transition_valid;
+}
+
+// static
+std::string MediaComponentDevice::StateToString(const State& state) {
+ switch (state) {
+ case kStateUninitialized:
+ return "Uninitialized";
+ case kStateIdle:
+ return "Idle";
+ case kStateRunning:
+ return "Running";
+ case kStatePaused:
+ return "Paused";
+ case kStateError:
+ return "Error";
+ default:
+ NOTREACHED() << "Unknown MediaComponentDevice::State: " << state;
+ return "";
+ }
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/backend/media_component_device.h b/chromium/chromecast/media/cma/backend/media_component_device.h
new file mode 100644
index 00000000000..fd62d8dfa2f
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_component_device.h
@@ -0,0 +1,140 @@
+// 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_BACKEND_MEDIA_COMPONENT_DEVICE_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_COMPONENT_DEVICE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time/time.h"
+
+namespace chromecast {
+namespace media {
+class DecoderBufferBase;
+class DecryptContext;
+
+// MediaComponentDevice -
+//
+// State machine:
+// -------------- kRunning <---
+// | ^ |
+// v | |
+// kUninitialized <--> kIdle -------------- |
+// ^ | |
+// | v |
+// -------------- kPaused <----
+// {any state} --> kError
+// kError --> kUninitialized
+//
+// Notes:
+// - Hardware resources are acquired when transitioning from the
+// |kUninitialized| state to the |kIdle| state.
+// - Buffers can be pushed only in the kRunning or kPaused states.
+// - The end of stream is signaled through a special buffer.
+// Once the end of stream buffer is fed, no other buffer
+// can be fed until the FSM goes through the kIdle state again.
+// - In both kPaused and kRunning states, frames can be fed.
+// However, frames are possibly rendered only in the kRunning state.
+// - In the kRunning state, frames are rendered according to the clock rate.
+// - All the hardware resources must be released in the |kError| state.
+//
+class MediaComponentDevice
+ : NON_EXPORTED_BASE(public base::NonThreadSafe) {
+ public:
+ enum State {
+ kStateUninitialized,
+ kStateIdle,
+ kStateRunning,
+ kStatePaused,
+ kStateError,
+ };
+
+ enum FrameStatus {
+ kFrameSuccess,
+ kFrameFailed,
+ kFramePending,
+ };
+ typedef base::Callback<void(FrameStatus)> FrameStatusCB;
+
+ struct Client {
+ Client();
+ ~Client();
+
+ // Invoked when playback reaches the end of stream.
+ base::Closure eos_cb;
+ };
+
+ // The statistics are computed since the media component left the idle state.
+ // For video, a sample is defined as a frame.
+ struct Statistics {
+ uint64 decoded_bytes;
+ uint64 decoded_samples;
+ uint64 dropped_samples;
+ };
+
+ // Returns whether or not transitioning from |state1| to |state2| is valid.
+ static bool IsValidStateTransition(State state1, State state2);
+
+ // Returns string representation of state (for logging)
+ static std::string StateToString(const State& state);
+
+ MediaComponentDevice();
+ virtual ~MediaComponentDevice();
+
+ // Register |client| as the media event handler.
+ virtual void SetClient(const Client& client) = 0;
+
+ // Changes the state and performs any necessary transitions.
+ // Returns true when successful.
+ virtual bool SetState(State new_state) = 0;
+
+ // Returns the current state of the media component.
+ virtual State GetState() const = 0;
+
+ // Sets the time where rendering should start.
+ // Return true when successful.
+ // Can only be invoked in state kStateIdle.
+ virtual bool SetStartPts(base::TimeDelta time) = 0;
+
+ // Pushes a frame.
+ // |completion_cb| is only invoked if the returned value is |kFramePending|.
+ // In this specific case, no additional frame can be pushed before
+ // |completion_cb| is invoked.
+ // Note: |completion_cb| cannot be invoked with |kFramePending|.
+ // Note: pushing the pending frame should be aborted when the state goes back
+ // to kStateIdle. |completion_cb| is not invoked in that case.
+ virtual FrameStatus PushFrame(
+ const scoped_refptr<DecryptContext>& decrypt_context,
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const FrameStatusCB& completion_cb) = 0;
+
+ // Returns the rendering time of the latest rendered sample.
+ // Can be invoked only in states kStateRunning or kStatePaused.
+ // Returns |kNoTimestamp()| if the playback time cannot be retrieved.
+ virtual base::TimeDelta GetRenderingTime() const = 0;
+
+ // Returns the pipeline latency: i.e. the amount of data
+ // in the pipeline that have not been rendered yet.
+ // Returns |kNoTimestamp()| if the latency is not available.
+ virtual base::TimeDelta GetRenderingDelay() const = 0;
+
+ // Returns the playback statistics. Statistics are computed since the media
+ // component left the idle state.
+ // Returns true when successful.
+ // Can only be invoked in state kStateRunning.
+ virtual bool GetStatistics(Statistics* stats) const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MediaComponentDevice);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_COMPONENT_DEVICE_H_
diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device.cc
new file mode 100644
index 00000000000..ea120c47829
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_pipeline_device.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/backend/media_pipeline_device.h"
+
+namespace chromecast {
+namespace media {
+
+MediaPipelineDevice::MediaPipelineDevice() {
+}
+
+MediaPipelineDevice::~MediaPipelineDevice() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device.h b/chromium/chromecast/media/cma/backend/media_pipeline_device.h
new file mode 100644
index 00000000000..8a961dc7fda
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_pipeline_device.h
@@ -0,0 +1,42 @@
+// 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_BACKEND_MEDIA_PIPELINE_DEVICE_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace chromecast {
+namespace media {
+class AudioPipelineDevice;
+class MediaClockDevice;
+class MediaPipelineDeviceParams;
+class VideoPipelineDevice;
+
+// MediaPipelineDevice is the owner of the underlying audio/video/clock
+// devices.
+class MediaPipelineDevice {
+ public:
+ MediaPipelineDevice();
+ virtual ~MediaPipelineDevice();
+
+ virtual AudioPipelineDevice* GetAudioPipelineDevice() const = 0;
+
+ virtual VideoPipelineDevice* GetVideoPipelineDevice() const = 0;
+
+ virtual MediaClockDevice* GetMediaClockDevice() const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MediaPipelineDevice);
+};
+
+// Factory to create a MediaPipelineDevice.
+scoped_ptr<MediaPipelineDevice> CreateMediaPipelineDevice(
+ const MediaPipelineDeviceParams& params);
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_H_
diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc
new file mode 100644
index 00000000000..c075191e62f
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc
@@ -0,0 +1,573 @@
+// 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/backend/media_pipeline_device_fake.h"
+
+#include <list>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop_proxy.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_component_device.h"
+#include "chromecast/media/cma/backend/video_pipeline_device.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/buffers.h"
+#include "media/base/video_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+
+class MediaClockDeviceFake : public MediaClockDevice {
+ public:
+ MediaClockDeviceFake();
+ virtual ~MediaClockDeviceFake();
+
+ // MediaClockDevice implementation.
+ virtual State GetState() const override;
+ virtual bool SetState(State new_state) override;
+ virtual bool ResetTimeline(base::TimeDelta time) override;
+ virtual bool SetRate(float rate) override;
+ virtual base::TimeDelta GetTime() override;
+
+ private:
+ State state_;
+
+ // Media time sampled at STC time |stc_|.
+ base::TimeDelta media_time_;
+ base::TimeTicks stc_;
+
+ float rate_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaClockDeviceFake);
+};
+
+MediaClockDeviceFake::MediaClockDeviceFake()
+ : state_(kStateUninitialized),
+ media_time_(::media::kNoTimestamp()) {
+ DetachFromThread();
+}
+
+MediaClockDeviceFake::~MediaClockDeviceFake() {
+}
+
+MediaClockDevice::State MediaClockDeviceFake::GetState() const {
+ DCHECK(CalledOnValidThread());
+ return state_;
+}
+
+bool MediaClockDeviceFake::SetState(State new_state) {
+ DCHECK(CalledOnValidThread());
+ if (!MediaClockDevice::IsValidStateTransition(state_, new_state))
+ return false;
+
+ if (new_state == state_)
+ return true;
+
+ state_ = new_state;
+
+ if (state_ == kStateRunning) {
+ stc_ = base::TimeTicks::Now();
+ DCHECK(media_time_ != ::media::kNoTimestamp());
+ return true;
+ }
+
+ if (state_ == kStateIdle) {
+ media_time_ = ::media::kNoTimestamp();
+ return true;
+ }
+
+ return true;
+}
+
+bool MediaClockDeviceFake::ResetTimeline(base::TimeDelta time) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(state_, kStateIdle);
+ media_time_ = time;
+ return true;
+}
+
+bool MediaClockDeviceFake::SetRate(float rate) {
+ DCHECK(CalledOnValidThread());
+ if (state_ == kStateRunning) {
+ base::TimeTicks now = base::TimeTicks::Now();
+ media_time_ = media_time_ + (now - stc_) * rate_;
+ stc_ = now;
+ }
+
+ rate_ = rate;
+ return true;
+}
+
+base::TimeDelta MediaClockDeviceFake::GetTime() {
+ DCHECK(CalledOnValidThread());
+ if (state_ != kStateRunning)
+ return media_time_;
+
+ if (media_time_ == ::media::kNoTimestamp())
+ return ::media::kNoTimestamp();
+
+ base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeDelta interpolated_media_time =
+ media_time_ + (now - stc_) * rate_;
+ return interpolated_media_time;
+}
+
+
+namespace {
+
+// Maximum number of frames that can be buffered.
+const size_t kMaxFrameCount = 20;
+
+} // namespace
+
+class MediaComponentDeviceFake : public MediaComponentDevice {
+ public:
+ explicit MediaComponentDeviceFake(MediaClockDeviceFake* media_clock_device);
+ virtual ~MediaComponentDeviceFake();
+
+ // MediaComponentDevice implementation.
+ virtual void SetClient(const Client& client) override;
+ virtual State GetState() const override;
+ virtual bool SetState(State new_state) override;
+ virtual bool SetStartPts(base::TimeDelta time) override;
+ virtual FrameStatus PushFrame(
+ const scoped_refptr<DecryptContext>& decrypt_context,
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const FrameStatusCB& completion_cb) override;
+ virtual base::TimeDelta GetRenderingTime() const override;
+ virtual base::TimeDelta GetRenderingDelay() const override;
+ virtual bool GetStatistics(Statistics* stats) const override;
+
+ private:
+ struct FakeDecoderBuffer {
+ FakeDecoderBuffer();
+ ~FakeDecoderBuffer();
+
+ // Buffer size.
+ size_t size;
+
+ // Presentation timestamp.
+ base::TimeDelta pts;
+ };
+
+ void RenderTask();
+
+ MediaClockDeviceFake* const media_clock_device_;
+ Client client_;
+
+ State state_;
+
+ // Indicate whether the end of stream has been received.
+ bool is_eos_;
+
+ // Media time of the last rendered audio sample.
+ base::TimeDelta rendering_time_;
+
+ // Frame decoded/rendered since the pipeline left the idle state.
+ uint64 decoded_frame_count_;
+ uint64 decoded_byte_count_;
+
+ // List of frames not rendered yet.
+ std::list<FakeDecoderBuffer> frames_;
+
+ // Indicate whether there is a scheduled rendering task.
+ bool scheduled_rendering_task_;
+
+ // Pending frame.
+ scoped_refptr<DecoderBufferBase> pending_buffer_;
+ FrameStatusCB frame_pushed_cb_;
+
+ base::WeakPtr<MediaComponentDeviceFake> weak_this_;
+ base::WeakPtrFactory<MediaComponentDeviceFake> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaComponentDeviceFake);
+};
+
+MediaComponentDeviceFake::FakeDecoderBuffer::FakeDecoderBuffer()
+ : size(0) {
+}
+
+MediaComponentDeviceFake::FakeDecoderBuffer::~FakeDecoderBuffer() {
+}
+
+MediaComponentDeviceFake::MediaComponentDeviceFake(
+ MediaClockDeviceFake* media_clock_device)
+ : media_clock_device_(media_clock_device),
+ state_(kStateUninitialized),
+ rendering_time_(::media::kNoTimestamp()),
+ decoded_frame_count_(0),
+ decoded_byte_count_(0),
+ scheduled_rendering_task_(false),
+ weak_factory_(this) {
+ weak_this_ = weak_factory_.GetWeakPtr();
+ DetachFromThread();
+}
+
+MediaComponentDeviceFake::~MediaComponentDeviceFake() {
+}
+
+void MediaComponentDeviceFake::SetClient(const Client& client) {
+ DCHECK(CalledOnValidThread());
+ client_ = client;
+}
+
+MediaComponentDevice::State MediaComponentDeviceFake::GetState() const {
+ DCHECK(CalledOnValidThread());
+ return state_;
+}
+
+bool MediaComponentDeviceFake::SetState(State new_state) {
+ DCHECK(CalledOnValidThread());
+ if (!MediaComponentDevice::IsValidStateTransition(state_, new_state))
+ return false;
+ state_ = new_state;
+
+ if (state_ == kStateIdle) {
+ // Back to the idle state: reset a bunch of parameters.
+ is_eos_ = false;
+ rendering_time_ = ::media::kNoTimestamp();
+ decoded_frame_count_ = 0;
+ decoded_byte_count_ = 0;
+ frames_.clear();
+ pending_buffer_ = scoped_refptr<DecoderBufferBase>();
+ frame_pushed_cb_.Reset();
+ return true;
+ }
+
+ if (state_ == kStateRunning) {
+ if (!scheduled_rendering_task_) {
+ scheduled_rendering_task_ = true;
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MediaComponentDeviceFake::RenderTask, weak_this_));
+ }
+ return true;
+ }
+
+ return true;
+}
+
+bool MediaComponentDeviceFake::SetStartPts(base::TimeDelta time) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(state_, kStateIdle);
+ rendering_time_ = time;
+ return true;
+}
+
+MediaComponentDevice::FrameStatus MediaComponentDeviceFake::PushFrame(
+ const scoped_refptr<DecryptContext>& decrypt_context,
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const FrameStatusCB& completion_cb) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(state_ == kStatePaused || state_ == kStateRunning);
+ DCHECK(!is_eos_);
+ DCHECK(!pending_buffer_.get());
+ DCHECK(buffer.get());
+
+ if (buffer->end_of_stream()) {
+ is_eos_ = true;
+ return kFrameSuccess;
+ }
+
+ if (frames_.size() > kMaxFrameCount) {
+ pending_buffer_ = buffer;
+ frame_pushed_cb_ = completion_cb;
+ return kFramePending;
+ }
+
+ FakeDecoderBuffer fake_buffer;
+ fake_buffer.size = buffer->data_size();
+ fake_buffer.pts = buffer->timestamp();
+ frames_.push_back(fake_buffer);
+ return kFrameSuccess;
+}
+
+base::TimeDelta MediaComponentDeviceFake::GetRenderingTime() const {
+ return rendering_time_;
+}
+
+base::TimeDelta MediaComponentDeviceFake::GetRenderingDelay() const {
+ NOTIMPLEMENTED();
+ return ::media::kNoTimestamp();
+}
+
+void MediaComponentDeviceFake::RenderTask() {
+ scheduled_rendering_task_ = false;
+
+ if (state_ != kStateRunning)
+ return;
+
+ base::TimeDelta media_time = media_clock_device_->GetTime();
+ if (media_time == ::media::kNoTimestamp()) {
+ scheduled_rendering_task_ = true;
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&MediaComponentDeviceFake::RenderTask, weak_this_),
+ base::TimeDelta::FromMilliseconds(50));
+ return;
+ }
+
+ while (!frames_.empty() && frames_.front().pts <= media_time) {
+ rendering_time_ = frames_.front().pts;
+ decoded_frame_count_++;
+ decoded_byte_count_ += frames_.front().size;
+ frames_.pop_front();
+ if (pending_buffer_.get()) {
+ FakeDecoderBuffer fake_buffer;
+ fake_buffer.size = pending_buffer_->data_size();
+ fake_buffer.pts = pending_buffer_->timestamp();
+ frames_.push_back(fake_buffer);
+ pending_buffer_ = scoped_refptr<DecoderBufferBase>();
+ base::ResetAndReturn(&frame_pushed_cb_).Run(kFrameSuccess);
+ }
+ }
+
+ if (frames_.empty() && is_eos_) {
+ if (!client_.eos_cb.is_null())
+ client_.eos_cb.Run();
+ return;
+ }
+
+ scheduled_rendering_task_ = true;
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&MediaComponentDeviceFake::RenderTask, weak_this_),
+ base::TimeDelta::FromMilliseconds(50));
+}
+
+bool MediaComponentDeviceFake::GetStatistics(Statistics* stats) const {
+ if (state_ != kStateRunning)
+ return false;
+
+ // Note: what is returned here is not the number of samples but the number of
+ // frames. The value is different for audio.
+ stats->decoded_bytes = decoded_byte_count_;
+ stats->decoded_samples = decoded_frame_count_;
+ stats->dropped_samples = 0;
+ return true;
+}
+
+
+class AudioPipelineDeviceFake : public AudioPipelineDevice {
+ public:
+ explicit AudioPipelineDeviceFake(MediaClockDeviceFake* media_clock_device);
+ virtual ~AudioPipelineDeviceFake();
+
+ // AudioPipelineDevice implementation.
+ virtual void SetClient(const Client& client) override;
+ virtual State GetState() const override;
+ virtual bool SetState(State new_state) override;
+ virtual bool SetStartPts(base::TimeDelta time) override;
+ virtual FrameStatus PushFrame(
+ const scoped_refptr<DecryptContext>& decrypt_context,
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const FrameStatusCB& completion_cb) override;
+ virtual base::TimeDelta GetRenderingTime() const override;
+ virtual base::TimeDelta GetRenderingDelay() const override;
+ virtual bool SetConfig(const ::media::AudioDecoderConfig& config) override;
+ virtual void SetStreamVolumeMultiplier(float multiplier) override;
+ virtual bool GetStatistics(Statistics* stats) const override;
+
+ private:
+ scoped_ptr<MediaComponentDeviceFake> fake_pipeline_;
+
+ ::media::AudioDecoderConfig config_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioPipelineDeviceFake);
+};
+
+AudioPipelineDeviceFake::AudioPipelineDeviceFake(
+ MediaClockDeviceFake* media_clock_device)
+ : fake_pipeline_(new MediaComponentDeviceFake(media_clock_device)) {
+ DetachFromThread();
+}
+
+AudioPipelineDeviceFake::~AudioPipelineDeviceFake() {
+}
+
+void AudioPipelineDeviceFake::SetClient(const Client& client) {
+ fake_pipeline_->SetClient(client);
+}
+
+MediaComponentDevice::State AudioPipelineDeviceFake::GetState() const {
+ return fake_pipeline_->GetState();
+}
+
+bool AudioPipelineDeviceFake::SetState(State new_state) {
+ bool success = fake_pipeline_->SetState(new_state);
+ if (!success)
+ return false;
+
+ if (new_state == kStateIdle) {
+ DCHECK(config_.IsValidConfig());
+ }
+ if (new_state == kStateUninitialized) {
+ config_ = ::media::AudioDecoderConfig();
+ }
+ return true;
+}
+
+bool AudioPipelineDeviceFake::SetStartPts(base::TimeDelta time) {
+ return fake_pipeline_->SetStartPts(time);
+}
+
+MediaComponentDevice::FrameStatus AudioPipelineDeviceFake::PushFrame(
+ const scoped_refptr<DecryptContext>& decrypt_context,
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const FrameStatusCB& completion_cb) {
+ return fake_pipeline_->PushFrame(decrypt_context, buffer, completion_cb);
+}
+
+base::TimeDelta AudioPipelineDeviceFake::GetRenderingTime() const {
+ return fake_pipeline_->GetRenderingTime();
+}
+
+base::TimeDelta AudioPipelineDeviceFake::GetRenderingDelay() const {
+ return fake_pipeline_->GetRenderingDelay();
+}
+
+bool AudioPipelineDeviceFake::SetConfig(
+ const ::media::AudioDecoderConfig& config) {
+ DCHECK(CalledOnValidThread());
+ if (!config.IsValidConfig())
+ return false;
+ config_ = config;
+ return true;
+}
+
+void AudioPipelineDeviceFake::SetStreamVolumeMultiplier(float multiplier) {
+ DCHECK(CalledOnValidThread());
+}
+
+bool AudioPipelineDeviceFake::GetStatistics(Statistics* stats) const {
+ return fake_pipeline_->GetStatistics(stats);
+}
+
+
+class VideoPipelineDeviceFake : public VideoPipelineDevice {
+ public:
+ explicit VideoPipelineDeviceFake(MediaClockDeviceFake* media_clock_device);
+ virtual ~VideoPipelineDeviceFake();
+
+ // VideoPipelineDevice implementation.
+ virtual void SetClient(const Client& client) override;
+ virtual State GetState() const override;
+ virtual bool SetState(State new_state) override;
+ virtual bool SetStartPts(base::TimeDelta time) override;
+ virtual FrameStatus PushFrame(
+ const scoped_refptr<DecryptContext>& decrypt_context,
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const FrameStatusCB& completion_cb) override;
+ virtual base::TimeDelta GetRenderingTime() const override;
+ virtual base::TimeDelta GetRenderingDelay() const override;
+ virtual void SetVideoClient(const VideoClient& client) override;
+ virtual bool SetConfig(const ::media::VideoDecoderConfig& config) override;
+ virtual bool GetStatistics(Statistics* stats) const override;
+
+ private:
+ scoped_ptr<MediaComponentDeviceFake> fake_pipeline_;
+
+ ::media::VideoDecoderConfig config_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoPipelineDeviceFake);
+};
+
+VideoPipelineDeviceFake::VideoPipelineDeviceFake(
+ MediaClockDeviceFake* media_clock_device)
+ : fake_pipeline_(new MediaComponentDeviceFake(media_clock_device)) {
+ DetachFromThread();
+}
+
+VideoPipelineDeviceFake::~VideoPipelineDeviceFake() {
+}
+
+void VideoPipelineDeviceFake::SetClient(const Client& client) {
+ fake_pipeline_->SetClient(client);
+}
+
+MediaComponentDevice::State VideoPipelineDeviceFake::GetState() const {
+ return fake_pipeline_->GetState();
+}
+
+bool VideoPipelineDeviceFake::SetState(State new_state) {
+ bool success = fake_pipeline_->SetState(new_state);
+ if (!success)
+ return false;
+
+ if (new_state == kStateIdle) {
+ DCHECK(config_.IsValidConfig());
+ }
+ if (new_state == kStateUninitialized) {
+ config_ = ::media::VideoDecoderConfig();
+ }
+ return true;
+}
+
+bool VideoPipelineDeviceFake::SetStartPts(base::TimeDelta time) {
+ return fake_pipeline_->SetStartPts(time);
+}
+
+MediaComponentDevice::FrameStatus VideoPipelineDeviceFake::PushFrame(
+ const scoped_refptr<DecryptContext>& decrypt_context,
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const FrameStatusCB& completion_cb) {
+ return fake_pipeline_->PushFrame(decrypt_context, buffer, completion_cb);
+}
+
+base::TimeDelta VideoPipelineDeviceFake::GetRenderingTime() const {
+ return fake_pipeline_->GetRenderingTime();
+}
+
+base::TimeDelta VideoPipelineDeviceFake::GetRenderingDelay() const {
+ return fake_pipeline_->GetRenderingDelay();
+}
+
+void VideoPipelineDeviceFake::SetVideoClient(const VideoClient& client) {
+}
+
+bool VideoPipelineDeviceFake::SetConfig(
+ const ::media::VideoDecoderConfig& config) {
+ DCHECK(CalledOnValidThread());
+ if (!config.IsValidConfig())
+ return false;
+ config_ = config;
+ return true;
+}
+
+bool VideoPipelineDeviceFake::GetStatistics(Statistics* stats) const {
+ return fake_pipeline_->GetStatistics(stats);
+}
+
+
+MediaPipelineDeviceFake::MediaPipelineDeviceFake()
+ : media_clock_device_(new MediaClockDeviceFake()),
+ audio_pipeline_device_(
+ new AudioPipelineDeviceFake(media_clock_device_.get())),
+ video_pipeline_device_(
+ new VideoPipelineDeviceFake(media_clock_device_.get())) {
+}
+
+MediaPipelineDeviceFake::~MediaPipelineDeviceFake() {
+}
+
+AudioPipelineDevice* MediaPipelineDeviceFake::GetAudioPipelineDevice() const {
+ return audio_pipeline_device_.get();
+}
+
+VideoPipelineDevice* MediaPipelineDeviceFake::GetVideoPipelineDevice() const {
+ return video_pipeline_device_.get();
+}
+
+MediaClockDevice* MediaPipelineDeviceFake::GetMediaClockDevice() const {
+ return media_clock_device_.get();
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h
new file mode 100644
index 00000000000..0534515bf9f
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h
@@ -0,0 +1,40 @@
+// 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_BACKEND_MEDIA_PIPELINE_DEVICE_FAKE_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_FAKE_H_
+
+#include "chromecast/media/cma/backend/media_pipeline_device.h"
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace chromecast {
+namespace media {
+class AudioPipelineDeviceFake;
+class MediaClockDeviceFake;
+class VideoPipelineDeviceFake;
+
+class MediaPipelineDeviceFake : public MediaPipelineDevice {
+ public:
+ MediaPipelineDeviceFake();
+ virtual ~MediaPipelineDeviceFake();
+
+ // MediaPipelineDevice implementation.
+ virtual AudioPipelineDevice* GetAudioPipelineDevice() const override;
+ virtual VideoPipelineDevice* GetVideoPipelineDevice() const override;
+ virtual MediaClockDevice* GetMediaClockDevice() const override;
+
+ private:
+ scoped_ptr<MediaClockDeviceFake> media_clock_device_;
+ scoped_ptr<AudioPipelineDeviceFake> audio_pipeline_device_;
+ scoped_ptr<VideoPipelineDeviceFake> video_pipeline_device_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaPipelineDeviceFake);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_H_
diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake_factory.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake_factory.cc
new file mode 100644
index 00000000000..646a4637158
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake_factory.cc
@@ -0,0 +1,18 @@
+// 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/backend/media_pipeline_device_fake.h"
+
+#include "base/memory/scoped_ptr.h"
+
+namespace chromecast {
+namespace media {
+
+scoped_ptr<MediaPipelineDevice> CreateMediaPipelineDevice(
+ const MediaPipelineDeviceParams& params) {
+ return scoped_ptr<MediaPipelineDevice>(new MediaPipelineDeviceFake());
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_params.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device_params.cc
new file mode 100644
index 00000000000..531c76884f9
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_params.cc
@@ -0,0 +1,18 @@
+// 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/backend/media_pipeline_device_params.h"
+
+namespace chromecast {
+namespace media {
+
+MediaPipelineDeviceParams::MediaPipelineDeviceParams()
+ : disable_synchronization(false) {
+}
+
+MediaPipelineDeviceParams::~MediaPipelineDeviceParams() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_params.h b/chromium/chromecast/media/cma/backend/media_pipeline_device_params.h
new file mode 100644
index 00000000000..9bf9c044f16
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_params.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_BACKEND_MEDIA_PIPELINE_DEVICE_PARAMS_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_PARAMS_H_
+
+#include "base/macros.h"
+
+namespace chromecast {
+namespace media {
+
+class MediaPipelineDeviceParams {
+ public:
+ MediaPipelineDeviceParams();
+ ~MediaPipelineDeviceParams();
+
+ // When set to true, synchronization is disabled and audio/video frames are
+ // rendered "right away":
+ // - for audio, frames are still rendered based on the sampling frequency
+ // - for video, frames are rendered as soon as available at the output of
+ // the video decoder.
+ // The assumption is that no B frames are used when synchronization is
+ // disabled, otherwise B frames would always be skipped.
+ bool disable_synchronization;
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_PARAMS_H_
diff --git a/chromium/chromecast/media/cma/backend/video_pipeline_device.cc b/chromium/chromecast/media/cma/backend/video_pipeline_device.cc
new file mode 100644
index 00000000000..d00c2cd4afe
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/video_pipeline_device.cc
@@ -0,0 +1,23 @@
+// 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/backend/video_pipeline_device.h"
+
+namespace chromecast {
+namespace media {
+
+VideoPipelineDevice::VideoClient::VideoClient() {
+}
+
+VideoPipelineDevice::VideoClient::~VideoClient() {
+}
+
+VideoPipelineDevice::VideoPipelineDevice() {
+}
+
+VideoPipelineDevice::~VideoPipelineDevice() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/backend/video_pipeline_device.h b/chromium/chromecast/media/cma/backend/video_pipeline_device.h
new file mode 100644
index 00000000000..eda48e206f4
--- /dev/null
+++ b/chromium/chromecast/media/cma/backend/video_pipeline_device.h
@@ -0,0 +1,63 @@
+// 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_BACKEND_VIDEO_PIPELINE_DEVICE_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_PIPELINE_DEVICE_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chromecast/media/cma/backend/media_component_device.h"
+
+namespace gfx {
+class Size;
+}
+
+namespace media {
+class VideoDecoderConfig;
+}
+
+namespace chromecast {
+namespace media {
+class DecoderBufferBase;
+
+// VideoPipelineDevice -
+//
+// Notes:
+// - Like a regular MediaComponentDevice, frames are possibly rendered only
+// in the kRunning state.
+// However, the first frame must be rendered regardless of the clock state:
+// - no synchronization needed to display the first frame,
+// - the clock rate has no impact on the presentation of the first frame.
+//
+class VideoPipelineDevice : public MediaComponentDevice {
+ public:
+ struct VideoClient {
+ VideoClient();
+ ~VideoClient();
+
+ // Invoked each time the natural size is updated.
+ base::Callback<void(const gfx::Size& natural_size)>
+ natural_size_changed_cb;
+ };
+
+ VideoPipelineDevice();
+ virtual ~VideoPipelineDevice();
+
+ // Registers |client| as the video specific event handler.
+ virtual void SetVideoClient(const VideoClient& client) = 0;
+
+ // Provide the video configuration.
+ // Must be called before switching from |kStateUninitialized| to |kStateIdle|.
+ // Afterwards, this can be invoked any time the configuration changes.
+ // Returns true if the configuration is a supported configuration.
+ virtual bool SetConfig(const ::media::VideoDecoderConfig& config) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VideoPipelineDevice);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_PIPELINE_DEVICE_H_
diff --git a/chromium/chromecast/media/cma/base/balanced_media_task_runner_factory.cc b/chromium/chromecast/media/cma/base/balanced_media_task_runner_factory.cc
new file mode 100644
index 00000000000..48413c0a9e2
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/balanced_media_task_runner_factory.cc
@@ -0,0 +1,252 @@
+// 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/balanced_media_task_runner_factory.h"
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "chromecast/media/cma/base/media_task_runner.h"
+#include "media/base/buffers.h"
+
+namespace chromecast {
+namespace media {
+
+// MediaTaskRunnerWithNotification -
+// Media task runner which also behaves as a media task runner observer.
+class MediaTaskRunnerWithNotification : public MediaTaskRunner {
+ public:
+ // Wraps a MediaTaskRunner so that a third party can:
+ // - be notified when a PostMediaTask is performed on this media task runner.
+ // |new_task_cb| is invoked in that case.
+ // - monitor the lifetime of the media task runner, i.e. check when the media
+ // task runner is not needed anymore.
+ // |shutdown_cb| is invoked in that case.
+ MediaTaskRunnerWithNotification(
+ const scoped_refptr<MediaTaskRunner>& media_task_runner,
+ const base::Closure& new_task_cb,
+ const base::Closure& shutdown_cb);
+
+ // MediaTaskRunner implementation.
+ virtual bool PostMediaTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta timestamp) override;
+
+ private:
+ virtual ~MediaTaskRunnerWithNotification();
+
+ scoped_refptr<MediaTaskRunner> const media_task_runner_;
+
+ const base::Closure new_task_cb_;
+ const base::Closure shutdown_cb_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaTaskRunnerWithNotification);
+};
+
+MediaTaskRunnerWithNotification::MediaTaskRunnerWithNotification(
+ const scoped_refptr<MediaTaskRunner>& media_task_runner,
+ const base::Closure& new_task_cb,
+ const base::Closure& shutdown_cb)
+ : media_task_runner_(media_task_runner),
+ new_task_cb_(new_task_cb),
+ shutdown_cb_(shutdown_cb) {
+}
+
+MediaTaskRunnerWithNotification::~MediaTaskRunnerWithNotification() {
+ shutdown_cb_.Run();
+}
+
+bool MediaTaskRunnerWithNotification::PostMediaTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta timestamp) {
+ bool may_run_in_future =
+ media_task_runner_->PostMediaTask(from_here, task, timestamp);
+ if (may_run_in_future)
+ new_task_cb_.Run();
+ return may_run_in_future;
+}
+
+
+// BalancedMediaTaskRunner -
+// Run media tasks whose timestamp is less or equal to a max timestamp.
+//
+// Restrictions of BalancedMediaTaskRunner:
+// - Can have at most one task in the queue.
+// - Tasks should be given by increasing timestamps.
+class BalancedMediaTaskRunner
+ : public MediaTaskRunner {
+ public:
+ explicit BalancedMediaTaskRunner(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+
+ // Schedule tasks whose timestamp is less than or equal to |max_timestamp|.
+ void ScheduleWork(base::TimeDelta max_timestamp);
+
+ // Return the timestamp of the last media task.
+ // Return ::media::kNoTimestamp() if no media task has been posted.
+ base::TimeDelta GetMediaTimestamp() const;
+
+ // MediaTaskRunner implementation.
+ virtual bool PostMediaTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta timestamp) override;
+
+ private:
+ virtual ~BalancedMediaTaskRunner();
+
+ scoped_refptr<base::SingleThreadTaskRunner> const task_runner_;
+
+ // Protects the following variables.
+ mutable base::Lock lock_;
+
+ // Possible pending media task.
+ tracked_objects::Location from_here_;
+ base::Closure pending_task_;
+
+ // Timestamp of the last posted task.
+ // Is initialized to ::media::kNoTimestamp().
+ base::TimeDelta last_timestamp_;
+
+ DISALLOW_COPY_AND_ASSIGN(BalancedMediaTaskRunner);
+};
+
+BalancedMediaTaskRunner::BalancedMediaTaskRunner(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+ : task_runner_(task_runner),
+ last_timestamp_(::media::kNoTimestamp()) {
+}
+
+BalancedMediaTaskRunner::~BalancedMediaTaskRunner() {
+}
+
+void BalancedMediaTaskRunner::ScheduleWork(base::TimeDelta max_media_time) {
+ base::Closure task;
+ {
+ base::AutoLock auto_lock(lock_);
+ if (pending_task_.is_null())
+ return;
+
+ if (last_timestamp_ != ::media::kNoTimestamp() &&
+ last_timestamp_ >= max_media_time) {
+ return;
+ }
+
+ task = base::ResetAndReturn(&pending_task_);
+ }
+ task_runner_->PostTask(from_here_, task);
+}
+
+base::TimeDelta BalancedMediaTaskRunner::GetMediaTimestamp() const {
+ base::AutoLock auto_lock(lock_);
+ return last_timestamp_;
+}
+
+bool BalancedMediaTaskRunner::PostMediaTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta timestamp) {
+ DCHECK(!task.is_null());
+
+ // Pass through for a task with no timestamp.
+ if (timestamp == ::media::kNoTimestamp()) {
+ return task_runner_->PostTask(from_here, task);
+ }
+
+ base::AutoLock auto_lock(lock_);
+
+ // Timestamps must be in order.
+ // Any task that does not meet that condition is simply discarded.
+ if (last_timestamp_ != ::media::kNoTimestamp() &&
+ timestamp < last_timestamp_) {
+ return false;
+ }
+
+ // Only support one pending task at a time.
+ DCHECK(pending_task_.is_null());
+ from_here_ = from_here;
+ pending_task_ = task;
+ last_timestamp_ = timestamp;
+
+ return true;
+}
+
+
+BalancedMediaTaskRunnerFactory::BalancedMediaTaskRunnerFactory(
+ base::TimeDelta max_delta)
+ : max_delta_(max_delta) {
+}
+
+BalancedMediaTaskRunnerFactory::~BalancedMediaTaskRunnerFactory() {
+}
+
+scoped_refptr<MediaTaskRunner>
+BalancedMediaTaskRunnerFactory::CreateMediaTaskRunner(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+ scoped_refptr<BalancedMediaTaskRunner> media_task_runner(
+ new BalancedMediaTaskRunner(task_runner));
+ scoped_refptr<MediaTaskRunnerWithNotification> media_task_runner_wrapper(
+ new MediaTaskRunnerWithNotification(
+ media_task_runner,
+ base::Bind(&BalancedMediaTaskRunnerFactory::OnNewTask, this),
+ base::Bind(
+ &BalancedMediaTaskRunnerFactory::UnregisterMediaTaskRunner,
+ this, media_task_runner)));
+ base::AutoLock auto_lock(lock_);
+ // Note that |media_task_runner| is inserted here and
+ // not |media_task_runner_wrapper|. Otherwise, we would always have one
+ // ref on |media_task_runner_wrapper| and would never get the release
+ // notification.
+ // When |media_task_runner_wrapper| is going away,
+ // BalancedMediaTaskRunnerFactory will receive a notification and will in
+ // turn remove |media_task_runner|.
+ task_runners_.insert(media_task_runner);
+ return media_task_runner_wrapper;
+}
+
+void BalancedMediaTaskRunnerFactory::OnNewTask() {
+ typedef
+ std::multimap<base::TimeDelta, scoped_refptr<BalancedMediaTaskRunner> >
+ TaskRunnerMap;
+ TaskRunnerMap runnable_task_runner;
+
+ base::AutoLock auto_lock(lock_);
+
+ // Get the minimum timestamp among all streams.
+ for (MediaTaskRunnerSet::const_iterator it = task_runners_.begin();
+ it != task_runners_.end(); ++it) {
+ base::TimeDelta timestamp((*it)->GetMediaTimestamp());
+ if (timestamp == ::media::kNoTimestamp())
+ continue;
+ runnable_task_runner.insert(
+ std::pair<base::TimeDelta, scoped_refptr<BalancedMediaTaskRunner> >(
+ timestamp, *it));
+ }
+
+ // If there is no media task, just returns.
+ if (runnable_task_runner.empty())
+ return;
+
+ // Run tasks which meet the balancing criteria.
+ base::TimeDelta min_timestamp(runnable_task_runner.begin()->first);
+ base::TimeDelta max_timestamp = min_timestamp + max_delta_;
+ for (TaskRunnerMap::iterator it = runnable_task_runner.begin();
+ it != runnable_task_runner.end(); ++it) {
+ (*it).second->ScheduleWork(max_timestamp);
+ }
+}
+
+void BalancedMediaTaskRunnerFactory::UnregisterMediaTaskRunner(
+ const scoped_refptr<BalancedMediaTaskRunner>& media_task_runner) {
+ base::AutoLock auto_lock(lock_);
+ task_runners_.erase(media_task_runner);
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/balanced_media_task_runner_factory.h b/chromium/chromecast/media/cma/base/balanced_media_task_runner_factory.h
new file mode 100644
index 00000000000..488373d5633
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/balanced_media_task_runner_factory.h
@@ -0,0 +1,68 @@
+// 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_BALANCED_TASK_RUNNER_FACTORY_H_
+#define CHROMECAST_MEDIA_CMA_BASE_BALANCED_TASK_RUNNER_FACTORY_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace chromecast {
+namespace media {
+class BalancedMediaTaskRunner;
+class MediaTaskRunner;
+
+// BalancedMediaTaskRunnerFactory -
+// Create media tasks runners that are loosely synchronized between each other.
+// For two tasks T1 and T2 with timestamps ts1 and ts2, the scheduler ensures
+// T2 is not scheduled before T1 if ts2 > ts1 + |max_delta|.
+class BalancedMediaTaskRunnerFactory
+ : public base::RefCountedThreadSafe<BalancedMediaTaskRunnerFactory> {
+ public:
+ explicit BalancedMediaTaskRunnerFactory(base::TimeDelta max_delta);
+
+ // Creates a media task runner using |task_runner| as the underlying
+ // regular task runner.
+ // Restriction on the returned media task runner:
+ // - can only schedule only one media task at a time.
+ // - timestamps of tasks posted on that task runner must be increasing.
+ scoped_refptr<MediaTaskRunner> CreateMediaTaskRunner(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+
+ private:
+ typedef std::set<scoped_refptr<BalancedMediaTaskRunner> > MediaTaskRunnerSet;
+
+ friend class base::RefCountedThreadSafe<BalancedMediaTaskRunnerFactory>;
+ virtual ~BalancedMediaTaskRunnerFactory();
+
+ // Invoked when one of the registered media task runners received a new media
+ // task.
+ void OnNewTask();
+
+ // Unregister a media task runner.
+ void UnregisterMediaTaskRunner(
+ const scoped_refptr<BalancedMediaTaskRunner>& media_task_runner);
+
+ // Maximum timestamp deviation between tasks from the registered task runners.
+ const base::TimeDelta max_delta_;
+
+ // Task runners created by the factory that have not been unregistered yet.
+ base::Lock lock_;
+ MediaTaskRunnerSet task_runners_;
+
+ DISALLOW_COPY_AND_ASSIGN(BalancedMediaTaskRunnerFactory);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_BALANCED_TASK_RUNNER_FACTORY_H_
diff --git a/chromium/chromecast/media/cma/base/balanced_media_task_runner_unittest.cc b/chromium/chromecast/media/cma/base/balanced_media_task_runner_unittest.cc
new file mode 100644
index 00000000000..e3448f67cac
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/balanced_media_task_runner_unittest.cc
@@ -0,0 +1,263 @@
+// 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 <list>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "chromecast/media/cma/base/balanced_media_task_runner_factory.h"
+#include "chromecast/media/cma/base/media_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+struct MediaTaskRunnerTestContext {
+ MediaTaskRunnerTestContext();
+ ~MediaTaskRunnerTestContext();
+
+ scoped_refptr<MediaTaskRunner> media_task_runner;
+
+ bool is_pending_task;
+
+ std::vector<base::TimeDelta> task_timestamp_list;
+
+ size_t task_index;
+ base::TimeDelta max_timestamp;
+};
+
+MediaTaskRunnerTestContext::MediaTaskRunnerTestContext() {
+}
+
+MediaTaskRunnerTestContext::~MediaTaskRunnerTestContext() {
+}
+
+} // namespace
+
+class BalancedMediaTaskRunnerTest : public testing::Test {
+ public:
+ BalancedMediaTaskRunnerTest();
+ virtual ~BalancedMediaTaskRunnerTest();
+
+ void SetupTest(base::TimeDelta max_delta,
+ const std::vector<std::vector<int> >& timestamps_in_ms,
+ const std::vector<size_t>& pattern,
+ const std::vector<int>& expected_task_timestamps_ms);
+ void ProcessAllTasks();
+
+ protected:
+ // Expected task order based on their timestamps.
+ std::list<base::TimeDelta> expected_task_timestamps_;
+
+ private:
+ void ScheduleTask();
+ void Task(size_t task_runner_id, base::TimeDelta timestamp);
+
+ void OnTestTimeout();
+
+ scoped_refptr<BalancedMediaTaskRunnerFactory> media_task_runner_factory_;
+
+ // Schedule first a task on media task runner #scheduling_pattern[0]
+ // then a task on media task runner #scheduling_pattern[1] and so on.
+ // Wrap around when reaching the end of the pattern.
+ std::vector<size_t> scheduling_pattern_;
+ size_t pattern_index_;
+
+ // For each media task runner, keep a track of which task has already been
+ // scheduled.
+ std::vector<MediaTaskRunnerTestContext> contexts_;
+
+ DISALLOW_COPY_AND_ASSIGN(BalancedMediaTaskRunnerTest);
+};
+
+BalancedMediaTaskRunnerTest::BalancedMediaTaskRunnerTest() {
+}
+
+BalancedMediaTaskRunnerTest::~BalancedMediaTaskRunnerTest() {
+}
+
+void BalancedMediaTaskRunnerTest::SetupTest(
+ base::TimeDelta max_delta,
+ const std::vector<std::vector<int> >& timestamps_in_ms,
+ const std::vector<size_t>& pattern,
+ const std::vector<int>& expected_task_timestamps_ms) {
+ media_task_runner_factory_ = new BalancedMediaTaskRunnerFactory(max_delta);
+
+ scheduling_pattern_ = pattern;
+ pattern_index_ = 0;
+
+ // Setup each task runner.
+ size_t n = timestamps_in_ms.size();
+ contexts_.resize(n);
+ for (size_t k = 0; k < n; k++) {
+ contexts_[k].media_task_runner =
+ media_task_runner_factory_->CreateMediaTaskRunner(
+ base::MessageLoopProxy::current());
+ contexts_[k].is_pending_task = false;
+ contexts_[k].task_index = 0;
+ contexts_[k].task_timestamp_list.resize(
+ timestamps_in_ms[k].size());
+ for (size_t i = 0; i < timestamps_in_ms[k].size(); i++) {
+ contexts_[k].task_timestamp_list[i] =
+ base::TimeDelta::FromMilliseconds(timestamps_in_ms[k][i]);
+ }
+ }
+
+ // Expected task order (for tasks that are actually run).
+ for (size_t k = 0; k < expected_task_timestamps_ms.size(); k++) {
+ expected_task_timestamps_.push_back(
+ base::TimeDelta::FromMilliseconds(expected_task_timestamps_ms[k]));
+ }
+}
+
+void BalancedMediaTaskRunnerTest::ProcessAllTasks() {
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&BalancedMediaTaskRunnerTest::OnTestTimeout,
+ base::Unretained(this)),
+ base::TimeDelta::FromSeconds(5));
+ ScheduleTask();
+}
+
+void BalancedMediaTaskRunnerTest::ScheduleTask() {
+ bool has_task = false;
+ for (size_t k = 0; k < contexts_.size(); k++) {
+ if (contexts_[k].task_index < contexts_[k].task_timestamp_list.size())
+ has_task = true;
+ }
+ if (!has_task) {
+ base::MessageLoop::current()->QuitWhenIdle();
+ return;
+ }
+
+ size_t next_pattern_index =
+ (pattern_index_ + 1) % scheduling_pattern_.size();
+
+ size_t task_runner_id = scheduling_pattern_[pattern_index_];
+ MediaTaskRunnerTestContext& context = contexts_[task_runner_id];
+
+ // Check whether all tasks have been scheduled for that task runner
+ // or if there is already one pending task.
+ if (context.task_index >= context.task_timestamp_list.size() ||
+ context.is_pending_task) {
+ pattern_index_ = next_pattern_index;
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&BalancedMediaTaskRunnerTest::ScheduleTask,
+ base::Unretained(this)));
+ return;
+ }
+
+ bool expected_may_run = false;
+ if (context.task_timestamp_list[context.task_index] >=
+ context.max_timestamp) {
+ expected_may_run = true;
+ context.max_timestamp = context.task_timestamp_list[context.task_index];
+ }
+
+ bool may_run = context.media_task_runner->PostMediaTask(
+ FROM_HERE,
+ base::Bind(&BalancedMediaTaskRunnerTest::Task,
+ base::Unretained(this),
+ task_runner_id,
+ context.task_timestamp_list[context.task_index]),
+ context.task_timestamp_list[context.task_index]);
+ EXPECT_EQ(may_run, expected_may_run);
+
+ if (may_run)
+ context.is_pending_task = true;
+
+ context.task_index++;
+ pattern_index_ = next_pattern_index;
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&BalancedMediaTaskRunnerTest::ScheduleTask,
+ base::Unretained(this)));
+}
+
+void BalancedMediaTaskRunnerTest::Task(
+ size_t task_runner_id, base::TimeDelta timestamp) {
+ ASSERT_FALSE(expected_task_timestamps_.empty());
+ EXPECT_EQ(timestamp, expected_task_timestamps_.front());
+ expected_task_timestamps_.pop_front();
+
+ contexts_[task_runner_id].is_pending_task = false;
+}
+
+void BalancedMediaTaskRunnerTest::OnTestTimeout() {
+ ADD_FAILURE() << "Test timed out";
+ if (base::MessageLoop::current())
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+TEST_F(BalancedMediaTaskRunnerTest, OneTaskRunner) {
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+
+ // Timestamps of tasks for the single task runner.
+ int timestamps0_ms[] = {0, 10, 20, 30, 40, 30, 50, 60, 20, 30, 70};
+ std::vector<std::vector<int> > timestamps_ms(1);
+ timestamps_ms[0] = std::vector<int>(
+ timestamps0_ms, timestamps0_ms + arraysize(timestamps0_ms));
+
+ // Scheduling pattern.
+ std::vector<size_t> scheduling_pattern(1);
+ scheduling_pattern[0] = 0;
+
+ // Expected results.
+ int expected_timestamps[] = {0, 10, 20, 30, 40, 50, 60, 70};
+ std::vector<int> expected_timestamps_ms(std::vector<int>(
+ expected_timestamps,
+ expected_timestamps + arraysize(expected_timestamps)));
+
+ SetupTest(base::TimeDelta::FromMilliseconds(30),
+ timestamps_ms,
+ scheduling_pattern,
+ expected_timestamps_ms);
+ ProcessAllTasks();
+ message_loop->Run();
+ EXPECT_TRUE(expected_task_timestamps_.empty());
+}
+
+TEST_F(BalancedMediaTaskRunnerTest, TwoTaskRunnerUnbalanced) {
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+
+ // Timestamps of tasks for the 2 task runners.
+ int timestamps0_ms[] = {0, 10, 20, 30, 40, 30, 50, 60, 20, 30, 70};
+ int timestamps1_ms[] = {5, 15, 25, 35, 45, 35, 55, 65, 25, 35, 75};
+ std::vector<std::vector<int> > timestamps_ms(2);
+ timestamps_ms[0] = std::vector<int>(
+ timestamps0_ms, timestamps0_ms + arraysize(timestamps0_ms));
+ timestamps_ms[1] = std::vector<int>(
+ timestamps1_ms, timestamps1_ms + arraysize(timestamps1_ms));
+
+ // Scheduling pattern.
+ size_t pattern[] = {1, 0, 0, 0, 0};
+ std::vector<size_t> scheduling_pattern = std::vector<size_t>(
+ pattern, pattern + arraysize(pattern));
+
+ // Expected results.
+ int expected_timestamps[] = {
+ 5, 0, 10, 20, 30, 15, 40, 25, 50, 35, 60, 45, 70, 55, 65, 75 };
+ std::vector<int> expected_timestamps_ms(std::vector<int>(
+ expected_timestamps,
+ expected_timestamps + arraysize(expected_timestamps)));
+
+ SetupTest(base::TimeDelta::FromMilliseconds(30),
+ timestamps_ms,
+ scheduling_pattern,
+ expected_timestamps_ms);
+ ProcessAllTasks();
+ message_loop->Run();
+ EXPECT_TRUE(expected_task_timestamps_.empty());
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/buffering_controller.cc b/chromium/chromecast/media/cma/base/buffering_controller.cc
new file mode 100644
index 00000000000..183b12628d2
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/buffering_controller.cc
@@ -0,0 +1,206 @@
+// 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_controller.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "chromecast/base/metrics/cast_metrics_helper.h"
+#include "chromecast/media/cma/base/buffering_state.h"
+#include "chromecast/media/cma/base/cma_logging.h"
+#include "media/base/buffers.h"
+
+namespace chromecast {
+namespace media {
+
+BufferingController::BufferingController(
+ const scoped_refptr<BufferingConfig>& config,
+ const BufferingNotificationCB& buffering_notification_cb)
+ : config_(config),
+ buffering_notification_cb_(buffering_notification_cb),
+ is_buffering_(false),
+ begin_buffering_time_(base::Time()),
+ initial_buffering_(true),
+ weak_factory_(this) {
+ weak_this_ = weak_factory_.GetWeakPtr();
+ thread_checker_.DetachFromThread();
+}
+
+BufferingController::~BufferingController() {
+ // Some weak pointers might possibly be invalidated here.
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void BufferingController::UpdateHighLevelThreshold(
+ base::TimeDelta high_level_threshold) {
+ // Can only decrease the high level threshold.
+ if (high_level_threshold > config_->high_level())
+ return;
+ CMALOG(kLogControl) << "High buffer threshold: "
+ << high_level_threshold.InMilliseconds();
+ config_->set_high_level(high_level_threshold);
+
+ // Make sure the low level threshold is somewhat consistent.
+ // Currently, we set it to one third of the high level threshold:
+ // this value could be adjusted in the future.
+ base::TimeDelta low_level_threshold = high_level_threshold / 3;
+ if (low_level_threshold <= config_->low_level()) {
+ CMALOG(kLogControl) << "Low buffer threshold: "
+ << low_level_threshold.InMilliseconds();
+ config_->set_low_level(low_level_threshold);
+ }
+
+ // Signal all the streams the config has changed.
+ for (StreamList::iterator it = stream_list_.begin();
+ it != stream_list_.end(); ++it) {
+ (*it)->OnConfigChanged();
+ }
+
+ // Once all the streams have been notified, the buffering state must be
+ // updated (no notification is received from the streams).
+ OnBufferingStateChanged(false, false);
+}
+
+scoped_refptr<BufferingState> BufferingController::AddStream() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Add a new stream to the list of streams being monitored.
+ scoped_refptr<BufferingState> buffering_state(new BufferingState(
+ config_,
+ base::Bind(&BufferingController::OnBufferingStateChanged, weak_this_,
+ false, false),
+ base::Bind(&BufferingController::UpdateHighLevelThreshold, weak_this_)));
+ stream_list_.push_back(buffering_state);
+
+ // Update the state and force a notification to the streams.
+ // TODO(damienv): Should this be a PostTask ?
+ OnBufferingStateChanged(true, false);
+
+ return buffering_state;
+}
+
+void BufferingController::SetMediaTime(base::TimeDelta time) {
+ for (StreamList::iterator it = stream_list_.begin();
+ it != stream_list_.end(); ++it) {
+ (*it)->SetMediaTime(time);
+ }
+}
+
+base::TimeDelta BufferingController::GetMaxRenderingTime() const {
+ base::TimeDelta max_rendering_time(::media::kNoTimestamp());
+ for (StreamList::const_iterator it = stream_list_.begin();
+ it != stream_list_.end(); ++it) {
+ base::TimeDelta max_stream_rendering_time =
+ (*it)->GetMaxRenderingTime();
+ if (max_stream_rendering_time == ::media::kNoTimestamp())
+ return ::media::kNoTimestamp();
+ if (max_rendering_time == ::media::kNoTimestamp() ||
+ max_stream_rendering_time < max_rendering_time) {
+ max_rendering_time = max_stream_rendering_time;
+ }
+ }
+ return max_rendering_time;
+}
+
+void BufferingController::Reset() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ is_buffering_ = false;
+ initial_buffering_ = true;
+ stream_list_.clear();
+}
+
+void BufferingController::OnBufferingStateChanged(
+ bool force_notification, bool buffering_timeout) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Log the state of each stream.
+ DumpState();
+
+ bool is_low_buffering = IsLowBufferLevel();
+ bool is_high_buffering = !is_low_buffering;
+ if (!buffering_timeout) {
+ // Hysteresis:
+ // - to leave buffering, not only should we leave the low buffer level state
+ // but we should go to the high buffer level state (medium is not enough).
+ is_high_buffering = IsHighBufferLevel();
+ }
+
+ bool is_buffering_prv = is_buffering_;
+ if (is_buffering_) {
+ if (is_high_buffering)
+ is_buffering_ = false;
+ } else {
+ if (is_low_buffering)
+ is_buffering_ = true;
+ }
+
+ // Start buffering.
+ if (is_buffering_ && !is_buffering_prv) {
+ begin_buffering_time_ = base::Time::Now();
+ }
+
+ // End buffering.
+ if (is_buffering_prv && !is_buffering_) {
+ // TODO(damienv): |buffering_user_time| could be a UMA histogram.
+ base::Time current_time = base::Time::Now();
+ base::TimeDelta buffering_user_time = current_time - begin_buffering_time_;
+ CMALOG(kLogControl)
+ << "Buffering took: "
+ << buffering_user_time.InMilliseconds() << "ms";
+ chromecast::metrics::CastMetricsHelper::BufferingType buffering_type =
+ initial_buffering_ ?
+ chromecast::metrics::CastMetricsHelper::kInitialBuffering :
+ chromecast::metrics::CastMetricsHelper::kBufferingAfterUnderrun;
+ chromecast::metrics::CastMetricsHelper::GetInstance()->LogTimeToBufferAv(
+ buffering_type, buffering_user_time);
+
+ // Only the first buffering report is considered "initial buffering".
+ initial_buffering_ = false;
+ }
+
+ if (is_buffering_prv != is_buffering_ || force_notification)
+ buffering_notification_cb_.Run(is_buffering_);
+}
+
+bool BufferingController::IsHighBufferLevel() {
+ if (stream_list_.empty())
+ return true;
+
+ bool is_high_buffering = true;
+ for (StreamList::iterator it = stream_list_.begin();
+ it != stream_list_.end(); ++it) {
+ BufferingState::State stream_state = (*it)->GetState();
+ is_high_buffering = is_high_buffering &&
+ ((stream_state == BufferingState::kHighLevel) ||
+ (stream_state == BufferingState::kEosReached));
+ }
+ return is_high_buffering;
+}
+
+bool BufferingController::IsLowBufferLevel() {
+ if (stream_list_.empty())
+ return false;
+
+ for (StreamList::iterator it = stream_list_.begin();
+ it != stream_list_.end(); ++it) {
+ BufferingState::State stream_state = (*it)->GetState();
+ if (stream_state == BufferingState::kLowLevel)
+ return true;
+ }
+
+ return false;
+}
+
+void BufferingController::DumpState() const {
+ CMALOG(kLogControl) << __FUNCTION__;
+ for (StreamList::const_iterator it = stream_list_.begin();
+ it != stream_list_.end(); ++it) {
+ CMALOG(kLogControl) << (*it)->ToString();
+ }
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/buffering_controller.h b/chromium/chromecast/media/cma/base/buffering_controller.h
new file mode 100644
index 00000000000..bfc2c5cb9aa
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/buffering_controller.h
@@ -0,0 +1,109 @@
+// 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_CONTROLLER_H
+#define CHROMECAST_MEDIA_CMA_BASE_BUFFERING_CONTROLLER_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 "base/time/time.h"
+
+namespace chromecast {
+namespace media {
+class BufferingConfig;
+class BufferingState;
+
+class BufferingController {
+ public:
+ typedef base::Callback<void(bool)> BufferingNotificationCB;
+
+ // Creates a buffering controller where the conditions to trigger rebuffering
+ // are given by |config|. The whole point of the buffering controller is to
+ // derive a single buffering state from the buffering state of various
+ // streams.
+ // |buffering_notification_cb| is a callback invoked to inform about possible
+ // changes of the buffering state.
+ BufferingController(
+ const scoped_refptr<BufferingConfig>& config,
+ const BufferingNotificationCB& buffering_notification_cb);
+ ~BufferingController();
+
+ // Creates a buffering state for one stream. This state is added to the list
+ // of streams monitored by the buffering controller.
+ scoped_refptr<BufferingState> AddStream();
+
+ // Sets the playback time.
+ void SetMediaTime(base::TimeDelta time);
+
+ // Returns the maximum media time available for rendering.
+ // Return kNoTimestamp() if unknown.
+ base::TimeDelta GetMaxRenderingTime() const;
+
+ // Returns whether there is an active buffering phase.
+ bool IsBuffering() const { return is_buffering_; }
+
+ // Resets the buffering controller. This includes removing all the streams
+ // that were previously added.
+ void Reset();
+
+ private:
+ // Invoked each time the buffering state of one of the streams has changed.
+ // If |force_notification| is set, |buffering_notification_cb_| is invoked
+ // regardless whether the buffering state has changed or not.
+ // If |buffering_timeout| is set, then the condition to leave the buffering
+ // state is relaxed (we don't want to wait more).
+ void OnBufferingStateChanged(bool force_notification,
+ bool buffering_timeout);
+
+ // Updates the high buffer level threshold to |high_level_threshold|
+ // if needed.
+ // This condition is triggered when one of the stream reached its maximum
+ // capacity. In that case, to avoid possible race condition (the buffering
+ // controller waits for more data to come but the buffer is to small to
+ // accomodate additional data), the thresholds in |config_| are adjusted
+ // accordingly.
+ void UpdateHighLevelThreshold(base::TimeDelta high_level_threshold);
+
+ // Determines the overall buffer level based on the buffer level of each
+ // stream.
+ bool IsHighBufferLevel();
+ bool IsLowBufferLevel();
+
+ // Logs the state of the buffering controller.
+ void DumpState() const;
+
+ base::ThreadChecker thread_checker_;
+
+ // Settings used to determine when to start/stop buffering.
+ scoped_refptr<BufferingConfig> config_;
+
+ // Callback invoked each time there is a change of the buffering state.
+ BufferingNotificationCB buffering_notification_cb_;
+
+ // State of the buffering controller.
+ bool is_buffering_;
+
+ // Start time of a re-buffering phase.
+ base::Time begin_buffering_time_;
+ bool initial_buffering_;
+
+ // Buffering level for each individual stream.
+ typedef std::list<scoped_refptr<BufferingState> > StreamList;
+ StreamList stream_list_;
+
+ base::WeakPtr<BufferingController> weak_this_;
+ base::WeakPtrFactory<BufferingController> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferingController);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_BUFFERING_CONTROLLER_H
diff --git a/chromium/chromecast/media/cma/base/buffering_controller_unittest.cc b/chromium/chromecast/media/cma/base/buffering_controller_unittest.cc
new file mode 100644
index 00000000000..75eaed9ce1a
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/buffering_controller_unittest.cc
@@ -0,0 +1,133 @@
+// 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 "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "chromecast/media/cma/base/buffering_controller.h"
+#include "chromecast/media/cma/base/buffering_state.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+class MockBufferingControllerClient {
+ public:
+ MockBufferingControllerClient();
+ ~MockBufferingControllerClient();
+
+ MOCK_METHOD1(OnBufferingNotification, void(bool is_buffering));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockBufferingControllerClient);
+};
+
+MockBufferingControllerClient::MockBufferingControllerClient() {
+}
+
+MockBufferingControllerClient::~MockBufferingControllerClient() {
+}
+
+} // namespace
+
+class BufferingControllerTest : public testing::Test {
+ public:
+ BufferingControllerTest();
+ virtual ~BufferingControllerTest();
+
+ protected:
+ scoped_ptr<BufferingController> buffering_controller_;
+
+ MockBufferingControllerClient client_;
+
+ // Buffer level under the low level threshold.
+ base::TimeDelta d1_;
+
+ // Buffer level between the low and the high level.
+ base::TimeDelta d2_;
+
+ // Buffer level above the high level.
+ base::TimeDelta d3_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BufferingControllerTest);
+};
+
+BufferingControllerTest::BufferingControllerTest() {
+ base::TimeDelta low_level_threshold(
+ base::TimeDelta::FromMilliseconds(2000));
+ base::TimeDelta high_level_threshold(
+ base::TimeDelta::FromMilliseconds(6000));
+
+ d1_ = low_level_threshold - base::TimeDelta::FromMilliseconds(50);
+ d2_ = (low_level_threshold + high_level_threshold) / 2;
+ d3_ = high_level_threshold + base::TimeDelta::FromMilliseconds(50);
+
+ scoped_refptr<BufferingConfig> buffering_config(
+ new BufferingConfig(low_level_threshold, high_level_threshold));
+ buffering_controller_.reset(new BufferingController(
+ buffering_config,
+ base::Bind(&MockBufferingControllerClient::OnBufferingNotification,
+ base::Unretained(&client_))));
+}
+
+BufferingControllerTest::~BufferingControllerTest() {
+}
+
+TEST_F(BufferingControllerTest, OneStream_Typical) {
+ EXPECT_CALL(client_, OnBufferingNotification(true)).Times(1);
+ scoped_refptr<BufferingState> buffering_state =
+ buffering_controller_->AddStream();
+ buffering_state->SetMediaTime(base::TimeDelta());
+
+ // Simulate pre-buffering.
+ buffering_state->SetBufferedTime(d2_);
+ EXPECT_EQ(buffering_state->GetState(), BufferingState::kMediumLevel);
+
+ EXPECT_CALL(client_, OnBufferingNotification(false)).Times(1);
+ buffering_state->SetBufferedTime(d3_);
+ EXPECT_EQ(buffering_state->GetState(), BufferingState::kHighLevel);
+
+ // Simulate some fluctuations of the buffering level.
+ buffering_state->SetBufferedTime(d2_);
+ EXPECT_EQ(buffering_state->GetState(), BufferingState::kMediumLevel);
+
+ // Simulate an underrun.
+ EXPECT_CALL(client_, OnBufferingNotification(true)).Times(1);
+ buffering_state->SetBufferedTime(d1_);
+ EXPECT_EQ(buffering_state->GetState(), BufferingState::kLowLevel);
+
+ EXPECT_CALL(client_, OnBufferingNotification(false)).Times(1);
+ buffering_state->SetBufferedTime(d3_);
+ EXPECT_EQ(buffering_state->GetState(), BufferingState::kHighLevel);
+
+ // Simulate the end of stream.
+ buffering_state->NotifyEos();
+ EXPECT_EQ(buffering_state->GetState(), BufferingState::kEosReached);
+
+ buffering_state->SetBufferedTime(d2_);
+ EXPECT_EQ(buffering_state->GetState(), BufferingState::kEosReached);
+
+ buffering_state->SetBufferedTime(d1_);
+ EXPECT_EQ(buffering_state->GetState(), BufferingState::kEosReached);
+}
+
+TEST_F(BufferingControllerTest, OneStream_LeaveBufferingOnEos) {
+ EXPECT_CALL(client_, OnBufferingNotification(true)).Times(1);
+ scoped_refptr<BufferingState> buffering_state =
+ buffering_controller_->AddStream();
+ buffering_state->SetMediaTime(base::TimeDelta());
+
+ EXPECT_CALL(client_, OnBufferingNotification(false)).Times(1);
+ buffering_state->NotifyEos();
+ EXPECT_EQ(buffering_state->GetState(), BufferingState::kEosReached);
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/buffering_frame_provider.cc b/chromium/chromecast/media/cma/base/buffering_frame_provider.cc
new file mode 100644
index 00000000000..b8d56985da4
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/buffering_frame_provider.cc
@@ -0,0 +1,140 @@
+// 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_frame_provider.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "chromecast/media/cma/base/buffering_state.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/buffers.h"
+
+namespace chromecast {
+namespace media {
+
+BufferingFrameProvider::BufferWithConfig::BufferWithConfig(
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config)
+ : buffer_(buffer),
+ audio_config_(audio_config),
+ video_config_(video_config) {
+}
+
+BufferingFrameProvider::BufferWithConfig::~BufferWithConfig() {
+}
+
+BufferingFrameProvider::BufferingFrameProvider(
+ scoped_ptr<CodedFrameProvider> coded_frame_provider,
+ size_t max_buffer_size,
+ size_t max_frame_size,
+ const FrameBufferedCB& frame_buffered_cb)
+ : coded_frame_provider_(coded_frame_provider.Pass()),
+ is_pending_request_(false),
+ is_eos_(false),
+ total_buffer_size_(0),
+ max_buffer_size_(max_buffer_size),
+ max_frame_size_(max_frame_size),
+ frame_buffered_cb_(frame_buffered_cb),
+ weak_factory_(this) {
+ DCHECK_LE(max_frame_size, max_buffer_size);
+ weak_this_ = weak_factory_.GetWeakPtr();
+ thread_checker_.DetachFromThread();
+}
+
+BufferingFrameProvider::~BufferingFrameProvider() {
+ // Required since some weak pointers might be released in the destructor.
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void BufferingFrameProvider::Read(const ReadCB& read_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ DCHECK(!read_cb.is_null());
+ read_cb_ = read_cb;
+
+ CompleteReadIfNeeded();
+
+ RequestBufferIfNeeded();
+}
+
+void BufferingFrameProvider::Flush(const base::Closure& flush_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Invalidate all the buffers that belong to this media timeline.
+ // This is needed since, even though |coded_frame_provider_| is flushed later
+ // in this function, there might be a pending task holding onto a buffer.
+ weak_factory_.InvalidateWeakPtrs();
+
+ // Create a new valid weak pointer that is used for the next media timeline.
+ weak_this_ = weak_factory_.GetWeakPtr();
+
+ is_pending_request_ = false;
+ is_eos_ = false;
+ buffer_list_.clear();
+ total_buffer_size_ = 0;
+ read_cb_.Reset();
+ coded_frame_provider_->Flush(flush_cb);
+}
+
+void BufferingFrameProvider::OnNewBuffer(
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config) {
+ is_pending_request_ = false;
+ buffer_list_.push_back(
+ BufferWithConfig(buffer, audio_config, video_config));
+
+ if (buffer->end_of_stream()) {
+ is_eos_ = true;
+ } else {
+ total_buffer_size_ += buffer->data_size();
+ }
+
+ if (!frame_buffered_cb_.is_null()) {
+ // If the next upcoming frame is possibly filling the whole buffer,
+ // then the buffer is considered as having reached its max capacity.
+ bool max_capacity_flag =
+ (total_buffer_size_ + max_frame_size_ >= max_buffer_size_);
+ frame_buffered_cb_.Run(buffer, max_capacity_flag);
+ }
+
+ RequestBufferIfNeeded();
+
+ CompleteReadIfNeeded();
+}
+
+void BufferingFrameProvider::RequestBufferIfNeeded() {
+ if (is_pending_request_)
+ return;
+
+ if (is_eos_ || total_buffer_size_ >= max_buffer_size_)
+ return;
+
+ is_pending_request_ = true;
+ coded_frame_provider_->Read(BindToCurrentLoop(
+ base::Bind(&BufferingFrameProvider::OnNewBuffer, weak_this_)));
+}
+
+void BufferingFrameProvider::CompleteReadIfNeeded() {
+ if (read_cb_.is_null())
+ return;
+
+ if (buffer_list_.empty())
+ return;
+
+ BufferWithConfig buffer_with_config(buffer_list_.front());
+ buffer_list_.pop_front();
+ if (!buffer_with_config.buffer()->end_of_stream())
+ total_buffer_size_ -= buffer_with_config.buffer()->data_size();
+
+ base::ResetAndReturn(&read_cb_).Run(
+ buffer_with_config.buffer(),
+ buffer_with_config.audio_config(),
+ buffer_with_config.video_config());
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/buffering_frame_provider.h b/chromium/chromecast/media/cma/base/buffering_frame_provider.h
new file mode 100644
index 00000000000..0397676bca0
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/buffering_frame_provider.h
@@ -0,0 +1,117 @@
+// 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_FRAME_PROVIDER_H_
+#define CHROMECAST_MEDIA_CMA_BASE_BUFFERING_FRAME_PROVIDER_H_
+
+#include <list>
+
+#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/base/coded_frame_provider.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/video_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+class DecoderBufferBase;
+
+// BufferingFrameProvider -
+// Fetch some data from another CodedFrameProvider up to a certain size limit.
+class BufferingFrameProvider : public CodedFrameProvider {
+ public:
+ typedef base::Callback<void(const scoped_refptr<DecoderBufferBase>&, bool)>
+ FrameBufferedCB;
+
+ // Creates a frame provider that buffers coded frames up to the
+ // |max_buffer_size| limit (given as a number of bytes).
+ // |max_frame_size| corresponds to an upper bound of the expected frame size.
+ // Each time a frame is buffered, |frame_buffered_cb| is invoked with the
+ // last frame buffered. The second parameter of the callback indicates
+ // whether the maximum capacity has been reached, i.e. whether the next frame
+ // size might overflow the buffer: |total_buffer_size_| + next_frame_size
+ // might be greater than |max_buffer_size|.
+ // Note: takes ownership of |coded_frame_provider|.
+ BufferingFrameProvider(
+ scoped_ptr<CodedFrameProvider> coded_frame_provider,
+ size_t max_buffer_size,
+ size_t max_frame_size,
+ const FrameBufferedCB& frame_buffered_cb);
+ virtual ~BufferingFrameProvider();
+
+ // CodedFrameProvider implementation.
+ virtual void Read(const ReadCB& read_cb) override;
+ virtual void Flush(const base::Closure& flush_cb) override;
+
+ private:
+ class BufferWithConfig {
+ public:
+ BufferWithConfig(
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config);
+ ~BufferWithConfig();
+
+ const scoped_refptr<DecoderBufferBase>& buffer() const { return buffer_; }
+ const ::media::AudioDecoderConfig& audio_config() const {
+ return audio_config_;
+ }
+ const ::media::VideoDecoderConfig& video_config() const {
+ return video_config_;
+ }
+
+ private:
+ scoped_refptr<DecoderBufferBase> buffer_;
+ ::media::AudioDecoderConfig audio_config_;
+ ::media::VideoDecoderConfig video_config_;
+ };
+
+ void OnNewBuffer(const scoped_refptr<DecoderBufferBase>& buffer,
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config);
+ void RequestBufferIfNeeded();
+ void CompleteReadIfNeeded();
+
+ base::ThreadChecker thread_checker_;
+
+ // Frame provider the buffering frame provider fetches data from.
+ scoped_ptr<CodedFrameProvider> coded_frame_provider_;
+
+ // Indicates whether there is a pending read request on
+ // |coded_frame_provider_|.
+ bool is_pending_request_;
+
+ // Indicates whether the end of stream has been reached.
+ bool is_eos_;
+
+ std::list<BufferWithConfig> buffer_list_;
+
+ // Size in bytes of audio/video buffers in |buffer_list_|.
+ size_t total_buffer_size_;
+
+ // Max amount of data to buffer.
+ // i.e. this is the maximum size of buffers in |buffer_list_|.
+ const size_t max_buffer_size_;
+
+ // Maximum expected frame size.
+ const size_t max_frame_size_;
+
+ // Callback invoked each time there is a new frame buffered.
+ FrameBufferedCB frame_buffered_cb_;
+
+ // Pending read callback.
+ ReadCB read_cb_;
+
+ base::WeakPtr<BufferingFrameProvider> weak_this_;
+ base::WeakPtrFactory<BufferingFrameProvider> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferingFrameProvider);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_BUFFERING_FRAME_PROVIDER_H_
diff --git a/chromium/chromecast/media/cma/base/buffering_frame_provider_unittest.cc b/chromium/chromecast/media/cma/base/buffering_frame_provider_unittest.cc
new file mode 100644
index 00000000000..c24862f4932
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/buffering_frame_provider_unittest.cc
@@ -0,0 +1,187 @@
+// 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 <list>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "chromecast/media/cma/base/buffering_frame_provider.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "chromecast/media/cma/test/frame_generator_for_test.h"
+#include "chromecast/media/cma/test/mock_frame_consumer.h"
+#include "chromecast/media/cma/test/mock_frame_provider.h"
+#include "media/base/audio_decoder_config.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 BufferingFrameProviderTest : public testing::Test {
+ public:
+ BufferingFrameProviderTest();
+ virtual ~BufferingFrameProviderTest();
+
+ // Setup the test.
+ void Configure(
+ size_t frame_count,
+ const std::vector<bool>& provider_delayed_pattern,
+ const std::vector<bool>& consumer_delayed_pattern);
+
+ // Start the test.
+ void Start();
+
+ protected:
+ scoped_ptr<BufferingFrameProvider> buffering_frame_provider_;
+ scoped_ptr<MockFrameConsumer> frame_consumer_;
+
+ private:
+ void OnTestTimeout();
+ void OnTestCompleted();
+
+ DISALLOW_COPY_AND_ASSIGN(BufferingFrameProviderTest);
+};
+
+BufferingFrameProviderTest::BufferingFrameProviderTest() {
+}
+
+BufferingFrameProviderTest::~BufferingFrameProviderTest() {
+}
+
+void BufferingFrameProviderTest::Configure(
+ size_t frame_count,
+ const std::vector<bool>& provider_delayed_pattern,
+ const std::vector<bool>& consumer_delayed_pattern) {
+ DCHECK_GE(frame_count, 1u);
+
+ // Frame generation on the producer and consumer side.
+ std::vector<FrameGeneratorForTest::FrameSpec> frame_specs(frame_count);
+ 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 = ((k % 3) == 0);
+ }
+ frame_specs[frame_specs.size() - 1].is_eos = true;
+
+ scoped_ptr<FrameGeneratorForTest> frame_generator_provider(
+ new FrameGeneratorForTest(frame_specs));
+ scoped_ptr<FrameGeneratorForTest> frame_generator_consumer(
+ new FrameGeneratorForTest(frame_specs));
+
+ scoped_ptr<MockFrameProvider> frame_provider(new MockFrameProvider());
+ frame_provider->Configure(provider_delayed_pattern,
+ frame_generator_provider.Pass());
+
+ size_t max_frame_size = 10 * 1024;
+ size_t buffer_size = 10 * max_frame_size;
+ buffering_frame_provider_.reset(
+ new BufferingFrameProvider(
+ scoped_ptr<CodedFrameProvider>(frame_provider.release()),
+ buffer_size,
+ max_frame_size,
+ BufferingFrameProvider::FrameBufferedCB()));
+
+ frame_consumer_.reset(
+ new MockFrameConsumer(buffering_frame_provider_.get()));
+ frame_consumer_->Configure(
+ consumer_delayed_pattern,
+ false,
+ frame_generator_consumer.Pass());
+}
+
+void BufferingFrameProviderTest::Start() {
+ frame_consumer_->Start(
+ base::Bind(&BufferingFrameProviderTest::OnTestCompleted,
+ base::Unretained(this)));
+}
+
+void BufferingFrameProviderTest::OnTestTimeout() {
+ ADD_FAILURE() << "Test timed out";
+ if (base::MessageLoop::current())
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+void BufferingFrameProviderTest::OnTestCompleted() {
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+TEST_F(BufferingFrameProviderTest, FastProviderSlowConsumer) {
+ bool provider_delayed_pattern[] = { false };
+ bool consumer_delayed_pattern[] = { true };
+
+ const size_t frame_count = 100u;
+ Configure(
+ frame_count,
+ std::vector<bool>(
+ provider_delayed_pattern,
+ provider_delayed_pattern + arraysize(provider_delayed_pattern)),
+ std::vector<bool>(
+ consumer_delayed_pattern,
+ consumer_delayed_pattern + arraysize(consumer_delayed_pattern)));
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&BufferingFrameProviderTest::Start, base::Unretained(this)));
+ message_loop->Run();
+};
+
+TEST_F(BufferingFrameProviderTest, SlowProviderFastConsumer) {
+ bool provider_delayed_pattern[] = { true };
+ bool consumer_delayed_pattern[] = { false };
+
+ const size_t frame_count = 100u;
+ Configure(
+ frame_count,
+ std::vector<bool>(
+ provider_delayed_pattern,
+ provider_delayed_pattern + arraysize(provider_delayed_pattern)),
+ std::vector<bool>(
+ consumer_delayed_pattern,
+ consumer_delayed_pattern + arraysize(consumer_delayed_pattern)));
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&BufferingFrameProviderTest::Start, base::Unretained(this)));
+ message_loop->Run();
+};
+
+TEST_F(BufferingFrameProviderTest, SlowFastProducerConsumer) {
+ // Lengths are prime between each other so we can test a lot of combinations.
+ bool provider_delayed_pattern[] = {
+ true, true, true, true, true,
+ false, false, false, false
+ };
+ bool consumer_delayed_pattern[] = {
+ true, true, true, true, true, true, true,
+ false, false, false, false, false, false, false
+ };
+
+ const size_t frame_count = 100u;
+ Configure(
+ frame_count,
+ std::vector<bool>(
+ provider_delayed_pattern,
+ provider_delayed_pattern + arraysize(provider_delayed_pattern)),
+ std::vector<bool>(
+ consumer_delayed_pattern,
+ consumer_delayed_pattern + arraysize(consumer_delayed_pattern)));
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&BufferingFrameProviderTest::Start, base::Unretained(this)));
+ message_loop->Run();
+};
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/buffering_state.cc b/chromium/chromecast/media/cma/base/buffering_state.cc
new file mode 100644
index 00000000000..e1fc49fe188
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/buffering_state.cc
@@ -0,0 +1,129 @@
+// 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_state.h"
+
+#include <sstream>
+
+#include "base/logging.h"
+#include "media/base/buffers.h"
+
+namespace chromecast {
+namespace media {
+
+BufferingConfig::BufferingConfig(
+ base::TimeDelta low_level_threshold,
+ base::TimeDelta high_level_threshold)
+ : low_level_threshold_(low_level_threshold),
+ high_level_threshold_(high_level_threshold) {
+}
+
+BufferingConfig::~BufferingConfig() {
+}
+
+
+BufferingState::BufferingState(
+ const scoped_refptr<BufferingConfig>& config,
+ const base::Closure& state_changed_cb,
+ const HighLevelBufferCB& high_level_buffer_cb)
+ : config_(config),
+ state_changed_cb_(state_changed_cb),
+ high_level_buffer_cb_(high_level_buffer_cb),
+ state_(kLowLevel),
+ media_time_(::media::kNoTimestamp()),
+ max_rendering_time_(::media::kNoTimestamp()),
+ buffered_time_(::media::kNoTimestamp()) {
+}
+
+BufferingState::~BufferingState() {
+}
+
+void BufferingState::OnConfigChanged() {
+ state_ = GetBufferLevelState();
+}
+
+void BufferingState::SetMediaTime(base::TimeDelta media_time) {
+ media_time_ = media_time;
+ switch (state_) {
+ case kLowLevel:
+ case kMediumLevel:
+ case kHighLevel:
+ UpdateState(GetBufferLevelState());
+ break;
+ case kEosReached:
+ break;
+ }
+}
+
+void BufferingState::SetMaxRenderingTime(base::TimeDelta max_rendering_time) {
+ max_rendering_time_ = max_rendering_time;
+}
+
+base::TimeDelta BufferingState::GetMaxRenderingTime() const {
+ return max_rendering_time_;
+}
+
+void BufferingState::SetBufferedTime(base::TimeDelta buffered_time) {
+ buffered_time_ = buffered_time;
+ switch (state_) {
+ case kLowLevel:
+ case kMediumLevel:
+ case kHighLevel:
+ UpdateState(GetBufferLevelState());
+ break;
+ case kEosReached:
+ break;
+ }
+}
+
+void BufferingState::NotifyEos() {
+ UpdateState(kEosReached);
+}
+
+void BufferingState::NotifyMaxCapacity(base::TimeDelta buffered_time) {
+ if (media_time_ == ::media::kNoTimestamp() ||
+ buffered_time == ::media::kNoTimestamp()) {
+ LOG(WARNING) << "Max capacity with no timestamp";
+ return;
+ }
+ base::TimeDelta buffer_duration = buffered_time - media_time_;
+ if (buffer_duration < config_->high_level())
+ high_level_buffer_cb_.Run(buffer_duration);
+}
+
+std::string BufferingState::ToString() const {
+ std::ostringstream s;
+ s << "state=" << state_
+ << " media_time_ms=" << media_time_.InMilliseconds()
+ << " buffered_time_ms=" << buffered_time_.InMilliseconds()
+ << " low_level_ms=" << config_->low_level().InMilliseconds()
+ << " high_level_ms=" << config_->high_level().InMilliseconds();
+ return s.str();
+}
+
+BufferingState::State BufferingState::GetBufferLevelState() const {
+ if (media_time_ == ::media::kNoTimestamp() ||
+ buffered_time_ == ::media::kNoTimestamp()) {
+ return kLowLevel;
+ }
+
+ base::TimeDelta buffer_duration = buffered_time_ - media_time_;
+ if (buffer_duration < config_->low_level())
+ return kLowLevel;
+ if (buffer_duration >= config_->high_level())
+ return kHighLevel;
+ return kMediumLevel;
+}
+
+void BufferingState::UpdateState(State new_state) {
+ if (new_state == state_)
+ return;
+
+ state_ = new_state;
+ if (!state_changed_cb_.is_null())
+ state_changed_cb_.Run();
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/buffering_state.h b/chromium/chromecast/media/cma/base/buffering_state.h
new file mode 100644
index 00000000000..ced8206d8a1
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/buffering_state.h
@@ -0,0 +1,138 @@
+// 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_STATE_H_
+#define CHROMECAST_MEDIA_CMA_BASE_BUFFERING_STATE_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+
+namespace chromecast {
+namespace media {
+
+class BufferingConfig : public base::RefCountedThreadSafe<BufferingConfig> {
+ public:
+ BufferingConfig(base::TimeDelta low_level_threshold,
+ base::TimeDelta high_level_threshold);
+
+ base::TimeDelta low_level() const { return low_level_threshold_; }
+ base::TimeDelta high_level() const { return high_level_threshold_; }
+
+ void set_low_level(base::TimeDelta low_level) {
+ low_level_threshold_ = low_level;
+ }
+ void set_high_level(base::TimeDelta high_level) {
+ high_level_threshold_ = high_level;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<BufferingConfig>;
+ virtual ~BufferingConfig();
+
+ base::TimeDelta low_level_threshold_;
+ base::TimeDelta high_level_threshold_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferingConfig);
+};
+
+class BufferingState
+ : public base::RefCountedThreadSafe<BufferingState> {
+ public:
+ typedef base::Callback<void(base::TimeDelta)> HighLevelBufferCB;
+
+ enum State {
+ kLowLevel,
+ kMediumLevel,
+ kHighLevel,
+ kEosReached,
+ };
+
+ // Creates a new buffering state. The initial state is |kLowLevel|.
+ // |state_changed_cb| is used to notify about possible state changes.
+ // |high_level_buffer_cb| is used to adjust the high buffer threshold
+ // when the underlying buffer is not large enough to accomodate
+ // the current high buffer level.
+ BufferingState(const scoped_refptr<BufferingConfig>& config,
+ const base::Closure& state_changed_cb,
+ const HighLevelBufferCB& high_level_buffer_cb);
+
+ // Returns the buffering state.
+ State GetState() const { return state_; }
+
+ // Invoked when the buffering configuration has changed.
+ // Based on the new configuration, the buffering state might change.
+ // However, |state_changed_cb_| is not triggered in that case.
+ void OnConfigChanged();
+
+ // Sets the current rendering time for this stream.
+ void SetMediaTime(base::TimeDelta media_time);
+
+ // Sets/gets the maximum rendering media time for this stream.
+ // The maximum rendering time is always lower than the buffered time.
+ void SetMaxRenderingTime(base::TimeDelta max_rendering_time);
+ base::TimeDelta GetMaxRenderingTime() const;
+
+ // Sets the buffered time.
+ void SetBufferedTime(base::TimeDelta buffered_time);
+
+ // Notifies the buffering state that all the frames for this stream have been
+ // buffered, i.e. the end of stream has been reached.
+ void NotifyEos();
+
+ // Notifies the buffering state the underlying buffer has reached
+ // its maximum capacity.
+ // The maximum frame timestamp in the buffer is given by |buffered_time|.
+ // Note: this timestamp can be different from the one provided through
+ // SetBufferedTime since SetBufferedTime takes the timestamp of a playable
+ // frame which is not necessarily the case here (e.g. missing key id).
+ void NotifyMaxCapacity(base::TimeDelta buffered_time);
+
+ // Buffering state as a human readable string, for debugging.
+ std::string ToString() const;
+
+ private:
+ friend class base::RefCountedThreadSafe<BufferingState>;
+ virtual ~BufferingState();
+
+ // Returns the state solely based on the buffered time.
+ State GetBufferLevelState() const;
+
+ // Updates the state to |new_state|.
+ void UpdateState(State new_state);
+
+ scoped_refptr<BufferingConfig> const config_;
+
+ // Callback invoked each time there is a change of state.
+ base::Closure state_changed_cb_;
+
+ // Callback invoked to adjust the high buffer level.
+ HighLevelBufferCB high_level_buffer_cb_;
+
+ // State.
+ State state_;
+
+ // Playback media time.
+ // Equal to kNoTimestamp() when not known.
+ base::TimeDelta media_time_;
+
+ // Maximum rendering media time.
+ // This corresponds to the timestamp of the last frame sent to the hardware
+ // decoder/renderer.
+ base::TimeDelta max_rendering_time_;
+
+ // Buffered media time.
+ // Equal to kNoTimestamp() when not known.
+ base::TimeDelta buffered_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferingState);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_BUFFERING_STATE_H_
diff --git a/chromium/chromecast/media/cma/base/cma_logging.h b/chromium/chromecast/media/cma/base/cma_logging.h
new file mode 100644
index 00000000000..1743cea310e
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/cma_logging.h
@@ -0,0 +1,23 @@
+// 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_CMA_LOGGING_H_
+#define CHROMECAST_MEDIA_CMA_BASE_CMA_LOGGING_H_
+
+#include "base/logging.h"
+
+namespace chromecast {
+namespace media {
+
+#define CMALOG(loglevel) VLOG(loglevel)
+
+enum {
+ kLogControl = 2,
+ kLogFrame = 3
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_CMA_LOGGING_H_
diff --git a/chromium/chromecast/media/cma/base/coded_frame_provider.cc b/chromium/chromecast/media/cma/base/coded_frame_provider.cc
new file mode 100644
index 00000000000..56e461cfd8d
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/coded_frame_provider.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/base/coded_frame_provider.h"
+
+namespace chromecast {
+namespace media {
+
+CodedFrameProvider::CodedFrameProvider() {
+}
+
+CodedFrameProvider::~CodedFrameProvider() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/coded_frame_provider.h b/chromium/chromecast/media/cma/base/coded_frame_provider.h
new file mode 100644
index 00000000000..b13231d8e59
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/coded_frame_provider.h
@@ -0,0 +1,51 @@
+// 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_CODED_FRAME_PROVIDER_H_
+#define CHROMECAST_MEDIA_CMA_BASE_CODED_FRAME_PROVIDER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+
+namespace media {
+class AudioDecoderConfig;
+class VideoDecoderConfig;
+}
+
+namespace chromecast {
+namespace media {
+class DecoderBufferBase;
+
+class CodedFrameProvider {
+ public:
+ typedef base::Callback<void(const scoped_refptr<DecoderBufferBase>&,
+ const ::media::AudioDecoderConfig&,
+ const ::media::VideoDecoderConfig&)> ReadCB;
+
+ CodedFrameProvider();
+ virtual ~CodedFrameProvider();
+
+ // Request a coded frame which is provided asynchronously through callback
+ // |read_cb|.
+ // If the frame is associated with a new video/audio configuration,
+ // these configurations are returned as part of the |read_cb| callback.
+ // Invoking the |read_cb| callback with invalid audio/video configurations
+ // means the configurations have not changed.
+ virtual void Read(const ReadCB& read_cb) = 0;
+
+ // Flush the coded frames held by the frame provider.
+ // Invoke callback |flush_cb| when completed.
+ // Note: any pending read is cancelled, meaning that any pending |read_cb|
+ // callback will not be invoked.
+ virtual void Flush(const base::Closure& flush_cb) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CodedFrameProvider);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_CODED_FRAME_PROVIDER_H_
diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc
new file mode 100644
index 00000000000..236505b9950
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc
@@ -0,0 +1,45 @@
+// 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/decoder_buffer_adapter.h"
+
+#include "media/base/decoder_buffer.h"
+
+namespace chromecast {
+namespace media {
+
+DecoderBufferAdapter::DecoderBufferAdapter(
+ const scoped_refptr< ::media::DecoderBuffer>& buffer)
+ : buffer_(buffer) {
+}
+
+DecoderBufferAdapter::~DecoderBufferAdapter() {
+}
+
+base::TimeDelta DecoderBufferAdapter::timestamp() const {
+ return buffer_->timestamp();
+}
+
+const uint8* DecoderBufferAdapter::data() const {
+ return buffer_->data();
+}
+
+uint8* DecoderBufferAdapter::writable_data() const {
+ return buffer_->writable_data();
+}
+
+int DecoderBufferAdapter::data_size() const {
+ return buffer_->data_size();
+}
+
+const ::media::DecryptConfig* DecoderBufferAdapter::decrypt_config() const {
+ return buffer_->decrypt_config();
+}
+
+bool DecoderBufferAdapter::end_of_stream() const {
+ return buffer_->end_of_stream();
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h
new file mode 100644
index 00000000000..1967fbebd67
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h
@@ -0,0 +1,45 @@
+// 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_DECODER_BUFFER_ADAPTER_H_
+#define CHROMECAST_MEDIA_CMA_BASE_DECODER_BUFFER_ADAPTER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+
+namespace media {
+class DecoderBuffer;
+}
+
+namespace chromecast {
+namespace media {
+
+// DecoderBufferAdapter wraps a ::media::DecoderBuffer
+// into a DecoderBufferBase.
+class DecoderBufferAdapter : public DecoderBufferBase {
+ public:
+ explicit DecoderBufferAdapter(
+ const scoped_refptr< ::media::DecoderBuffer>& buffer);
+
+ // DecoderBufferBase implementation.
+ virtual base::TimeDelta timestamp() const override;
+ virtual const uint8* data() const override;
+ virtual uint8* writable_data() const override;
+ virtual int data_size() const override;
+ virtual const ::media::DecryptConfig* decrypt_config() const override;
+ virtual bool end_of_stream() const override;
+
+ private:
+ virtual ~DecoderBufferAdapter();
+
+ scoped_refptr< ::media::DecoderBuffer> const buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(DecoderBufferAdapter);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_DECODER_BUFFER_ADAPTER_H_
diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_base.cc b/chromium/chromecast/media/cma/base/decoder_buffer_base.cc
new file mode 100644
index 00000000000..40c0a7d0066
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/decoder_buffer_base.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/base/decoder_buffer_base.h"
+
+namespace chromecast {
+namespace media {
+
+DecoderBufferBase::DecoderBufferBase() {
+}
+
+DecoderBufferBase::~DecoderBufferBase() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_base.h b/chromium/chromecast/media/cma/base/decoder_buffer_base.h
new file mode 100644
index 00000000000..9143104f14a
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/decoder_buffer_base.h
@@ -0,0 +1,57 @@
+// 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_DECODER_BUFFER_BASE_H_
+#define CHROMECAST_MEDIA_CMA_BASE_DECODER_BUFFER_BASE_H_
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+
+namespace media {
+class DecryptConfig;
+}
+
+namespace chromecast {
+namespace media {
+
+// DecoderBufferBase exposes only the properties of an audio/video buffer.
+// The way a DecoderBufferBase is created and organized in memory
+// is left as a detail of the implementation of derived classes.
+class DecoderBufferBase
+ : public base::RefCountedThreadSafe<DecoderBufferBase> {
+ public:
+ DecoderBufferBase();
+
+ // Returns the PTS of the frame.
+ virtual base::TimeDelta timestamp() const = 0;
+
+ // Gets the frame data.
+ virtual const uint8* data() const = 0;
+ virtual uint8* writable_data() const = 0;
+
+ // Returns the size of the frame in bytes.
+ virtual int data_size() const = 0;
+
+ // Returns the decrypt configuration.
+ // Returns NULL if the buffer has no decrypt info.
+ virtual const ::media::DecryptConfig* decrypt_config() const = 0;
+
+ // Indicate if this is a special frame that indicates the end of the stream.
+ // If true, functions to access the frame content cannot be called.
+ virtual bool end_of_stream() const = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<DecoderBufferBase>;
+ virtual ~DecoderBufferBase();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DecoderBufferBase);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_DECODER_BUFFER_BASE_H_
diff --git a/chromium/chromecast/media/cma/base/media_task_runner.cc b/chromium/chromecast/media/cma/base/media_task_runner.cc
new file mode 100644
index 00000000000..5b5423bbdf1
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/media_task_runner.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/base/media_task_runner.h"
+
+namespace chromecast {
+namespace media {
+
+MediaTaskRunner::MediaTaskRunner() {
+}
+
+MediaTaskRunner::~MediaTaskRunner() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/base/media_task_runner.h b/chromium/chromecast/media/cma/base/media_task_runner.h
new file mode 100644
index 00000000000..c4a3012c941
--- /dev/null
+++ b/chromium/chromecast/media/cma/base/media_task_runner.h
@@ -0,0 +1,44 @@
+// 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_MEDIA_TASK_RUNNER_H_
+#define CHROMECAST_MEDIA_CMA_BASE_MEDIA_TASK_RUNNER_H_
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+
+namespace chromecast {
+namespace media {
+
+class MediaTaskRunner
+ : public base::RefCountedThreadSafe<MediaTaskRunner> {
+ public:
+ MediaTaskRunner();
+
+ // Post a task with the given media |timestamp|. If |timestamp| is equal to
+ // |kNoTimestamp()|, the task is scheduled right away.
+ // How the media timestamp is used to schedule the task is an implementation
+ // detail of derived classes.
+ // Returns true if the task may be run at some point in the future, and false
+ // if the task definitely will not be run.
+ virtual bool PostMediaTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta timestamp) = 0;
+
+ protected:
+ virtual ~MediaTaskRunner();
+ friend class base::RefCountedThreadSafe<MediaTaskRunner>;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MediaTaskRunner);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_BASE_MEDIA_TASK_RUNNER_H_
diff --git a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc
new file mode 100644
index 00000000000..db0d7b70ab2
--- /dev/null
+++ b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc
@@ -0,0 +1,207 @@
+// 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/filters/demuxer_stream_adapter.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/single_thread_task_runner.h"
+#include "chromecast/media/cma/base/balanced_media_task_runner_factory.h"
+#include "chromecast/media/cma/base/cma_logging.h"
+#include "chromecast/media/cma/base/decoder_buffer_adapter.h"
+#include "chromecast/media/cma/base/media_task_runner.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/buffers.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/demuxer_stream.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+class DummyMediaTaskRunner : public MediaTaskRunner {
+ public:
+ DummyMediaTaskRunner(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+
+ // MediaTaskRunner implementation.
+ virtual bool PostMediaTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta timestamp) override;
+
+ private:
+ virtual ~DummyMediaTaskRunner();
+
+ scoped_refptr<base::SingleThreadTaskRunner> const task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(DummyMediaTaskRunner);
+};
+
+DummyMediaTaskRunner::DummyMediaTaskRunner(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+ : task_runner_(task_runner) {
+}
+
+DummyMediaTaskRunner::~DummyMediaTaskRunner() {
+}
+
+bool DummyMediaTaskRunner::PostMediaTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta timestamp) {
+ return task_runner_->PostTask(from_here, task);
+}
+
+} // namespace
+
+DemuxerStreamAdapter::DemuxerStreamAdapter(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ const scoped_refptr<BalancedMediaTaskRunnerFactory>&
+ media_task_runner_factory,
+ ::media::DemuxerStream* demuxer_stream)
+ : task_runner_(task_runner),
+ media_task_runner_factory_(media_task_runner_factory),
+ media_task_runner_(new DummyMediaTaskRunner(task_runner)),
+ demuxer_stream_(demuxer_stream),
+ is_pending_read_(false),
+ is_pending_demuxer_read_(false),
+ weak_factory_(this) {
+ weak_this_ = weak_factory_.GetWeakPtr();
+ ResetMediaTaskRunner();
+ thread_checker_.DetachFromThread();
+}
+
+DemuxerStreamAdapter::~DemuxerStreamAdapter() {
+ // Needed since we use weak pointers:
+ // weak pointers must be invalidated on the same thread.
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void DemuxerStreamAdapter::Read(const ReadCB& read_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ DCHECK(flush_cb_.is_null());
+
+ // Support only one read at a time.
+ DCHECK(!is_pending_read_);
+ is_pending_read_ = true;
+ ReadInternal(read_cb);
+}
+
+void DemuxerStreamAdapter::ReadInternal(const ReadCB& read_cb) {
+ bool may_run_in_future = media_task_runner_->PostMediaTask(
+ FROM_HERE,
+ base::Bind(&DemuxerStreamAdapter::RequestBuffer, weak_this_, read_cb),
+ max_pts_);
+ DCHECK(may_run_in_future);
+}
+
+void DemuxerStreamAdapter::Flush(const base::Closure& flush_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ CMALOG(kLogControl) << __FUNCTION__;
+
+ // Flush cancels any pending read.
+ is_pending_read_ = false;
+
+ // Reset the decoder configurations.
+ audio_config_ = ::media::AudioDecoderConfig();
+ video_config_ = ::media::VideoDecoderConfig();
+
+ // Create a new media task runner for the upcoming media timeline.
+ ResetMediaTaskRunner();
+
+ DCHECK(flush_cb_.is_null());
+ if (is_pending_demuxer_read_) {
+ // If there is a pending demuxer read, the implicit contract
+ // is that the pending read must be completed before invoking the
+ // flush callback.
+ flush_cb_ = flush_cb;
+ return;
+ }
+
+ // At this point, there is no more pending demuxer read,
+ // so all the previous tasks associated with the current timeline
+ // can be cancelled.
+ weak_factory_.InvalidateWeakPtrs();
+ weak_this_ = weak_factory_.GetWeakPtr();
+
+ CMALOG(kLogControl) << "Flush done";
+ flush_cb.Run();
+}
+
+void DemuxerStreamAdapter::ResetMediaTaskRunner() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ max_pts_ = ::media::kNoTimestamp();
+ if (media_task_runner_factory_.get()) {
+ media_task_runner_ =
+ media_task_runner_factory_->CreateMediaTaskRunner(task_runner_);
+ }
+}
+
+void DemuxerStreamAdapter::RequestBuffer(const ReadCB& read_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ is_pending_demuxer_read_ = true;
+ demuxer_stream_->Read(::media::BindToCurrentLoop(
+ base::Bind(&DemuxerStreamAdapter::OnNewBuffer, weak_this_, read_cb)));
+}
+
+void DemuxerStreamAdapter::OnNewBuffer(
+ const ReadCB& read_cb,
+ ::media::DemuxerStream::Status status,
+ const scoped_refptr< ::media::DecoderBuffer>& input) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ is_pending_demuxer_read_ = false;
+
+ // Just discard the buffer in the flush stage.
+ if (!flush_cb_.is_null()) {
+ CMALOG(kLogControl) << "Flush done";
+ base::ResetAndReturn(&flush_cb_).Run();
+ return;
+ }
+
+ if (status == ::media::DemuxerStream::kAborted) {
+ DCHECK(input.get() == NULL);
+ return;
+ }
+
+ if (status == ::media::DemuxerStream::kConfigChanged) {
+ DCHECK(input.get() == NULL);
+ if (demuxer_stream_->type() == ::media::DemuxerStream::VIDEO)
+ video_config_ = demuxer_stream_->video_decoder_config();
+ if (demuxer_stream_->type() == ::media::DemuxerStream::AUDIO)
+ audio_config_ = demuxer_stream_->audio_decoder_config();
+
+ // Got a new config, but we still need to get a frame.
+ ReadInternal(read_cb);
+ return;
+ }
+
+ DCHECK_EQ(status, ::media::DemuxerStream::kOk);
+
+ // Updates the timestamp used for task scheduling.
+ if (!input->end_of_stream() &&
+ input->timestamp() != ::media::kNoTimestamp() &&
+ (max_pts_ == ::media::kNoTimestamp() || input->timestamp() > max_pts_)) {
+ max_pts_ = input->timestamp();
+ }
+
+ // Provides the buffer as well as possibly valid audio and video configs.
+ is_pending_read_ = false;
+ scoped_refptr<DecoderBufferBase> buffer(new DecoderBufferAdapter(input));
+ read_cb.Run(buffer, audio_config_, video_config_);
+
+ // Back to the default audio/video config:
+ // an invalid audio/video config means there is no config update.
+ if (audio_config_.IsValidConfig())
+ audio_config_ = ::media::AudioDecoderConfig();
+ if (video_config_.IsValidConfig())
+ video_config_ = ::media::VideoDecoderConfig();
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.h b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.h
new file mode 100644
index 00000000000..7d8c8b33702
--- /dev/null
+++ b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.h
@@ -0,0 +1,93 @@
+// 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_FILTERS_DEMUXER_STREAM_ADAPTER_H_
+#define CHROMECAST_MEDIA_CMA_FILTERS_DEMUXER_STREAM_ADAPTER_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "chromecast/media/cma/base/coded_frame_provider.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/video_decoder_config.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+class DemuxerStream;
+}
+
+namespace chromecast {
+namespace media {
+class BalancedMediaTaskRunnerFactory;
+class MediaTaskRunner;
+
+// DemuxerStreamAdapter wraps a DemuxerStream into a CodedFrameProvider.
+class DemuxerStreamAdapter : public CodedFrameProvider {
+ public:
+ DemuxerStreamAdapter(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ const scoped_refptr<BalancedMediaTaskRunnerFactory>&
+ media_task_runner_factory,
+ ::media::DemuxerStream* demuxer_stream);
+ virtual ~DemuxerStreamAdapter();
+
+ // CodedFrameProvider implementation.
+ virtual void Read(const ReadCB& read_cb) override;
+ virtual void Flush(const base::Closure& flush_cb) override;
+
+ private:
+ void ResetMediaTaskRunner();
+
+ void ReadInternal(const ReadCB& read_cb);
+ void RequestBuffer(const ReadCB& read_cb);
+
+ // Callback invoked from the demuxer stream to signal a buffer is ready.
+ void OnNewBuffer(const ReadCB& read_cb,
+ ::media::DemuxerStream::Status status,
+ const scoped_refptr< ::media::DecoderBuffer>& input);
+
+ base::ThreadChecker thread_checker_;
+
+ // Task runner DemuxerStreamAdapter is running on.
+ scoped_refptr<base::SingleThreadTaskRunner> const task_runner_;
+
+ // Media task runner to pace requests to the DemuxerStream.
+ scoped_refptr<BalancedMediaTaskRunnerFactory> const
+ media_task_runner_factory_;
+ scoped_refptr<MediaTaskRunner> media_task_runner_;
+ base::TimeDelta max_pts_;
+
+ // Frames are provided by |demuxer_stream_|.
+ ::media::DemuxerStream* const demuxer_stream_;
+
+ // Indicate if there is a pending read.
+ bool is_pending_read_;
+
+ // Indicate if |demuxer_stream_| has a pending read.
+ bool is_pending_demuxer_read_;
+
+ // In case of a pending flush operation, this is the callback
+ // that is invoked when flush is completed.
+ base::Closure flush_cb_;
+
+ // Audio/video configuration that applies to the next frame.
+ ::media::AudioDecoderConfig audio_config_;
+ ::media::VideoDecoderConfig video_config_;
+
+ base::WeakPtr<DemuxerStreamAdapter> weak_this_;
+ base::WeakPtrFactory<DemuxerStreamAdapter> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DemuxerStreamAdapter);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_FILTERS_DEMUXER_STREAM_ADAPTER_H_
diff --git a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc
new file mode 100644
index 00000000000..bee63eb4d45
--- /dev/null
+++ b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc
@@ -0,0 +1,345 @@
+// 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 <list>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "chromecast/media/cma/base/balanced_media_task_runner_factory.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "chromecast/media/cma/filters/demuxer_stream_adapter.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/video_decoder_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+class DummyDemuxerStream : public ::media::DemuxerStream {
+ public:
+ // Creates a demuxer stream which provides frames either with a delay
+ // or instantly. The scheduling pattern is the following:
+ // - provides |delayed_frame_count| frames with a delay,
+ // - then provides the following |cycle_count| - |delayed_frame_count|
+ // instantly,
+ // - then provides |delayed_frame_count| frames with a delay,
+ // - ... and so on.
+ // Special cases:
+ // - all frames are delayed: |delayed_frame_count| = |cycle_count|
+ // - all frames are provided instantly: |delayed_frame_count| = 0
+ // |config_idx| is a list of frame index before which there is
+ // a change of decoder configuration.
+ DummyDemuxerStream(int cycle_count,
+ int delayed_frame_count,
+ const std::list<int>& config_idx);
+ 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;
+
+ bool has_pending_read() const {
+ return has_pending_read_;
+ }
+
+ private:
+ void DoRead(const ReadCB& read_cb);
+
+ // Demuxer configuration.
+ const int cycle_count_;
+ const int delayed_frame_count_;
+ std::list<int> config_idx_;
+
+ // Number of frames sent so far.
+ int frame_count_;
+
+ bool has_pending_read_;
+
+ DISALLOW_COPY_AND_ASSIGN(DummyDemuxerStream);
+};
+
+DummyDemuxerStream::DummyDemuxerStream(
+ int cycle_count,
+ int delayed_frame_count,
+ const std::list<int>& config_idx)
+ : cycle_count_(cycle_count),
+ delayed_frame_count_(delayed_frame_count),
+ config_idx_(config_idx),
+ frame_count_(0),
+ has_pending_read_(false) {
+ DCHECK_LE(delayed_frame_count, cycle_count);
+}
+
+DummyDemuxerStream::~DummyDemuxerStream() {
+}
+
+void DummyDemuxerStream::Read(const ReadCB& read_cb) {
+ has_pending_read_ = true;
+ if (!config_idx_.empty() && config_idx_.front() == frame_count_) {
+ config_idx_.pop_front();
+ has_pending_read_ = false;
+ read_cb.Run(kConfigChanged,
+ scoped_refptr< ::media::DecoderBuffer>());
+ return;
+ }
+
+ if ((frame_count_ % cycle_count_) < delayed_frame_count_) {
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&DummyDemuxerStream::DoRead, base::Unretained(this),
+ read_cb),
+ base::TimeDelta::FromMilliseconds(20));
+ return;
+ }
+ DoRead(read_cb);
+}
+
+::media::AudioDecoderConfig DummyDemuxerStream::audio_decoder_config() {
+ LOG(FATAL) << "DummyDemuxerStream is a video DemuxerStream";
+ return ::media::AudioDecoderConfig();
+}
+
+::media::VideoDecoderConfig DummyDemuxerStream::video_decoder_config() {
+ gfx::Size coded_size(640, 480);
+ gfx::Rect visible_rect(640, 480);
+ gfx::Size natural_size(640, 480);
+ return ::media::VideoDecoderConfig(
+ ::media::kCodecH264,
+ ::media::VIDEO_CODEC_PROFILE_UNKNOWN,
+ ::media::VideoFrame::YV12,
+ coded_size,
+ visible_rect,
+ natural_size,
+ NULL, 0,
+ false);
+}
+
+::media::DemuxerStream::Type DummyDemuxerStream::type() {
+ return VIDEO;
+}
+
+bool DummyDemuxerStream::SupportsConfigChanges() {
+ return true;
+}
+
+::media::VideoRotation DummyDemuxerStream::video_rotation() {
+ return ::media::VIDEO_ROTATION_0;
+}
+
+void DummyDemuxerStream::DoRead(const ReadCB& read_cb) {
+ has_pending_read_ = false;
+ scoped_refptr< ::media::DecoderBuffer> buffer(
+ new ::media::DecoderBuffer(16));
+ buffer->set_timestamp(frame_count_ * base::TimeDelta::FromMilliseconds(40));
+ frame_count_++;
+ read_cb.Run(kOk, buffer);
+}
+
+} // namespace
+
+class DemuxerStreamAdapterTest : public testing::Test {
+ public:
+ DemuxerStreamAdapterTest();
+ virtual ~DemuxerStreamAdapterTest();
+
+ void Initialize(::media::DemuxerStream* demuxer_stream);
+ void Start();
+
+ protected:
+ void OnTestTimeout();
+ void OnNewFrame(const scoped_refptr<DecoderBufferBase>& buffer,
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config);
+ void OnFlushCompleted();
+
+ // Total number of frames to request.
+ int total_frames_;
+
+ // Number of demuxer read before issuing an early flush.
+ int early_flush_idx_;
+ bool use_post_task_for_flush_;
+
+ // Number of expected read frames.
+ int total_expected_frames_;
+
+ // Number of frames actually read so far.
+ int frame_received_count_;
+
+ // List of expected frame indices with decoder config changes.
+ std::list<int> config_idx_;
+
+ scoped_ptr<DummyDemuxerStream> demuxer_stream_;
+
+ scoped_ptr<CodedFrameProvider> coded_frame_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(DemuxerStreamAdapterTest);
+};
+
+DemuxerStreamAdapterTest::DemuxerStreamAdapterTest()
+ : use_post_task_for_flush_(false) {
+}
+
+DemuxerStreamAdapterTest::~DemuxerStreamAdapterTest() {
+}
+
+void DemuxerStreamAdapterTest::Initialize(
+ ::media::DemuxerStream* demuxer_stream) {
+ coded_frame_provider_.reset(
+ new DemuxerStreamAdapter(
+ base::MessageLoopProxy::current(),
+ scoped_refptr<BalancedMediaTaskRunnerFactory>(),
+ demuxer_stream));
+}
+
+void DemuxerStreamAdapterTest::Start() {
+ frame_received_count_ = 0;
+
+ // TODO(damienv): currently, test assertions which fail do not trigger the
+ // exit of the unit test, the message loop is still running. Find a different
+ // way to exit the unit test.
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&DemuxerStreamAdapterTest::OnTestTimeout,
+ base::Unretained(this)),
+ base::TimeDelta::FromSeconds(5));
+
+ coded_frame_provider_->Read(
+ base::Bind(&DemuxerStreamAdapterTest::OnNewFrame,
+ base::Unretained(this)));
+}
+
+void DemuxerStreamAdapterTest::OnTestTimeout() {
+ ADD_FAILURE() << "Test timed out";
+ if (base::MessageLoop::current())
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+void DemuxerStreamAdapterTest::OnNewFrame(
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config) {
+ if (video_config.IsValidConfig()) {
+ ASSERT_GT(config_idx_.size(), 0);
+ ASSERT_EQ(frame_received_count_, config_idx_.front());
+ config_idx_.pop_front();
+ }
+
+ ASSERT_TRUE(buffer.get() != NULL);
+ ASSERT_EQ(buffer->timestamp(),
+ frame_received_count_ * base::TimeDelta::FromMilliseconds(40));
+ frame_received_count_++;
+
+ if (frame_received_count_ >= total_frames_) {
+ coded_frame_provider_->Flush(
+ base::Bind(&DemuxerStreamAdapterTest::OnFlushCompleted,
+ base::Unretained(this)));
+ return;
+ }
+
+ coded_frame_provider_->Read(
+ base::Bind(&DemuxerStreamAdapterTest::OnNewFrame,
+ base::Unretained(this)));
+
+ ASSERT_LE(frame_received_count_, early_flush_idx_);
+ if (frame_received_count_ == early_flush_idx_) {
+ base::Closure flush_cb =
+ base::Bind(&DemuxerStreamAdapterTest::OnFlushCompleted,
+ base::Unretained(this));
+ if (use_post_task_for_flush_) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&CodedFrameProvider::Flush,
+ base::Unretained(coded_frame_provider_.get()),
+ flush_cb));
+ } else {
+ coded_frame_provider_->Flush(flush_cb);
+ }
+ return;
+ }
+}
+
+void DemuxerStreamAdapterTest::OnFlushCompleted() {
+ ASSERT_EQ(frame_received_count_, total_expected_frames_);
+ ASSERT_FALSE(demuxer_stream_->has_pending_read());
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+TEST_F(DemuxerStreamAdapterTest, NoDelay) {
+ total_frames_ = 10;
+ early_flush_idx_ = total_frames_; // No early flush.
+ total_expected_frames_ = 10;
+ config_idx_.push_back(0);
+ config_idx_.push_back(5);
+
+ int cycle_count = 1;
+ int delayed_frame_count = 0;
+ demuxer_stream_.reset(
+ new DummyDemuxerStream(
+ cycle_count, delayed_frame_count, config_idx_));
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ Initialize(demuxer_stream_.get());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&DemuxerStreamAdapterTest::Start, base::Unretained(this)));
+ message_loop->Run();
+}
+
+TEST_F(DemuxerStreamAdapterTest, AllDelayed) {
+ total_frames_ = 10;
+ early_flush_idx_ = total_frames_; // No early flush.
+ total_expected_frames_ = 10;
+ config_idx_.push_back(0);
+ config_idx_.push_back(5);
+
+ int cycle_count = 1;
+ int delayed_frame_count = 1;
+ demuxer_stream_.reset(
+ new DummyDemuxerStream(
+ cycle_count, delayed_frame_count, config_idx_));
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ Initialize(demuxer_stream_.get());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&DemuxerStreamAdapterTest::Start, base::Unretained(this)));
+ message_loop->Run();
+}
+
+TEST_F(DemuxerStreamAdapterTest, AllDelayedEarlyFlush) {
+ total_frames_ = 10;
+ early_flush_idx_ = 5;
+ use_post_task_for_flush_ = true;
+ total_expected_frames_ = 5;
+ config_idx_.push_back(0);
+ config_idx_.push_back(3);
+
+ int cycle_count = 1;
+ int delayed_frame_count = 1;
+ demuxer_stream_.reset(
+ new DummyDemuxerStream(
+ cycle_count, delayed_frame_count, config_idx_));
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ Initialize(demuxer_stream_.get());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&DemuxerStreamAdapterTest::Start, base::Unretained(this)));
+ message_loop->Run();
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc/media_memory_chunk.cc b/chromium/chromecast/media/cma/ipc/media_memory_chunk.cc
new file mode 100644
index 00000000000..0d6896958b9
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc/media_memory_chunk.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/ipc/media_memory_chunk.h"
+
+namespace chromecast {
+namespace media {
+
+MediaMemoryChunk::~MediaMemoryChunk() {
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc/media_memory_chunk.h b/chromium/chromecast/media/cma/ipc/media_memory_chunk.h
new file mode 100644
index 00000000000..1b91c840a9d
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc/media_memory_chunk.h
@@ -0,0 +1,39 @@
+// 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_IPC_MEDIA_MEMORY_CHUNK_H_
+#define CHROMECAST_MEDIA_CMA_IPC_MEDIA_MEMORY_CHUNK_H_
+
+#include "base/basictypes.h"
+
+namespace chromecast {
+namespace media {
+
+// MediaMemoryChunk represents a block of memory without doing any assumption
+// about the type of memory (e.g. shared memory) nor about the underlying
+// memory ownership.
+// The block of memory can be invalidated under the cover (e.g. if the derived
+// class does not own the underlying memory),
+// in that case, MediaMemoryChunk::valid() will return false.
+class MediaMemoryChunk {
+ public:
+ virtual ~MediaMemoryChunk();
+
+ // Returns the start of the block of memory.
+ virtual void* data() const = 0;
+
+ // Returns the size of the block of memory.
+ virtual size_t size() const = 0;
+
+ // Returns whether the underlying block of memory is valid.
+ // Since MediaMemoryChunk does not specify a memory ownership model,
+ // the underlying block of memory might be invalidated by a third party.
+ // In that case, valid() will return false.
+ virtual bool valid() const = 0;
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_IPC_MEDIA_MEMORY_CHUNK_H_
diff --git a/chromium/chromecast/media/cma/ipc/media_message.cc b/chromium/chromecast/media/cma/ipc/media_message.cc
new file mode 100644
index 00000000000..8dfcd7cb885
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc/media_message.cc
@@ -0,0 +1,198 @@
+// 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/ipc/media_message.h"
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "chromecast/media/cma/ipc/media_memory_chunk.h"
+
+namespace chromecast {
+namespace media {
+
+// static
+scoped_ptr<MediaMessage> MediaMessage::CreateDummyMessage(
+ uint32 type) {
+ return scoped_ptr<MediaMessage>(
+ new MediaMessage(type, std::numeric_limits<size_t>::max()));
+}
+
+// static
+scoped_ptr<MediaMessage> MediaMessage::CreateMessage(
+ uint32 type,
+ const MemoryAllocatorCB& memory_allocator,
+ size_t msg_content_capacity) {
+ size_t msg_size = minimum_msg_size() + msg_content_capacity;
+
+ // Make the message size a multiple of the alignment
+ // so that if we have proper alignment for array of messages.
+ size_t end_alignment = msg_size % ALIGNOF(SerializedMsg);
+ if (end_alignment != 0)
+ msg_size += ALIGNOF(SerializedMsg) - end_alignment;
+
+ scoped_ptr<MediaMemoryChunk> memory(memory_allocator.Run(msg_size));
+ if (!memory)
+ return scoped_ptr<MediaMessage>();
+
+ return scoped_ptr<MediaMessage>(new MediaMessage(type, memory.Pass()));
+}
+
+// static
+scoped_ptr<MediaMessage> MediaMessage::CreateMessage(
+ uint32 type,
+ scoped_ptr<MediaMemoryChunk> memory) {
+ return scoped_ptr<MediaMessage>(new MediaMessage(type, memory.Pass()));
+}
+
+// static
+scoped_ptr<MediaMessage> MediaMessage::MapMessage(
+ scoped_ptr<MediaMemoryChunk> memory) {
+ return scoped_ptr<MediaMessage>(new MediaMessage(memory.Pass()));
+}
+
+MediaMessage::MediaMessage(uint32 type, size_t msg_size)
+ : is_dummy_msg_(true),
+ cached_header_(&cached_msg_.header),
+ msg_(&cached_msg_),
+ msg_read_only_(&cached_msg_),
+ rd_offset_(0) {
+ cached_header_->size = msg_size;
+ cached_header_->type = type;
+ cached_header_->content_size = 0;
+}
+
+MediaMessage::MediaMessage(uint32 type, scoped_ptr<MediaMemoryChunk> memory)
+ : is_dummy_msg_(false),
+ cached_header_(&cached_msg_.header),
+ msg_(static_cast<SerializedMsg*>(memory->data())),
+ msg_read_only_(msg_),
+ mem_(memory.Pass()),
+ rd_offset_(0) {
+ CHECK(mem_->valid());
+ CHECK_GE(mem_->size(), minimum_msg_size());
+
+ // Check memory alignment:
+ // needed to cast properly |msg_dst| to a SerializedMsg.
+ CHECK_EQ(
+ reinterpret_cast<uintptr_t>(mem_->data()) % ALIGNOF(SerializedMsg), 0u);
+
+ // Make sure that |mem_->data()| + |mem_->size()| is also aligned correctly.
+ // This is needed if we append a second serialized message next to this one.
+ // The second serialized message must be aligned correctly.
+ // It is similar to what a compiler is doing for arrays of structures.
+ CHECK_EQ(mem_->size() % ALIGNOF(SerializedMsg), 0u);
+
+ cached_header_->size = mem_->size();
+ cached_header_->type = type;
+ cached_header_->content_size = 0;
+ msg_->header = *cached_header_;
+}
+
+MediaMessage::MediaMessage(scoped_ptr<MediaMemoryChunk> memory)
+ : is_dummy_msg_(false),
+ cached_header_(&cached_msg_.header),
+ msg_(NULL),
+ msg_read_only_(static_cast<SerializedMsg*>(memory->data())),
+ mem_(memory.Pass()),
+ rd_offset_(0) {
+ CHECK(mem_->valid());
+
+ // Check memory alignment.
+ CHECK_EQ(
+ reinterpret_cast<uintptr_t>(mem_->data()) % ALIGNOF(SerializedMsg), 0u);
+
+ // Cache the message header which cannot be modified while reading.
+ CHECK_GE(mem_->size(), minimum_msg_size());
+ *cached_header_ = msg_read_only_->header;
+ CHECK_GE(cached_header_->size, minimum_msg_size());
+
+ // Make sure if we have 2 consecutive serialized messages in memory,
+ // the 2nd message is also aligned correctly.
+ CHECK_EQ(cached_header_->size % ALIGNOF(SerializedMsg), 0u);
+
+ size_t max_content_size = cached_header_->size - minimum_msg_size();
+ CHECK_LE(cached_header_->content_size, max_content_size);
+}
+
+MediaMessage::~MediaMessage() {
+}
+
+bool MediaMessage::IsSerializedMsgAvailable() const {
+ return !is_dummy_msg_ && mem_->valid();
+}
+
+bool MediaMessage::WriteBuffer(const void* src, size_t size) {
+ // No message to write into.
+ if (!msg_)
+ return false;
+
+ // The underlying memory was invalidated.
+ if (!is_dummy_msg_ && !mem_->valid())
+ return false;
+
+ size_t max_content_size = cached_header_->size - minimum_msg_size();
+ if (cached_header_->content_size + size > max_content_size) {
+ cached_header_->content_size = max_content_size;
+ msg_->header.content_size = cached_header_->content_size;
+ return false;
+ }
+
+ // Write the message only for non-dummy messages.
+ if (!is_dummy_msg_) {
+ uint8* wr_ptr = &msg_->content + cached_header_->content_size;
+ memcpy(wr_ptr, src, size);
+ }
+
+ cached_header_->content_size += size;
+ msg_->header.content_size = cached_header_->content_size;
+ return true;
+}
+
+bool MediaMessage::ReadBuffer(void* dst, size_t size) {
+ // No read possible for a dummy message.
+ if (is_dummy_msg_)
+ return false;
+
+ // The underlying memory was invalidated.
+ if (!mem_->valid())
+ return false;
+
+ if (rd_offset_ + size > cached_header_->content_size) {
+ rd_offset_ = cached_header_->content_size;
+ return false;
+ }
+
+ const uint8* rd_ptr = &msg_read_only_->content + rd_offset_;
+ memcpy(dst, rd_ptr, size);
+ rd_offset_ += size;
+ return true;
+}
+
+void* MediaMessage::GetWritableBuffer(size_t size) {
+ // No read possible for a dummy message.
+ if (is_dummy_msg_)
+ return NULL;
+
+ // The underlying memory was invalidated.
+ if (!mem_->valid())
+ return NULL;
+
+ if (rd_offset_ + size > cached_header_->content_size) {
+ rd_offset_ = cached_header_->content_size;
+ return NULL;
+ }
+
+ uint8* rd_ptr = &msg_read_only_->content + rd_offset_;
+ rd_offset_ += size;
+ return rd_ptr;
+}
+
+const void* MediaMessage::GetBuffer(size_t size) {
+ return GetWritableBuffer(size);
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc/media_message.h b/chromium/chromecast/media/cma/ipc/media_message.h
new file mode 100644
index 00000000000..066409be3d8
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc/media_message.h
@@ -0,0 +1,165 @@
+// 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_IPC_MEDIA_MESSAGE_H_
+#define CHROMECAST_MEDIA_CMA_IPC_MEDIA_MESSAGE_H_
+
+#include <stddef.h>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace chromecast {
+namespace media {
+class MediaMemoryChunk;
+
+// MediaMessage -
+// Represents a media message, including:
+// - a message header that gives for example the message size or its type,
+// - the content of the message,
+// - and some possible padding if the content does not occupy the whole
+// reserved space.
+//
+class MediaMessage {
+ public:
+ // Memory allocator: given a number of bytes to allocate,
+ // return the pointer to the allocated block if successful
+ // or NULL if allocation failed.
+ typedef base::Callback<scoped_ptr<MediaMemoryChunk>(size_t)>
+ MemoryAllocatorCB;
+
+ // Creates a message with no associated memory for its content, i.e.
+ // each write on this message is a dummy operation.
+ // This type of message can be useful to calculate first the size of the
+ // message, before allocating the real message.
+ static scoped_ptr<MediaMessage> CreateDummyMessage(uint32 type);
+
+ // Creates a message with a capacity of at least |msg_content_capacity|
+ // bytes. The actual content size can be smaller than its capacity.
+ // The message can be populated with some Write functions.
+ static scoped_ptr<MediaMessage> CreateMessage(
+ uint32 type,
+ const MemoryAllocatorCB& memory_allocator,
+ size_t msg_content_capacity);
+
+ // Creates a message of type |type| whose serialized structure is stored
+ // in |mem|.
+ static scoped_ptr<MediaMessage> CreateMessage(
+ uint32 type,
+ scoped_ptr<MediaMemoryChunk> mem);
+
+ // Creates a message from a memory area which already contains
+ // the serialized structure of the message.
+ // Only Read functions can be invoked on this type of message.
+ static scoped_ptr<MediaMessage> MapMessage(
+ scoped_ptr<MediaMemoryChunk> mem);
+
+ // Return the minimum size of a message.
+ static size_t minimum_msg_size() {
+ return offsetof(SerializedMsg, content);
+ }
+
+ ~MediaMessage();
+
+ // Indicate whether the underlying serialized structure of the message is
+ // available.
+ // Note: the serialized structure might be unavailable in case of a dummy
+ // message or if the underlying memory has been invalidated.
+ bool IsSerializedMsgAvailable() const;
+
+ // Return the message and the total size of the message
+ // incuding the header, the content and the possible padding.
+ const void* msg() const { return msg_read_only_; }
+ size_t size() const { return cached_msg_.header.size; }
+
+ // Return the size of the message without padding.
+ size_t actual_size() const {
+ return minimum_msg_size() + cached_msg_.header.content_size;
+ }
+
+ // Return the size of the content of the message.
+ size_t content_size() const { return cached_msg_.header.content_size; }
+
+ // Return the type of the message.
+ uint32 type() const { return cached_msg_.header.type; }
+
+ // Append a POD to the message.
+ // Return true if the POD has been succesfully written.
+ template<typename T> bool WritePod(T* const& pod);
+ template<typename T> bool WritePod(const T& pod) {
+ return WriteBuffer(&pod, sizeof(T));
+ }
+
+ // Append a raw buffer to the message.
+ bool WriteBuffer(const void* src, size_t size);
+
+ // Read a POD from the message.
+ template<typename T> bool ReadPod(T* pod) {
+ return ReadBuffer(pod, sizeof(T));
+ }
+
+ // Read |size| bytes from the message from the last read position
+ // and write it to |dst|.
+ bool ReadBuffer(void* dst, size_t size);
+
+ // Return a pointer to a buffer of size |size|.
+ // Return NULL if not successful.
+ const void* GetBuffer(size_t size);
+ void* GetWritableBuffer(size_t size);
+
+ private:
+ MediaMessage(uint32 type, size_t msg_size);
+ MediaMessage(uint32 type, scoped_ptr<MediaMemoryChunk> memory);
+ MediaMessage(scoped_ptr<MediaMemoryChunk> memory);
+
+ struct Header {
+ // Total size of the message (including both header & content).
+ uint32 size;
+ // Indicate the message type.
+ uint32 type;
+ // Actual size of the content in the message.
+ uint32 content_size;
+ };
+
+ struct SerializedMsg {
+ // Message header.
+ Header header;
+
+ // Start of the content of the message.
+ // Use uint8_t since no special alignment is needed.
+ uint8 content;
+ };
+
+ // Indicate whether the message is a dummy message, i.e. a message without
+ // a complete underlying serialized structure: only the message header is
+ // available.
+ bool is_dummy_msg_;
+
+ // |cached_msg_| is used for 2 purposes:
+ // - to create a dummy message
+ // - for security purpose: cache the msg header to avoid browser security
+ // issues.
+ SerializedMsg cached_msg_;
+ Header* const cached_header_;
+
+ SerializedMsg* msg_;
+ SerializedMsg* msg_read_only_;
+
+ // Memory allocated to store the underlying serialized structure into memory.
+ // Note: a dummy message has no underlying serialized structure:
+ // |mem_| is a null pointer in that case.
+ scoped_ptr<MediaMemoryChunk> mem_;
+
+ // Read iterator into the message.
+ size_t rd_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaMessage);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif
diff --git a/chromium/chromecast/media/cma/ipc/media_message_fifo.cc b/chromium/chromecast/media/cma/ipc/media_message_fifo.cc
new file mode 100644
index 00000000000..9f8ad001dc8
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc/media_message_fifo.cc
@@ -0,0 +1,403 @@
+// 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/ipc/media_message_fifo.h"
+
+#include "base/atomicops.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "chromecast/media/cma/base/cma_logging.h"
+#include "chromecast/media/cma/ipc/media_memory_chunk.h"
+#include "chromecast/media/cma/ipc/media_message.h"
+#include "chromecast/media/cma/ipc/media_message_type.h"
+
+namespace chromecast {
+namespace media {
+
+class MediaMessageFlag
+ : public base::RefCountedThreadSafe<MediaMessageFlag> {
+ public:
+ // |offset| is the offset in the fifo of the media message.
+ explicit MediaMessageFlag(size_t offset);
+
+ bool IsValid() const;
+
+ void Invalidate();
+
+ size_t offset() const { return offset_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<MediaMessageFlag>;
+ virtual ~MediaMessageFlag();
+
+ const size_t offset_;
+ bool flag_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaMessageFlag);
+};
+
+MediaMessageFlag::MediaMessageFlag(size_t offset)
+ : offset_(offset),
+ flag_(true) {
+}
+
+MediaMessageFlag::~MediaMessageFlag() {
+}
+
+bool MediaMessageFlag::IsValid() const {
+ return flag_;
+}
+
+void MediaMessageFlag::Invalidate() {
+ flag_ = false;
+}
+
+class FifoOwnedMemory : public MediaMemoryChunk {
+ public:
+ FifoOwnedMemory(void* data, size_t size,
+ const scoped_refptr<MediaMessageFlag>& flag,
+ const base::Closure& release_msg_cb);
+ virtual ~FifoOwnedMemory();
+
+ // MediaMemoryChunk implementation.
+ virtual void* data() const override { return data_; }
+ virtual size_t size() const override { return size_; }
+ virtual bool valid() const override { return flag_->IsValid(); }
+
+ private:
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ base::Closure release_msg_cb_;
+
+ void* const data_;
+ const size_t size_;
+ scoped_refptr<MediaMessageFlag> flag_;
+
+ DISALLOW_COPY_AND_ASSIGN(FifoOwnedMemory);
+};
+
+FifoOwnedMemory::FifoOwnedMemory(
+ void* data, size_t size,
+ const scoped_refptr<MediaMessageFlag>& flag,
+ const base::Closure& release_msg_cb)
+ : task_runner_(base::MessageLoopProxy::current()),
+ release_msg_cb_(release_msg_cb),
+ data_(data),
+ size_(size),
+ flag_(flag) {
+}
+
+FifoOwnedMemory::~FifoOwnedMemory() {
+ // Release the flag before notifying that the message has been released.
+ flag_ = scoped_refptr<MediaMessageFlag>();
+ if (!release_msg_cb_.is_null()) {
+ if (task_runner_->BelongsToCurrentThread()) {
+ release_msg_cb_.Run();
+ } else {
+ task_runner_->PostTask(FROM_HERE, release_msg_cb_);
+ }
+ }
+}
+
+MediaMessageFifo::MediaMessageFifo(
+ scoped_ptr<MediaMemoryChunk> mem, bool init)
+ : mem_(mem.Pass()),
+ weak_factory_(this) {
+ CHECK_EQ(reinterpret_cast<uintptr_t>(mem_->data()) % ALIGNOF(Descriptor),
+ 0u);
+ CHECK_GE(mem_->size(), sizeof(Descriptor));
+ Descriptor* desc = static_cast<Descriptor*>(mem_->data());
+ base_ = static_cast<void*>(&desc->first_item);
+
+ // TODO(damienv): remove cast when atomic size_t is defined in Chrome.
+ // Currently, the sign differs.
+ rd_offset_ = reinterpret_cast<AtomicSize*>(&(desc->rd_offset));
+ wr_offset_ = reinterpret_cast<AtomicSize*>(&(desc->wr_offset));
+
+ size_t max_size = mem_->size() -
+ (static_cast<char*>(base_) - static_cast<char*>(mem_->data()));
+ if (init) {
+ size_ = max_size;
+ desc->size = size_;
+ internal_rd_offset_ = 0;
+ internal_wr_offset_ = 0;
+ base::subtle::Release_Store(rd_offset_, 0);
+ base::subtle::Release_Store(wr_offset_, 0);
+ } else {
+ size_ = desc->size;
+ CHECK_LE(size_, max_size);
+ internal_rd_offset_ = current_rd_offset();
+ internal_wr_offset_ = current_wr_offset();
+ }
+ CMALOG(kLogControl)
+ << "MediaMessageFifo:" << " init=" << init << " size=" << size_;
+ CHECK_GT(size_, 0) << size_;
+
+ weak_this_ = weak_factory_.GetWeakPtr();
+ thread_checker_.DetachFromThread();
+}
+
+MediaMessageFifo::~MediaMessageFifo() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void MediaMessageFifo::ObserveReadActivity(
+ const base::Closure& read_event_cb) {
+ read_event_cb_ = read_event_cb;
+}
+
+void MediaMessageFifo::ObserveWriteActivity(
+ const base::Closure& write_event_cb) {
+ write_event_cb_ = write_event_cb;
+}
+
+scoped_ptr<MediaMemoryChunk> MediaMessageFifo::ReserveMemory(
+ size_t size_to_reserve) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Capture first both the read and write offsets.
+ // and exit right away if not enough free space.
+ size_t wr_offset = internal_wr_offset();
+ size_t rd_offset = current_rd_offset();
+ size_t allocated_size = (size_ + wr_offset - rd_offset) % size_;
+ size_t free_size = size_ - 1 - allocated_size;
+ if (free_size < size_to_reserve)
+ return scoped_ptr<MediaMemoryChunk>();
+ CHECK_LE(MediaMessage::minimum_msg_size(), size_to_reserve);
+
+ // Note: in the next 2 conditions, we have:
+ // trailing_byte_count < size_to_reserve
+ // and since at this stage: size_to_reserve <= free_size
+ // we also have trailing_byte_count <= free_size
+ // which means that all the trailing bytes are free space in the fifo.
+ size_t trailing_byte_count = size_ - wr_offset;
+ if (trailing_byte_count < MediaMessage::minimum_msg_size()) {
+ // If there is no space to even write the smallest message,
+ // skip the trailing bytes and come back to the beginning of the fifo.
+ // (no way to insert a padding message).
+ if (free_size < trailing_byte_count)
+ return scoped_ptr<MediaMemoryChunk>();
+ wr_offset = 0;
+ CommitInternalWrite(wr_offset);
+
+ } else if (trailing_byte_count < size_to_reserve) {
+ // At this point, we know we have at least the space to write a message.
+ // However, to avoid splitting a message, a padding message is needed.
+ scoped_ptr<MediaMemoryChunk> mem(
+ ReserveMemoryNoCheck(trailing_byte_count));
+ scoped_ptr<MediaMessage> padding_message(
+ MediaMessage::CreateMessage(PaddingMediaMsg, mem.Pass()));
+ }
+
+ // Recalculate the free size and exit if not enough free space.
+ wr_offset = internal_wr_offset();
+ allocated_size = (size_ + wr_offset - rd_offset) % size_;
+ free_size = size_ - 1 - allocated_size;
+ if (free_size < size_to_reserve)
+ return scoped_ptr<MediaMemoryChunk>();
+
+ return ReserveMemoryNoCheck(size_to_reserve);
+}
+
+scoped_ptr<MediaMessage> MediaMessageFifo::Pop() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Capture the read and write offsets.
+ size_t rd_offset = internal_rd_offset();
+ size_t wr_offset = current_wr_offset();
+ size_t allocated_size = (size_ + wr_offset - rd_offset) % size_;
+
+ if (allocated_size < MediaMessage::minimum_msg_size())
+ return scoped_ptr<MediaMessage>();
+
+ size_t trailing_byte_count = size_ - rd_offset;
+ if (trailing_byte_count < MediaMessage::minimum_msg_size()) {
+ // If there is no space to even have the smallest message,
+ // skip the trailing bytes and come back to the beginning of the fifo.
+ // Note: all the trailing bytes correspond to allocated bytes since:
+ // trailing_byte_count < MediaMessage::minimum_msg_size() <= allocated_size
+ rd_offset = 0;
+ allocated_size -= trailing_byte_count;
+ trailing_byte_count = size_;
+ CommitInternalRead(rd_offset);
+ }
+
+ // The message should not be longer than the allocated size
+ // but since a message is a contiguous area of memory, it should also be
+ // smaller than |trailing_byte_count|.
+ size_t max_msg_size = std::min(allocated_size, trailing_byte_count);
+ if (max_msg_size < MediaMessage::minimum_msg_size())
+ return scoped_ptr<MediaMessage>();
+ void* msg_src = static_cast<uint8*>(base_) + rd_offset;
+
+ // Create a flag to protect the serialized structure of the message
+ // from being overwritten.
+ // The serialized structure starts at offset |rd_offset|.
+ scoped_refptr<MediaMessageFlag> rd_flag(new MediaMessageFlag(rd_offset));
+ rd_flags_.push_back(rd_flag);
+ scoped_ptr<MediaMemoryChunk> mem(
+ new FifoOwnedMemory(
+ msg_src, max_msg_size, rd_flag,
+ base::Bind(&MediaMessageFifo::OnRdMemoryReleased, weak_this_)));
+
+ // Create the message which wraps its the serialized structure.
+ scoped_ptr<MediaMessage> message(MediaMessage::MapMessage(mem.Pass()));
+ CHECK(message);
+
+ // Update the internal read pointer.
+ rd_offset = (rd_offset + message->size()) % size_;
+ CommitInternalRead(rd_offset);
+
+ return message.Pass();
+}
+
+void MediaMessageFifo::Flush() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ size_t wr_offset = current_wr_offset();
+
+ // Invalidate every memory region before flushing.
+ while (!rd_flags_.empty()) {
+ CMALOG(kLogControl) << "Invalidate flag";
+ rd_flags_.front()->Invalidate();
+ rd_flags_.pop_front();
+ }
+
+ // Flush by setting the read pointer to the value of the write pointer.
+ // Update first the internal read pointer then the public one.
+ CommitInternalRead(wr_offset);
+ CommitRead(wr_offset);
+}
+
+scoped_ptr<MediaMemoryChunk> MediaMessageFifo::ReserveMemoryNoCheck(
+ size_t size_to_reserve) {
+ size_t wr_offset = internal_wr_offset();
+
+ // Memory block corresponding to the serialized structure of the message.
+ void* msg_start = static_cast<uint8*>(base_) + wr_offset;
+ scoped_refptr<MediaMessageFlag> wr_flag(new MediaMessageFlag(wr_offset));
+ wr_flags_.push_back(wr_flag);
+ scoped_ptr<MediaMemoryChunk> mem(
+ new FifoOwnedMemory(
+ msg_start, size_to_reserve, wr_flag,
+ base::Bind(&MediaMessageFifo::OnWrMemoryReleased, weak_this_)));
+
+ // Update the internal write pointer.
+ wr_offset = (wr_offset + size_to_reserve) % size_;
+ CommitInternalWrite(wr_offset);
+
+ return mem.Pass();
+}
+
+void MediaMessageFifo::OnWrMemoryReleased() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (wr_flags_.empty()) {
+ // Sanity check: when there is no protected memory area,
+ // the external write offset has no reason to be different from
+ // the internal write offset.
+ DCHECK_EQ(current_wr_offset(), internal_wr_offset());
+ return;
+ }
+
+ // Update the external write offset.
+ while (!wr_flags_.empty() &&
+ (!wr_flags_.front()->IsValid() || wr_flags_.front()->HasOneRef())) {
+ // TODO(damienv): Could add a sanity check to make sure the offset is
+ // between the external write offset and the read offset (not included).
+ wr_flags_.pop_front();
+ }
+
+ // Update the read offset to the first locked memory area
+ // or to the internal read pointer if nothing prevents it.
+ size_t external_wr_offset = internal_wr_offset();
+ if (!wr_flags_.empty())
+ external_wr_offset = wr_flags_.front()->offset();
+ CommitWrite(external_wr_offset);
+}
+
+void MediaMessageFifo::OnRdMemoryReleased() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (rd_flags_.empty()) {
+ // Sanity check: when there is no protected memory area,
+ // the external read offset has no reason to be different from
+ // the internal read offset.
+ DCHECK_EQ(current_rd_offset(), internal_rd_offset());
+ return;
+ }
+
+ // Update the external read offset.
+ while (!rd_flags_.empty() &&
+ (!rd_flags_.front()->IsValid() || rd_flags_.front()->HasOneRef())) {
+ // TODO(damienv): Could add a sanity check to make sure the offset is
+ // between the external read offset and the write offset.
+ rd_flags_.pop_front();
+ }
+
+ // Update the read offset to the first locked memory area
+ // or to the internal read pointer if nothing prevents it.
+ size_t external_rd_offset = internal_rd_offset();
+ if (!rd_flags_.empty())
+ external_rd_offset = rd_flags_.front()->offset();
+ CommitRead(external_rd_offset);
+}
+
+size_t MediaMessageFifo::current_rd_offset() const {
+ DCHECK_EQ(sizeof(size_t), sizeof(AtomicSize));
+ size_t rd_offset = base::subtle::Acquire_Load(rd_offset_);
+ CHECK_LT(rd_offset, size_);
+ return rd_offset;
+}
+
+size_t MediaMessageFifo::current_wr_offset() const {
+ DCHECK_EQ(sizeof(size_t), sizeof(AtomicSize));
+
+ // When the fifo consumer acquires the write offset,
+ // we have to make sure that any possible following reads are actually
+ // returning results at least inline with the memory snapshot taken
+ // when the write offset was sampled.
+ // That's why an Acquire_Load is used here.
+ size_t wr_offset = base::subtle::Acquire_Load(wr_offset_);
+ CHECK_LT(wr_offset, size_);
+ return wr_offset;
+}
+
+void MediaMessageFifo::CommitRead(size_t new_rd_offset) {
+ // Add a memory fence to ensure the message content is completely read
+ // before updating the read offset.
+ base::subtle::Release_Store(rd_offset_, new_rd_offset);
+
+ // Since rd_offset_ is updated by a release_store above, any thread that
+ // does acquire_load is guaranteed to see the new rd_offset_ set above.
+ // So it is safe to send the notification.
+ if (!read_event_cb_.is_null()) {
+ read_event_cb_.Run();
+ }
+}
+
+void MediaMessageFifo::CommitWrite(size_t new_wr_offset) {
+ // Add a memory fence to ensure the message content is written
+ // before updating the write offset.
+ base::subtle::Release_Store(wr_offset_, new_wr_offset);
+
+ // Since wr_offset_ is updated by a release_store above, any thread that
+ // does acquire_load is guaranteed to see the new wr_offset_ set above.
+ // So it is safe to send the notification.
+ if (!write_event_cb_.is_null()) {
+ write_event_cb_.Run();
+ }
+}
+
+void MediaMessageFifo::CommitInternalRead(size_t new_rd_offset) {
+ internal_rd_offset_ = new_rd_offset;
+}
+
+void MediaMessageFifo::CommitInternalWrite(size_t new_wr_offset) {
+ internal_wr_offset_ = new_wr_offset;
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc/media_message_fifo.h b/chromium/chromecast/media/cma/ipc/media_message_fifo.h
new file mode 100644
index 00000000000..dcc60dbd60b
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc/media_message_fifo.h
@@ -0,0 +1,208 @@
+// 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_IPC_MEDIA_MESSAGE_FIFO_H_
+#define CHROMECAST_MEDIA_CMA_IPC_MEDIA_MESSAGE_FIFO_H_
+
+#include <list>
+
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/logging.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"
+
+namespace chromecast {
+namespace media {
+class MediaMemoryChunk;
+class MediaMessage;
+class MediaMessageFlag;
+
+// MediaMessageFifo is a lock free fifo implementation
+// to pass audio/video data from one thread to another or from one process
+// to another one (in that case using shared memory).
+//
+// Assuming the feeder and the consumer have a common block of shared memory
+// (representing the serialized structure of the fifo),
+// the feeder (which must be running on a single thread) instantiates its own
+// instance of MediaMessageFifo, same applies to the consumer.
+//
+// Example: assuming the block of shared memory is given by |mem|, a typical
+// feeder (using MediaMessageFifo instance fifo_feeder) will push messages
+// in the following way:
+// // Create a dummy message to calculate the size of the serialized message.
+// scoped_ptr<MediaMessage> dummy_msg(
+// MediaMessage::CreateDummyMessage(msg_type));
+// // ...
+// // Write all the fields to the dummy message.
+// // ...
+//
+// // Create the real message, once the size of the serialized message
+// // is known.
+// scoped_ptr<MediaMessage> msg(
+// MediaMessage::CreateMessage(
+// msg_type,
+// base::Bind(&MediaMessageFifo::ReserveMemory,
+// base::Unretained(fifo_feeder.get())),
+// dummy_msg->content_size()));
+// if (!msg) {
+// // Not enough space for the message:
+// // retry later (e.g. when receiving a read activity event, meaning
+// // some enough space might have been release).
+// return;
+// }
+// // ...
+// // Write all the fields to the real message
+// // in exactly the same way it was done for the dummy message.
+// // ...
+// // Once message |msg| is going out of scope, then MediaMessageFifo
+// // fifo_feeder is informed that the message is not needed anymore
+// // (i.e. it was fully written), and fifo_feeder can then update
+// // the external write pointer of the fifo so that the consumer
+// // can start consuming this message.
+//
+// A typical consumer (using MediaMessageFifo instance fifo_consumer)
+// will retrive messages in the following way:
+// scoped_ptr<MediaMessage> msg(fifo_consumer->Pop());
+// if (!msg) {
+// // The fifo is empty, i.e. no message left.
+// // Try reading again later (e.g. after receiving a write activity event.
+// return;
+// }
+// // Parse the message using Read functions of MediaMessage:
+// // ...
+// // Once the message is going out of scope, MediaMessageFifo will receive
+// // a notification that the underlying memory can be released
+// // (i.e. the external read pointer can be updated).
+//
+//
+class MediaMessageFifo {
+ public:
+ // Creates a media message fifo using |mem| as the underlying serialized
+ // structure.
+ // If |init| is true, the underlying fifo structure is initialized.
+ MediaMessageFifo(scoped_ptr<MediaMemoryChunk> mem, bool init);
+ ~MediaMessageFifo();
+
+ // When the consumer and the feeder are living in two different processes,
+ // we might want to convey some messages between these two processes to notify
+ // about some fifo activity.
+ void ObserveReadActivity(const base::Closure& read_event_cb);
+ void ObserveWriteActivity(const base::Closure& write_event_cb);
+
+ // Reserves a writeable block of memory at the back of the fifo,
+ // corresponding to the serialized structure of the message.
+ // Returns NULL if the required size cannot be allocated.
+ scoped_ptr<MediaMemoryChunk> ReserveMemory(size_t size);
+
+ // Pop a message from the queue.
+ // Returns a null pointer if there is no message left.
+ scoped_ptr<MediaMessage> Pop();
+
+ // Flush the fifo.
+ void Flush();
+
+ private:
+ struct Descriptor {
+ size_t size;
+ size_t rd_offset;
+ size_t wr_offset;
+
+ // Ensure the first item has the same alignment as an int64.
+ int64 first_item;
+ };
+
+ // Add some accessors to ensure security on the browser process side.
+ size_t current_rd_offset() const;
+ size_t current_wr_offset() const;
+ size_t internal_rd_offset() const {
+ DCHECK_LT(internal_rd_offset_, size_);
+ return internal_rd_offset_;
+ }
+ size_t internal_wr_offset() const {
+ DCHECK_LT(internal_wr_offset_, size_);
+ return internal_wr_offset_;
+ }
+
+ // Reserve a block of free memory without doing any check on the available
+ // space. Invoke this function only when all the checks have been done.
+ scoped_ptr<MediaMemoryChunk> ReserveMemoryNoCheck(size_t size);
+
+ // Invoked each time there is a memory region in the free space of the fifo
+ // that has possibly been written.
+ void OnWrMemoryReleased();
+
+ // Invoked each time there is a memory region in the allocated space
+ // of the fifo that has possibly been released.
+ void OnRdMemoryReleased();
+
+ // Functions to modify the internal/external read/write pointers.
+ void CommitRead(size_t new_rd_offset);
+ void CommitWrite(size_t new_wr_offset);
+ void CommitInternalRead(size_t new_rd_offset);
+ void CommitInternalWrite(size_t new_wr_offset);
+
+ // An instance of MediaMessageFifo must be running on a single thread.
+ // If the fifo feeder and consumer are living on 2 different threads
+ // or 2 different processes, they must create their own instance
+ // of MediaMessageFifo using the same underlying block of (shared) memory
+ // in the constructor.
+ base::ThreadChecker thread_checker_;
+
+ // Callbacks invoked to notify either of some read or write activity on the
+ // fifo. This is especially useful when the feeder and consumer are living in
+ // two different processes.
+ base::Closure read_event_cb_;
+ base::Closure write_event_cb_;
+
+ // The serialized structure of the fifo.
+ scoped_ptr<MediaMemoryChunk> mem_;
+
+ // The size in bytes of the fifo is cached locally for security purpose.
+ // (the renderer process cannot modify the size and make the browser process
+ // access out of range addresses).
+ size_t size_;
+
+ // TODO(damienv): This is a work-around since atomicops.h does not define
+ // an atomic size_t type.
+#if SIZE_MAX == UINT32_MAX
+ typedef base::subtle::Atomic32 AtomicSize;
+#elif SIZE_MAX == UINT64_MAX
+ typedef base::subtle::Atomic64 AtomicSize;
+#elif
+#error "Unsupported size_t"
+#endif
+ AtomicSize* rd_offset_;
+ AtomicSize* wr_offset_;
+
+ // Internal read offset: this is where data is actually read from.
+ // The external offset |rd_offset_| is only used to protect data from being
+ // overwritten by the feeder.
+ // At any time, the internal read pointer must be between the external read
+ // offset and the write offset (circular fifo definition of "between").
+ size_t internal_rd_offset_;
+ size_t internal_wr_offset_;
+
+ // Note: all the memory read/write are followed by a memory fence before
+ // updating the rd/wr pointer.
+ void* base_;
+
+ // Protects the messages that are either being read or written.
+ std::list<scoped_refptr<MediaMessageFlag> > rd_flags_;
+ std::list<scoped_refptr<MediaMessageFlag> > wr_flags_;
+
+ base::WeakPtr<MediaMessageFifo> weak_this_;
+ base::WeakPtrFactory<MediaMessageFifo> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaMessageFifo);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_IPC_MEDIA_MESSAGE_FIFO_H_
diff --git a/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc b/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc
new file mode 100644
index 00000000000..bc04b70a0cc
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc
@@ -0,0 +1,195 @@
+// 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 "base/basictypes.h"
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "chromecast/media/cma/ipc/media_memory_chunk.h"
+#include "chromecast/media/cma/ipc/media_message.h"
+#include "chromecast/media/cma/ipc/media_message_fifo.h"
+#include "chromecast/media/cma/ipc/media_message_type.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+class FifoMemoryChunk : public MediaMemoryChunk {
+ public:
+ FifoMemoryChunk(void* mem, size_t size)
+ : mem_(mem), size_(size) {}
+ virtual ~FifoMemoryChunk() {}
+
+ virtual void* data() const override { return mem_; }
+ virtual size_t size() const override { return size_; }
+ virtual bool valid() const override { return true; }
+
+ private:
+ void* mem_;
+ size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(FifoMemoryChunk);
+};
+
+void MsgProducer(scoped_ptr<MediaMessageFifo> fifo,
+ int msg_count,
+ base::WaitableEvent* event) {
+
+ for (int k = 0; k < msg_count; k++) {
+ uint32 msg_type = 0x2 + (k % 5);
+ uint32 max_msg_content_size = k % 64;
+ do {
+ scoped_ptr<MediaMessage> msg1(
+ MediaMessage::CreateMessage(
+ msg_type,
+ base::Bind(&MediaMessageFifo::ReserveMemory,
+ base::Unretained(fifo.get())),
+ max_msg_content_size));
+ if (msg1)
+ break;
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
+ } while(true);
+ }
+
+ fifo.reset();
+
+ event->Signal();
+}
+
+void MsgConsumer(scoped_ptr<MediaMessageFifo> fifo,
+ int msg_count,
+ base::WaitableEvent* event) {
+
+ int k = 0;
+ while (k < msg_count) {
+ uint32 msg_type = 0x2 + (k % 5);
+ do {
+ scoped_ptr<MediaMessage> msg2(fifo->Pop());
+ if (msg2) {
+ if (msg2->type() != PaddingMediaMsg) {
+ EXPECT_EQ(msg2->type(), msg_type);
+ k++;
+ }
+ break;
+ }
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
+ } while(true);
+ }
+
+ fifo.reset();
+
+ event->Signal();
+}
+
+void MsgProducerConsumer(
+ scoped_ptr<MediaMessageFifo> producer_fifo,
+ scoped_ptr<MediaMessageFifo> consumer_fifo,
+ base::WaitableEvent* event) {
+ for (int k = 0; k < 2048; k++) {
+ // Should have enough space to create a message.
+ uint32 msg_type = 0x2 + (k % 5);
+ uint32 max_msg_content_size = k % 64;
+ scoped_ptr<MediaMessage> msg1(
+ MediaMessage::CreateMessage(
+ msg_type,
+ base::Bind(&MediaMessageFifo::ReserveMemory,
+ base::Unretained(producer_fifo.get())),
+ max_msg_content_size));
+ EXPECT_TRUE(msg1);
+
+ // Make sure the message is commited.
+ msg1.reset();
+
+ // At this point, we should have a message to read.
+ scoped_ptr<MediaMessage> msg2(consumer_fifo->Pop());
+ EXPECT_TRUE(msg2);
+ }
+
+ producer_fifo.reset();
+ consumer_fifo.reset();
+
+ event->Signal();
+}
+
+} // namespace
+
+TEST(MediaMessageFifoTest, AlternateWriteRead) {
+ size_t buffer_size = 64 * 1024;
+ scoped_ptr<uint64[]> buffer(new uint64[buffer_size / sizeof(uint64)]);
+
+ scoped_ptr<base::Thread> thread(
+ new base::Thread("FeederConsumerThread"));
+ thread->Start();
+
+ scoped_ptr<MediaMessageFifo> producer_fifo(new MediaMessageFifo(
+ scoped_ptr<MediaMemoryChunk>(
+ new FifoMemoryChunk(&buffer[0], buffer_size)),
+ true));
+ scoped_ptr<MediaMessageFifo> consumer_fifo(new MediaMessageFifo(
+ scoped_ptr<MediaMemoryChunk>(
+ new FifoMemoryChunk(&buffer[0], buffer_size)),
+ false));
+
+ base::WaitableEvent event(false, false);
+ thread->message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&MsgProducerConsumer,
+ base::Passed(&producer_fifo),
+ base::Passed(&consumer_fifo),
+ &event));
+ event.Wait();
+
+ thread.reset();
+}
+
+TEST(MediaMessageFifoTest, MultiThreaded) {
+ size_t buffer_size = 64 * 1024;
+ scoped_ptr<uint64[]> buffer(new uint64[buffer_size / sizeof(uint64)]);
+
+ scoped_ptr<base::Thread> producer_thread(
+ new base::Thread("FeederThread"));
+ scoped_ptr<base::Thread> consumer_thread(
+ new base::Thread("ConsumerThread"));
+ producer_thread->Start();
+ consumer_thread->Start();
+
+ scoped_ptr<MediaMessageFifo> producer_fifo(new MediaMessageFifo(
+ scoped_ptr<MediaMemoryChunk>(
+ new FifoMemoryChunk(&buffer[0], buffer_size)),
+ true));
+ scoped_ptr<MediaMessageFifo> consumer_fifo(new MediaMessageFifo(
+ scoped_ptr<MediaMemoryChunk>(
+ new FifoMemoryChunk(&buffer[0], buffer_size)),
+ false));
+
+ base::WaitableEvent producer_event_done(false, false);
+ base::WaitableEvent consumer_event_done(false, false);
+
+ const int msg_count = 2048;
+ producer_thread->message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&MsgProducer,
+ base::Passed(&producer_fifo),
+ msg_count,
+ &producer_event_done));
+ consumer_thread->message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&MsgConsumer,
+ base::Passed(&consumer_fifo),
+ msg_count,
+ &consumer_event_done));
+
+ producer_event_done.Wait();
+ consumer_event_done.Wait();
+
+ producer_thread.reset();
+ consumer_thread.reset();
+}
+
+} // namespace media
+} // namespace chromecast
+
diff --git a/chromium/chromecast/media/cma/ipc/media_message_type.h b/chromium/chromecast/media/cma/ipc/media_message_type.h
new file mode 100644
index 00000000000..23ff8474d32
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc/media_message_type.h
@@ -0,0 +1,15 @@
+// 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_IPC_MEDIA_MESSAGE_TYPE_H_
+#define CHROMECAST_MEDIA_CMA_IPC_MEDIA_MESSAGE_TYPE_H_
+
+enum MediaMessageType {
+ PaddingMediaMsg = 1,
+ FrameMediaMsg,
+ AudioConfigMediaMsg,
+ VideoConfigMediaMsg,
+};
+
+#endif
diff --git a/chromium/chromecast/media/cma/ipc/media_message_unittest.cc b/chromium/chromecast/media/cma/ipc/media_message_unittest.cc
new file mode 100644
index 00000000000..6f34f3eaf89
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc/media_message_unittest.cc
@@ -0,0 +1,146 @@
+// 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 "base/basictypes.h"
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "chromecast/media/cma/ipc/media_memory_chunk.h"
+#include "chromecast/media/cma/ipc/media_message.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+class ExternalMemoryBlock
+ : public MediaMemoryChunk {
+ public:
+ ExternalMemoryBlock(void* data, size_t size)
+ : data_(data), size_(size) {}
+ virtual ~ExternalMemoryBlock() {}
+
+ // MediaMemoryChunk implementation.
+ virtual void* data() const override { return data_; }
+ virtual size_t size() const override { return size_; }
+ virtual bool valid() const override { return true; }
+
+ private:
+ void* const data_;
+ const size_t size_;
+};
+
+scoped_ptr<MediaMemoryChunk> DummyAllocator(
+ void* data, size_t size, size_t alloc_size) {
+ CHECK_LE(alloc_size, size);
+ return scoped_ptr<MediaMemoryChunk>(
+ new ExternalMemoryBlock(data, alloc_size));
+}
+
+}
+
+TEST(MediaMessageTest, WriteRead) {
+ int buffer_size = 1024;
+ scoped_ptr<uint8[]> buffer(new uint8[buffer_size]);
+ MediaMessage::MemoryAllocatorCB mem_alloc_cb(
+ base::Bind(&DummyAllocator, buffer.get(), buffer_size));
+ uint32 type = 0x1;
+ int msg_content_capacity = 512;
+
+ // Write a message.
+ int count = 64;
+ scoped_ptr<MediaMessage> msg1(
+ MediaMessage::CreateMessage(type, mem_alloc_cb, msg_content_capacity));
+ for (int k = 0; k < count; k++) {
+ int v1 = 2 * k + 1;
+ EXPECT_TRUE(msg1->WritePod(v1));
+ uint8 v2 = k;
+ EXPECT_TRUE(msg1->WritePod(v2));
+ }
+ EXPECT_EQ(msg1->content_size(), count * (sizeof(int) + sizeof(uint8)));
+
+ // Verify the integrity of the message.
+ scoped_ptr<MediaMessage> msg2(
+ MediaMessage::MapMessage(scoped_ptr<MediaMemoryChunk>(
+ new ExternalMemoryBlock(&buffer[0], buffer_size))));
+ for (int k = 0; k < count; k++) {
+ int v1;
+ int expected_v1 = 2 * k + 1;
+ EXPECT_TRUE(msg2->ReadPod(&v1));
+ EXPECT_EQ(v1, expected_v1);
+ uint8 v2;
+ uint8 expected_v2 = k;
+ EXPECT_TRUE(msg2->ReadPod(&v2));
+ EXPECT_EQ(v2, expected_v2);
+ }
+}
+
+TEST(MediaMessageTest, WriteOverflow) {
+ int buffer_size = 1024;
+ scoped_ptr<uint8[]> buffer(new uint8[buffer_size]);
+ MediaMessage::MemoryAllocatorCB mem_alloc_cb(
+ base::Bind(&DummyAllocator, buffer.get(), buffer_size));
+ uint32 type = 0x1;
+ int msg_content_capacity = 8;
+
+ scoped_ptr<MediaMessage> msg1(
+ MediaMessage::CreateMessage(type, mem_alloc_cb, msg_content_capacity));
+ uint32 v1 = 0;
+ uint8 v2 = 0;
+ EXPECT_TRUE(msg1->WritePod(v1));
+ EXPECT_TRUE(msg1->WritePod(v1));
+
+ EXPECT_FALSE(msg1->WritePod(v1));
+ EXPECT_FALSE(msg1->WritePod(v2));
+}
+
+TEST(MediaMessageTest, ReadOverflow) {
+ int buffer_size = 1024;
+ scoped_ptr<uint8[]> buffer(new uint8[buffer_size]);
+ MediaMessage::MemoryAllocatorCB mem_alloc_cb(
+ base::Bind(&DummyAllocator, buffer.get(), buffer_size));
+ uint32 type = 0x1;
+ int msg_content_capacity = 8;
+
+ scoped_ptr<MediaMessage> msg1(
+ MediaMessage::CreateMessage(type, mem_alloc_cb, msg_content_capacity));
+ uint32 v1 = 0xcd;
+ EXPECT_TRUE(msg1->WritePod(v1));
+ EXPECT_TRUE(msg1->WritePod(v1));
+
+ scoped_ptr<MediaMessage> msg2(
+ MediaMessage::MapMessage(scoped_ptr<MediaMemoryChunk>(
+ new ExternalMemoryBlock(&buffer[0], buffer_size))));
+ uint32 v2;
+ EXPECT_TRUE(msg2->ReadPod(&v2));
+ EXPECT_EQ(v2, v1);
+ EXPECT_TRUE(msg2->ReadPod(&v2));
+ EXPECT_EQ(v2, v1);
+ EXPECT_FALSE(msg2->ReadPod(&v2));
+}
+
+TEST(MediaMessageTest, DummyMessage) {
+ int buffer_size = 1024;
+ scoped_ptr<uint8[]> buffer(new uint8[buffer_size]);
+ MediaMessage::MemoryAllocatorCB mem_alloc_cb(
+ base::Bind(&DummyAllocator, buffer.get(), buffer_size));
+ uint32 type = 0x1;
+
+ // Create first a dummy message to estimate the content size.
+ scoped_ptr<MediaMessage> msg1(
+ MediaMessage::CreateDummyMessage(type));
+ uint32 v1 = 0xcd;
+ EXPECT_TRUE(msg1->WritePod(v1));
+ EXPECT_TRUE(msg1->WritePod(v1));
+
+ // Create the real message and write the actual content.
+ scoped_ptr<MediaMessage> msg2(
+ MediaMessage::CreateMessage(type, mem_alloc_cb, msg1->content_size()));
+ EXPECT_TRUE(msg2->WritePod(v1));
+ EXPECT_TRUE(msg2->WritePod(v1));
+ EXPECT_FALSE(msg2->WritePod(v1));
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.cc b/chromium/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.cc
new file mode 100644
index 00000000000..d595bdaac3d
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.cc
@@ -0,0 +1,70 @@
+// 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/ipc_streamer/audio_decoder_config_marshaller.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "chromecast/media/cma/ipc/media_message.h"
+#include "media/base/audio_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+const size_t kMaxExtraDataSize = 16 * 1024;
+}
+
+// static
+void AudioDecoderConfigMarshaller::Write(
+ const ::media::AudioDecoderConfig& config, MediaMessage* msg) {
+ CHECK(msg->WritePod(config.codec()));
+ CHECK(msg->WritePod(config.channel_layout()));
+ CHECK(msg->WritePod(config.samples_per_second()));
+ CHECK(msg->WritePod(config.sample_format()));
+ CHECK(msg->WritePod(config.is_encrypted()));
+ CHECK(msg->WritePod(config.extra_data_size()));
+ if (config.extra_data_size() > 0)
+ CHECK(msg->WriteBuffer(config.extra_data(), config.extra_data_size()));
+}
+
+// static
+::media::AudioDecoderConfig AudioDecoderConfigMarshaller::Read(
+ MediaMessage* msg) {
+ ::media::AudioCodec codec;
+ ::media::SampleFormat sample_format;
+ ::media::ChannelLayout channel_layout;
+ int samples_per_second;
+ bool is_encrypted;
+ size_t extra_data_size;
+ scoped_ptr<uint8[]> extra_data;
+
+ CHECK(msg->ReadPod(&codec));
+ CHECK(msg->ReadPod(&channel_layout));
+ CHECK(msg->ReadPod(&samples_per_second));
+ CHECK(msg->ReadPod(&sample_format));
+ CHECK(msg->ReadPod(&is_encrypted));
+ CHECK(msg->ReadPod(&extra_data_size));
+
+ CHECK_GE(codec, ::media::kUnknownAudioCodec);
+ CHECK_LE(codec, ::media::kAudioCodecMax);
+ CHECK_GE(channel_layout, ::media::CHANNEL_LAYOUT_NONE);
+ CHECK_LE(channel_layout, ::media::CHANNEL_LAYOUT_MAX);
+ CHECK_GE(sample_format, ::media::kUnknownSampleFormat);
+ CHECK_LE(sample_format, ::media::kSampleFormatMax);
+ CHECK_LT(extra_data_size, kMaxExtraDataSize);
+ if (extra_data_size > 0) {
+ extra_data.reset(new uint8[extra_data_size]);
+ CHECK(msg->ReadBuffer(extra_data.get(), extra_data_size));
+ }
+
+ return ::media::AudioDecoderConfig(
+ codec, sample_format,
+ channel_layout, samples_per_second,
+ extra_data.get(), extra_data_size,
+ is_encrypted);
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h b/chromium/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h
new file mode 100644
index 00000000000..33f960e0c8b
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h
@@ -0,0 +1,27 @@
+// 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_IPC_STREAMER_AUDIO_DECODER_CONFIG_MARSHALLER_H_
+#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_AUDIO_DECODER_CONFIG_MARSHALLER_H_
+
+#include "media/base/audio_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+class MediaMessage;
+
+class AudioDecoderConfigMarshaller {
+ public:
+ // Writes the serialized structure of |config| into |msg|.
+ static void Write(
+ const ::media::AudioDecoderConfig& config, MediaMessage* msg);
+
+ // Returns an AudioDecoderConfig from its serialized structure.
+ static ::media::AudioDecoderConfig Read(MediaMessage* msg);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_AUDIO_DECODER_CONFIG_MARSHALLER_H_
diff --git a/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.cc b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.cc
new file mode 100644
index 00000000000..6bc25edc386
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.cc
@@ -0,0 +1,200 @@
+// 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/ipc_streamer/av_streamer_proxy.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "chromecast/media/cma/base/coded_frame_provider.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "chromecast/media/cma/ipc/media_memory_chunk.h"
+#include "chromecast/media/cma/ipc/media_message.h"
+#include "chromecast/media/cma/ipc/media_message_fifo.h"
+#include "chromecast/media/cma/ipc/media_message_type.h"
+#include "chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h"
+#include "chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.h"
+#include "chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h"
+
+namespace chromecast {
+namespace media {
+
+AvStreamerProxy::AvStreamerProxy()
+ : is_running_(false),
+ pending_read_(false),
+ pending_av_data_(false),
+ weak_factory_(this) {
+ weak_this_ = weak_factory_.GetWeakPtr();
+ thread_checker_.DetachFromThread();
+}
+
+AvStreamerProxy::~AvStreamerProxy() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void AvStreamerProxy::SetCodedFrameProvider(
+ scoped_ptr<CodedFrameProvider> frame_provider) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!frame_provider_);
+ frame_provider_.reset(frame_provider.release());
+}
+
+void AvStreamerProxy::SetMediaMessageFifo(
+ scoped_ptr<MediaMessageFifo> fifo) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!fifo_);
+ fifo_.reset(fifo.release());
+}
+
+void AvStreamerProxy::Start() {
+ DCHECK(!is_running_);
+
+ is_running_ = true;
+ RequestBufferIfNeeded();
+}
+
+void AvStreamerProxy::StopAndFlush(const base::Closure& done_cb) {
+ is_running_ = false;
+
+ pending_av_data_ = false;
+ pending_audio_config_ = ::media::AudioDecoderConfig();
+ pending_video_config_ = ::media::VideoDecoderConfig();
+ pending_buffer_ = scoped_refptr<DecoderBufferBase>();
+
+ pending_read_ = false;
+ frame_provider_->Flush(done_cb);
+}
+
+void AvStreamerProxy::OnFifoReadEvent() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Some enough space might have been released
+ // to accommodate the pending data.
+ if (pending_av_data_)
+ ProcessPendingData();
+}
+
+void AvStreamerProxy::RequestBufferIfNeeded() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!is_running_ || pending_read_ || pending_av_data_)
+ return;
+
+ // |frame_provider_| is assumed to run on the same message loop.
+ // Add a BindToCurrentLoop if that's not the case in the future.
+ pending_read_ = true;
+ frame_provider_->Read(base::Bind(&AvStreamerProxy::OnNewBuffer, weak_this_));
+}
+
+void AvStreamerProxy::OnNewBuffer(
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ pending_read_ = false;
+
+ if (buffer->end_of_stream())
+ is_running_ = false;
+
+ DCHECK(!pending_av_data_);
+ pending_av_data_ = true;
+
+ pending_buffer_ = buffer;
+ pending_audio_config_ = audio_config;
+ pending_video_config_ = video_config;
+
+ ProcessPendingData();
+}
+
+void AvStreamerProxy::ProcessPendingData() {
+ if (pending_audio_config_.IsValidConfig()) {
+ if (!SendAudioDecoderConfig(pending_audio_config_))
+ return;
+ pending_audio_config_ = ::media::AudioDecoderConfig();
+ }
+
+ if (pending_video_config_.IsValidConfig()) {
+ if (!SendVideoDecoderConfig(pending_video_config_))
+ return;
+ pending_video_config_ = ::media::VideoDecoderConfig();
+ }
+
+ if (pending_buffer_.get()) {
+ if (!SendBuffer(pending_buffer_))
+ return;
+ pending_buffer_ = scoped_refptr<DecoderBufferBase>();
+ }
+
+ pending_av_data_ = false;
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AvStreamerProxy::RequestBufferIfNeeded, weak_this_));
+}
+
+bool AvStreamerProxy::SendAudioDecoderConfig(
+ const ::media::AudioDecoderConfig& config) {
+ // Create a dummy message to calculate first the message size.
+ scoped_ptr<MediaMessage> dummy_msg(
+ MediaMessage::CreateDummyMessage(AudioConfigMediaMsg));
+ AudioDecoderConfigMarshaller::Write(config, dummy_msg.get());
+
+ // Create the real message and write the actual content.
+ scoped_ptr<MediaMessage> msg(
+ MediaMessage::CreateMessage(
+ AudioConfigMediaMsg,
+ base::Bind(&MediaMessageFifo::ReserveMemory,
+ base::Unretained(fifo_.get())),
+ dummy_msg->content_size()));
+ if (!msg)
+ return false;
+
+ AudioDecoderConfigMarshaller::Write(config, msg.get());
+ return true;
+}
+
+bool AvStreamerProxy::SendVideoDecoderConfig(
+ const ::media::VideoDecoderConfig& config) {
+ // Create a dummy message to calculate first the message size.
+ scoped_ptr<MediaMessage> dummy_msg(
+ MediaMessage::CreateDummyMessage(VideoConfigMediaMsg));
+ VideoDecoderConfigMarshaller::Write(config, dummy_msg.get());
+
+ // Create the real message and write the actual content.
+ scoped_ptr<MediaMessage> msg(
+ MediaMessage::CreateMessage(
+ VideoConfigMediaMsg,
+ base::Bind(&MediaMessageFifo::ReserveMemory,
+ base::Unretained(fifo_.get())),
+ dummy_msg->content_size()));
+ if (!msg)
+ return false;
+
+ VideoDecoderConfigMarshaller::Write(config, msg.get());
+ return true;
+}
+
+bool AvStreamerProxy::SendBuffer(
+ const scoped_refptr<DecoderBufferBase>& buffer) {
+ // Create a dummy message to calculate first the message size.
+ scoped_ptr<MediaMessage> dummy_msg(
+ MediaMessage::CreateDummyMessage(FrameMediaMsg));
+ DecoderBufferBaseMarshaller::Write(buffer, dummy_msg.get());
+
+ // Create the real message and write the actual content.
+ scoped_ptr<MediaMessage> msg(
+ MediaMessage::CreateMessage(
+ FrameMediaMsg,
+ base::Bind(&MediaMessageFifo::ReserveMemory,
+ base::Unretained(fifo_.get())),
+ dummy_msg->content_size()));
+ if (!msg)
+ return false;
+
+ DecoderBufferBaseMarshaller::Write(buffer, msg.get());
+ return true;
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.h b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.h
new file mode 100644
index 00000000000..e750c85ae8c
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.h
@@ -0,0 +1,88 @@
+// 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_IPC_STREAMER_AV_STREAMER_PROXY_H_
+#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_AV_STREAMER_PROXY_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/video_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+class CodedFrameProvider;
+class DecoderBufferBase;
+class MediaMessageFifo;
+
+// AvStreamerProxy streams audio/video data from a coded frame provider
+// to a media message fifo.
+class AvStreamerProxy {
+ public:
+ AvStreamerProxy();
+ ~AvStreamerProxy();
+
+ // AvStreamerProxy gets frames and audio/video config
+ // from the |frame_provider| and feed them into the |fifo|.
+ void SetCodedFrameProvider(scoped_ptr<CodedFrameProvider> frame_provider);
+ void SetMediaMessageFifo(scoped_ptr<MediaMessageFifo> fifo);
+
+ // Starts fetching A/V buffers.
+ void Start();
+
+ // Stops fetching A/V buffers and flush the pending buffers,
+ // invoking |flush_cb| when done.
+ void StopAndFlush(const base::Closure& flush_cb);
+
+ // Event invoked when some data have been read from the fifo.
+ // This means some data can now possibly be written into the fifo.
+ void OnFifoReadEvent();
+
+ private:
+ void RequestBufferIfNeeded();
+
+ void OnNewBuffer(const scoped_refptr<DecoderBufferBase>& buffer,
+ const ::media::AudioDecoderConfig& audio_config,
+ const ::media::VideoDecoderConfig& video_config);
+
+ void ProcessPendingData();
+
+ bool SendAudioDecoderConfig(const ::media::AudioDecoderConfig& config);
+ bool SendVideoDecoderConfig(const ::media::VideoDecoderConfig& config);
+ bool SendBuffer(const scoped_refptr<DecoderBufferBase>& buffer);
+
+ base::ThreadChecker thread_checker_;
+
+ scoped_ptr<CodedFrameProvider> frame_provider_;
+
+ // Fifo used to convey A/V configs and buffers.
+ scoped_ptr<MediaMessageFifo> fifo_;
+
+ // State.
+ bool is_running_;
+
+ // Indicates if there is a pending request to the coded frame provider.
+ bool pending_read_;
+
+ // Pending config & buffer.
+ // |pending_av_data_| is set as long as one of the pending audio/video
+ // config or buffer is valid.
+ bool pending_av_data_;
+ ::media::AudioDecoderConfig pending_audio_config_;
+ ::media::VideoDecoderConfig pending_video_config_;
+ scoped_refptr<DecoderBufferBase> pending_buffer_;
+
+ base::WeakPtr<AvStreamerProxy> weak_this_;
+ base::WeakPtrFactory<AvStreamerProxy> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AvStreamerProxy);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_AV_STREAMER_PROXY_H_
diff --git a/chromium/chromecast/media/cma/ipc_streamer/av_streamer_unittest.cc b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_unittest.cc
new file mode 100644
index 00000000000..54a9e14ba07
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_unittest.cc
@@ -0,0 +1,253 @@
+// 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 <list>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "chromecast/media/cma/ipc/media_memory_chunk.h"
+#include "chromecast/media/cma/ipc/media_message_fifo.h"
+#include "chromecast/media/cma/ipc_streamer/av_streamer_proxy.h"
+#include "chromecast/media/cma/ipc_streamer/coded_frame_provider_host.h"
+#include "chromecast/media/cma/test/frame_generator_for_test.h"
+#include "chromecast/media/cma/test/mock_frame_consumer.h"
+#include "chromecast/media/cma/test/mock_frame_provider.h"
+#include "media/base/audio_decoder_config.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 {
+
+namespace {
+
+class FifoMemoryChunk : public MediaMemoryChunk {
+ public:
+ FifoMemoryChunk(void* mem, size_t size)
+ : mem_(mem), size_(size) {}
+ virtual ~FifoMemoryChunk() {}
+
+ virtual void* data() const override { return mem_; }
+ virtual size_t size() const override { return size_; }
+ virtual bool valid() const override { return true; }
+
+ private:
+ void* mem_;
+ size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(FifoMemoryChunk);
+};
+
+} // namespace
+
+class AvStreamerTest : public testing::Test {
+ public:
+ AvStreamerTest();
+ virtual ~AvStreamerTest();
+
+ // Setups the test.
+ void Configure(
+ size_t frame_count,
+ const std::vector<bool>& provider_delayed_pattern,
+ const std::vector<bool>& consumer_delayed_pattern);
+
+ // Starts the test.
+ void Start();
+
+ protected:
+ scoped_ptr<uint64[]> fifo_mem_;
+
+ scoped_ptr<AvStreamerProxy> av_buffer_proxy_;
+ scoped_ptr<CodedFrameProviderHost> coded_frame_provider_host_;
+ scoped_ptr<MockFrameConsumer> frame_consumer_;
+
+ private:
+ void OnTestTimeout();
+ void OnTestCompleted();
+
+ void OnFifoRead();
+ void OnFifoWrite();
+
+ DISALLOW_COPY_AND_ASSIGN(AvStreamerTest);
+};
+
+AvStreamerTest::AvStreamerTest() {
+}
+
+AvStreamerTest::~AvStreamerTest() {
+}
+
+void AvStreamerTest::Configure(
+ size_t frame_count,
+ const std::vector<bool>& provider_delayed_pattern,
+ const std::vector<bool>& consumer_delayed_pattern) {
+ // Frame generation on the producer and consumer side.
+ std::vector<FrameGeneratorForTest::FrameSpec> frame_specs;
+ frame_specs.resize(frame_count);
+ 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 = ((k % 3) == 0);
+ }
+ frame_specs[frame_specs.size() - 1].is_eos = true;
+
+ scoped_ptr<FrameGeneratorForTest> frame_generator_provider(
+ new FrameGeneratorForTest(frame_specs));
+ scoped_ptr<FrameGeneratorForTest> frame_generator_consumer(
+ new FrameGeneratorForTest(frame_specs));
+
+ scoped_ptr<MockFrameProvider> frame_provider(new MockFrameProvider());
+ frame_provider->Configure(provider_delayed_pattern,
+ frame_generator_provider.Pass());
+
+ size_t fifo_size_div_8 = 512;
+ fifo_mem_.reset(new uint64[fifo_size_div_8]);
+ scoped_ptr<MediaMessageFifo> producer_fifo(
+ new MediaMessageFifo(
+ scoped_ptr<MediaMemoryChunk>(
+ new FifoMemoryChunk(&fifo_mem_[0], fifo_size_div_8 * 8)),
+ true));
+ scoped_ptr<MediaMessageFifo> consumer_fifo(
+ new MediaMessageFifo(
+ scoped_ptr<MediaMemoryChunk>(
+ new FifoMemoryChunk(&fifo_mem_[0], fifo_size_div_8 * 8)),
+ false));
+ producer_fifo->ObserveWriteActivity(
+ base::Bind(&AvStreamerTest::OnFifoWrite, base::Unretained(this)));
+ consumer_fifo->ObserveReadActivity(
+ base::Bind(&AvStreamerTest::OnFifoRead, base::Unretained(this)));
+
+ av_buffer_proxy_.reset(
+ new AvStreamerProxy());
+ av_buffer_proxy_->SetCodedFrameProvider(
+ scoped_ptr<CodedFrameProvider>(frame_provider.release()));
+ av_buffer_proxy_->SetMediaMessageFifo(producer_fifo.Pass());
+
+ coded_frame_provider_host_.reset(
+ new CodedFrameProviderHost(consumer_fifo.Pass()));
+
+ frame_consumer_.reset(
+ new MockFrameConsumer(coded_frame_provider_host_.get()));
+ frame_consumer_->Configure(
+ consumer_delayed_pattern,
+ false,
+ frame_generator_consumer.Pass());
+}
+
+void AvStreamerTest::Start() {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AvStreamerProxy::Start,
+ base::Unretained(av_buffer_proxy_.get())));
+
+ frame_consumer_->Start(
+ base::Bind(&AvStreamerTest::OnTestCompleted,
+ base::Unretained(this)));
+}
+
+void AvStreamerTest::OnTestTimeout() {
+ ADD_FAILURE() << "Test timed out";
+ if (base::MessageLoop::current())
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+void AvStreamerTest::OnTestCompleted() {
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+void AvStreamerTest::OnFifoWrite() {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&CodedFrameProviderHost::OnFifoWriteEvent,
+ base::Unretained(coded_frame_provider_host_.get())));
+}
+
+void AvStreamerTest::OnFifoRead() {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AvStreamerProxy::OnFifoReadEvent,
+ base::Unretained(av_buffer_proxy_.get())));
+}
+
+TEST_F(AvStreamerTest, FastProviderSlowConsumer) {
+ bool provider_delayed_pattern[] = { false };
+ bool consumer_delayed_pattern[] = { true };
+
+ const size_t frame_count = 100u;
+ Configure(
+ frame_count,
+ std::vector<bool>(
+ provider_delayed_pattern,
+ provider_delayed_pattern + arraysize(provider_delayed_pattern)),
+ std::vector<bool>(
+ consumer_delayed_pattern,
+ consumer_delayed_pattern + arraysize(consumer_delayed_pattern)));
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&AvStreamerTest::Start, base::Unretained(this)));
+ message_loop->Run();
+};
+
+TEST_F(AvStreamerTest, SlowProviderFastConsumer) {
+ bool provider_delayed_pattern[] = { true };
+ bool consumer_delayed_pattern[] = { false };
+
+ const size_t frame_count = 100u;
+ Configure(
+ frame_count,
+ std::vector<bool>(
+ provider_delayed_pattern,
+ provider_delayed_pattern + arraysize(provider_delayed_pattern)),
+ std::vector<bool>(
+ consumer_delayed_pattern,
+ consumer_delayed_pattern + arraysize(consumer_delayed_pattern)));
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&AvStreamerTest::Start, base::Unretained(this)));
+ message_loop->Run();
+};
+
+TEST_F(AvStreamerTest, SlowFastProducerConsumer) {
+ // Pattern lengths are prime between each other
+ // so that a lot of combinations can be tested.
+ bool provider_delayed_pattern[] = {
+ true, true, true, true, true,
+ false, false, false, false
+ };
+ bool consumer_delayed_pattern[] = {
+ true, true, true, true, true, true, true,
+ false, false, false, false, false, false, false
+ };
+
+ const size_t frame_count = 100u;
+ Configure(
+ frame_count,
+ std::vector<bool>(
+ provider_delayed_pattern,
+ provider_delayed_pattern + arraysize(provider_delayed_pattern)),
+ std::vector<bool>(
+ consumer_delayed_pattern,
+ consumer_delayed_pattern + arraysize(consumer_delayed_pattern)));
+
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
+ message_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&AvStreamerTest::Start, base::Unretained(this)));
+ message_loop->Run();
+};
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc_streamer/coded_frame_provider_host.cc b/chromium/chromecast/media/cma/ipc_streamer/coded_frame_provider_host.cc
new file mode 100644
index 00000000000..97f372c5851
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/coded_frame_provider_host.cc
@@ -0,0 +1,96 @@
+// 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/ipc_streamer/coded_frame_provider_host.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "chromecast/media/cma/ipc/media_message.h"
+#include "chromecast/media/cma/ipc/media_message_fifo.h"
+#include "chromecast/media/cma/ipc/media_message_type.h"
+#include "chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h"
+#include "chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.h"
+#include "chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h"
+#include "media/base/buffers.h"
+#include "media/base/decrypt_config.h"
+
+namespace chromecast {
+namespace media {
+
+CodedFrameProviderHost::CodedFrameProviderHost(
+ scoped_ptr<MediaMessageFifo> media_message_fifo)
+ : fifo_(media_message_fifo.Pass()),
+ weak_factory_(this) {
+ weak_this_ = weak_factory_.GetWeakPtr();
+ thread_checker_.DetachFromThread();
+}
+
+CodedFrameProviderHost::~CodedFrameProviderHost() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void CodedFrameProviderHost::Read(const ReadCB& read_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Cannot be called if there is already a pending read.
+ DCHECK(read_cb_.is_null());
+ read_cb_ = read_cb;
+
+ ReadMessages();
+}
+
+void CodedFrameProviderHost::Flush(const base::Closure& flush_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ audio_config_ = ::media::AudioDecoderConfig();
+ video_config_ = ::media::VideoDecoderConfig();
+ read_cb_.Reset();
+ fifo_->Flush();
+ flush_cb.Run();
+}
+
+void CodedFrameProviderHost::OnFifoWriteEvent() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ReadMessages();
+}
+
+base::Closure CodedFrameProviderHost::GetFifoWriteEventCb() {
+ return base::Bind(&CodedFrameProviderHost::OnFifoWriteEvent, weak_this_);
+}
+
+void CodedFrameProviderHost::ReadMessages() {
+ // Read messages until a frame is provided (i.e. not just the audio/video
+ // configurations).
+ while (!read_cb_.is_null()) {
+ scoped_ptr<MediaMessage> msg(fifo_->Pop());
+ if (!msg)
+ break;
+
+ if (msg->type() == PaddingMediaMsg) {
+ // Ignore the message.
+ } else if (msg->type() == AudioConfigMediaMsg) {
+ audio_config_ = AudioDecoderConfigMarshaller::Read(msg.get());
+ } else if (msg->type() == VideoConfigMediaMsg) {
+ video_config_ = VideoDecoderConfigMarshaller::Read(msg.get());
+ } else if (msg->type() == FrameMediaMsg) {
+ scoped_refptr<DecoderBufferBase> buffer =
+ DecoderBufferBaseMarshaller::Read(msg.Pass());
+ base::ResetAndReturn(&read_cb_).Run(
+ buffer, audio_config_, video_config_);
+ audio_config_ = ::media::AudioDecoderConfig();
+ video_config_ = ::media::VideoDecoderConfig();
+ } else {
+ // Receiving an unexpected message.
+ // Possible use case (except software bugs): the renderer process has
+ // been compromised and an invalid message value has been written to
+ // the fifo. Crash the browser process in this case to avoid further
+ // security implications (so do not use NOTREACHED which crashes only
+ // in debug builds).
+ LOG(FATAL) << "Unknown media message";
+ }
+ }
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc_streamer/coded_frame_provider_host.h b/chromium/chromecast/media/cma/ipc_streamer/coded_frame_provider_host.h
new file mode 100644
index 00000000000..9dc9471bc5b
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/coded_frame_provider_host.h
@@ -0,0 +1,61 @@
+// 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_IPC_STREAMER_CODED_FRAME_PROVIDER_HOST_H_
+#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_CODED_FRAME_PROVIDER_HOST_H_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "chromecast/media/cma/base/coded_frame_provider.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/video_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+class MediaMessageFifo;
+
+// CodedFrameProviderHost is a frame provider that gets the frames
+// from a media message fifo.
+class CodedFrameProviderHost : public CodedFrameProvider {
+ public:
+ // Note: if the media message fifo is located into shared memory,
+ // the caller must make sure the shared memory segment is valid
+ // during the whole lifetime of this object.
+ explicit CodedFrameProviderHost(
+ scoped_ptr<MediaMessageFifo> media_message_fifo);
+ virtual ~CodedFrameProviderHost();
+
+ // CodedFrameProvider implementation.
+ virtual void Read(const ReadCB& read_cb) override;
+ virtual void Flush(const base::Closure& flush_cb) override;
+
+ // Invoked when some data has been written into the fifo.
+ void OnFifoWriteEvent();
+ base::Closure GetFifoWriteEventCb();
+
+ private:
+ void ReadMessages();
+
+ base::ThreadChecker thread_checker_;
+
+ // Fifo holding the frames.
+ scoped_ptr<MediaMessageFifo> fifo_;
+
+ ReadCB read_cb_;
+
+ // Audio/video configuration for the next A/V buffer.
+ ::media::AudioDecoderConfig audio_config_;
+ ::media::VideoDecoderConfig video_config_;
+
+ base::WeakPtr<CodedFrameProviderHost> weak_this_;
+ base::WeakPtrFactory<CodedFrameProviderHost> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CodedFrameProviderHost);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_CODED_FRAME_PROVIDER_HOST_H_
diff --git a/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.cc b/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.cc
new file mode 100644
index 00000000000..dc1f8f5714e
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.cc
@@ -0,0 +1,177 @@
+// 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/ipc_streamer/decoder_buffer_base_marshaller.h"
+
+#include "base/logging.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "chromecast/media/cma/ipc/media_message.h"
+#include "chromecast/media/cma/ipc/media_message_type.h"
+#include "chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.h"
+#include "media/base/decrypt_config.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+const size_t kMaxFrameSize = 4 * 1024 * 1024;
+
+class DecoderBufferFromMsg : public DecoderBufferBase {
+ public:
+ explicit DecoderBufferFromMsg(scoped_ptr<MediaMessage> msg);
+
+ void Initialize();
+
+ // DecoderBufferBase implementation.
+ virtual base::TimeDelta timestamp() const override;
+ virtual const uint8* data() const override;
+ virtual uint8* writable_data() const override;
+ virtual int data_size() const override;
+ virtual const ::media::DecryptConfig* decrypt_config() const override;
+ virtual bool end_of_stream() const override;
+
+ private:
+ virtual ~DecoderBufferFromMsg();
+
+ // Indicates whether this is an end of stream frame.
+ bool is_eos_;
+
+ // Frame timestamp.
+ base::TimeDelta pts_;
+
+ // CENC parameters.
+ scoped_ptr< ::media::DecryptConfig> decrypt_config_;
+
+ // Size of the frame.
+ int data_size_;
+
+ // Keeps the message since frame data is not copied.
+ scoped_ptr<MediaMessage> msg_;
+ uint8* data_;
+
+ DISALLOW_COPY_AND_ASSIGN(DecoderBufferFromMsg);
+};
+
+DecoderBufferFromMsg::DecoderBufferFromMsg(
+ scoped_ptr<MediaMessage> msg)
+ : msg_(msg.Pass()),
+ is_eos_(true),
+ data_(NULL) {
+ CHECK(msg_);
+}
+
+DecoderBufferFromMsg::~DecoderBufferFromMsg() {
+}
+
+void DecoderBufferFromMsg::Initialize() {
+ CHECK_EQ(msg_->type(), FrameMediaMsg);
+
+ CHECK(msg_->ReadPod(&is_eos_));
+ if (is_eos_)
+ return;
+
+ int64 pts_internal = 0;
+ CHECK(msg_->ReadPod(&pts_internal));
+ pts_ = base::TimeDelta::FromInternalValue(pts_internal);
+
+ bool has_decrypt_config = false;
+ CHECK(msg_->ReadPod(&has_decrypt_config));
+ if (has_decrypt_config)
+ decrypt_config_.reset(DecryptConfigMarshaller::Read(msg_.get()).release());
+
+ CHECK(msg_->ReadPod(&data_size_));
+ CHECK_GT(data_size_, 0);
+ CHECK_LT(data_size_, kMaxFrameSize);
+
+ // Get a pointer to the frame data inside the message.
+ // Avoid copying the frame data here.
+ data_ = static_cast<uint8*>(msg_->GetWritableBuffer(data_size_));
+ CHECK(data_);
+
+ if (decrypt_config_) {
+ uint32 subsample_total_size = 0;
+ for (size_t k = 0; k < decrypt_config_->subsamples().size(); k++) {
+ subsample_total_size += decrypt_config_->subsamples()[k].clear_bytes;
+ subsample_total_size += decrypt_config_->subsamples()[k].cypher_bytes;
+ }
+ CHECK_EQ(subsample_total_size, data_size_);
+ }
+}
+
+base::TimeDelta DecoderBufferFromMsg::timestamp() const {
+ return pts_;
+}
+
+const uint8* DecoderBufferFromMsg::data() const {
+ CHECK(msg_->IsSerializedMsgAvailable());
+ return data_;
+}
+
+uint8* DecoderBufferFromMsg::writable_data() const {
+ CHECK(msg_->IsSerializedMsgAvailable());
+ return data_;
+}
+
+int DecoderBufferFromMsg::data_size() const {
+ return data_size_;
+}
+
+const ::media::DecryptConfig* DecoderBufferFromMsg::decrypt_config() const {
+ return decrypt_config_.get();
+}
+
+bool DecoderBufferFromMsg::end_of_stream() const {
+ return is_eos_;
+}
+
+} // namespace
+
+// static
+void DecoderBufferBaseMarshaller::Write(
+ const scoped_refptr<DecoderBufferBase>& buffer,
+ MediaMessage* msg) {
+ CHECK(msg->WritePod(buffer->end_of_stream()));
+ if (buffer->end_of_stream())
+ return;
+
+ CHECK(msg->WritePod(buffer->timestamp().ToInternalValue()));
+
+ bool has_decrypt_config =
+ (buffer->decrypt_config() != NULL &&
+ buffer->decrypt_config()->iv().size() > 0);
+ CHECK(msg->WritePod(has_decrypt_config));
+
+ if (has_decrypt_config) {
+ // DecryptConfig may contain 0 subsamples if all content is encrypted.
+ // Map this case to a single fully-encrypted "subsample" for more consistent
+ // backend handling.
+ if (buffer->decrypt_config()->subsamples().empty()) {
+ std::vector< ::media::SubsampleEntry> encrypted_subsample_list(1);
+ encrypted_subsample_list[0].clear_bytes = 0;
+ encrypted_subsample_list[0].cypher_bytes = buffer->data_size();
+ ::media::DecryptConfig full_sample_config(
+ buffer->decrypt_config()->key_id(),
+ buffer->decrypt_config()->iv(),
+ encrypted_subsample_list);
+ DecryptConfigMarshaller::Write(full_sample_config, msg);
+ } else {
+ DecryptConfigMarshaller::Write(*buffer->decrypt_config(), msg);
+ }
+ }
+
+ CHECK(msg->WritePod(buffer->data_size()));
+ CHECK(msg->WriteBuffer(buffer->data(), buffer->data_size()));
+}
+
+// static
+scoped_refptr<DecoderBufferBase> DecoderBufferBaseMarshaller::Read(
+ scoped_ptr<MediaMessage> msg) {
+ scoped_refptr<DecoderBufferFromMsg> buffer(
+ new DecoderBufferFromMsg(msg.Pass()));
+ buffer->Initialize();
+ return buffer;
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.h b/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.h
new file mode 100644
index 00000000000..bd5dbfe9b11
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.h
@@ -0,0 +1,29 @@
+// 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_IPC_STREAMER_DECODER_BUFFER_BASE_MARSHALLER_H_
+#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_DECODER_BUFFER_BASE_MARSHALLER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace chromecast {
+namespace media {
+class DecoderBufferBase;
+class MediaMessage;
+
+class DecoderBufferBaseMarshaller {
+ public:
+ // Writes the serialized structure of |config| into |msg|.
+ static void Write(
+ const scoped_refptr<DecoderBufferBase>& buffer, MediaMessage* msg);
+
+ // Returns a decoder buffer from its serialized structure.
+ static scoped_refptr<DecoderBufferBase> Read(scoped_ptr<MediaMessage> msg);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_DECODER_BUFFER_BASE_MARSHALLER_H_
diff --git a/chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.cc b/chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.cc
new file mode 100644
index 00000000000..9e3de954ccb
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.cc
@@ -0,0 +1,75 @@
+// 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/ipc_streamer/decrypt_config_marshaller.h"
+
+#include "base/logging.h"
+#include "chromecast/media/cma/ipc/media_message.h"
+#include "media/base/decrypt_config.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+const size_t kMaxKeyIdSize = 256;
+const size_t kMaxIvSize = 256;
+const size_t kMaxSubsampleCount = 1024;
+}
+
+// static
+void DecryptConfigMarshaller::Write(
+ const ::media::DecryptConfig& config, MediaMessage* msg) {
+ CHECK_GT(config.key_id().size(), 0);
+ CHECK_GT(config.iv().size(), 0);
+ CHECK_GT(config.subsamples().size(), 0);
+
+ CHECK(msg->WritePod(config.key_id().size()));
+ CHECK(msg->WriteBuffer(config.key_id().data(), config.key_id().size()));
+ CHECK(msg->WritePod(config.iv().size()));
+ CHECK(msg->WriteBuffer(config.iv().data(), config.iv().size()));
+ CHECK(msg->WritePod(config.subsamples().size()));
+ for (size_t k = 0; k < config.subsamples().size(); k++) {
+ CHECK(msg->WritePod(config.subsamples()[k].clear_bytes));
+ CHECK(msg->WritePod(config.subsamples()[k].cypher_bytes));
+ }
+}
+
+// static
+scoped_ptr< ::media::DecryptConfig> DecryptConfigMarshaller::Read(
+ MediaMessage* msg) {
+ size_t key_id_size = 0;
+ CHECK(msg->ReadPod(&key_id_size));
+ CHECK_GT(key_id_size, 0);
+ CHECK_LT(key_id_size, kMaxKeyIdSize);
+ scoped_ptr<char[]> key_id(new char[key_id_size]);
+ CHECK(msg->ReadBuffer(key_id.get(), key_id_size));
+
+ size_t iv_size = 0;
+ CHECK(msg->ReadPod(&iv_size));
+ CHECK_GT(iv_size, 0);
+ CHECK_LT(iv_size, kMaxIvSize);
+ scoped_ptr<char[]> iv(new char[iv_size]);
+ CHECK(msg->ReadBuffer(iv.get(), iv_size));
+
+ size_t subsample_count = 0;
+ CHECK(msg->ReadPod(&subsample_count));
+ CHECK_GT(subsample_count, 0);
+ CHECK_LT(subsample_count, kMaxSubsampleCount);
+ std::vector< ::media::SubsampleEntry> subsamples(subsample_count);
+ for (size_t k = 0; k < subsample_count; k++) {
+ subsamples[k].clear_bytes = 0;
+ subsamples[k].cypher_bytes = 0;
+ CHECK(msg->ReadPod(&subsamples[k].clear_bytes));
+ CHECK(msg->ReadPod(&subsamples[k].cypher_bytes));
+ }
+
+ return scoped_ptr< ::media::DecryptConfig>(
+ new ::media::DecryptConfig(
+ std::string(key_id.get(), key_id_size),
+ std::string(iv.get(), iv_size),
+ subsamples));
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.h b/chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.h
new file mode 100644
index 00000000000..b600ba41c14
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.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_IPC_STREAMER_DECRYPT_CONFIG_MARSHALLER_H_
+#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_DECRYPT_CONFIG_MARSHALLER_H_
+
+#include "base/memory/scoped_ptr.h"
+
+namespace media {
+class DecryptConfig;
+}
+
+namespace chromecast {
+namespace media {
+class MediaMessage;
+
+class DecryptConfigMarshaller {
+ public:
+ // Writes the serialized structure of |config| into |msg|.
+ static void Write(
+ const ::media::DecryptConfig& config, MediaMessage* msg);
+
+ // Returns a DecryptConfig from its serialized structure.
+ static scoped_ptr< ::media::DecryptConfig> Read(MediaMessage* msg);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_DECRYPT_CONFIG_MARSHALLER_H_
diff --git a/chromium/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.cc b/chromium/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.cc
new file mode 100644
index 00000000000..645056ce22d
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.cc
@@ -0,0 +1,113 @@
+// 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/ipc_streamer/video_decoder_config_marshaller.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "chromecast/media/cma/ipc/media_message.h"
+#include "media/base/video_decoder_config.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+const size_t kMaxExtraDataSize = 16 * 1024;
+
+class SizeMarshaller {
+ public:
+ static void Write(const gfx::Size& size, MediaMessage* msg) {
+ CHECK(msg->WritePod(size.width()));
+ CHECK(msg->WritePod(size.height()));
+ }
+
+ static gfx::Size Read(MediaMessage* msg) {
+ int w, h;
+ CHECK(msg->ReadPod(&w));
+ CHECK(msg->ReadPod(&h));
+ return gfx::Size(w, h);
+ }
+};
+
+class RectMarshaller {
+ public:
+ static void Write(const gfx::Rect& rect, MediaMessage* msg) {
+ CHECK(msg->WritePod(rect.x()));
+ CHECK(msg->WritePod(rect.y()));
+ CHECK(msg->WritePod(rect.width()));
+ CHECK(msg->WritePod(rect.height()));
+ }
+
+ static gfx::Rect Read(MediaMessage* msg) {
+ int x, y, w, h;
+ CHECK(msg->ReadPod(&x));
+ CHECK(msg->ReadPod(&y));
+ CHECK(msg->ReadPod(&w));
+ CHECK(msg->ReadPod(&h));
+ return gfx::Rect(x, y, w, h);
+ }
+};
+
+} // namespace
+
+// static
+void VideoDecoderConfigMarshaller::Write(
+ const ::media::VideoDecoderConfig& config, MediaMessage* msg) {
+ CHECK(msg->WritePod(config.codec()));
+ CHECK(msg->WritePod(config.profile()));
+ CHECK(msg->WritePod(config.format()));
+ SizeMarshaller::Write(config.coded_size(), msg);
+ RectMarshaller::Write(config.visible_rect(), msg);
+ SizeMarshaller::Write(config.natural_size(), msg);
+ CHECK(msg->WritePod(config.is_encrypted()));
+ CHECK(msg->WritePod(config.extra_data_size()));
+ if (config.extra_data_size() > 0)
+ CHECK(msg->WriteBuffer(config.extra_data(), config.extra_data_size()));
+}
+
+// static
+::media::VideoDecoderConfig VideoDecoderConfigMarshaller::Read(
+ MediaMessage* msg) {
+ ::media::VideoCodec codec;
+ ::media::VideoCodecProfile profile;
+ ::media::VideoFrame::Format format;
+ gfx::Size coded_size;
+ gfx::Rect visible_rect;
+ gfx::Size natural_size;
+ bool is_encrypted;
+ size_t extra_data_size;
+ scoped_ptr<uint8[]> extra_data;
+
+ CHECK(msg->ReadPod(&codec));
+ CHECK(msg->ReadPod(&profile));
+ CHECK(msg->ReadPod(&format));
+ coded_size = SizeMarshaller::Read(msg);
+ visible_rect = RectMarshaller::Read(msg);
+ natural_size = SizeMarshaller::Read(msg);
+ CHECK(msg->ReadPod(&is_encrypted));
+ CHECK(msg->ReadPod(&extra_data_size));
+
+ CHECK_GE(codec, ::media::kUnknownVideoCodec);
+ CHECK_LE(codec, ::media::kVideoCodecMax);
+ CHECK_GE(profile, ::media::VIDEO_CODEC_PROFILE_UNKNOWN);
+ CHECK_LE(profile, ::media::VIDEO_CODEC_PROFILE_MAX);
+ CHECK_GE(format, ::media::VideoFrame::UNKNOWN);
+ CHECK_LE(format, ::media::VideoFrame::FORMAT_MAX);
+ CHECK_LT(extra_data_size, kMaxExtraDataSize);
+ if (extra_data_size > 0) {
+ extra_data.reset(new uint8[extra_data_size]);
+ CHECK(msg->ReadBuffer(extra_data.get(), extra_data_size));
+ }
+
+ return ::media::VideoDecoderConfig(
+ codec, profile, format,
+ coded_size, visible_rect, natural_size,
+ extra_data.get(), extra_data_size,
+ is_encrypted);
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromium/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h b/chromium/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h
new file mode 100644
index 00000000000..5ba7baec067
--- /dev/null
+++ b/chromium/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h
@@ -0,0 +1,27 @@
+// 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_IPC_STREAMER_VIDEO_DECODER_CONFIG_MARSHALLER_H_
+#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_VIDEO_DECODER_CONFIG_MARSHALLER_H_
+
+#include "media/base/video_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+class MediaMessage;
+
+class VideoDecoderConfigMarshaller {
+ public:
+ // Writes the serialized structure of |config| into |msg|.
+ static void Write(
+ const ::media::VideoDecoderConfig& config, MediaMessage* msg);
+
+ // Returns a VideoDecoderConfig from its serialized structure.
+ static ::media::VideoDecoderConfig Read(MediaMessage* msg);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_VIDEO_DECODER_CONFIG_MARSHALLER_H_
diff --git a/chromium/chromecast/media/media.gyp b/chromium/chromecast/media/media.gyp
new file mode 100644
index 00000000000..9eaf8d1be75
--- /dev/null
+++ b/chromium/chromecast/media/media.gyp
@@ -0,0 +1,208 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromecast_branding%': 'Chromium',
+ },
+ 'targets': [
+ {
+ 'target_name': 'media_base',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../crypto/crypto.gyp:crypto',
+ '../../third_party/widevine/cdm/widevine_cdm.gyp:widevine_cdm_version_h',
+ ],
+ 'sources': [
+ 'base/decrypt_context.cc',
+ 'base/decrypt_context.h',
+ 'base/decrypt_context_clearkey.cc',
+ 'base/decrypt_context_clearkey.h',
+ 'base/key_systems_common.cc',
+ 'base/key_systems_common.h',
+ ],
+ 'conditions': [
+ ['chromecast_branding=="Chrome"', {
+ 'dependencies': [
+ '<(cast_internal_gyp):media_base_internal',
+ ],
+ }, {
+ 'sources': [
+ 'base/key_systems_common_simple.cc',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'cma_base',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../chromecast.gyp:cast_base',
+ '../../base/base.gyp:base',
+ '../../media/media.gyp:media',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'cma/base/balanced_media_task_runner_factory.cc',
+ 'cma/base/balanced_media_task_runner_factory.h',
+ 'cma/base/buffering_controller.cc',
+ 'cma/base/buffering_controller.h',
+ 'cma/base/buffering_frame_provider.cc',
+ 'cma/base/buffering_frame_provider.h',
+ 'cma/base/buffering_state.cc',
+ 'cma/base/buffering_state.h',
+ 'cma/base/cma_logging.h',
+ 'cma/base/coded_frame_provider.cc',
+ 'cma/base/coded_frame_provider.h',
+ 'cma/base/decoder_buffer_adapter.cc',
+ 'cma/base/decoder_buffer_adapter.h',
+ 'cma/base/decoder_buffer_base.cc',
+ 'cma/base/decoder_buffer_base.h',
+ 'cma/base/media_task_runner.cc',
+ 'cma/base/media_task_runner.h',
+ ],
+ },
+ {
+ 'target_name': 'cma_backend',
+ 'type': '<(component)',
+ 'dependencies': [
+ 'cma_base',
+ 'media_base',
+ '../../base/base.gyp:base',
+ '../../media/media.gyp:media',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'cma/backend/audio_pipeline_device.cc',
+ 'cma/backend/audio_pipeline_device.h',
+ 'cma/backend/media_clock_device.cc',
+ 'cma/backend/media_clock_device.h',
+ 'cma/backend/media_component_device.cc',
+ 'cma/backend/media_component_device.h',
+ 'cma/backend/media_pipeline_device.cc',
+ 'cma/backend/media_pipeline_device.h',
+ 'cma/backend/media_pipeline_device_fake.cc',
+ 'cma/backend/media_pipeline_device_fake.h',
+ 'cma/backend/media_pipeline_device_params.cc',
+ 'cma/backend/media_pipeline_device_params.h',
+ 'cma/backend/video_pipeline_device.cc',
+ 'cma/backend/video_pipeline_device.h',
+ ],
+ 'conditions': [
+ ['chromecast_branding=="Chrome"', {
+ 'dependencies': [
+ '<(cast_internal_gyp):cma_backend_internal',
+ ],
+ }, {
+ 'sources': [
+ 'cma/backend/media_pipeline_device_fake_factory.cc',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'cma_ipc',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ ],
+ 'sources': [
+ 'cma/ipc/media_memory_chunk.cc',
+ 'cma/ipc/media_memory_chunk.h',
+ 'cma/ipc/media_message.cc',
+ 'cma/ipc/media_message.h',
+ 'cma/ipc/media_message_fifo.cc',
+ 'cma/ipc/media_message_fifo.h',
+ ],
+ },
+ {
+ 'target_name': 'cma_ipc_streamer',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../media/media.gyp:media',
+ 'cma_base',
+ ],
+ 'sources': [
+ 'cma/ipc_streamer/audio_decoder_config_marshaller.cc',
+ 'cma/ipc_streamer/audio_decoder_config_marshaller.h',
+ 'cma/ipc_streamer/av_streamer_proxy.cc',
+ 'cma/ipc_streamer/av_streamer_proxy.h',
+ 'cma/ipc_streamer/coded_frame_provider_host.cc',
+ 'cma/ipc_streamer/coded_frame_provider_host.h',
+ 'cma/ipc_streamer/decoder_buffer_base_marshaller.cc',
+ 'cma/ipc_streamer/decoder_buffer_base_marshaller.h',
+ 'cma/ipc_streamer/decrypt_config_marshaller.cc',
+ 'cma/ipc_streamer/decrypt_config_marshaller.h',
+ 'cma/ipc_streamer/video_decoder_config_marshaller.cc',
+ 'cma/ipc_streamer/video_decoder_config_marshaller.h',
+ ],
+ },
+ {
+ 'target_name': 'cma_filters',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../media/media.gyp:media',
+ 'cma_base',
+ ],
+ 'sources': [
+ 'cma/filters/demuxer_stream_adapter.cc',
+ 'cma/filters/demuxer_stream_adapter.h',
+ ],
+ },
+ {
+ 'target_name': 'cast_media',
+ 'type': 'none',
+ 'dependencies': [
+ 'cma_backend',
+ 'cma_base',
+ 'cma_filters',
+ 'cma_ipc',
+ 'cma_ipc_streamer',
+ ],
+ },
+ {
+ 'target_name': 'cast_media_unittests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ 'cast_media',
+ '../../base/base.gyp:base',
+ '../../base/base.gyp:base_i18n',
+ '../../base/base.gyp:test_support_base',
+ '../../chromecast/chromecast.gyp:cast_metrics_test_support',
+ '../../media/media.gyp:media_test_support',
+ '../../testing/gmock.gyp:gmock',
+ '../../testing/gtest.gyp:gtest',
+ '../../testing/gtest.gyp:gtest_main',
+ ],
+ 'sources': [
+ 'cma/backend/audio_video_pipeline_device_unittest.cc',
+ 'cma/base/balanced_media_task_runner_unittest.cc',
+ 'cma/base/buffering_controller_unittest.cc',
+ 'cma/base/buffering_frame_provider_unittest.cc',
+ 'cma/filters/demuxer_stream_adapter_unittest.cc',
+ 'cma/ipc/media_message_fifo_unittest.cc',
+ 'cma/ipc/media_message_unittest.cc',
+ 'cma/ipc_streamer/av_streamer_unittest.cc',
+ 'cma/test/frame_generator_for_test.cc',
+ 'cma/test/frame_generator_for_test.h',
+ 'cma/test/frame_segmenter_for_test.cc',
+ 'cma/test/frame_segmenter_for_test.h',
+ 'cma/test/media_component_device_feeder_for_test.cc',
+ 'cma/test/media_component_device_feeder_for_test.h',
+ 'cma/test/mock_frame_consumer.cc',
+ 'cma/test/mock_frame_consumer.h',
+ 'cma/test/mock_frame_provider.cc',
+ 'cma/test/mock_frame_provider.h',
+ 'cma/test/run_all_unittests.cc',
+ ],
+ },
+ ],
+}