diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-06-18 14:10:49 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2015-06-18 13:53:24 +0000 |
commit | 813fbf95af77a531c57a8c497345ad2c61d475b3 (patch) | |
tree | 821b2c8de8365f21b6c9ba17a236fb3006a1d506 /chromium/chromecast | |
parent | af6588f8d723931a298c995fa97259bb7f7deb55 (diff) | |
download | qtwebengine-chromium-813fbf95af77a531c57a8c497345ad2c61d475b3.tar.gz |
BASELINE: Update chromium to 44.0.2403.47
Change-Id: Ie056fedba95cf5e5c76b30c4b2c80fca4764aa2f
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/chromecast')
275 files changed, 13793 insertions, 1850 deletions
diff --git a/chromium/chromecast/BUILD.gn b/chromium/chromecast/BUILD.gn new file mode 100644 index 00000000000..6169cf86808 --- /dev/null +++ b/chromium/chromecast/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2015 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. + +import("//chromecast/chromecast.gni") + +config("config") { + defines = [] + + if (use_playready) { + defines += [ "PLAYREADY_CDM_AVAILABLE" ] + } +} + +component("chromecast") { + deps = [ + "//chromecast/base", + "//chromecast/base/metrics", + "//chromecast/media", + ] +} diff --git a/chromium/chromecast/DEPS b/chromium/chromecast/DEPS index d2783405985..eae26b0f640 100644 --- a/chromium/chromecast/DEPS +++ b/chromium/chromecast/DEPS @@ -5,6 +5,7 @@ include_rules = [ # sub-directory within chromecast/. "-chromecast", "+chromecast/base", + "+chromecast/public", # Other Chromecast-wide dependencies. "+content/public/common", diff --git a/chromium/chromecast/OWNERS b/chromium/chromecast/OWNERS index 45c91ccae95..470605e46e3 100644 --- a/chromium/chromecast/OWNERS +++ b/chromium/chromecast/OWNERS @@ -1,4 +1,3 @@ damienv@chromium.org gunsch@chromium.org -kolla@chromium.org lcwu@chromium.org diff --git a/chromium/chromecast/android/DEPS b/chromium/chromecast/android/DEPS index 9659c0589ed..df718c8e423 100644 --- a/chromium/chromecast/android/DEPS +++ b/chromium/chromecast/android/DEPS @@ -3,4 +3,5 @@ include_rules = [ "+chromecast/android", "+chromecast/browser/android", "+chromecast/crash/android", + "+components/external_video_surface", ] diff --git a/chromium/chromecast/app/cast_main_delegate.cc b/chromium/chromecast/app/cast_main_delegate.cc index 78e4d55e0c5..58c7507eaf3 100644 --- a/chromium/chromecast/app/cast_main_delegate.cc +++ b/chromium/chromecast/app/cast_main_delegate.cc @@ -4,16 +4,21 @@ #include "chromecast/app/cast_main_delegate.h" +#include <string> + #include "base/command_line.h" #include "base/cpu.h" +#include "base/lazy_instance.h" #include "base/logging.h" #include "base/path_service.h" #include "base/posix/global_descriptors.h" +#include "chromecast/base/cast_paths.h" #include "chromecast/browser/cast_content_browser_client.h" -#include "chromecast/common/cast_paths.h" #include "chromecast/common/cast_resource_delegate.h" #include "chromecast/common/global_descriptors.h" +#include "chromecast/crash/cast_crash_reporter_client.h" #include "chromecast/renderer/cast_content_renderer_client.h" +#include "components/crash/app/crash_reporter_client.h" #include "content/public/browser/browser_main_runner.h" #include "content/public/common/content_switches.h" #include "ui/base/resource/resource_bundle.h" @@ -22,6 +27,15 @@ #include "chromecast/crash/android/crash_handler.h" #endif // defined(OS_ANDROID) +namespace { + +#if !defined(OS_ANDROID) +base::LazyInstance<chromecast::CastCrashReporterClient>::Leaky + g_crash_reporter_client = LAZY_INSTANCE_INITIALIZER; +#endif // !defined(OS_ANDROID) + +} // namespace + namespace chromecast { namespace shell { @@ -69,6 +83,12 @@ void CastMainDelegate::PreSandboxStartup() { base::FilePath log_file; PathService::Get(FILE_CAST_ANDROID_LOG, &log_file); chromecast::CrashHandler::Initialize(process_type, log_file); +#else + crash_reporter::SetCrashReporterClient(g_crash_reporter_client.Pointer()); + + if (process_type != switches::kZygoteProcess) { + CastCrashReporterClient::InitCrashReporter(process_type); + } #endif // defined(OS_ANDROID) InitializeResourceBundle(); @@ -92,6 +112,10 @@ int CastMainDelegate::RunProcess( #if !defined(OS_ANDROID) void CastMainDelegate::ZygoteForked() { + const base::CommandLine* command_line(base::CommandLine::ForCurrentProcess()); + std::string process_type = + command_line->GetSwitchValueASCII(switches::kProcessType); + CastCrashReporterClient::InitCrashReporter(process_type); } #endif // !defined(OS_ANDROID) diff --git a/chromium/chromecast/app/cast_main_delegate.h b/chromium/chromecast/app/cast_main_delegate.h index d078655f69d..0fe77690126 100644 --- a/chromium/chromecast/app/cast_main_delegate.h +++ b/chromium/chromecast/app/cast_main_delegate.h @@ -26,20 +26,19 @@ class CastContentRendererClient; class CastMainDelegate : public content::ContentMainDelegate { public: CastMainDelegate(); - virtual ~CastMainDelegate(); + ~CastMainDelegate() override; // content::ContentMainDelegate implementation: - virtual bool BasicStartupComplete(int* exit_code) override; - virtual void PreSandboxStartup() override; - virtual int RunProcess( + bool BasicStartupComplete(int* exit_code) override; + void PreSandboxStartup() override; + int RunProcess( const std::string& process_type, const content::MainFunctionParams& main_function_params) override; #if !defined(OS_ANDROID) - virtual void ZygoteForked() override; + void ZygoteForked() override; #endif // !defined(OS_ANDROID) - virtual content::ContentBrowserClient* CreateContentBrowserClient() override; - virtual content::ContentRendererClient* - CreateContentRendererClient() override; + content::ContentBrowserClient* CreateContentBrowserClient() override; + content::ContentRendererClient* CreateContentRendererClient() override; private: void InitializeResourceBundle(); diff --git a/chromium/chromecast/app/resources/shell_devtools_discovery_page.html b/chromium/chromecast/app/resources/shell_devtools_discovery_page.html index 09bb9c438a8..f8d69d9523f 100644 --- a/chromium/chromecast/app/resources/shell_devtools_discovery_page.html +++ b/chromium/chromecast/app/resources/shell_devtools_discovery_page.html @@ -2,18 +2,6 @@ <head> <title>Cast shell remote debugging</title> <style> - .local-ui-link { - background-color: #eee; - border: 1px solid #ccc; - color: #333; - display: block; - font-family: monospace; - font-size: 11px; - margin: 4px; - padding: 4px; - width: 100%; - } - .help { font-size: 11px; } @@ -50,15 +38,6 @@ function appendItem(metadata) { frontend_link.textContent = 'Remote Debugging (AppEngine)' frontend_link.href = metadata.devtoolsFrontendUrl; item_container.appendChild(frontend_link); - - var devtools_protocol_link = document.createElement('textarea'); - devtools_protocol_link.className = 'local-ui-link'; - devtools_protocol_link.value = metadata.devtoolsFrontendUrl.replace( - "https://chrome-devtools-frontend.appspot.com", - "chrome-devtools://devtools/remote"); - // Highlight text when clicked. - devtools_protocol_link.onclick = function() { this.select(); } - item_container.appendChild(devtools_protocol_link); } else { frontend_header.textContent += " (already has active debugging session)"; } @@ -69,16 +48,9 @@ function appendItem(metadata) { <h3>Help</h3> <div id="help"> - Note: there are two debugging options presented for each page above. Either - is a valid way to initiate a remote debugging session. - <ul> - <li>For the first option (link), you may have to select the shield icon in - the address bar to establish a connection. See the <a - href="https://support.google.com/chrome/answer/1342714?hl=en">help - center</a> for more information.</li> - <li>For the second option (textarea), simply copy/paste the URL into - Chrome's URL bar.</li> - </ul> + You may have to select the shield icon in the address bar to establish a connection. + See the <a href="https://support.google.com/chrome/answer/1342714?hl=en">help + center</a> for more information. </div> </body> diff --git a/chromium/chromecast/base/BUILD.gn b/chromium/chromecast/base/BUILD.gn new file mode 100644 index 00000000000..b105a71926a --- /dev/null +++ b/chromium/chromecast/base/BUILD.gn @@ -0,0 +1,12 @@ +# Copyright 2015 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. + +source_set("base") { + sources = [ + "cast_paths.cc", + "cast_paths.h", + ] + + configs += [ "//chromecast:config" ] +} diff --git a/chromium/chromecast/common/cast_paths.cc b/chromium/chromecast/base/cast_paths.cc index d5d471ce97a..ce726848dcc 100644 --- a/chromium/chromecast/common/cast_paths.cc +++ b/chromium/chromecast/base/cast_paths.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromecast/common/cast_paths.h" +#include "chromecast/base/cast_paths.h" #include "base/base_paths.h" #include "base/files/file_path.h" diff --git a/chromium/chromecast/common/cast_paths.h b/chromium/chromecast/base/cast_paths.h index 5ab156c20fb..11433a407ef 100644 --- a/chromium/chromecast/common/cast_paths.h +++ b/chromium/chromecast/base/cast_paths.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMECAST_COMMON_CAST_PATHS_H_ -#define CHROMECAST_COMMON_CAST_PATHS_H_ +#ifndef CHROMECAST_BASE_CAST_PATHS_H_ +#define CHROMECAST_BASE_CAST_PATHS_H_ #include "build/build_config.h" @@ -31,4 +31,4 @@ void RegisterPathProvider(); } // namespace chromecast -#endif // CHROMECAST_COMMON_CAST_PATHS_H_ +#endif // CHROMECAST_BASE_CAST_PATHS_H_ diff --git a/chromium/chromecast/base/cast_sys_info_dummy.cc b/chromium/chromecast/base/cast_sys_info_dummy.cc new file mode 100644 index 00000000000..57e8beae188 --- /dev/null +++ b/chromium/chromecast/base/cast_sys_info_dummy.cc @@ -0,0 +1,79 @@ +// Copyright 2015 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/base/cast_sys_info_dummy.h" + +namespace chromecast { + +CastSysInfoDummy::CastSysInfoDummy() { +} + +CastSysInfoDummy::~CastSysInfoDummy() { +} + +CastSysInfo::BuildType CastSysInfoDummy::GetBuildType() { + return BUILD_ENG; +} + +std::string CastSysInfoDummy::GetSystemReleaseChannel() { + return ""; +} + +std::string CastSysInfoDummy::GetSerialNumber() { + return "dummy.serial.number"; +} + +std::string CastSysInfoDummy::GetProductName() { + return ""; +} + +std::string CastSysInfoDummy::GetDeviceModel() { + return ""; +} + +std::string CastSysInfoDummy::GetBoardName() { + return ""; +} + +std::string CastSysInfoDummy::GetBoardRevision() { + return ""; +} + +std::string CastSysInfoDummy::GetManufacturer() { + return ""; +} + +std::string CastSysInfoDummy::GetSystemBuildNumber() { + return ""; +} + +std::string CastSysInfoDummy::GetFactoryCountry() { + return "US"; +} + +std::string CastSysInfoDummy::GetFactoryLocale(std::string* second_locale) { + return "en-US"; +} + +std::string CastSysInfoDummy::GetWifiInterface() { + return ""; +} + +std::string CastSysInfoDummy::GetApInterface() { + return ""; +} + +std::string CastSysInfoDummy::GetGlVendor() { + return ""; +} + +std::string CastSysInfoDummy::GetGlRenderer() { + return ""; +} + +std::string CastSysInfoDummy::GetGlVersion() { + return ""; +} + +} // namespace chromecast diff --git a/chromium/chromecast/base/cast_sys_info_dummy.h b/chromium/chromecast/base/cast_sys_info_dummy.h new file mode 100644 index 00000000000..89a8b8beeb9 --- /dev/null +++ b/chromium/chromecast/base/cast_sys_info_dummy.h @@ -0,0 +1,42 @@ +// Copyright 2015 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_BASE_CAST_SYS_INFO_DUMMY_H_ +#define CHROMECAST_BASE_CAST_SYS_INFO_DUMMY_H_ + +#include "base/macros.h" +#include "chromecast/public/cast_sys_info.h" + +namespace chromecast { + +class CastSysInfoDummy : public CastSysInfo { + public: + CastSysInfoDummy(); + ~CastSysInfoDummy() override; + + // CastSysInfo implementation: + BuildType GetBuildType() override; + std::string GetSystemReleaseChannel() override; + std::string GetSerialNumber() override; + std::string GetProductName() override; + std::string GetDeviceModel() override; + std::string GetBoardName() override; + std::string GetBoardRevision() override; + std::string GetManufacturer() override; + std::string GetSystemBuildNumber() override; + std::string GetFactoryCountry() override; + std::string GetFactoryLocale(std::string* second_locale) override; + std::string GetWifiInterface() override; + std::string GetApInterface() override; + std::string GetGlVendor() override; + std::string GetGlRenderer() override; + std::string GetGlVersion() override; + + private: + DISALLOW_COPY_AND_ASSIGN(CastSysInfoDummy); +}; + +} // namespace chromecast + +#endif // CHROMECAST_BASE_CAST_SYS_INFO_DUMMY_H_ diff --git a/chromium/chromecast/base/cast_sys_info_util.h b/chromium/chromecast/base/cast_sys_info_util.h new file mode 100644 index 00000000000..05ca069d655 --- /dev/null +++ b/chromium/chromecast/base/cast_sys_info_util.h @@ -0,0 +1,21 @@ +// Copyright 2015 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_BASE_CAST_SYS_INFO_UTIL_H_ +#define CHROMECAST_BASE_CAST_SYS_INFO_UTIL_H_ + +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" + +namespace chromecast { + +class CastSysInfo; + +scoped_ptr<CastSysInfo> CreateSysInfo(); + +} // namespace chromecast + +#endif // CHROMECAST_BASE_CAST_SYS_INFO_UTIL_H_ diff --git a/chromium/chromecast/base/cast_sys_info_util_simple.cc b/chromium/chromecast/base/cast_sys_info_util_simple.cc new file mode 100644 index 00000000000..68ab9dffb2c --- /dev/null +++ b/chromium/chromecast/base/cast_sys_info_util_simple.cc @@ -0,0 +1,16 @@ +// Copyright 2015 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/base/cast_sys_info_util.h" + +#include "chromecast/base/cast_sys_info_dummy.h" + +namespace chromecast { + +// static +scoped_ptr<CastSysInfo> CreateSysInfo() { + return make_scoped_ptr(new CastSysInfoDummy()); +} + +} // namespace chromecast diff --git a/chromium/chromecast/base/metrics/BUILD.gn b/chromium/chromecast/base/metrics/BUILD.gn new file mode 100644 index 00000000000..7ed81a562fe --- /dev/null +++ b/chromium/chromecast/base/metrics/BUILD.gn @@ -0,0 +1,35 @@ +# Copyright 2015 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. + +source_set("metrics") { + sources = [ + "cast_histograms.h", + "cast_metrics_helper.cc", + "cast_metrics_helper.h", + "grouped_histogram.cc", + "grouped_histogram.h", + ] + + deps = [ + "//chromecast/base", + ] + + configs += [ "//chromecast:config" ] +} + +source_set("test_support") { + testonly = true + + sources = [ + "cast_metrics_test_helper.cc", + "cast_metrics_test_helper.h", + ] + + deps = [ + ":metrics", + "//chromecast/base", + ] + + configs += [ "//chromecast:config" ] +} diff --git a/chromium/chromecast/base/metrics/cast_histograms.h b/chromium/chromecast/base/metrics/cast_histograms.h index 4907c1a84ef..19cb98e7cfb 100644 --- a/chromium/chromecast/base/metrics/cast_histograms.h +++ b/chromium/chromecast/base/metrics/cast_histograms.h @@ -12,15 +12,14 @@ // through base::subtle::Release_Store() and base::subtle::Acquire_Load(). // If the histogram name changes between subsequent calls, use this non-cached // version that always calls histogram_factory_get_invocation. -#define STATIC_HISTOGRAM_POINTER_BLOCK_NO_CACHE( \ - constant_histogram_name, \ - histogram_add_method_invocation, \ - histogram_factory_get_invocation) \ - do { \ +#define STATIC_HISTOGRAM_POINTER_BLOCK_NO_CACHE( \ + constant_histogram_name, histogram_add_method_invocation, \ + histogram_factory_get_invocation) \ + do { \ base::HistogramBase* histogram_pointer = histogram_factory_get_invocation; \ - if (DCHECK_IS_ON) \ - histogram_pointer->CheckName(constant_histogram_name); \ - histogram_pointer->histogram_add_method_invocation; \ + if (DCHECK_IS_ON()) \ + histogram_pointer->CheckName(constant_histogram_name); \ + histogram_pointer->histogram_add_method_invocation; \ } while (0) #define UMA_HISTOGRAM_CUSTOM_TIMES_NO_CACHE( \ @@ -40,10 +39,4 @@ base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ boundary_value + 1, base::Histogram::kUmaTargetedHistogramFlag)) -#define UMA_HISTOGRAM_ENUMERATION_COUNT_NO_CACHE(name, sample, count, \ - boundary_value) \ - STATIC_HISTOGRAM_POINTER_BLOCK_NO_CACHE(name, AddCount(sample, count), \ - base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ - boundary_value + 1, base::HistogramBase::kUmaTargetedHistogramFlag)) - #endif // CHROMECAST_BASE_METRICS_CAST_HISTOGRAMS_H_ diff --git a/chromium/chromecast/base/metrics/cast_metrics_helper.cc b/chromium/chromecast/base/metrics/cast_metrics_helper.cc index e4923552e1f..85cdfe8931e 100644 --- a/chromium/chromecast/base/metrics/cast_metrics_helper.cc +++ b/chromium/chromecast/base/metrics/cast_metrics_helper.cc @@ -10,7 +10,10 @@ #include "base/message_loop/message_loop_proxy.h" #include "base/metrics/histogram.h" #include "base/metrics/user_metrics.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" #include "chromecast/base/metrics/cast_histograms.h" +#include "chromecast/base/metrics/grouped_histogram.h" namespace chromecast { namespace metrics { @@ -35,8 +38,55 @@ const int kDisplayedFramesPerSecondPeriod = 1000000; // Sample every 5 seconds, represented in microseconds. const int kNominalVideoSamplePeriod = 5000000; +const char kMetricsNameAppInfoDelimiter = '#'; + } // namespace +// static + +// NOTE(gfhuang): This is a hacky way to encode/decode app infos into a +// string. Mainly because it's hard to add another metrics serialization type +// into components/metrics/serialization/. +// static +bool CastMetricsHelper::DecodeAppInfoFromMetricsName( + const std::string& metrics_name, + std::string* action_name, + std::string* app_id, + std::string* session_id, + std::string* sdk_version) { + DCHECK(action_name); + DCHECK(app_id); + DCHECK(session_id); + DCHECK(sdk_version); + if (metrics_name.find(kMetricsNameAppInfoDelimiter) == std::string::npos) + return false; + + std::vector<std::string> tokens; + base::SplitString(metrics_name, kMetricsNameAppInfoDelimiter, &tokens); + DCHECK_EQ(tokens.size(), 4u); + // The order of tokens should match EncodeAppInfoIntoMetricsName(). + *action_name = tokens[0]; + *app_id = tokens[1]; + *session_id = tokens[2]; + *sdk_version = tokens[3]; + return true; +} + +// static +std::string CastMetricsHelper::EncodeAppInfoIntoMetricsName( + const std::string& action_name, + const std::string& app_id, + const std::string& session_id, + const std::string& sdk_version) { + std::vector<std::string> parts; + parts.push_back(action_name); + parts.push_back(app_id); + parts.push_back(session_id); + parts.push_back(sdk_version); + return JoinString(parts, kMetricsNameAppInfoDelimiter); +} + +// static CastMetricsHelper* CastMetricsHelper::GetInstance() { DCHECK(g_instance); return g_instance; @@ -45,7 +95,8 @@ CastMetricsHelper* CastMetricsHelper::GetInstance() { CastMetricsHelper::CastMetricsHelper( scoped_refptr<base::MessageLoopProxy> message_loop_proxy) : message_loop_proxy_(message_loop_proxy), - metrics_sink_(NULL) { + metrics_sink_(NULL), + record_action_callback_(base::Bind(&base::RecordComputedAction)) { DCHECK(message_loop_proxy_.get()); DCHECK(!g_instance); g_instance = this; @@ -62,21 +113,49 @@ CastMetricsHelper::~CastMetricsHelper() { g_instance = NULL; } -void CastMetricsHelper::TagAppStart(const std::string& arg_app_name) { - MAKE_SURE_THREAD(TagAppStart, arg_app_name); - app_name_ = arg_app_name; +void CastMetricsHelper::UpdateCurrentAppInfo(const std::string& app_id, + const std::string& session_id) { + MAKE_SURE_THREAD(UpdateCurrentAppInfo, app_id, session_id); + app_id_ = app_id; + session_id_ = session_id; app_start_time_ = base::TimeTicks::Now(); new_startup_time_ = true; + TagAppStartForGroupedHistograms(app_id_); + sdk_version_.clear(); +} + +void CastMetricsHelper::UpdateSDKInfo(const std::string& sdk_version) { + MAKE_SURE_THREAD(UpdateSDKInfo, sdk_version); + sdk_version_ = sdk_version; } void CastMetricsHelper::LogMediaPlay() { MAKE_SURE_THREAD(LogMediaPlay); - base::RecordComputedAction(GetMetricsNameWithAppName("MediaPlay", "")); + RecordSimpleAction(EncodeAppInfoIntoMetricsName( + "MediaPlay", + app_id_, + session_id_, + sdk_version_)); } void CastMetricsHelper::LogMediaPause() { MAKE_SURE_THREAD(LogMediaPause); - base::RecordComputedAction(GetMetricsNameWithAppName("MediaPause", "")); + RecordSimpleAction(EncodeAppInfoIntoMetricsName( + "MediaPause", + app_id_, + session_id_, + sdk_version_)); +} + +void CastMetricsHelper::LogTimeToFirstPaint() { + MAKE_SURE_THREAD(LogTimeToFirstPaint); + if (app_id_.empty()) + return; + base::TimeDelta launch_time = base::TimeTicks::Now() - app_start_time_; + const std::string uma_name(GetMetricsNameWithAppName("Startup", + "TimeToFirstPaint")); + LogMediumTimeHistogramEvent(uma_name, launch_time); + LOG(INFO) << uma_name << " is " << launch_time.InSecondsF() << " seconds."; } void CastMetricsHelper::LogTimeToDisplayVideo() { @@ -174,10 +253,10 @@ std::string CastMetricsHelper::GetMetricsNameWithAppName( const std::string& suffix) const { DCHECK(message_loop_proxy_->BelongsToCurrentThread()); std::string metrics_name(prefix); - if (!app_name_.empty()) { + if (!app_id_.empty()) { if (!metrics_name.empty()) metrics_name.push_back('.'); - metrics_name.append(app_name_); + metrics_name.append(app_id_); } if (!suffix.empty()) { if (!metrics_name.empty()) @@ -192,6 +271,22 @@ void CastMetricsHelper::SetMetricsSink(MetricsSink* delegate) { metrics_sink_ = delegate; } +void CastMetricsHelper::SetRecordActionCallback( + const RecordActionCallback& callback) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + record_action_callback_ = callback; +} + +void CastMetricsHelper::RecordSimpleAction(const std::string& action) { + MAKE_SURE_THREAD(RecordSimpleAction, action); + + if (metrics_sink_) { + metrics_sink_->OnAction(action); + } else { + record_action_callback_.Run(action); + } +} + void CastMetricsHelper::LogEnumerationHistogramEvent( const std::string& name, int value, int num_buckets) { MAKE_SURE_THREAD(LogEnumerationHistogramEvent, name, value, num_buckets); diff --git a/chromium/chromecast/base/metrics/cast_metrics_helper.h b/chromium/chromecast/base/metrics/cast_metrics_helper.h index 4f06a46e1c0..144d9dc3b32 100644 --- a/chromium/chromecast/base/metrics/cast_metrics_helper.h +++ b/chromium/chromecast/base/metrics/cast_metrics_helper.h @@ -7,6 +7,7 @@ #include <string> +#include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/time/time.h" @@ -28,10 +29,13 @@ class CastMetricsHelper { kAbortedBuffering, }; + typedef base::Callback<void(const std::string&)> RecordActionCallback; + class MetricsSink { public: virtual ~MetricsSink() {} + virtual void OnAction(const std::string& action) = 0; virtual void OnEnumerationEvent(const std::string& name, int value, int num_buckets) = 0; virtual void OnTimeEvent(const std::string& name, @@ -41,19 +45,40 @@ class CastMetricsHelper { int num_buckets) = 0; }; + // Decodes action_name/app_id/session_id/sdk_version from metrics name. + // Return false if the metrics name is not generated from + // EncodeAppInfoIntoMetricsName() with correct format. + static bool DecodeAppInfoFromMetricsName( + const std::string& metrics_name, + std::string* action_name, + std::string* app_id, + std::string* session_id, + std::string* sdk_version); + static CastMetricsHelper* GetInstance(); explicit CastMetricsHelper( scoped_refptr<base::MessageLoopProxy> message_loop_proxy); virtual ~CastMetricsHelper(); - // This function stores the name and startup time of the active application. - virtual void TagAppStart(const std::string& app_name); + // This function updates the info and stores the startup time of the current + // active application + virtual void UpdateCurrentAppInfo(const std::string& app_id, + const std::string& session_id); + // This function updates the sdk version of the current active application + virtual void UpdateSDKInfo(const std::string& sdk_version); // Logs UMA record for media play/pause user actions. virtual void LogMediaPlay(); virtual void LogMediaPause(); + // Logs a simple UMA user action. + // This is used as an in-place replacement of content::RecordComputedAction(). + virtual void RecordSimpleAction(const std::string& action); + + // Logs UMA record of the time the app made its first paint. + virtual void LogTimeToFirstPaint(); + // Logs UMA record of the elapsed time from the app launch // to the time first video frame is displayed. virtual void LogTimeToDisplayVideo(); @@ -81,6 +106,11 @@ class CastMetricsHelper { // Caller retains ownership of MetricsSink. virtual void SetMetricsSink(MetricsSink* delegate); + // Sets a default callback to record user action when MetricsSink is not set. + // This function could be called multiple times (in unittests), and + // CastMetricsHelper only honors the last one. + virtual void SetRecordActionCallback(const RecordActionCallback& callback); + protected: // Creates a CastMetricsHelper instance with no MessageLoopProxy. This should // only be used by tests, since invoking any non-overridden methods on this @@ -88,6 +118,12 @@ class CastMetricsHelper { CastMetricsHelper(); private: + static std::string EncodeAppInfoIntoMetricsName( + const std::string& action_name, + const std::string& app_id, + const std::string& session_id, + const std::string& sdk_version); + void LogEnumerationHistogramEvent(const std::string& name, int value, int num_buckets); void LogTimeHistogramEvent(const std::string& name, @@ -103,8 +139,10 @@ class CastMetricsHelper { // Start time of the most recent app. base::TimeTicks app_start_time_; - // Currently running app name. Used to construct histogram name. - std::string app_name_; + // Currently running app id. Used to construct histogram name. + std::string app_id_; + std::string session_id_; + std::string sdk_version_; // Whether a new app start time has been stored but not recorded. // After the startup time has been used to generate an UMA event, @@ -114,6 +152,8 @@ class CastMetricsHelper { base::TimeTicks previous_video_stat_sample_time_; MetricsSink* metrics_sink_; + // Default RecordAction callback when metrics_sink_ is not set. + RecordActionCallback record_action_callback_; DISALLOW_COPY_AND_ASSIGN(CastMetricsHelper); }; diff --git a/chromium/chromecast/base/metrics/cast_metrics_test_helper.cc b/chromium/chromecast/base/metrics/cast_metrics_test_helper.cc index 6aa2a86a6c8..c78abe10409 100644 --- a/chromium/chromecast/base/metrics/cast_metrics_test_helper.cc +++ b/chromium/chromecast/base/metrics/cast_metrics_test_helper.cc @@ -16,21 +16,23 @@ namespace { class CastMetricsHelperStub : public CastMetricsHelper { public: CastMetricsHelperStub(); - virtual ~CastMetricsHelperStub(); - - virtual void TagAppStart(const std::string& arg_app_name) override; - virtual void LogMediaPlay() override; - virtual void LogMediaPause() override; - virtual void LogTimeToDisplayVideo() override; - virtual void LogTimeToBufferAv(BufferingType buffering_type, - base::TimeDelta time) override; - virtual void ResetVideoFrameSampling() override; - virtual void LogFramesPer5Seconds( + ~CastMetricsHelperStub() override; + + void UpdateCurrentAppInfo(const std::string& app_id, + const std::string& session_id) override; + void UpdateSDKInfo(const std::string& sdk_version) override; + void LogMediaPlay() override; + void LogMediaPause() override; + void LogTimeToDisplayVideo() override; + void LogTimeToBufferAv(BufferingType buffering_type, + base::TimeDelta time) override; + void ResetVideoFrameSampling() override; + void LogFramesPer5Seconds( int displayed_frames, int dropped_frames, int delayed_frames, int error_frames) override; - virtual std::string GetMetricsNameWithAppName( + std::string GetMetricsNameWithAppName( const std::string& prefix, const std::string& suffix) const override; - virtual void SetMetricsSink(MetricsSink* delegate) override; + void SetMetricsSink(MetricsSink* delegate) override; private: DISALLOW_COPY_AND_ASSIGN(CastMetricsHelperStub); @@ -49,7 +51,12 @@ CastMetricsHelperStub::~CastMetricsHelperStub() { stub_instance_exists = false; } -void CastMetricsHelperStub::TagAppStart(const std::string& arg_app_name) { +void CastMetricsHelperStub::UpdateCurrentAppInfo( + const std::string& app_id, + const std::string& session_id) { +} + +void CastMetricsHelperStub::UpdateSDKInfo(const std::string& sdk_version) { } void CastMetricsHelperStub::LogMediaPlay() { diff --git a/chromium/chromecast/base/metrics/grouped_histogram.cc b/chromium/chromecast/base/metrics/grouped_histogram.cc new file mode 100644 index 00000000000..83188506694 --- /dev/null +++ b/chromium/chromecast/base/metrics/grouped_histogram.cc @@ -0,0 +1,181 @@ +// 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/base/metrics/grouped_histogram.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/metrics/histogram.h" +#include "base/metrics/statistics_recorder.h" +#include "base/strings/stringprintf.h" +#include "base/synchronization/lock.h" +#include "base/time/time.h" + +namespace chromecast { +namespace metrics { + +namespace { + +const char kAppNameErrorNoApp[] = "ERROR_NO_APP_REGISTERED"; + +// Current app name guarded by lock. +struct CurrentAppNameWithLock { + base::Lock lock; + std::string app_name; +}; + +base::LazyInstance<CurrentAppNameWithLock> g_current_app = + LAZY_INSTANCE_INITIALIZER; + +std::string GetAppName() { + base::AutoLock lock(g_current_app.Get().lock); + const std::string& app_name = g_current_app.Get().app_name; + return app_name.empty() ? kAppNameErrorNoApp : app_name; +} + +struct HistogramArgs { + const char* name; + int minimum; + int maximum; + size_t bucket_count; +}; + +// List of metrics to collect using a GroupedHistogram. +// +// When adding more Histograms to this list, find the source of the +// Histogram and look for the construction arguments it uses to add it in. +const HistogramArgs kHistogramsToGroup[] = { + { + "DNS.TotalTime", + 1, + 1000 * 60 * 60, + 100, + }, + { + "Net.DNS_Resolution_And_TCP_Connection_Latency2", + 1, + 1000 * 60 * 10, + 100, + }, + { + "Net.SSL_Connection_Latency2", + 1, + 1000 * 60, + 100, + }, + { + "Net.TCP_Connection_Latency", + 1, + 1000 * 60 * 10, + 100, + }, + { + "Net.HttpJob.TotalTime", + 1, + 1000 * 10, + 50, + }, +}; + +// This class is used to override a Histogram to generate per-app metrics. +// It intercepts calls to Add() for a given metric and generates new metrics +// of the form "<metric-name>.<app-name>". +class GroupedHistogram : public base::Histogram { + public: + GroupedHistogram(const std::string& metric_to_override, + Sample minimum, + Sample maximum, + const base::BucketRanges* ranges) + : Histogram(metric_to_override, minimum, maximum, ranges), + metric_to_group_(metric_to_override), + minimum_(minimum), + maximum_(maximum), + bucket_count_(ranges->bucket_count()) { + } + + ~GroupedHistogram() override { + } + + // base::Histogram implementation: + void Add(Sample value) override { + Histogram::Add(value); + std::string name(base::StringPrintf("%s.%s", + histogram_name().c_str(), + GetAppName().c_str())); + HistogramBase* grouped_histogram = + base::Histogram::FactoryGet(name, + minimum_, + maximum_, + bucket_count_, + flags()); + DCHECK(grouped_histogram); + grouped_histogram->Add(value); + } + + private: + // Saved construction arguments for reconstructing the Histogram later (with + // a suffixed app name). + std::string metric_to_group_; + Sample minimum_; + Sample maximum_; + size_t bucket_count_; + + DISALLOW_COPY_AND_ASSIGN(GroupedHistogram); +}; + +// Registers a GroupedHistogram with StatisticsRecorder. Must be called +// before any Histogram of the same name has been used. +// It acts similarly to Histogram::FactoryGet but checks that +// the histogram is being newly created and does not already exist. +void PreregisterHistogram(const std::string& name, + GroupedHistogram::Sample minimum, + GroupedHistogram::Sample maximum, + size_t bucket_count, + int32 flags) { + DCHECK(base::StatisticsRecorder::IsActive()); + DCHECK(base::Histogram::InspectConstructionArguments( + name, &minimum, &maximum, &bucket_count)); + DCHECK(!base::StatisticsRecorder::FindHistogram(name)) + << "Failed to preregister " << name << ", Histogram already exists."; + + // To avoid racy destruction at shutdown, the following will be leaked. + base::BucketRanges* ranges = new base::BucketRanges(bucket_count + 1); + base::Histogram::InitializeBucketRanges(minimum, maximum, ranges); + const base::BucketRanges* registered_ranges = + base::StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); + + GroupedHistogram* tentative_histogram = + new GroupedHistogram(name, minimum, maximum, registered_ranges); + + tentative_histogram->SetFlags(flags); + base::HistogramBase* histogram = + base::StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); + + DCHECK_EQ(histogram, tentative_histogram); + DCHECK_EQ(base::HISTOGRAM, histogram->GetHistogramType()); + DCHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count)); +} + +} // namespace + +void PreregisterAllGroupedHistograms() { + base::StatisticsRecorder::Initialize(); + for (size_t i = 0; i < arraysize(kHistogramsToGroup); ++i) { + PreregisterHistogram( + kHistogramsToGroup[i].name, + kHistogramsToGroup[i].minimum, + kHistogramsToGroup[i].maximum, + kHistogramsToGroup[i].bucket_count, + base::HistogramBase::kUmaTargetedHistogramFlag); + } +} + +void TagAppStartForGroupedHistograms(const std::string& app_name) { + base::AutoLock lock(g_current_app.Get().lock); + g_current_app.Get().app_name = app_name; +} + +} // namespace metrics +} // namespace chromecast diff --git a/chromium/chromecast/base/metrics/grouped_histogram.h b/chromium/chromecast/base/metrics/grouped_histogram.h new file mode 100644 index 00000000000..b5202e621f1 --- /dev/null +++ b/chromium/chromecast/base/metrics/grouped_histogram.h @@ -0,0 +1,25 @@ +// 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_BASE_METRICS_GROUPED_HISTOGRAM_H_ +#define CHROMECAST_BASE_METRICS_GROUPED_HISTOGRAM_H_ + +#include <string> + +namespace chromecast { +namespace metrics { + +// Registers a predefined list of histograms to be collected per-app. Must be +// called before any histograms of the same name are used or registration will +// fail. +void PreregisterAllGroupedHistograms(); + +// Sets the current app name to be used for subsequent grouped histogram +// samples (a new metric is generated with the app name as a suffix). +void TagAppStartForGroupedHistograms(const std::string& app_name); + +} // namespace metrics +} // namespace chromecast + +#endif // CHROMECAST_BASE_METRICS_GROUPED_HISTOGRAM_H_ diff --git a/chromium/chromecast/base/serializers.cc b/chromium/chromecast/base/serializers.cc new file mode 100644 index 00000000000..366a5d2f59b --- /dev/null +++ b/chromium/chromecast/base/serializers.cc @@ -0,0 +1,33 @@ +// Copyright 2015 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/base/serializers.h" + +#include "base/json/json_string_value_serializer.h" +#include "base/logging.h" + +namespace chromecast { + +scoped_ptr<base::Value> DeserializeFromJson(const std::string& text) { + JSONStringValueDeserializer deserializer(text); + + int error_code; + std::string error_msg; + scoped_ptr<base::Value> value( + deserializer.Deserialize(&error_code, &error_msg)); + DLOG_IF(ERROR, !value) << "JSON error " << error_code << ":" << error_msg; + + // Value will hold the nullptr in case of an error. + return value.Pass(); +} + +scoped_ptr<std::string> SerializeToJson(const base::Value& value) { + scoped_ptr<std::string> json_str(new std::string()); + JSONStringValueSerializer serializer(json_str.get()); + if (!serializer.Serialize(value)) + json_str.reset(nullptr); + return json_str.Pass(); +} + +} // namespace chromecast diff --git a/chromium/chromecast/base/serializers.h b/chromium/chromecast/base/serializers.h new file mode 100644 index 00000000000..df278fdbc0f --- /dev/null +++ b/chromium/chromecast/base/serializers.h @@ -0,0 +1,29 @@ +// Copyright 2015 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_BASE_SERIALIZERS_H_ +#define CHROMECAST_BASE_SERIALIZERS_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" + +namespace base { +class Value; +} + +namespace chromecast { + +// Helper function which deserializes JSON |text| into a base::Value. If |text| +// is empty, is not valid JSON, or if some other deserialization error occurs, +// the return value will hold the NULL pointer. +scoped_ptr<base::Value> DeserializeFromJson(const std::string& text); + +// Helper function which serializes |value| into a JSON string. If a +// serialization error occurs,the return value will hold the NULL pointer. +scoped_ptr<std::string> SerializeToJson(const base::Value& value); + +} // namespace chromecast + +#endif // CHROMECAST_BASE_SERIALIZERS_H_ diff --git a/chromium/chromecast/base/serializers_unittest.cc b/chromium/chromecast/base/serializers_unittest.cc new file mode 100644 index 00000000000..e82d0dc321a --- /dev/null +++ b/chromium/chromecast/base/serializers_unittest.cc @@ -0,0 +1,76 @@ +// Copyright 2015 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/values.h" +#include "chromecast/base/serializers.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { +namespace { +const char kEmptyJsonString[] = "{}"; +const char kProperJsonString[] = + "{\n" + " \"compound\": {\n" + " \"a\": 1,\n" + " \"b\": 2\n" + " },\n" + " \"some_String\": \"1337\",\n" + " \"some_int\": 42,\n" + " \"the_list\": [ \"val1\", \"val2\" ]\n" + "}\n"; +const char kPoorlyFormedJsonString[] = "{\"key\":"; +const char kTestKey[] = "test_key"; +const char kTestValue[] = "test_value"; + +} // namespace + +TEST(DeserializeFromJson, EmptyString) { + std::string str; + scoped_ptr<base::Value> value = DeserializeFromJson(str); + EXPECT_EQ(nullptr, value.get()); +} + +TEST(DeserializeFromJson, EmptyJsonObject) { + std::string str = kEmptyJsonString; + scoped_ptr<base::Value> value = DeserializeFromJson(str); + EXPECT_NE(nullptr, value.get()); +} + +TEST(DeserializeFromJson, ProperJsonObject) { + std::string str = kProperJsonString; + scoped_ptr<base::Value> value = DeserializeFromJson(str); + EXPECT_NE(nullptr, value.get()); +} + +TEST(DeserializeFromJson, PoorlyFormedJsonObject) { + std::string str = kPoorlyFormedJsonString; + scoped_ptr<base::Value> value = DeserializeFromJson(str); + EXPECT_EQ(nullptr, value.get()); +} + +TEST(SerializeToJson, BadValue) { + base::BinaryValue value(scoped_ptr<char[]>(new char[12]), 12); + scoped_ptr<std::string> str = SerializeToJson(value); + EXPECT_EQ(nullptr, str.get()); +} + +TEST(SerializeToJson, EmptyValue) { + base::DictionaryValue value; + scoped_ptr<std::string> str = SerializeToJson(value); + ASSERT_NE(nullptr, str.get()); + EXPECT_EQ(kEmptyJsonString, *str); +} + +TEST(SerializeToJson, PopulatedValue) { + base::DictionaryValue orig_value; + orig_value.SetString(kTestKey, kTestValue); + scoped_ptr<std::string> str = SerializeToJson(orig_value); + ASSERT_NE(nullptr, str.get()); + + scoped_ptr<base::Value> new_value = DeserializeFromJson(*str); + ASSERT_NE(nullptr, new_value.get()); + EXPECT_TRUE(new_value->Equals(&orig_value)); +} + +} // namespace chromecast diff --git a/chromium/chromecast/browser/DEPS b/chromium/chromecast/browser/DEPS index fcbae35b8d6..4efe5cf0160 100644 --- a/chromium/chromecast/browser/DEPS +++ b/chromium/chromecast/browser/DEPS @@ -3,7 +3,14 @@ include_rules = [ # embedder and can include from all other chromecast/ directories. "+cc/base/switches.h", "+chromecast", + "+components/cdm/browser", "+components/crash", + "+components/devtools_discovery", + "+components/devtools_http_handler", + "+components/external_video_surface", + "+components/network_hints/browser", "+content/public/browser", + "+gin/v8_initializer.h", + "+media/audio", "+media/base", ] diff --git a/chromium/chromecast/browser/android/apk/AndroidManifest.xml b/chromium/chromecast/browser/android/apk/AndroidManifest.xml deleted file mode 100644 index 8dc8d978ed5..00000000000 --- a/chromium/chromecast/browser/android/apk/AndroidManifest.xml +++ /dev/null @@ -1,141 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- 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. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.chromium.chromecast.shell"> - - <permission android:name="org.chromium.chromecast.shell.permission.SANDBOX" - android:protectionLevel="signature" /> - - <application android:name="org.chromium.chromecast.shell.CastApplication" - android:icon="@mipmap/app_icon" - android:label="@string/app_name"> - <activity android:name="org.chromium.chromecast.shell.CastShellActivity" - android:theme="@style/CastShellTheme" - android:exported="true" - android:hardwareAccelerated="true" - android:taskAffinity=".CastShellActivity" - android:configChanges="orientation|keyboardHidden|keyboard|screenSize" - android:excludeFromRecents="true" - android:noHistory="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> - </intent-filter> - </activity> - - <!-- The following service entries exist in order to allow us to - start more than one sandboxed process. --> - <service android:name="org.chromium.content.app.SandboxedProcessService0" - android:process=":sandboxed_process0" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService1" - android:process=":sandboxed_process1" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService2" - android:process=":sandboxed_process2" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService3" - android:process=":sandboxed_process3" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService4" - android:process=":sandboxed_process4" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService5" - android:process=":sandboxed_process5" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService6" - android:process=":sandboxed_process6" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService7" - android:process=":sandboxed_process7" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService8" - android:process=":sandboxed_process8" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService9" - android:process=":sandboxed_process9" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService10" - android:process=":sandboxed_process10" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService11" - android:process=":sandboxed_process11" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService12" - android:process=":sandboxed_process12" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService13" - android:process=":sandboxed_process13" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService14" - android:process=":sandboxed_process14" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService15" - android:process=":sandboxed_process15" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService16" - android:process=":sandboxed_process16" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService17" - android:process=":sandboxed_process17" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService18" - android:process=":sandboxed_process18" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - <service android:name="org.chromium.content.app.SandboxedProcessService19" - android:process=":sandboxed_process19" - android:permission="org.chromium.chromecast.shell.permission.SANDBOX" - android:isolatedProcess="true" - android:exported="false" /> - </application> - - <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> - <uses-permission android:name="android.permission.INTERNET"/> - <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> - <uses-permission android:name="android.permission.WAKE_LOCK"/> - -</manifest> diff --git a/chromium/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 b/chromium/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 new file mode 100644 index 00000000000..7b02275ffcf --- /dev/null +++ b/chromium/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.chromium.chromecast.shell"> + + <application android:name="org.chromium.chromecast.shell.CastApplication" + android:icon="@mipmap/app_icon" + android:label="@string/app_name"> + <activity android:name="org.chromium.chromecast.shell.CastShellActivity" + android:theme="@style/CastShellTheme" + android:exported="true" + android:hardwareAccelerated="true" + android:taskAffinity=".CastShellActivity" + android:configChanges="orientation|keyboardHidden|keyboard|screenSize" + android:excludeFromRecents="true" + android:noHistory="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <!-- The following service entries exist in order to allow us to + start more than one sandboxed process. --> + {% set num_sandboxed_services = 20 %} + <meta-data android:name="org.chromium.content.browser.NUM_SANDBOXED_SERVICES" + android:value="{{ num_sandboxed_services }}"/> + {% for i in range(num_sandboxed_services) %} + <service android:name="org.chromium.content.app.SandboxedProcessService{{ i }}" + android:process=":sandboxed_process{{ i }}" + android:isolatedProcess="true" + android:exported="false" /> + {% endfor %} + + {% set num_privileged_services = 3 %} + <meta-data android:name="org.chromium.content.browser.NUM_PRIVILEGED_SERVICES" + android:value="{{ num_privileged_services }}"/> + {% for i in range(num_privileged_services) %} + <service android:name="org.chromium.content.app.PrivilegedProcessService{{ i }}" + android:process=":privileged_process{{ i }}" + android:isolatedProcess="false" + android:exported="false" /> + {% endfor %} + </application> + + <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> + <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> + <uses-permission android:name="android.permission.WAKE_LOCK"/> + +</manifest> diff --git a/chromium/chromecast/browser/android/cast_window_android.cc b/chromium/chromecast/browser/android/cast_window_android.cc index ef279802cfe..7c2e3547b30 100644 --- a/chromium/chromecast/browser/android/cast_window_android.cc +++ b/chromium/chromecast/browser/android/cast_window_android.cc @@ -6,12 +6,16 @@ #include "base/message_loop/message_loop_proxy.h" #include "chromecast/browser/android/cast_window_manager.h" +#include "chromecast/browser/cast_content_window.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" #include "content/public/common/renderer_preferences.h" #include "jni/CastWindowAndroid_jni.h" +#include "ui/gfx/skia_util.h" namespace chromecast { namespace shell { @@ -29,67 +33,67 @@ bool CastWindowAndroid::RegisterJni(JNIEnv* env) { return RegisterNativesImpl(env); } -CastWindowAndroid::CastWindowAndroid(content::WebContents* web_contents) - : content::WebContentsObserver(web_contents), - weak_factory_(this) { -} - -CastWindowAndroid::~CastWindowAndroid() { -} - // static CastWindowAndroid* CastWindowAndroid::CreateNewWindow( content::BrowserContext* browser_context, const GURL& url) { - content::WebContents::CreateParams create_params(browser_context); - create_params.routing_id = MSG_ROUTING_NONE; - content::WebContents* web_contents = - content::WebContents::Create(create_params); - CastWindowAndroid* shell = CreateCastWindowAndroid( - web_contents, - create_params.initial_size); + CastWindowAndroid* window_android = new CastWindowAndroid(browser_context); + window_android->Initialize(); + if (!url.is_empty()) - shell->LoadURL(url); - return shell; + window_android->LoadURL(url); + + content::RenderWidgetHostView* rwhv = + window_android->web_contents_->GetRenderWidgetHostView(); + if (rwhv) { + rwhv->SetBackgroundColor(SK_ColorBLACK); + } + + return window_android; } -// static -CastWindowAndroid* CastWindowAndroid::CreateCastWindowAndroid( - content::WebContents* web_contents, - const gfx::Size& initial_size) { - CastWindowAndroid* shell = new CastWindowAndroid(web_contents); +CastWindowAndroid::CastWindowAndroid(content::BrowserContext* browser_context) + : browser_context_(browser_context), + content_window_(new CastContentWindow), + weak_factory_(this) { +} - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::ScopedJavaLocalRef<jobject> shell_android( - CreateCastWindowView(shell)); +void CastWindowAndroid::Initialize() { + web_contents_ = + content_window_->CreateWebContents(gfx::Size(), browser_context_); + web_contents_->SetDelegate(this); + content::WebContentsObserver::Observe(web_contents_.get()); - shell->java_object_.Reset(env, shell_android.Release()); - shell->web_contents_.reset(web_contents); - web_contents->SetDelegate(shell); + JNIEnv* env = base::android::AttachCurrentThread(); + window_java_.Reset(CreateCastWindowView(this)); Java_CastWindowAndroid_initFromNativeWebContents( - env, shell->java_object_.obj(), reinterpret_cast<jint>(web_contents)); + env, window_java_.obj(), web_contents_->GetJavaWebContents().obj(), + web_contents_->GetRenderProcessHost()->GetID()); // Enabling hole-punching also requires runtime renderer preference - web_contents->GetMutableRendererPrefs()-> - use_video_overlay_for_embedded_encrypted_video = true; - web_contents->GetRenderViewHost()->SyncRendererPrefs(); + content::RendererPreferences* prefs = + web_contents_->GetMutableRendererPrefs(); + prefs->use_video_overlay_for_embedded_encrypted_video = true; + prefs->use_view_overlay_for_all_video = true; + web_contents_->GetRenderViewHost()->SyncRendererPrefs(); +} - return shell; +CastWindowAndroid::~CastWindowAndroid() { } void CastWindowAndroid::Close() { // Close page first, which fires the window.unload event. The WebContents // itself will be destroyed after browser-process has received renderer // notification that the page is closed. - web_contents_->GetRenderViewHost()->ClosePage(); + web_contents_->ClosePage(); } void CastWindowAndroid::Destroy() { // Note: if multiple windows becomes supported, this may close other devtools // sessions. content::DevToolsAgentHost::DetachAllClients(); - CloseCastWindowView(java_object_.obj()); + CloseCastWindowView(window_java_.obj()); delete this; } @@ -105,7 +109,7 @@ void CastWindowAndroid::LoadURL(const GURL& url) { void CastWindowAndroid::AddNewContents(content::WebContents* source, content::WebContents* new_contents, WindowOpenDisposition disposition, - const gfx::Rect& initial_pos, + const gfx::Rect& initial_rect, bool user_gesture, bool* was_blocked) { NOTIMPLEMENTED(); @@ -121,20 +125,6 @@ void CastWindowAndroid::CloseContents(content::WebContents* source) { // give (and guarantee) the renderer enough time to finish 'onunload' // handler (but we don't want to wait any longer than that to delay the // starting of next app). - - if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) { - // When shutting down in a test context, the last remaining WebContents - // is torn down at browser-thread shutdown time. Call Destroy directly to - // avoid losing the last posted task to delete this object. - // TODO(gunsch): This could probably be avoided by using a - // CompletionCallback in StopCurrentApp to wait until the app is completely - // stopped. This might require a separate message loop and might only be - // appropriate for test contexts or during shutdown, since it triggers a - // wait on the main thread. - Destroy(); - return; - } - base::MessageLoopProxy::current()->PostDelayedTask( FROM_HERE, base::Bind(&CastWindowAndroid::Destroy, weak_factory_.GetWeakPtr()), diff --git a/chromium/chromecast/browser/android/cast_window_android.h b/chromium/chromecast/browser/android/cast_window_android.h index fec8b39e1fd..7c5cd7415f1 100644 --- a/chromium/chromecast/browser/android/cast_window_android.h +++ b/chromium/chromecast/browser/android/cast_window_android.h @@ -21,8 +21,8 @@ #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/content_switches.h" +#include "ui/gfx/geometry/size.h" #include "ui/gfx/native_widget_types.h" -#include "ui/gfx/size.h" class GURL; @@ -33,6 +33,7 @@ class WebContents; } // namespace content namespace chromecast { +class CastContentWindow; namespace shell { class CastWindowAndroid : public content::WebContentsDelegate, @@ -43,7 +44,7 @@ class CastWindowAndroid : public content::WebContentsDelegate, content::BrowserContext* browser_context, const GURL& url); - virtual ~CastWindowAndroid(); + ~CastWindowAndroid() override; void LoadURL(const GURL& url); // Calls RVH::ClosePage() and waits for acknowledgement before closing/ @@ -56,35 +57,33 @@ class CastWindowAndroid : public content::WebContentsDelegate, static bool RegisterJni(JNIEnv* env); // content::WebContentsDelegate implementation: - virtual void AddNewContents(content::WebContents* source, - content::WebContents* new_contents, - WindowOpenDisposition disposition, - const gfx::Rect& initial_pos, - bool user_gesture, - bool* was_blocked) override; - virtual void CloseContents(content::WebContents* source) override; - virtual bool CanOverscrollContent() const override; - virtual bool AddMessageToConsole(content::WebContents* source, - int32 level, - const base::string16& message, - int32 line_no, - const base::string16& source_id) override; - virtual void ActivateContents(content::WebContents* contents) override; - virtual void DeactivateContents(content::WebContents* contents) override; + void AddNewContents(content::WebContents* source, + content::WebContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture, + bool* was_blocked) override; + void CloseContents(content::WebContents* source) override; + bool CanOverscrollContent() const override; + bool AddMessageToConsole(content::WebContents* source, + int32 level, + const base::string16& message, + int32 line_no, + const base::string16& source_id) override; + void ActivateContents(content::WebContents* contents) override; + void DeactivateContents(content::WebContents* contents) override; // content::WebContentsObserver implementation: - virtual void RenderProcessGone(base::TerminationStatus status) override; + void RenderProcessGone(base::TerminationStatus status) override; private: - explicit CastWindowAndroid(content::WebContents* web_contents); + explicit CastWindowAndroid(content::BrowserContext* browser_context); + void Initialize(); - // Helper to create a new CastWindowAndroid given a newly created WebContents. - static CastWindowAndroid* CreateCastWindowAndroid( - content::WebContents* web_contents, - const gfx::Size& initial_size); - - base::android::ScopedJavaGlobalRef<jobject> java_object_; + content::BrowserContext* browser_context_; + base::android::ScopedJavaGlobalRef<jobject> window_java_; scoped_ptr<content::WebContents> web_contents_; + scoped_ptr<CastContentWindow> content_window_; base::WeakPtrFactory<CastWindowAndroid> weak_factory_; diff --git a/chromium/chromecast/browser/android/cast_window_manager.cc b/chromium/chromecast/browser/android/cast_window_manager.cc index 814731caafd..01fe689b3d5 100644 --- a/chromium/chromecast/browser/android/cast_window_manager.cc +++ b/chromium/chromecast/browser/android/cast_window_manager.cc @@ -11,12 +11,12 @@ #include "base/android/scoped_java_ref.h" #include "base/bind.h" #include "base/lazy_instance.h" +#include "base/prefs/pref_service.h" #include "chromecast/browser/android/cast_window_android.h" #include "chromecast/browser/cast_browser_context.h" #include "chromecast/browser/cast_browser_main_parts.h" #include "chromecast/browser/cast_browser_process.h" #include "chromecast/browser/cast_content_browser_client.h" -#include "chromecast/common/chromecast_config.h" #include "chromecast/common/pref_names.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" @@ -80,7 +80,7 @@ void EnableDevTools(JNIEnv* env, jclass clazz, jboolean enable) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // The specific port value doesn't matter since Android uses Unix domain // sockets, only whether or not it is zero. - chromecast::ChromecastConfig::GetInstance()->pref_service()-> + CastBrowserProcess::GetInstance()->pref_service()-> SetInteger(prefs::kRemoteDebuggingPort, enable ? 1 : 0); } diff --git a/chromium/chromecast/browser/android/external_video_surface_container_impl.cc b/chromium/chromecast/browser/android/external_video_surface_container_impl.cc deleted file mode 100644 index 5dbb144cbae..00000000000 --- a/chromium/chromecast/browser/android/external_video_surface_container_impl.cc +++ /dev/null @@ -1,91 +0,0 @@ -// 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/browser/android/external_video_surface_container_impl.h" - -#include "base/android/jni_android.h" -#include "content/public/browser/android/content_view_core.h" -#include "jni/ExternalVideoSurfaceContainer_jni.h" -#include "ui/gfx/rect_f.h" - -namespace chromecast { -namespace shell { - -ExternalVideoSurfaceContainerImpl::ExternalVideoSurfaceContainerImpl( - content::WebContents* web_contents) { - content::ContentViewCore* cvc = - content::ContentViewCore::FromWebContents(web_contents); - if (cvc) { - JNIEnv* env = base::android::AttachCurrentThread(); - jobject_.Reset( - Java_ExternalVideoSurfaceContainer_create( - env, reinterpret_cast<intptr_t>(this), cvc->GetJavaObject().obj())); - } -} - -ExternalVideoSurfaceContainerImpl::~ExternalVideoSurfaceContainerImpl() { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_ExternalVideoSurfaceContainer_destroy(env, jobject_.obj()); - jobject_.Reset(); -} - -void ExternalVideoSurfaceContainerImpl::RequestExternalVideoSurface( - int player_id, - const SurfaceCreatedCB& surface_created_cb, - const SurfaceDestroyedCB& surface_destroyed_cb) { - surface_created_cb_ = surface_created_cb; - surface_destroyed_cb_ = surface_destroyed_cb; - - JNIEnv* env = base::android::AttachCurrentThread(); - Java_ExternalVideoSurfaceContainer_requestExternalVideoSurface( - env, jobject_.obj(), static_cast<jint>(player_id)); -} - -void ExternalVideoSurfaceContainerImpl::ReleaseExternalVideoSurface( - int player_id) { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_ExternalVideoSurfaceContainer_releaseExternalVideoSurface( - env, jobject_.obj(), static_cast<jint>(player_id)); - - surface_created_cb_.Reset(); - surface_destroyed_cb_.Reset(); -} - -void ExternalVideoSurfaceContainerImpl::OnFrameInfoUpdated() { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_ExternalVideoSurfaceContainer_onFrameInfoUpdated(env, jobject_.obj()); -} - -void ExternalVideoSurfaceContainerImpl::OnExternalVideoSurfacePositionChanged( - int player_id, const gfx::RectF& rect) { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_ExternalVideoSurfaceContainer_onExternalVideoSurfacePositionChanged( - env, - jobject_.obj(), - static_cast<jint>(player_id), - static_cast<jfloat>(rect.x()), - static_cast<jfloat>(rect.y()), - static_cast<jfloat>(rect.x() + rect.width()), - static_cast<jfloat>(rect.y() + rect.height())); -} - -// Methods called from Java. -void ExternalVideoSurfaceContainerImpl::SurfaceCreated( - JNIEnv* env, jobject obj, jint player_id, jobject jsurface) { - if (!surface_created_cb_.is_null()) - surface_created_cb_.Run(static_cast<int>(player_id), jsurface); -} - -void ExternalVideoSurfaceContainerImpl::SurfaceDestroyed( - JNIEnv* env, jobject obj, jint player_id) { - if (!surface_destroyed_cb_.is_null()) - surface_destroyed_cb_.Run(static_cast<int>(player_id)); -} - -bool RegisterExternalVideoSurfaceContainer(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -} // namespace shell -} // namespace chromecast diff --git a/chromium/chromecast/browser/android/external_video_surface_container_impl.h b/chromium/chromecast/browser/android/external_video_surface_container_impl.h deleted file mode 100644 index b05b1882960..00000000000 --- a/chromium/chromecast/browser/android/external_video_surface_container_impl.h +++ /dev/null @@ -1,57 +0,0 @@ -// 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_BROWSER_ANDROID_EXTERNAL_VIDEO_SURFACE_CONTAINER_IMPL_H_ -#define CHROMECAST_BROWSER_ANDROID_EXTERNAL_VIDEO_SURFACE_CONTAINER_IMPL_H_ - -#include <jni.h> - -#include "base/android/scoped_java_ref.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "content/public/browser/android/external_video_surface_container.h" - -namespace chromecast { -namespace shell { - -class ExternalVideoSurfaceContainerImpl - : public content::ExternalVideoSurfaceContainer { - public: - typedef base::Callback<void(int, jobject)> SurfaceCreatedCB; - typedef base::Callback<void(int)> SurfaceDestroyedCB; - - ExternalVideoSurfaceContainerImpl(content::WebContents* contents); - - // ExternalVideoSurfaceContainer implementation. - virtual void RequestExternalVideoSurface( - int player_id, - const SurfaceCreatedCB& surface_created_cb, - const SurfaceDestroyedCB& surface_destroyed_cb) override; - virtual void ReleaseExternalVideoSurface(int player_id) override; - virtual void OnFrameInfoUpdated() override; - virtual void OnExternalVideoSurfacePositionChanged( - int player_id, const gfx::RectF& rect) override; - - // Methods called from Java. - void SurfaceCreated( - JNIEnv* env, jobject obj, jint player_id, jobject jsurface); - void SurfaceDestroyed(JNIEnv* env, jobject obj, jint player_id); - - private: - virtual ~ExternalVideoSurfaceContainerImpl(); - - base::android::ScopedJavaGlobalRef<jobject> jobject_; - - SurfaceCreatedCB surface_created_cb_; - SurfaceDestroyedCB surface_destroyed_cb_; - - DISALLOW_COPY_AND_ASSIGN(ExternalVideoSurfaceContainerImpl); -}; - -bool RegisterExternalVideoSurfaceContainer(JNIEnv* env); - -} // namespace shell -} // namespace chromecast - -#endif // CHROMECAST_BROWSER_ANDROID_EXTERNAL_VIDEO_SURFACE_CONTAINER_IMPL_H_ diff --git a/chromium/chromecast/browser/cast_browser_context.cc b/chromium/chromecast/browser/cast_browser_context.cc index ed19a05ef1f..51a9fc02550 100644 --- a/chromium/chromecast/browser/cast_browser_context.cc +++ b/chromium/chromecast/browser/cast_browser_context.cc @@ -8,9 +8,10 @@ #include "base/files/file_util.h" #include "base/macros.h" #include "base/path_service.h" +#include "chromecast/base/cast_paths.h" #include "chromecast/browser/cast_download_manager_delegate.h" +#include "chromecast/browser/cast_permission_manager.h" #include "chromecast/browser/url_request_context_factory.h" -#include "chromecast/common/cast_paths.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_context.h" #include "content/public/browser/storage_partition.h" @@ -27,15 +28,15 @@ class CastBrowserContext::CastResourceContext : explicit CastResourceContext( URLRequestContextFactory* url_request_context_factory) : url_request_context_factory_(url_request_context_factory) {} - virtual ~CastResourceContext() {} + ~CastResourceContext() override {} // ResourceContext implementation: - virtual net::HostResolver* GetHostResolver() override { + net::HostResolver* GetHostResolver() override { return url_request_context_factory_->GetMainGetter()-> GetURLRequestContext()->host_resolver(); } - virtual net::URLRequestContext* GetRequestContext() override { + net::URLRequestContext* GetRequestContext() override { return url_request_context_factory_->GetMainGetter()-> GetURLRequestContext(); } @@ -77,6 +78,12 @@ void CastBrowserContext::InitWhileIOAllowed() { #endif // defined(OS_ANDROID) } +scoped_ptr<content::ZoomLevelDelegate> +CastBrowserContext::CreateZoomLevelDelegate( + const base::FilePath& partition_path) { + return nullptr; +} + base::FilePath CastBrowserContext::GetPath() const { return path_; } @@ -110,6 +117,10 @@ CastBrowserContext::GetMediaRequestContextForStoragePartition( return GetMediaRequestContext(); } +net::URLRequestContextGetter* CastBrowserContext::GetSystemRequestContext() { + return url_request_context_factory_->GetSystemGetter(); +} + content::ResourceContext* CastBrowserContext::GetResourceContext() { return resource_context_.get(); } @@ -120,23 +131,25 @@ CastBrowserContext::GetDownloadManagerDelegate() { } content::BrowserPluginGuestManager* CastBrowserContext::GetGuestManager() { - NOTIMPLEMENTED(); - return NULL; + return nullptr; } storage::SpecialStoragePolicy* CastBrowserContext::GetSpecialStoragePolicy() { - NOTIMPLEMENTED(); - return NULL; + return nullptr; } content::PushMessagingService* CastBrowserContext::GetPushMessagingService() { - NOTIMPLEMENTED(); - return NULL; + return nullptr; } content::SSLHostStateDelegate* CastBrowserContext::GetSSLHostStateDelegate() { - NOTIMPLEMENTED(); - return NULL; + return nullptr; +} + +content::PermissionManager* CastBrowserContext::GetPermissionManager() { + if (!permission_manager_.get()) + permission_manager_.reset(new CastPermissionManager()); + return permission_manager_.get(); } } // namespace shell diff --git a/chromium/chromecast/browser/cast_browser_context.h b/chromium/chromecast/browser/cast_browser_context.h index 931d886fd13..ae0cf13002e 100644 --- a/chromium/chromecast/browser/cast_browser_context.h +++ b/chromium/chromecast/browser/cast_browser_context.h @@ -23,28 +23,31 @@ class CastBrowserContext : public content::BrowserContext { public: explicit CastBrowserContext( URLRequestContextFactory* url_request_context_factory); - virtual ~CastBrowserContext(); + ~CastBrowserContext() override; // BrowserContext implementation: - virtual base::FilePath GetPath() const override; - virtual bool IsOffTheRecord() const override; - virtual net::URLRequestContextGetter* GetRequestContext() override; - virtual net::URLRequestContextGetter* GetRequestContextForRenderProcess( + scoped_ptr<content::ZoomLevelDelegate> CreateZoomLevelDelegate( + const base::FilePath& partition_path) override; + base::FilePath GetPath() const override; + bool IsOffTheRecord() const override; + net::URLRequestContextGetter* GetRequestContext() override; + net::URLRequestContextGetter* GetRequestContextForRenderProcess( int renderer_child_id) override; - virtual net::URLRequestContextGetter* GetMediaRequestContext() override; - virtual net::URLRequestContextGetter* GetMediaRequestContextForRenderProcess( + net::URLRequestContextGetter* GetMediaRequestContext() override; + net::URLRequestContextGetter* GetMediaRequestContextForRenderProcess( int renderer_child_id) override; - virtual net::URLRequestContextGetter* - GetMediaRequestContextForStoragePartition( - const base::FilePath& partition_path, - bool in_memory) override; - virtual content::ResourceContext* GetResourceContext() override; - virtual content::DownloadManagerDelegate* - GetDownloadManagerDelegate() override; - virtual content::BrowserPluginGuestManager* GetGuestManager() override; - virtual storage::SpecialStoragePolicy* GetSpecialStoragePolicy() override; - virtual content::PushMessagingService* GetPushMessagingService() override; - virtual content::SSLHostStateDelegate* GetSSLHostStateDelegate() override; + net::URLRequestContextGetter* GetMediaRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory) override; + content::ResourceContext* GetResourceContext() override; + content::DownloadManagerDelegate* GetDownloadManagerDelegate() override; + content::BrowserPluginGuestManager* GetGuestManager() override; + storage::SpecialStoragePolicy* GetSpecialStoragePolicy() override; + content::PushMessagingService* GetPushMessagingService() override; + content::SSLHostStateDelegate* GetSSLHostStateDelegate() override; + content::PermissionManager* GetPermissionManager() override; + + net::URLRequestContextGetter* GetSystemRequestContext(); private: class CastResourceContext; @@ -57,6 +60,7 @@ class CastBrowserContext : public content::BrowserContext { base::FilePath path_; scoped_ptr<CastResourceContext> resource_context_; scoped_ptr<CastDownloadManagerDelegate> download_manager_delegate_; + scoped_ptr<content::PermissionManager> permission_manager_; DISALLOW_COPY_AND_ASSIGN(CastBrowserContext); }; diff --git a/chromium/chromecast/browser/cast_browser_main_parts.cc b/chromium/chromecast/browser/cast_browser_main_parts.cc index cb072003646..91b13cef9f7 100644 --- a/chromium/chromecast/browser/cast_browser_main_parts.cc +++ b/chromium/chromecast/browser/cast_browser_main_parts.cc @@ -4,32 +4,149 @@ #include "chromecast/browser/cast_browser_main_parts.h" +#include <string> +#if !defined(OS_ANDROID) +#include <signal.h> +#include <sys/prctl.h> +#endif + #include "base/command_line.h" +#include "base/files/file_util.h" #include "base/message_loop/message_loop.h" +#include "base/path_service.h" #include "base/prefs/pref_registry_simple.h" +#include "base/run_loop.h" #include "cc/base/switches.h" +#include "chromecast/base/cast_paths.h" +#include "chromecast/base/cast_sys_info_util.h" #include "chromecast/base/metrics/cast_metrics_helper.h" +#include "chromecast/base/metrics/grouped_histogram.h" #include "chromecast/browser/cast_browser_context.h" #include "chromecast/browser/cast_browser_process.h" #include "chromecast/browser/devtools/remote_debugging_server.h" #include "chromecast/browser/metrics/cast_metrics_prefs.h" #include "chromecast/browser/metrics/cast_metrics_service_client.h" +#include "chromecast/browser/pref_service_helper.h" #include "chromecast/browser/service/cast_service.h" #include "chromecast/browser/url_request_context_factory.h" -#include "chromecast/browser/webui/webui_cast.h" -#include "chromecast/common/chromecast_config.h" +#include "chromecast/common/chromecast_switches.h" #include "chromecast/common/platform_client_auth.h" -#include "chromecast/net/network_change_notifier_cast.h" -#include "chromecast/net/network_change_notifier_factory_cast.h" +#include "chromecast/media/base/key_systems_common.h" +#include "chromecast/net/connectivity_checker.h" +#include "chromecast/public/cast_media_shlib.h" +#include "chromecast/public/cast_sys_info.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/gpu_data_manager.h" #include "content/public/common/content_switches.h" +#include "media/audio/audio_manager.h" +#include "media/audio/audio_manager_factory.h" +#include "media/base/browser_cdm_factory.h" #include "media/base/media_switches.h" #if defined(OS_ANDROID) +#include "chromecast/browser/media/cast_media_client_android.h" #include "chromecast/crash/android/crash_handler.h" #include "components/crash/browser/crash_dump_manager_android.h" +#include "media/base/android/media_client_android.h" #include "net/android/network_change_notifier_factory_android.h" -#endif // defined(OS_ANDROID) +#else +#include "chromecast/browser/media/cast_browser_cdm_factory.h" +#include "chromecast/net/network_change_notifier_factory_cast.h" +#endif + +#if defined(USE_AURA) +#include "ui/aura/env.h" +#include "ui/aura/test/test_screen.h" +#include "ui/gfx/screen.h" +#endif + +namespace { + +#if !defined(OS_ANDROID) +int kSignalsToRunClosure[] = { SIGTERM, SIGINT, }; + +// Closure to run on SIGTERM and SIGINT. +base::Closure* g_signal_closure = NULL; + +void RunClosureOnSignal(int signum) { + LOG(ERROR) << "Got signal " << signum; + DCHECK(g_signal_closure); + // Expect main thread got this signal. Otherwise, weak_ptr of run_loop will + // crash the process. + g_signal_closure->Run(); +} + +void RegisterClosureOnSignal(const base::Closure& closure) { + DCHECK(!g_signal_closure); + DCHECK_GT(arraysize(kSignalsToRunClosure), 0U); + + // Allow memory leak by intention. + g_signal_closure = new base::Closure(closure); + + struct sigaction sa_new; + memset(&sa_new, 0, sizeof(sa_new)); + sa_new.sa_handler = RunClosureOnSignal; + sigfillset(&sa_new.sa_mask); + sa_new.sa_flags = SA_RESTART; + + for (size_t i = 0; i < arraysize(kSignalsToRunClosure); i++) { + struct sigaction sa_old; + if (sigaction(kSignalsToRunClosure[i], &sa_new, &sa_old) == -1) { + NOTREACHED(); + } else { + DCHECK_EQ(sa_old.sa_handler, SIG_DFL); + } + } + + // Get the first signal to exit when the parent process dies. + prctl(PR_SET_PDEATHSIG, kSignalsToRunClosure[0]); +} + +const int kKillOnAlarmTimeoutSec = 5; // 5 seconds + +void KillOnAlarm(int signum) { + LOG(ERROR) << "Got alarm signal for termination: " << signum; + raise(SIGKILL); +} + +void RegisterKillOnAlarm(int timeout_seconds) { + struct sigaction sa_new; + memset(&sa_new, 0, sizeof(sa_new)); + sa_new.sa_handler = KillOnAlarm; + sigfillset(&sa_new.sa_mask); + sa_new.sa_flags = SA_RESTART; + + struct sigaction sa_old; + if (sigaction(SIGALRM, &sa_new, &sa_old) == -1) { + NOTREACHED(); + } else { + DCHECK_EQ(sa_old.sa_handler, SIG_DFL); + } + + if (alarm(timeout_seconds) > 0) + NOTREACHED() << "Previous alarm() was cancelled"; +} + +void DeregisterKillOnAlarm() { + // Explicitly cancel any outstanding alarm() calls. + alarm(0); + + struct sigaction sa_new; + memset(&sa_new, 0, sizeof(sa_new)); + sa_new.sa_handler = SIG_DFL; + sigfillset(&sa_new.sa_mask); + sa_new.sa_flags = SA_RESTART; + + struct sigaction sa_old; + if (sigaction(SIGALRM, &sa_new, &sa_old) == -1) { + NOTREACHED(); + } else { + DCHECK_EQ(sa_old.sa_handler, KillOnAlarm); + } +} +#endif // !defined(OS_ANDROID) + +} // namespace namespace chromecast { namespace shell { @@ -43,28 +160,34 @@ struct DefaultCommandLineSwitch { DefaultCommandLineSwitch g_default_switches[] = { #if defined(OS_ANDROID) - { switches::kMediaDrmEnableNonCompositing, ""}, + // Disables Chromecast-specific WiFi-related features on ATV for now. + { switches::kNoWifi, "" }, { switches::kEnableOverlayFullscreenVideo, ""}, { switches::kDisableInfobarForProtectedMediaIdentifier, ""}, { switches::kDisableGestureRequirementForMediaPlayback, ""}, - { switches::kForceUseOverlayEmbeddedVideo, ""}, #endif - { switches::kDisableApplicationCache, "" }, - { switches::kDisablePlugins, "" }, // Always enable HTMLMediaElement logs. { switches::kBlinkPlatformLogChannels, "Media"}, -#if defined(OS_LINUX) && defined(ARCH_CPU_X86_FAMILY) +#if defined(DISABLE_DISPLAY) + { switches::kDisableGpu, "" }, +#endif +#if defined(OS_LINUX) +#if defined(ARCH_CPU_X86_FAMILY) // This is needed for now to enable the egltest Ozone platform to work with // current Linux/NVidia OpenGL drivers. { switches::kIgnoreGpuBlacklist, ""}, - // TODO(gusfernandez): This is needed to fix a bug with - // glPostSubBufferCHROMIUM (crbug.com/429200) - { cc::switches::kUIDisablePartialSwap, ""}, +#elif defined(ARCH_CPU_ARM_FAMILY) && !defined(DISABLE_DISPLAY) + // On Linux arm, enable CMA pipeline by default. + { switches::kEnableCmaMediaPipeline, "" }, #endif +#endif // defined(OS_LINUX) + // Needed to fix a bug where the raster thread doesn't get scheduled for a + // substantial time (~5 seconds). See https://crbug.com/441895. + { switches::kUseNormalPriorityForTileTaskWorkerThreads, "" }, { NULL, NULL }, // Termination }; -void AddDefaultCommandLineSwitches(CommandLine* command_line) { +void AddDefaultCommandLineSwitches(base::CommandLine* command_line) { int i = 0; while (g_default_switches[i].switch_name != NULL) { command_line->AppendSwitchASCII( @@ -78,12 +201,14 @@ void AddDefaultCommandLineSwitches(CommandLine* command_line) { CastBrowserMainParts::CastBrowserMainParts( const content::MainFunctionParams& parameters, - URLRequestContextFactory* url_request_context_factory) + URLRequestContextFactory* url_request_context_factory, + scoped_ptr<::media::AudioManagerFactory> audio_manager_factory) : BrowserMainParts(), cast_browser_process_(new CastBrowserProcess()), parameters_(parameters), - url_request_context_factory_(url_request_context_factory) { - CommandLine* command_line = CommandLine::ForCurrentProcess(); + url_request_context_factory_(url_request_context_factory), + audio_manager_factory_(audio_manager_factory.Pass()) { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); AddDefaultCommandLineSwitches(command_line); } @@ -91,6 +216,17 @@ CastBrowserMainParts::~CastBrowserMainParts() { } void CastBrowserMainParts::PreMainMessageLoopStart() { + // GroupedHistograms needs to be initialized before any threads are created + // to prevent race conditions between calls to Preregister and those threads + // attempting to collect metrics. + // This call must also be before NetworkChangeNotifier, as it generates + // Net/DNS metrics. + metrics::PreregisterAllGroupedHistograms(); + + // Set the platform's implementation of AudioManagerFactory. + if (audio_manager_factory_) + ::media::AudioManager::SetFactory(audio_manager_factory_.release()); + #if defined(OS_ANDROID) net::NetworkChangeNotifier::SetFactory( new net::NetworkChangeNotifierFactoryAndroid()); @@ -101,8 +237,8 @@ void CastBrowserMainParts::PreMainMessageLoopStart() { } void CastBrowserMainParts::PostMainMessageLoopStart() { - cast_browser_process_->SetMetricsHelper( - new metrics::CastMetricsHelper(base::MessageLoopProxy::current())); + cast_browser_process_->SetMetricsHelper(make_scoped_ptr( + new metrics::CastMetricsHelper(base::MessageLoopProxy::current()))); #if defined(OS_ANDROID) base::MessageLoopForUI::current()->Start(); @@ -110,66 +246,143 @@ void CastBrowserMainParts::PostMainMessageLoopStart() { } int CastBrowserMainParts::PreCreateThreads() { - PrefRegistrySimple* pref_registry = new PrefRegistrySimple(); - metrics::RegisterPrefs(pref_registry); - ChromecastConfig::Create(pref_registry); +#if defined(OS_ANDROID) + // GPU process is started immediately after threads are created, requiring + // CrashDumpManager to be initialized beforehand. + base::FilePath crash_dumps_dir; + if (!chromecast::CrashHandler::GetCrashDumpLocation(&crash_dumps_dir)) { + LOG(ERROR) << "Could not find crash dump location."; + } + cast_browser_process_->SetCrashDumpManager( + make_scoped_ptr(new breakpad::CrashDumpManager(crash_dumps_dir))); +#else + base::FilePath home_dir; + CHECK(PathService::Get(DIR_CAST_HOME, &home_dir)); + if (!base::CreateDirectory(home_dir)) + return 1; +#endif + +#if defined(USE_AURA) + // Screen can (and should) exist even with no displays connected. Its presence + // is assumed as an interface to access display information, e.g. from metrics + // code. See CastContentWindow::CreateWindowTree for update when resolution + // is available. + DCHECK(!gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE)); + gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, + aura::TestScreen::Create(gfx::Size(0, 0))); +#endif return 0; } void CastBrowserMainParts::PreMainMessageLoopRun() { + // Set GL strings so GPU config code can make correct feature blacklisting/ + // whitelisting decisions. + // Note: SetGLStrings MUST be called after GpuDataManager::Initialize. + scoped_ptr<CastSysInfo> sys_info = CreateSysInfo(); + content::GpuDataManager::GetInstance()->SetGLStrings( + sys_info->GetGlVendor(), sys_info->GetGlRenderer(), + sys_info->GetGlVersion()); + + scoped_refptr<PrefRegistrySimple> pref_registry(new PrefRegistrySimple()); + metrics::RegisterPrefs(pref_registry.get()); + cast_browser_process_->SetPrefService( + PrefServiceHelper::CreatePrefService(pref_registry.get())); + + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); +#if defined(OS_ANDROID) + ::media::SetMediaClientAndroid(new media::CastMediaClientAndroid()); +#else + if (cmd_line->HasSwitch(switches::kEnableCmaMediaPipeline)) + ::media::SetBrowserCdmFactory(new media::CastBrowserCdmFactory()); +#endif // defined(OS_ANDROID) + + cast_browser_process_->SetConnectivityChecker( + make_scoped_refptr(new ConnectivityChecker( + content::BrowserThread::GetMessageLoopProxyForThread( + content::BrowserThread::IO)))); + url_request_context_factory_->InitializeOnUIThread(); cast_browser_process_->SetBrowserContext( - new CastBrowserContext(url_request_context_factory_)); + make_scoped_ptr(new CastBrowserContext(url_request_context_factory_))); cast_browser_process_->SetMetricsServiceClient( metrics::CastMetricsServiceClient::Create( content::BrowserThread::GetBlockingPool(), - ChromecastConfig::GetInstance()->pref_service(), + cast_browser_process_->pref_service(), cast_browser_process_->browser_context()->GetRequestContext())); -#if defined(OS_ANDROID) - base::FilePath crash_dumps_dir; - if (!chromecast::CrashHandler::GetCrashDumpLocation(&crash_dumps_dir)) { - LOG(ERROR) << "Could not find crash dump location."; - } - cast_browser_process_->SetCrashDumpManager( - new breakpad::CrashDumpManager(crash_dumps_dir)); -#endif - - if (!PlatformClientAuth::Initialize()) { + if (!PlatformClientAuth::Initialize()) LOG(ERROR) << "PlatformClientAuth::Initialize failed."; - } - cast_browser_process_->SetRemoteDebuggingServer(new RemoteDebuggingServer()); + cast_browser_process_->SetRemoteDebuggingServer( + make_scoped_ptr(new RemoteDebuggingServer())); - InitializeWebUI(); + media::CastMediaShlib::Initialize(cmd_line->argv()); cast_browser_process_->SetCastService(CastService::Create( cast_browser_process_->browser_context(), - url_request_context_factory_->GetSystemGetter(), - base::Bind( - &metrics::CastMetricsServiceClient::EnableMetricsService, - base::Unretained(cast_browser_process_->metrics_service_client())))); + cast_browser_process_->pref_service(), + cast_browser_process_->metrics_service_client(), + url_request_context_factory_->GetSystemGetter())); + cast_browser_process_->cast_service()->Initialize(); - // Initializing network delegates must happen after Cast service is created. + // Initializing metrics service and network delegates must happen after cast + // service is intialized because CastMetricsServiceClient and + // CastNetworkDelegate may use components initialized by cast service. + cast_browser_process_->metrics_service_client() + ->Initialize(cast_browser_process_->cast_service()); url_request_context_factory_->InitializeNetworkDelegates(); + cast_browser_process_->cast_service()->Start(); } bool CastBrowserMainParts::MainMessageLoopRun(int* result_code) { - // If parameters_.ui_task is not NULL, we are running browser tests. In this - // case, the browser's main message loop will not run. +#if defined(OS_ANDROID) + // Android does not use native main MessageLoop. + NOTREACHED(); + return true; +#else + base::RunLoop run_loop; + base::Closure quit_closure(run_loop.QuitClosure()); + RegisterClosureOnSignal(quit_closure); + + // If parameters_.ui_task is not NULL, we are running browser tests. if (parameters_.ui_task) { - parameters_.ui_task->Run(); - } else { - base::MessageLoopForUI::current()->Run(); + base::MessageLoop* message_loop = base::MessageLoopForUI::current(); + message_loop->PostTask(FROM_HERE, *parameters_.ui_task); + message_loop->PostTask(FROM_HERE, quit_closure); } + + run_loop.Run(); + + // Once the main loop has stopped running, we give the browser process a few + // seconds to stop cast service and finalize all resources. If a hang occurs + // and cast services refuse to terminate successfully, then we SIGKILL the + // current process to avoid indefinte hangs. + RegisterKillOnAlarm(kKillOnAlarmTimeoutSec); + + cast_browser_process_->cast_service()->Stop(); return true; +#endif } void CastBrowserMainParts::PostMainMessageLoopRun() { - cast_browser_process_->cast_service()->Stop(); +#if defined(OS_ANDROID) + // Android does not use native main MessageLoop. + NOTREACHED(); +#else + cast_browser_process_->cast_service()->Finalize(); + cast_browser_process_->metrics_service_client()->Finalize(); cast_browser_process_.reset(); + +#if defined(USE_AURA) + aura::Env::DeleteInstance(); +#endif + + DeregisterKillOnAlarm(); +#endif + + media::CastMediaShlib::Finalize(); } } // namespace shell diff --git a/chromium/chromecast/browser/cast_browser_main_parts.h b/chromium/chromecast/browser/cast_browser_main_parts.h index 3220faf8e08..49954cef55d 100644 --- a/chromium/chromecast/browser/cast_browser_main_parts.h +++ b/chromium/chromecast/browser/cast_browser_main_parts.h @@ -11,6 +11,10 @@ #include "content/public/browser/browser_main_parts.h" #include "content/public/common/main_function_params.h" +namespace media { +class AudioManagerFactory; +} + namespace chromecast { namespace shell { class CastBrowserProcess; @@ -18,23 +22,26 @@ class URLRequestContextFactory; class CastBrowserMainParts : public content::BrowserMainParts { public: + // This class does not take ownership of |url_request_content_factory|. CastBrowserMainParts( const content::MainFunctionParams& parameters, - URLRequestContextFactory* url_request_context_factory); - virtual ~CastBrowserMainParts(); + URLRequestContextFactory* url_request_context_factory, + scoped_ptr<::media::AudioManagerFactory> audio_manager_factory); + ~CastBrowserMainParts() override; // content::BrowserMainParts implementation: - virtual void PreMainMessageLoopStart() override; - virtual void PostMainMessageLoopStart() override; - virtual int PreCreateThreads() override; - virtual void PreMainMessageLoopRun() override; - virtual bool MainMessageLoopRun(int* result_code) override; - virtual void PostMainMessageLoopRun() override; + void PreMainMessageLoopStart() override; + void PostMainMessageLoopStart() override; + int PreCreateThreads() override; + void PreMainMessageLoopRun() override; + bool MainMessageLoopRun(int* result_code) override; + void PostMainMessageLoopRun() override; private: scoped_ptr<CastBrowserProcess> cast_browser_process_; const content::MainFunctionParams parameters_; // For running browser tests. URLRequestContextFactory* const url_request_context_factory_; + scoped_ptr<::media::AudioManagerFactory> audio_manager_factory_; DISALLOW_COPY_AND_ASSIGN(CastBrowserMainParts); }; diff --git a/chromium/chromecast/browser/cast_browser_process.cc b/chromium/chromecast/browser/cast_browser_process.cc index 050ff0d3fe8..25fcd44ff4a 100644 --- a/chromium/chromecast/browser/cast_browser_process.cc +++ b/chromium/chromecast/browser/cast_browser_process.cc @@ -5,20 +5,19 @@ #include "chromecast/browser/cast_browser_process.h" #include "base/logging.h" +#include "base/prefs/pref_service.h" #include "chromecast/base/metrics/cast_metrics_helper.h" #include "chromecast/browser/cast_browser_context.h" +#include "chromecast/browser/cast_resource_dispatcher_host_delegate.h" #include "chromecast/browser/devtools/remote_debugging_server.h" #include "chromecast/browser/metrics/cast_metrics_service_client.h" #include "chromecast/browser/service/cast_service.h" +#include "chromecast/net/connectivity_checker.h" #if defined(OS_ANDROID) #include "components/crash/browser/crash_dump_manager_android.h" #endif // defined(OS_ANDROID) -#if defined(USE_AURA) -#include "ui/aura/env.h" -#endif - namespace chromecast { namespace shell { @@ -39,48 +38,64 @@ CastBrowserProcess::CastBrowserProcess() { CastBrowserProcess::~CastBrowserProcess() { DCHECK_EQ(g_instance, this); -#if defined(USE_AURA) - aura::Env::DeleteInstance(); -#endif + if (pref_service_) + pref_service_->CommitPendingWrite(); g_instance = NULL; } void CastBrowserProcess::SetBrowserContext( - CastBrowserContext* browser_context) { + scoped_ptr<CastBrowserContext> browser_context) { DCHECK(!browser_context_); - browser_context_.reset(browser_context); + browser_context_.swap(browser_context); } -void CastBrowserProcess::SetCastService(CastService* cast_service) { +void CastBrowserProcess::SetCastService(scoped_ptr<CastService> cast_service) { DCHECK(!cast_service_); - cast_service_.reset(cast_service); -} - -void CastBrowserProcess::SetRemoteDebuggingServer( - RemoteDebuggingServer* remote_debugging_server) { - DCHECK(!remote_debugging_server_); - remote_debugging_server_.reset(remote_debugging_server); + cast_service_.swap(cast_service); } void CastBrowserProcess::SetMetricsHelper( - metrics::CastMetricsHelper* metrics_helper) { + scoped_ptr<metrics::CastMetricsHelper> metrics_helper) { DCHECK(!metrics_helper_); - metrics_helper_.reset(metrics_helper); + metrics_helper_.swap(metrics_helper); } void CastBrowserProcess::SetMetricsServiceClient( - metrics::CastMetricsServiceClient* metrics_service_client) { + scoped_ptr<metrics::CastMetricsServiceClient> metrics_service_client) { DCHECK(!metrics_service_client_); - metrics_service_client_.reset(metrics_service_client); + metrics_service_client_.swap(metrics_service_client); +} + +void CastBrowserProcess::SetPrefService(scoped_ptr<PrefService> pref_service) { + DCHECK(!pref_service_); + pref_service_.swap(pref_service); +} + +void CastBrowserProcess::SetRemoteDebuggingServer( + scoped_ptr<RemoteDebuggingServer> remote_debugging_server) { + DCHECK(!remote_debugging_server_); + remote_debugging_server_.swap(remote_debugging_server); +} + +void CastBrowserProcess::SetResourceDispatcherHostDelegate( + scoped_ptr<CastResourceDispatcherHostDelegate> delegate) { + DCHECK(!resource_dispatcher_host_delegate_); + resource_dispatcher_host_delegate_.swap(delegate); } #if defined(OS_ANDROID) void CastBrowserProcess::SetCrashDumpManager( - breakpad::CrashDumpManager* crash_dump_manager) { + scoped_ptr<breakpad::CrashDumpManager> crash_dump_manager) { DCHECK(!crash_dump_manager_); - crash_dump_manager_.reset(crash_dump_manager); + crash_dump_manager_.swap(crash_dump_manager); } #endif // defined(OS_ANDROID) +void CastBrowserProcess::SetConnectivityChecker( + scoped_refptr<ConnectivityChecker> connectivity_checker) { + DCHECK(!connectivity_checker_); + connectivity_checker_.swap(connectivity_checker); +} + } // namespace shell } // namespace chromecast diff --git a/chromium/chromecast/browser/cast_browser_process.h b/chromium/chromecast/browser/cast_browser_process.h index 7f0ac7910df..f4bec89e7a1 100644 --- a/chromium/chromecast/browser/cast_browser_process.h +++ b/chromium/chromecast/browser/cast_browser_process.h @@ -6,15 +6,18 @@ #define CHROMECAST_BROWSER_CAST_BROWSER_PROCESS_H_ #include "base/macros.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +class PrefService; + namespace breakpad { class CrashDumpManager; } // namespace breakpad namespace chromecast { class CastService; -class WebCryptoServer; +class ConnectivityChecker; namespace metrics { class CastMetricsHelper; @@ -23,6 +26,7 @@ class CastMetricsServiceClient; namespace shell { class CastBrowserContext; +class CastResourceDispatcherHostDelegate; class RemoteDebuggingServer; class CastBrowserProcess { @@ -34,30 +38,51 @@ class CastBrowserProcess { CastBrowserProcess(); virtual ~CastBrowserProcess(); - void SetBrowserContext(CastBrowserContext* browser_context); - void SetCastService(CastService* cast_service); - void SetRemoteDebuggingServer(RemoteDebuggingServer* remote_debugging_server); - void SetMetricsHelper(metrics::CastMetricsHelper* metrics_helper); + void SetBrowserContext(scoped_ptr<CastBrowserContext> browser_context); + void SetCastService(scoped_ptr<CastService> cast_service); + void SetMetricsHelper(scoped_ptr<metrics::CastMetricsHelper> metrics_helper); void SetMetricsServiceClient( - metrics::CastMetricsServiceClient* metrics_service_client); + scoped_ptr<metrics::CastMetricsServiceClient> metrics_service_client); + void SetPrefService(scoped_ptr<PrefService> pref_service); + void SetRemoteDebuggingServer( + scoped_ptr<RemoteDebuggingServer> remote_debugging_server); + void SetResourceDispatcherHostDelegate( + scoped_ptr<CastResourceDispatcherHostDelegate> delegate); #if defined(OS_ANDROID) - void SetCrashDumpManager(breakpad::CrashDumpManager* crash_dump_manager); + void SetCrashDumpManager( + scoped_ptr<breakpad::CrashDumpManager> crash_dump_manager); #endif // defined(OS_ANDROID) + void SetConnectivityChecker( + scoped_refptr<ConnectivityChecker> connectivity_checker); CastBrowserContext* browser_context() const { return browser_context_.get(); } CastService* cast_service() const { return cast_service_.get(); } metrics::CastMetricsServiceClient* metrics_service_client() const { return metrics_service_client_.get(); } + PrefService* pref_service() const { return pref_service_.get(); } + CastResourceDispatcherHostDelegate* resource_dispatcher_host_delegate() + const { + return resource_dispatcher_host_delegate_.get(); + } + ConnectivityChecker* connectivity_checker() const { + return connectivity_checker_.get(); + } private: - scoped_ptr<CastBrowserContext> browser_context_; + // Note: The following order should match the order they are set in + // CastBrowserMainParts. scoped_ptr<metrics::CastMetricsHelper> metrics_helper_; + scoped_ptr<PrefService> pref_service_; + scoped_refptr<ConnectivityChecker> connectivity_checker_; + scoped_ptr<CastBrowserContext> browser_context_; scoped_ptr<metrics::CastMetricsServiceClient> metrics_service_client_; - scoped_ptr<RemoteDebuggingServer> remote_debugging_server_; + scoped_ptr<CastResourceDispatcherHostDelegate> + resource_dispatcher_host_delegate_; #if defined(OS_ANDROID) scoped_ptr<breakpad::CrashDumpManager> crash_dump_manager_; #endif // defined(OS_ANDROID) + scoped_ptr<RemoteDebuggingServer> remote_debugging_server_; // Note: CastService must be destroyed before others. scoped_ptr<CastService> cast_service_; diff --git a/chromium/chromecast/browser/cast_content_browser_client.cc b/chromium/chromecast/browser/cast_content_browser_client.cc index 397603d8e1a..3e075faadf7 100644 --- a/chromium/chromecast/browser/cast_content_browser_client.cc +++ b/chromium/chromecast/browser/cast_content_browser_client.cc @@ -4,42 +4,57 @@ #include "chromecast/browser/cast_content_browser_client.h" +#include <string> + +#include "base/base_switches.h" #include "base/command_line.h" #include "base/files/scoped_file.h" +#include "base/i18n/icu_util.h" #include "base/i18n/rtl.h" #include "base/path_service.h" +#include "chromecast/base/cast_paths.h" #include "chromecast/browser/cast_browser_context.h" #include "chromecast/browser/cast_browser_main_parts.h" #include "chromecast/browser/cast_browser_process.h" #include "chromecast/browser/cast_network_delegate.h" -#include "chromecast/browser/devtools/cast_dev_tools_delegate.h" +#include "chromecast/browser/cast_quota_permission_context.h" +#include "chromecast/browser/cast_resource_dispatcher_host_delegate.h" #include "chromecast/browser/geolocation/cast_access_token_store.h" +#include "chromecast/browser/media/cma_message_filter_host.h" #include "chromecast/browser/url_request_context_factory.h" -#include "chromecast/common/cast_paths.h" +#include "chromecast/common/chromecast_switches.h" #include "chromecast/common/global_descriptors.h" #include "components/crash/app/breakpad_linux.h" +#include "components/crash/browser/crash_handler_host_linux.h" +#include "components/network_hints/browser/network_hints_message_filter.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/certificate_request_result_type.h" +#include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/resource_dispatcher_host.h" +#include "content/public/browser/web_contents.h" #include "content/public/common/content_descriptors.h" #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "content/public/common/web_preferences.h" +#include "gin/v8_initializer.h" +#include "media/audio/audio_manager_factory.h" #include "net/ssl/ssl_cert_request_info.h" - -#if defined(OS_ANDROID) -#include "chromecast/browser/android/external_video_surface_container_impl.h" -#endif // defined(OS_ANDROID) +#include "net/url_request/url_request_context_getter.h" +#include "ui/gl/gl_switches.h" #if defined(OS_ANDROID) #include "components/crash/browser/crash_dump_manager_android.h" +#include "components/external_video_surface/browser/android/external_video_surface_container_impl.h" #endif // defined(OS_ANDROID) namespace chromecast { namespace shell { CastContentBrowserClient::CastContentBrowserClient() - : url_request_context_factory_(new URLRequestContextFactory()) { + : v8_natives_fd_(-1), + v8_snapshot_fd_(-1), + url_request_context_factory_(new URLRequestContextFactory()) { } CastContentBrowserClient::~CastContentBrowserClient() { @@ -52,11 +67,47 @@ CastContentBrowserClient::~CastContentBrowserClient() { content::BrowserMainParts* CastContentBrowserClient::CreateBrowserMainParts( const content::MainFunctionParams& parameters) { return new CastBrowserMainParts(parameters, - url_request_context_factory_.get()); + url_request_context_factory_.get(), + PlatformCreateAudioManagerFactory()); } void CastContentBrowserClient::RenderProcessWillLaunch( content::RenderProcessHost* host) { +#if !defined(OS_ANDROID) + scoped_refptr<media::CmaMessageFilterHost> cma_message_filter( + new media::CmaMessageFilterHost(host->GetID())); + host->AddFilter(cma_message_filter.get()); +#endif // !defined(OS_ANDROID) + + // Forcibly trigger I/O-thread URLRequestContext initialization before + // getting HostResolver. + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&net::URLRequestContextGetter::GetURLRequestContext, + base::Unretained( + url_request_context_factory_->GetSystemGetter())), + base::Bind(&CastContentBrowserClient::AddNetworkHintsMessageFilter, + base::Unretained(this), host->GetID())); + + auto extra_filters = PlatformGetBrowserMessageFilters(); + for (auto const& filter : extra_filters) { + host->AddFilter(filter.get()); + } +} + +void CastContentBrowserClient::AddNetworkHintsMessageFilter( + int render_process_id, net::URLRequestContext* context) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + content::RenderProcessHost* host = + content::RenderProcessHost::FromID(render_process_id); + if (!host) + return; + + scoped_refptr<content::BrowserMessageFilter> network_hints_message_filter( + new network_hints::NetworkHintsMessageFilter( + url_request_context_factory_->host_resolver())); + host->AddFilter(network_hints_message_filter.get()); } net::URLRequestContextGetter* CastContentBrowserClient::CreateRequestContext( @@ -79,9 +130,6 @@ bool CastContentBrowserClient::IsHandledURL(const GURL& url) { content::kChromeUIScheme, content::kChromeDevToolsScheme, url::kDataScheme, -#if defined(OS_ANDROID) - url::kFileScheme, -#endif // defined(OS_ANDROID) }; const std::string& scheme = url.scheme(); @@ -89,6 +137,12 @@ bool CastContentBrowserClient::IsHandledURL(const GURL& url) { if (scheme == kProtocolList[i]) return true; } + + if (scheme == url::kFileScheme) { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableLocalFileAccesses); + } + return false; } @@ -98,15 +152,42 @@ void CastContentBrowserClient::AppendExtraCommandLineSwitches( std::string process_type = command_line->GetSwitchValueNative(switches::kProcessType); + base::CommandLine* browser_command_line = + base::CommandLine::ForCurrentProcess(); + +#if defined(V8_USE_EXTERNAL_STARTUP_DATA) + if (process_type != switches::kZygoteProcess) { + command_line->AppendSwitch(::switches::kV8NativesPassedByFD); + command_line->AppendSwitch(::switches::kV8SnapshotPassedByFD); + } +#endif // V8_USE_EXTERNAL_STARTUP_DATA + + // IsCrashReporterEnabled() is set when InitCrashReporter() is called, and + // controlled by GetBreakpadClient()->EnableBreakpadForProcess(), therefore + // it's ok to add switch to every process here. + if (breakpad::IsCrashReporterEnabled()) { + command_line->AppendSwitch(switches::kEnableCrashReporter); + } // Renderer process command-line if (process_type == switches::kRendererProcess) { // Any browser command-line switches that should be propagated to // the renderer go here. -#if defined(OS_ANDROID) - command_line->AppendSwitch(switches::kForceUseOverlayEmbeddedVideo); -#endif // defined(OS_ANDROID) + + if (browser_command_line->HasSwitch(switches::kEnableCmaMediaPipeline)) + command_line->AppendSwitch(switches::kEnableCmaMediaPipeline); } + +#if defined(OS_LINUX) + // Necessary for accelerated 2d canvas. By default on Linux, Chromium assumes + // GLES2 contexts can be lost to a power-save mode, which breaks GPU canvas + // apps. + if (process_type == switches::kGpuProcess) { + command_line->AppendSwitch(switches::kGpuNoContextLost); + } +#endif + + PlatformAppendExtraCommandLineSwitches(command_line); } content::AccessTokenStore* CastContentBrowserClient::CreateAccessTokenStore() { @@ -116,7 +197,6 @@ content::AccessTokenStore* CastContentBrowserClient::CreateAccessTokenStore() { void CastContentBrowserClient::OverrideWebkitPrefs( content::RenderViewHost* render_view_host, - const GURL& url, content::WebPreferences* prefs) { prefs->allow_scripts_to_close_windows = true; // TODO(lcwu): http://crbug.com/391089. This pref is set to true by default @@ -126,11 +206,23 @@ void CastContentBrowserClient::OverrideWebkitPrefs( prefs->allow_running_insecure_content = true; } +void CastContentBrowserClient::ResourceDispatcherHostCreated() { + CastBrowserProcess::GetInstance()->SetResourceDispatcherHostDelegate( + make_scoped_ptr(new CastResourceDispatcherHostDelegate)); + content::ResourceDispatcherHost::Get()->SetDelegate( + CastBrowserProcess::GetInstance()->resource_dispatcher_host_delegate()); +} + std::string CastContentBrowserClient::GetApplicationLocale() { const std::string locale(base::i18n::GetConfiguredLocale()); return locale.empty() ? "en-US" : locale; } +content::QuotaPermissionContext* +CastContentBrowserClient::CreateQuotaPermissionContext() { + return new CastQuotaPermissionContext(); +} + void CastContentBrowserClient::AllowCertificateError( int render_process_id, int render_view_id, @@ -150,16 +242,15 @@ void CastContentBrowserClient::AllowCertificateError( } void CastContentBrowserClient::SelectClientCertificate( - int render_process_id, - int render_view_id, + content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, - const base::Callback<void(net::X509Certificate*)>& callback) { + scoped_ptr<content::ClientCertificateDelegate> delegate) { GURL requesting_url("https://" + cert_request_info->host_and_port.ToString()); if (!requesting_url.is_valid()) { LOG(ERROR) << "Invalid URL string: " << requesting_url.possibly_invalid_spec(); - callback.Run(NULL); + delegate->ContinueWithCertificate(nullptr); return; } @@ -170,28 +261,33 @@ void CastContentBrowserClient::SelectClientCertificate( // it, because CastNetworkDelegate is bound to the IO thread. // Subsequently, the callback must then itself be performed back here // on the UI thread. + // + // TODO(davidben): Stop using child ID to identify an app. DCHECK_CURRENTLY_ON(content::BrowserThread::UI); content::BrowserThread::PostTaskAndReplyWithResult( - content::BrowserThread::IO, - FROM_HERE, - base::Bind( - &CastContentBrowserClient::SelectClientCertificateOnIOThread, - base::Unretained(this), - requesting_url), - callback); + content::BrowserThread::IO, FROM_HERE, + base::Bind(&CastContentBrowserClient::SelectClientCertificateOnIOThread, + base::Unretained(this), requesting_url, + web_contents->GetRenderProcessHost()->GetID()), + base::Bind(&content::ClientCertificateDelegate::ContinueWithCertificate, + base::Owned(delegate.release()))); } net::X509Certificate* CastContentBrowserClient::SelectClientCertificateOnIOThread( - GURL requesting_url) { + GURL requesting_url, + int render_process_id) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); CastNetworkDelegate* network_delegate = url_request_context_factory_->app_network_delegate(); - if (network_delegate->IsWhitelisted(requesting_url, false)) { + if (network_delegate->IsWhitelisted(requesting_url, + render_process_id, false)) { return CastNetworkDelegate::DeviceCert(); } else { LOG(ERROR) << "Invalid host for client certificate request: " - << requesting_url.host(); + << requesting_url.host() + << " with render_process_id: " + << render_process_id; return NULL; } } @@ -215,27 +311,35 @@ bool CastContentBrowserClient::CanCreateWindow( return false; } -content::DevToolsManagerDelegate* -CastContentBrowserClient::GetDevToolsManagerDelegate() { - return new CastDevToolsManagerDelegate(); -} - void CastContentBrowserClient::GetAdditionalMappedFilesForChildProcess( const base::CommandLine& command_line, int child_process_id, content::FileDescriptorInfo* mappings) { +#if defined(V8_USE_EXTERNAL_STARTUP_DATA) + if (v8_natives_fd_.get() == -1 || v8_snapshot_fd_.get() == -1) { + int v8_natives_fd = -1; + int v8_snapshot_fd = -1; + if (gin::V8Initializer::OpenV8FilesForChildProcesses(&v8_natives_fd, + &v8_snapshot_fd)) { + v8_natives_fd_.reset(v8_natives_fd); + v8_snapshot_fd_.reset(v8_snapshot_fd); + } + } + DCHECK(v8_natives_fd_.get() != -1 && v8_snapshot_fd_.get() != -1); + mappings->Share(kV8NativesDataDescriptor, v8_natives_fd_.get()); + mappings->Share(kV8SnapshotDataDescriptor, v8_snapshot_fd_.get()); +#endif // V8_USE_EXTERNAL_STARTUP_DATA #if defined(OS_ANDROID) - int flags = base::File::FLAG_OPEN | base::File::FLAG_READ; - base::FilePath pak_file; - CHECK(PathService::Get(FILE_CAST_PAK, &pak_file)); - base::File pak_with_flags(pak_file, flags); - if (!pak_with_flags.IsValid()) { + const int flags_open_read = base::File::FLAG_OPEN | base::File::FLAG_READ; + base::FilePath pak_file_path; + CHECK(PathService::Get(FILE_CAST_PAK, &pak_file_path)); + base::File pak_file(pak_file_path, flags_open_read); + if (!pak_file.IsValid()) { NOTREACHED() << "Failed to open file when creating renderer process: " << "cast_shell.pak"; } - mappings->Transfer( - kAndroidPakDescriptor, - base::ScopedFD(pak_with_flags.TakePlatformFile())); + mappings->Transfer(kAndroidPakDescriptor, + base::ScopedFD(pak_file.TakePlatformFile())); if (breakpad::IsCrashReporterEnabled()) { base::File minidump_file( @@ -249,6 +353,21 @@ void CastContentBrowserClient::GetAdditionalMappedFilesForChildProcess( base::ScopedFD(minidump_file.TakePlatformFile())); } } + + base::FilePath app_data_path; + CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &app_data_path)); + base::FilePath icudata_path = + app_data_path.AppendASCII(base::i18n::kIcuDataFileName); + base::File icudata_file(icudata_path, flags_open_read); + if (!icudata_file.IsValid()) + NOTREACHED() << "Failed to open ICU file when creating renderer process"; + mappings->Transfer(kAndroidICUDataDescriptor, + base::ScopedFD(icudata_file.TakePlatformFile())); +#else + int crash_signal_fd = GetCrashSignalFD(command_line); + if (crash_signal_fd >= 0) { + mappings->Share(kCrashDumpSignal, crash_signal_fd); + } #endif // defined(OS_ANDROID) } @@ -256,10 +375,49 @@ void CastContentBrowserClient::GetAdditionalMappedFilesForChildProcess( content::ExternalVideoSurfaceContainer* CastContentBrowserClient::OverrideCreateExternalVideoSurfaceContainer( content::WebContents* web_contents) { - return new ExternalVideoSurfaceContainerImpl(web_contents); + return external_video_surface::ExternalVideoSurfaceContainerImpl::Create( + web_contents); } #endif // defined(OS_ANDROID) && defined(VIDEO_HOLE) +#if !defined(OS_ANDROID) +int CastContentBrowserClient::GetCrashSignalFD( + const base::CommandLine& command_line) { + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + + if (process_type == switches::kRendererProcess || + process_type == switches::kGpuProcess) { + breakpad::CrashHandlerHostLinux* crash_handler = + crash_handlers_[process_type]; + if (!crash_handler) { + crash_handler = CreateCrashHandlerHost(process_type); + crash_handlers_[process_type] = crash_handler; + } + return crash_handler->GetDeathSignalSocket(); + } + + return -1; +} + +breakpad::CrashHandlerHostLinux* +CastContentBrowserClient::CreateCrashHandlerHost( + const std::string& process_type) { + // Let cast shell dump to /tmp. Internal minidump generator code can move it + // to /data/minidumps later, since /data/minidumps is file lock-controlled. + base::FilePath dumps_path; + PathService::Get(base::DIR_TEMP, &dumps_path); + + // Alway set "upload" to false to use our own uploader. + breakpad::CrashHandlerHostLinux* crash_handler = + new breakpad::CrashHandlerHostLinux( + process_type, dumps_path, false /* upload */); + // StartUploaderThread() even though upload is diferred. + // Breakpad-related memory is freed in the uploader thread. + crash_handler->StartUploaderThread(); + return crash_handler; +} +#endif // !defined(OS_ANDROID) } // namespace shell } // namespace chromecast diff --git a/chromium/chromecast/browser/cast_content_browser_client.h b/chromium/chromecast/browser/cast_content_browser_client.h index 00ae07228f7..5e5c262f92a 100644 --- a/chromium/chromecast/browser/cast_content_browser_client.h +++ b/chromium/chromecast/browser/cast_content_browser_client.h @@ -5,10 +5,30 @@ #ifndef CHROMECAST_BROWSER_CAST_CONTENT_BROWSER_CLIENT_H_ #define CHROMECAST_BROWSER_CAST_CONTENT_BROWSER_CLIENT_H_ +#include <map> +#include <string> +#include <vector> + #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "content/public/browser/content_browser_client.h" +namespace breakpad { +class CrashHandlerHostLinux; +} + +namespace content { +class BrowserMessageFilter; +} + +namespace media { +class AudioManagerFactory; +} + +namespace net { +class HostResolver; +} + namespace chromecast { namespace shell { @@ -18,27 +38,35 @@ class URLRequestContextFactory; class CastContentBrowserClient: public content::ContentBrowserClient { public: CastContentBrowserClient(); - virtual ~CastContentBrowserClient(); + ~CastContentBrowserClient() override; + + // Appends extra command line arguments before launching a new process. + void PlatformAppendExtraCommandLineSwitches(base::CommandLine* command_line); + + // Returns any BrowserMessageFilters from the platform implementation that + // should be added when launching a new render process. + std::vector<scoped_refptr<content::BrowserMessageFilter>> + PlatformGetBrowserMessageFilters(); // content::ContentBrowserClient implementation: - virtual content::BrowserMainParts* CreateBrowserMainParts( + content::BrowserMainParts* CreateBrowserMainParts( const content::MainFunctionParams& parameters) override; - virtual void RenderProcessWillLaunch( - content::RenderProcessHost* host) override; - virtual net::URLRequestContextGetter* CreateRequestContext( + void RenderProcessWillLaunch(content::RenderProcessHost* host) override; + net::URLRequestContextGetter* CreateRequestContext( content::BrowserContext* browser_context, content::ProtocolHandlerMap* protocol_handlers, content::URLRequestInterceptorScopedVector request_interceptors) override; - virtual bool IsHandledURL(const GURL& url) override; - virtual void AppendExtraCommandLineSwitches(base::CommandLine* command_line, - int child_process_id) override; - virtual content::AccessTokenStore* CreateAccessTokenStore() override; - virtual void OverrideWebkitPrefs(content::RenderViewHost* render_view_host, - const GURL& url, - content::WebPreferences* prefs) override; - virtual std::string GetApplicationLocale() override; - virtual void AllowCertificateError( + bool IsHandledURL(const GURL& url) override; + void AppendExtraCommandLineSwitches(base::CommandLine* command_line, + int child_process_id) override; + content::AccessTokenStore* CreateAccessTokenStore() override; + void OverrideWebkitPrefs(content::RenderViewHost* render_view_host, + content::WebPreferences* prefs) override; + void ResourceDispatcherHostCreated() override; + std::string GetApplicationLocale() override; + content::QuotaPermissionContext* CreateQuotaPermissionContext() override; + void AllowCertificateError( int render_process_id, int render_view_id, int cert_error, @@ -50,12 +78,11 @@ class CastContentBrowserClient: public content::ContentBrowserClient { bool expired_previous_decision, const base::Callback<void(bool)>& callback, content::CertificateRequestResultType* result) override; - virtual void SelectClientCertificate( - int render_process_id, - int render_frame_id, + void SelectClientCertificate( + content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, - const base::Callback<void(net::X509Certificate*)>& callback) override; - virtual bool CanCreateWindow( + scoped_ptr<content::ClientCertificateDelegate> delegate) override; + bool CanCreateWindow( const GURL& opener_url, const GURL& opener_top_level_frame_url, const GURL& source_origin, @@ -70,21 +97,40 @@ class CastContentBrowserClient: public content::ContentBrowserClient { int render_process_id, int opener_id, bool* no_javascript_access) override; - virtual content::DevToolsManagerDelegate* - GetDevToolsManagerDelegate() override; - virtual void GetAdditionalMappedFilesForChildProcess( + void GetAdditionalMappedFilesForChildProcess( const base::CommandLine& command_line, int child_process_id, content::FileDescriptorInfo* mappings) override; #if defined(OS_ANDROID) && defined(VIDEO_HOLE) - virtual content::ExternalVideoSurfaceContainer* + content::ExternalVideoSurfaceContainer* OverrideCreateExternalVideoSurfaceContainer( content::WebContents* web_contents) override; #endif // defined(OS_ANDROID) && defined(VIDEO_HOLE) private: + void AddNetworkHintsMessageFilter(int render_process_id, + net::URLRequestContext* context); + net::X509Certificate* SelectClientCertificateOnIOThread( - GURL requesting_url); + GURL requesting_url, + int render_process_id); + + scoped_ptr<::media::AudioManagerFactory> PlatformCreateAudioManagerFactory(); + +#if !defined(OS_ANDROID) + // Returns the crash signal FD corresponding to the current process type. + int GetCrashSignalFD(const base::CommandLine& command_line); + + // Creates a CrashHandlerHost instance for the given process type. + breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost( + const std::string& process_type); + + // A static cache to hold crash_handlers for each process_type + std::map<std::string, breakpad::CrashHandlerHostLinux*> crash_handlers_; +#endif + + base::ScopedFD v8_natives_fd_; + base::ScopedFD v8_snapshot_fd_; scoped_ptr<URLRequestContextFactory> url_request_context_factory_; diff --git a/chromium/chromecast/browser/cast_content_browser_client_simple.cc b/chromium/chromecast/browser/cast_content_browser_client_simple.cc new file mode 100644 index 00000000000..bde5a71d028 --- /dev/null +++ b/chromium/chromecast/browser/cast_content_browser_client_simple.cc @@ -0,0 +1,30 @@ +// Copyright 2015 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/browser/cast_content_browser_client.h" + +#include "content/public/browser/browser_message_filter.h" +#include "media/audio/audio_manager_factory.h" + +namespace chromecast { +namespace shell { + +void CastContentBrowserClient::PlatformAppendExtraCommandLineSwitches( + base::CommandLine* command_line) { +} + +std::vector<scoped_refptr<content::BrowserMessageFilter>> +CastContentBrowserClient::PlatformGetBrowserMessageFilters() { + return std::vector<scoped_refptr<content::BrowserMessageFilter>>(); +} + +scoped_ptr<::media::AudioManagerFactory> +CastContentBrowserClient::PlatformCreateAudioManagerFactory() { + // Return nullptr. The factory will not be set, and the statically linked + // implementation of AudioManager will be used. + return scoped_ptr<::media::AudioManagerFactory>(); +} + +} // namespace shell +} // namespace chromecast diff --git a/chromium/chromecast/browser/cast_content_window.cc b/chromium/chromecast/browser/cast_content_window.cc index 8cf64b33295..27c91b74cb4 100644 --- a/chromium/chromecast/browser/cast_content_window.cc +++ b/chromium/chromecast/browser/cast_content_window.cc @@ -5,12 +5,14 @@ #include "chromecast/browser/cast_content_window.h" #include "base/threading/thread_restrictions.h" +#include "chromecast/base/metrics/cast_metrics_helper.h" #include "content/public/browser/web_contents.h" #include "ipc/ipc_message.h" #if defined(USE_AURA) #include "ui/aura/env.h" #include "ui/aura/layout_manager.h" +#include "ui/aura/test/test_focus_client.h" #include "ui/aura/test/test_screen.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" @@ -22,24 +24,24 @@ namespace chromecast { class CastFillLayout : public aura::LayoutManager { public: explicit CastFillLayout(aura::Window* root) : root_(root) {} - virtual ~CastFillLayout() {} + ~CastFillLayout() override {} private: - virtual void OnWindowResized() override {} + void OnWindowResized() override {} - virtual void OnWindowAddedToLayout(aura::Window* child) override { + void OnWindowAddedToLayout(aura::Window* child) override { child->SetBounds(root_->bounds()); } - virtual void OnWillRemoveWindowFromLayout(aura::Window* child) override {} + void OnWillRemoveWindowFromLayout(aura::Window* child) override {} - virtual void OnWindowRemovedFromLayout(aura::Window* child) override {} + void OnWindowRemovedFromLayout(aura::Window* child) override {} - virtual void OnChildWindowVisibilityChanged(aura::Window* child, - bool visible) override {} + void OnChildWindowVisibilityChanged(aura::Window* child, + bool visible) override {} - virtual void SetChildBounds(aura::Window* child, - const gfx::Rect& requested_bounds) override { + void SetChildBounds(aura::Window* child, + const gfx::Rect& requested_bounds) override { SetChildBoundsDirect(child, requested_bounds); } @@ -59,9 +61,9 @@ CastContentWindow::~CastContentWindow() { #endif } -scoped_ptr<content::WebContents> CastContentWindow::Create( +void CastContentWindow::CreateWindowTree( const gfx::Size& initial_size, - content::BrowserContext* browser_context) { + content::WebContents* web_contents) { #if defined(USE_AURA) // Aura initialization // TODO(lcwu): We only need a minimal implementation of gfx::Screen @@ -83,16 +85,14 @@ scoped_ptr<content::WebContents> CastContentWindow::Create( window_tree_host_->InitHost(); window_tree_host_->window()->SetLayoutManager( new CastFillLayout(window_tree_host_->window())); - window_tree_host_->Show(); -#endif + window_tree_host_->compositor()->SetBackgroundColor(SK_ColorBLACK); - content::WebContents::CreateParams create_params(browser_context, NULL); - create_params.routing_id = MSG_ROUTING_NONE; - create_params.initial_size = initial_size; - content::WebContents* web_contents = content::WebContents::Create( - create_params); + focus_client_.reset(new aura::test::TestFocusClient()); + aura::client::SetFocusClient( + window_tree_host_->window(), focus_client_.get()); + + window_tree_host_->Show(); -#if defined(USE_AURA) // Add and show content's view/window aura::Window* content_window = web_contents->GetNativeView(); aura::Window* parent = window_tree_host_->window(); @@ -101,8 +101,30 @@ scoped_ptr<content::WebContents> CastContentWindow::Create( } content_window->Show(); #endif +} +scoped_ptr<content::WebContents> CastContentWindow::CreateWebContents( + const gfx::Size& initial_size, + content::BrowserContext* browser_context) { + content::WebContents::CreateParams create_params(browser_context, NULL); + create_params.routing_id = MSG_ROUTING_NONE; + create_params.initial_size = initial_size; + content::WebContents* web_contents = content::WebContents::Create( + create_params); + content::WebContentsObserver::Observe(web_contents); return make_scoped_ptr(web_contents); } +void CastContentWindow::DidFirstVisuallyNonEmptyPaint() { + metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstPaint(); +} + +void CastContentWindow::MediaPaused() { + metrics::CastMetricsHelper::GetInstance()->LogMediaPause(); +} + +void CastContentWindow::MediaStartedPlaying() { + metrics::CastMetricsHelper::GetInstance()->LogMediaPlay(); +} + } // namespace chromecast diff --git a/chromium/chromecast/browser/cast_content_window.h b/chromium/chromecast/browser/cast_content_window.h index 7101db1f00a..1360a5e0ca4 100644 --- a/chromium/chromecast/browser/cast_content_window.h +++ b/chromium/chromecast/browser/cast_content_window.h @@ -7,10 +7,14 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "content/public/browser/web_contents_observer.h" namespace aura { class WindowTreeHost; -} +namespace test { +class TestFocusClient; +} // namespace test +} // namespace aura namespace content { class BrowserContext; @@ -23,21 +27,30 @@ class Size; namespace chromecast { -class CastContentWindow { +class CastContentWindow : public content::WebContentsObserver { public: CastContentWindow(); // Removes the window from the screen. - ~CastContentWindow(); + ~CastContentWindow() override; + + // Create a window with the given size for |web_contents|. + void CreateWindowTree(const gfx::Size& initial_size, + content::WebContents* web_contents); - // Create a window with the given size. - scoped_ptr<content::WebContents> Create( + scoped_ptr<content::WebContents> CreateWebContents( const gfx::Size& initial_size, content::BrowserContext* browser_context); + // content::WebContentsObserver implementation: + void DidFirstVisuallyNonEmptyPaint() override; + void MediaPaused() override; + void MediaStartedPlaying() override; + private: #if defined(USE_AURA) scoped_ptr<aura::WindowTreeHost> window_tree_host_; + scoped_ptr<aura::test::TestFocusClient> focus_client_; #endif DISALLOW_COPY_AND_ASSIGN(CastContentWindow); diff --git a/chromium/chromecast/browser/cast_download_manager_delegate.h b/chromium/chromecast/browser/cast_download_manager_delegate.h index d77e07f1da9..e6c963f131f 100644 --- a/chromium/chromecast/browser/cast_download_manager_delegate.h +++ b/chromium/chromecast/browser/cast_download_manager_delegate.h @@ -14,20 +14,18 @@ namespace shell { class CastDownloadManagerDelegate : public content::DownloadManagerDelegate { public: CastDownloadManagerDelegate(); - virtual ~CastDownloadManagerDelegate(); + ~CastDownloadManagerDelegate() override; // content::DownloadManagerDelegate implementation: - virtual void GetNextId( - const content::DownloadIdCallback& callback) override; - virtual bool DetermineDownloadTarget( + void GetNextId(const content::DownloadIdCallback& callback) override; + bool DetermineDownloadTarget( content::DownloadItem* item, const content::DownloadTargetCallback& callback) override; - virtual bool ShouldOpenFileBasedOnExtension( - const base::FilePath& path) override; - virtual bool ShouldCompleteDownload( + bool ShouldOpenFileBasedOnExtension(const base::FilePath& path) override; + bool ShouldCompleteDownload( content::DownloadItem* item, const base::Closure& complete_callback) override; - virtual bool ShouldOpenDownload( + bool ShouldOpenDownload( content::DownloadItem* item, const content::DownloadOpenDelayedCallback& callback) override; @@ -38,4 +36,4 @@ class CastDownloadManagerDelegate : public content::DownloadManagerDelegate { } // namespace shell } // namespace chromecast -#endif // CHROMECAST_BROWSER_CAST_DOWNLOAD_MANAGER_DELEGATE_H_
\ No newline at end of file +#endif // CHROMECAST_BROWSER_CAST_DOWNLOAD_MANAGER_DELEGATE_H_ diff --git a/chromium/chromecast/browser/cast_http_user_agent_settings.cc b/chromium/chromecast/browser/cast_http_user_agent_settings.cc index 9eb27058666..ef752dc3287 100644 --- a/chromium/chromecast/browser/cast_http_user_agent_settings.cc +++ b/chromium/chromecast/browser/cast_http_user_agent_settings.cc @@ -4,6 +4,7 @@ #include "chromecast/browser/cast_http_user_agent_settings.h" +#include "base/i18n/rtl.h" #include "base/logging.h" #include "chromecast/common/cast_content_client.h" #include "content/public/browser/browser_thread.h" @@ -19,23 +20,33 @@ namespace chromecast { namespace shell { CastHttpUserAgentSettings::CastHttpUserAgentSettings() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } CastHttpUserAgentSettings::~CastHttpUserAgentSettings() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } std::string CastHttpUserAgentSettings::GetAcceptLanguage() const { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (accept_language_.empty()) { + std::string new_locale( +#if defined(OS_ANDROID) + // TODO(byungchul): Use transient locale set when new app starts. + base::android::GetDefaultLocale() +#else + base::i18n::GetConfiguredLocale() +#endif + ); + if (new_locale != last_locale_ || accept_language_.empty()) { + last_locale_ = new_locale; accept_language_ = net::HttpUtil::GenerateAcceptLanguageHeader( #if defined(OS_ANDROID) - base::android::GetDefaultLocale() + last_locale_ #else l10n_util::GetStringUTF8(IDS_CHROMECAST_SETTINGS_ACCEPT_LANGUAGES) -#endif // defined(OS_ANDROID) +#endif ); + LOG(INFO) << "Locale changed: accept_language=" << accept_language_; } return accept_language_; } diff --git a/chromium/chromecast/browser/cast_http_user_agent_settings.h b/chromium/chromecast/browser/cast_http_user_agent_settings.h index 90a432b11b5..8deb7f2585a 100644 --- a/chromium/chromecast/browser/cast_http_user_agent_settings.h +++ b/chromium/chromecast/browser/cast_http_user_agent_settings.h @@ -7,7 +7,6 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/prefs/pref_member.h" #include "net/url_request/http_user_agent_settings.h" namespace chromecast { @@ -16,13 +15,14 @@ namespace shell { class CastHttpUserAgentSettings : public net::HttpUserAgentSettings { public: CastHttpUserAgentSettings(); - virtual ~CastHttpUserAgentSettings(); + ~CastHttpUserAgentSettings() override; // net::HttpUserAgentSettings implementation: - virtual std::string GetAcceptLanguage() const override; - virtual std::string GetUserAgent() const override; + std::string GetAcceptLanguage() const override; + std::string GetUserAgent() const override; private: + mutable std::string last_locale_; mutable std::string accept_language_; DISALLOW_COPY_AND_ASSIGN(CastHttpUserAgentSettings); diff --git a/chromium/chromecast/browser/cast_network_delegate.cc b/chromium/chromecast/browser/cast_network_delegate.cc index ff0fcc958e2..51d9fd4aa5b 100644 --- a/chromium/chromecast/browser/cast_network_delegate.cc +++ b/chromium/chromecast/browser/cast_network_delegate.cc @@ -20,13 +20,10 @@ CastNetworkDelegate::~CastNetworkDelegate() { bool CastNetworkDelegate::OnCanAccessFile(const net::URLRequest& request, const base::FilePath& path) const { -#if defined(OS_ANDROID) - // On Chromecast, there's no reason to allow local file access. if (base::CommandLine::ForCurrentProcess()-> HasSwitch(switches::kEnableLocalFileAccesses)) { return true; } -#endif // defined(OS_ANDROID) LOG(WARNING) << "Could not access file " << path.value() << ". All file accesses are forbidden."; diff --git a/chromium/chromecast/browser/cast_network_delegate.h b/chromium/chromecast/browser/cast_network_delegate.h index 982237f8630..f3372ebbd88 100644 --- a/chromium/chromecast/browser/cast_network_delegate.h +++ b/chromium/chromecast/browser/cast_network_delegate.h @@ -5,7 +5,7 @@ #ifndef CHROMECAST_BROWSER_CAST_NETWORK_DELEGATE_H_ #define CHROMECAST_BROWSER_CAST_NETWORK_DELEGATE_H_ -#include "net/base/network_delegate.h" +#include "net/base/network_delegate_impl.h" namespace net { class X509Certificate; @@ -14,22 +14,23 @@ class X509Certificate; namespace chromecast { namespace shell { -class CastNetworkDelegate : public net::NetworkDelegate { +class CastNetworkDelegate : public net::NetworkDelegateImpl { public: static CastNetworkDelegate* Create(); static net::X509Certificate* DeviceCert(); CastNetworkDelegate(); - virtual ~CastNetworkDelegate(); + ~CastNetworkDelegate() override; virtual void Initialize(bool use_sync_signing) = 0; - virtual bool IsWhitelisted(const GURL& gurl, bool for_device_auth) const = 0; + virtual bool IsWhitelisted(const GURL& gurl, int render_process_id, + bool for_device_auth) const = 0; private: // net::NetworkDelegate implementation: - virtual bool OnCanAccessFile(const net::URLRequest& request, - const base::FilePath& path) const override; + bool OnCanAccessFile(const net::URLRequest& request, + const base::FilePath& path) const override; DISALLOW_COPY_AND_ASSIGN(CastNetworkDelegate); }; diff --git a/chromium/chromecast/browser/cast_network_delegate_simple.cc b/chromium/chromecast/browser/cast_network_delegate_simple.cc index 14aabf5d42d..2ba0cd6dae1 100644 --- a/chromium/chromecast/browser/cast_network_delegate_simple.cc +++ b/chromium/chromecast/browser/cast_network_delegate_simple.cc @@ -18,9 +18,9 @@ class CastNetworkDelegateSimple : public CastNetworkDelegate { private: // CastNetworkDelegate implementation: - virtual void Initialize(bool use_sync_signing) override {} - virtual bool IsWhitelisted(const GURL& gurl, - bool for_device_auth) const override { + void Initialize(bool use_sync_signing) override {} + bool IsWhitelisted(const GURL& gurl, int render_process_id, + bool for_device_auth) const override { return false; } diff --git a/chromium/chromecast/browser/cast_permission_manager.cc b/chromium/chromecast/browser/cast_permission_manager.cc new file mode 100644 index 00000000000..54352c0db79 --- /dev/null +++ b/chromium/chromecast/browser/cast_permission_manager.cc @@ -0,0 +1,72 @@ +// Copyright 2015 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/browser/cast_permission_manager.h" + +#include "base/callback.h" +#include "base/logging.h" +#include "content/public/browser/permission_type.h" + +namespace chromecast { +namespace shell { + +CastPermissionManager::CastPermissionManager() + : content::PermissionManager() { +} + +CastPermissionManager::~CastPermissionManager() { +} + +void CastPermissionManager::RequestPermission( + content::PermissionType permission, + content::WebContents* web_contents, + int request_id, + const GURL& origin, + bool user_gesture, + const base::Callback<void(content::PermissionStatus)>& callback) { + LOG(INFO) << __FUNCTION__ << ": " << static_cast<int>(permission); + callback.Run(content::PermissionStatus::PERMISSION_STATUS_GRANTED); +} + +void CastPermissionManager::CancelPermissionRequest( + content::PermissionType permission, + content::WebContents* web_contents, + int request_id, + const GURL& origin) { +} + +void CastPermissionManager::ResetPermission( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { +} + +content::PermissionStatus CastPermissionManager::GetPermissionStatus( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { + LOG(INFO) << __FUNCTION__ << ": " << static_cast<int>(permission); + return content::PermissionStatus::PERMISSION_STATUS_GRANTED; +} + +void CastPermissionManager::RegisterPermissionUsage( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { +} + +int CastPermissionManager::SubscribePermissionStatusChange( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin, + const base::Callback<void(content::PermissionStatus)>& callback) { + return -1; +} + +void CastPermissionManager::UnsubscribePermissionStatusChange( + int subscription_id) { +} + +} // namespace shell +} // namespace chromecast diff --git a/chromium/chromecast/browser/cast_permission_manager.h b/chromium/chromecast/browser/cast_permission_manager.h new file mode 100644 index 00000000000..c82f08a4be2 --- /dev/null +++ b/chromium/chromecast/browser/cast_permission_manager.h @@ -0,0 +1,56 @@ +// Copyright 2015 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_BROWSER_CAST_PERMISSION_MANAGER_H_ +#define CHROMECAST_BROWSER_CAST_PERMISSION_MANAGER_H_ + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "content/public/browser/permission_manager.h" + +namespace chromecast { +namespace shell { + +class CastPermissionManager : public content::PermissionManager { + public: + CastPermissionManager(); + ~CastPermissionManager() override; + + // content::PermissionManager implementation: + void RequestPermission( + content::PermissionType permission, + content::WebContents* web_contents, + int request_id, + const GURL& requesting_origin, + bool user_gesture, + const base::Callback<void(content::PermissionStatus)>& callback) override; + void CancelPermissionRequest(content::PermissionType permission, + content::WebContents* web_contents, + int request_id, + const GURL& requesting_origin) override; + void ResetPermission(content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + content::PermissionStatus GetPermissionStatus( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + void RegisterPermissionUsage(content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + int SubscribePermissionStatusChange( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin, + const base::Callback<void(content::PermissionStatus)>& callback) override; + void UnsubscribePermissionStatusChange(int subscription_id) override; + + private: + DISALLOW_COPY_AND_ASSIGN(CastPermissionManager); +}; + +} // namespace shell +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_CAST_PERMISSION_MANAGER_H_ diff --git a/chromium/chromecast/browser/cast_quota_permission_context.cc b/chromium/chromecast/browser/cast_quota_permission_context.cc new file mode 100644 index 00000000000..e0ca80460e9 --- /dev/null +++ b/chromium/chromecast/browser/cast_quota_permission_context.cc @@ -0,0 +1,23 @@ +// Copyright 2015 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/browser/cast_quota_permission_context.h" + +namespace chromecast { + +CastQuotaPermissionContext::CastQuotaPermissionContext() { +} + +CastQuotaPermissionContext::~CastQuotaPermissionContext() { +} + +void CastQuotaPermissionContext::RequestQuotaPermission( + const content::StorageQuotaParams& params, + int render_process_id, + const PermissionCallback& callback) { + callback.Run( + content::QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_ALLOW); +} + +} // namespace chromecast diff --git a/chromium/chromecast/browser/cast_quota_permission_context.h b/chromium/chromecast/browser/cast_quota_permission_context.h new file mode 100644 index 00000000000..2dc418616c7 --- /dev/null +++ b/chromium/chromecast/browser/cast_quota_permission_context.h @@ -0,0 +1,30 @@ +// Copyright 2015 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_BROWSER_CAST_QUOTA_PERMISSION_CONTEXT_H_ +#define CHROMECAST_BROWSER_CAST_QUOTA_PERMISSION_CONTEXT_H_ + +#include "base/macros.h" +#include "content/public/browser/quota_permission_context.h" + +namespace chromecast { + +class CastQuotaPermissionContext : public content::QuotaPermissionContext { + public: + CastQuotaPermissionContext(); + + // content::QuotaPermissionContext implementation: + void RequestQuotaPermission(const content::StorageQuotaParams& params, + int render_process_id, + const PermissionCallback& callback) override; + + private: + ~CastQuotaPermissionContext() override; + + DISALLOW_COPY_AND_ASSIGN(CastQuotaPermissionContext); +}; + +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_CAST_QUOTA_PERMISSION_CONTEXT_H_ diff --git a/chromium/chromecast/browser/cast_resource_dispatcher_host_delegate.cc b/chromium/chromecast/browser/cast_resource_dispatcher_host_delegate.cc new file mode 100644 index 00000000000..9a935fb3923 --- /dev/null +++ b/chromium/chromecast/browser/cast_resource_dispatcher_host_delegate.cc @@ -0,0 +1,26 @@ +// Copyright 2015 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/browser/cast_resource_dispatcher_host_delegate.h" + +#include "chromecast/browser/cast_browser_process.h" +#include "chromecast/net/connectivity_checker.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_request.h" + +namespace chromecast { +namespace shell { + +void CastResourceDispatcherHostDelegate::RequestComplete( + net::URLRequest* url_request) { + if (!url_request->status().is_success()) { + LOG(ERROR) << "Failed to load resource " << url_request->url() + << "; status:" << url_request->status().status() << ", error:" + << net::ErrorToShortString(url_request->status().error()); + CastBrowserProcess::GetInstance()->connectivity_checker()->Check(); + } +} + +} // namespace shell +} // namespace chromecast diff --git a/chromium/chromecast/browser/cast_resource_dispatcher_host_delegate.h b/chromium/chromecast/browser/cast_resource_dispatcher_host_delegate.h new file mode 100644 index 00000000000..8295ccd0706 --- /dev/null +++ b/chromium/chromecast/browser/cast_resource_dispatcher_host_delegate.h @@ -0,0 +1,29 @@ +// Copyright 2015 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_BROWSER_CAST_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ +#define CHROMECAST_BROWSER_CAST_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ + +#include "base/macros.h" +#include "content/public/browser/resource_dispatcher_host_delegate.h" + +namespace chromecast { +namespace shell { + +class CastResourceDispatcherHostDelegate + : public content::ResourceDispatcherHostDelegate { + public: + CastResourceDispatcherHostDelegate() {} + + // content::ResourceDispatcherHostDelegate implementation: + void RequestComplete(net::URLRequest* url_request) override; + + private: + DISALLOW_COPY_AND_ASSIGN(CastResourceDispatcherHostDelegate); +}; + +} // namespace shell +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_CAST_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ diff --git a/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.cc b/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.cc index 354e384e228..93f17044a90 100644 --- a/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.cc +++ b/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.cc @@ -4,94 +4,13 @@ #include "chromecast/browser/devtools/cast_dev_tools_delegate.h" -#include "base/files/file_path.h" #include "base/macros.h" -#include "base/strings/utf_string_conversions.h" -#include "content/public/browser/devtools_agent_host.h" -#include "content/public/browser/devtools_target.h" -#include "content/public/browser/favicon_status.h" -#include "content/public/browser/navigation_entry.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_delegate.h" #include "grit/shell_resources.h" #include "ui/base/resource/resource_bundle.h" namespace chromecast { namespace shell { -namespace { - -const char kTargetTypePage[] = "page"; -const char kTargetTypeServiceWorker[] = "service_worker"; -const char kTargetTypeSharedWorker[] = "worker"; -const char kTargetTypeOther[] = "other"; - -class Target : public content::DevToolsTarget { - public: - explicit Target(scoped_refptr<content::DevToolsAgentHost> agent_host); - - virtual std::string GetId() const override { return agent_host_->GetId(); } - virtual std::string GetParentId() const override { return std::string(); } - virtual std::string GetType() const override { - switch (agent_host_->GetType()) { - case content::DevToolsAgentHost::TYPE_WEB_CONTENTS: - return kTargetTypePage; - case content::DevToolsAgentHost::TYPE_SERVICE_WORKER: - return kTargetTypeServiceWorker; - case content::DevToolsAgentHost::TYPE_SHARED_WORKER: - return kTargetTypeSharedWorker; - default: - break; - } - return kTargetTypeOther; - } - virtual std::string GetTitle() const override { - return agent_host_->GetTitle(); - } - virtual std::string GetDescription() const override { return std::string(); } - virtual GURL GetURL() const override { - return agent_host_->GetURL(); - } - virtual GURL GetFaviconURL() const override { return favicon_url_; } - virtual base::TimeTicks GetLastActivityTime() const override { - return last_activity_time_; - } - virtual bool IsAttached() const override { - return agent_host_->IsAttached(); - } - virtual scoped_refptr<content::DevToolsAgentHost> GetAgentHost() - const override { - return agent_host_; - } - virtual bool Activate() const override { - return agent_host_->Activate(); - } - virtual bool Close() const override { - return agent_host_->Close(); - } - - private: - scoped_refptr<content::DevToolsAgentHost> agent_host_; - GURL favicon_url_; - base::TimeTicks last_activity_time_; - - DISALLOW_COPY_AND_ASSIGN(Target); -}; - -Target::Target(scoped_refptr<content::DevToolsAgentHost> agent_host) - : agent_host_(agent_host) { - if (content::WebContents* web_contents = agent_host_->GetWebContents()) { - content::NavigationController& controller = web_contents->GetController(); - content::NavigationEntry* entry = controller.GetActiveEntry(); - if (entry != NULL && entry->GetURL().is_valid()) - favicon_url_ = entry->GetFavicon().url; - last_activity_time_ = web_contents->GetLastActiveTime(); - } -} - -} // namespace - // CastDevToolsDelegate ----------------------------------------------------- CastDevToolsDelegate::CastDevToolsDelegate() { @@ -109,54 +28,13 @@ std::string CastDevToolsDelegate::GetDiscoveryPageHTML() { #endif // defined(OS_ANDROID) } -bool CastDevToolsDelegate::BundlesFrontendResources() { - return false; -} - -base::FilePath CastDevToolsDelegate::GetDebugFrontendDir() { - return base::FilePath(); -} - -scoped_ptr<net::StreamListenSocket> -CastDevToolsDelegate::CreateSocketForTethering( - net::StreamListenSocket::Delegate* delegate, - std::string* name) { - return scoped_ptr<net::StreamListenSocket>(); -} - -// CastDevToolsManagerDelegate ----------------------------------------------- - -CastDevToolsManagerDelegate::CastDevToolsManagerDelegate() { -} - -CastDevToolsManagerDelegate::~CastDevToolsManagerDelegate() { -} - -base::DictionaryValue* CastDevToolsManagerDelegate::HandleCommand( - content::DevToolsAgentHost* agent_host, - base::DictionaryValue* command) { - return NULL; -} - -std::string CastDevToolsManagerDelegate::GetPageThumbnailData( - const GURL& url) { - return ""; -} - -scoped_ptr<content::DevToolsTarget> -CastDevToolsManagerDelegate::CreateNewTarget(const GURL& url) { - return scoped_ptr<content::DevToolsTarget>(); +std::string CastDevToolsDelegate::GetFrontendResource( + const std::string& path) { + return std::string(); } -void CastDevToolsManagerDelegate::EnumerateTargets(TargetCallback callback) { - TargetList targets; - content::DevToolsAgentHost::List agents = - content::DevToolsAgentHost::GetOrCreateAll(); - for (content::DevToolsAgentHost::List::iterator it = agents.begin(); - it != agents.end(); ++it) { - targets.push_back(new Target(*it)); - } - callback.Run(targets); +std::string CastDevToolsDelegate::GetPageThumbnailData(const GURL& url) { + return std::string(); } } // namespace shell diff --git a/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.h b/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.h index 18160708337..190abec1244 100644 --- a/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.h +++ b/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.h @@ -5,62 +5,27 @@ #ifndef CHROMECAST_BROWSER_DEVTOOLS_CAST_DEV_TOOLS_DELEGATE_H_ #define CHROMECAST_BROWSER_DEVTOOLS_CAST_DEV_TOOLS_DELEGATE_H_ -#include "content/public/browser/devtools_http_handler_delegate.h" -#include "content/public/browser/devtools_manager_delegate.h" +#include "components/devtools_http_handler/devtools_http_handler_delegate.h" #include "net/socket/stream_listen_socket.h" -namespace base { -class FilePath; -} - -namespace content { -class BrowserContext; -} - namespace chromecast { namespace shell { -class CastDevToolsDelegate : public content::DevToolsHttpHandlerDelegate { +class CastDevToolsDelegate : + public devtools_http_handler::DevToolsHttpHandlerDelegate { public: CastDevToolsDelegate(); - virtual ~CastDevToolsDelegate(); + ~CastDevToolsDelegate() override; - // DevToolsHttpHandlerDelegate implementation. - virtual std::string GetDiscoveryPageHTML() override; - virtual bool BundlesFrontendResources() override; - virtual base::FilePath GetDebugFrontendDir() override; - virtual scoped_ptr<net::StreamListenSocket> CreateSocketForTethering( - net::StreamListenSocket::Delegate* delegate, - std::string* name) override; + // devtools_http_handler::DevToolsHttpHandlerDelegate implementation. + std::string GetDiscoveryPageHTML() override; + std::string GetFrontendResource(const std::string& path) override; + std::string GetPageThumbnailData(const GURL& url) override; private: DISALLOW_COPY_AND_ASSIGN(CastDevToolsDelegate); }; -class CastDevToolsManagerDelegate : public content::DevToolsManagerDelegate { - public: - CastDevToolsManagerDelegate(); - virtual ~CastDevToolsManagerDelegate(); - - // DevToolsManagerDelegate implementation. - virtual void Inspect( - content::BrowserContext* browser_context, - content::DevToolsAgentHost* agent_host) override {} - virtual void DevToolsAgentStateChanged( - content::DevToolsAgentHost* agent_host, - bool attached) override {} - virtual base::DictionaryValue* HandleCommand( - content::DevToolsAgentHost* agent_host, - base::DictionaryValue* command) override; - virtual scoped_ptr<content::DevToolsTarget> CreateNewTarget( - const GURL& url) override; - virtual void EnumerateTargets(TargetCallback callback) override; - virtual std::string GetPageThumbnailData(const GURL& url) override; - - private: - DISALLOW_COPY_AND_ASSIGN(CastDevToolsManagerDelegate); -}; - } // namespace shell } // namespace chromecast diff --git a/chromium/chromecast/browser/devtools/remote_debugging_server.cc b/chromium/chromecast/browser/devtools/remote_debugging_server.cc index e0abdc072d8..6391b6d6303 100644 --- a/chromium/chromecast/browser/devtools/remote_debugging_server.cc +++ b/chromium/chromecast/browser/devtools/remote_debugging_server.cc @@ -9,14 +9,16 @@ #include "base/command_line.h" #include "base/files/file_path.h" #include "base/strings/stringprintf.h" +#include "chromecast/browser/cast_browser_process.h" #include "chromecast/browser/devtools/cast_dev_tools_delegate.h" -#include "chromecast/common/chromecast_config.h" +#include "chromecast/common/cast_content_client.h" #include "chromecast/common/pref_names.h" +#include "components/devtools_http_handler/devtools_http_handler.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/devtools_http_handler.h" #include "content/public/common/content_switches.h" #include "content/public/common/user_agent.h" +#include "net/base/net_errors.h" #include "net/socket/tcp_server_socket.h" #if defined(OS_ANDROID) @@ -24,54 +26,71 @@ #include "net/socket/unix_domain_server_socket_posix.h" #endif // defined(OS_ANDROID) +using devtools_http_handler::DevToolsHttpHandler; + namespace chromecast { namespace shell { namespace { const char kFrontEndURL[] = - "https://chrome-devtools-frontend.appspot.com/serve_rev/%s/devtools.html"; -const int kDefaultRemoteDebuggingPort = 9222; + "https://chrome-devtools-frontend.appspot.com/serve_rev/%s/inspector.html"; +const uint16 kDefaultRemoteDebuggingPort = 9222; + +const int kBackLog = 10; #if defined(OS_ANDROID) class UnixDomainServerSocketFactory - : public content::DevToolsHttpHandler::ServerSocketFactory { + : public DevToolsHttpHandler::ServerSocketFactory { public: explicit UnixDomainServerSocketFactory(const std::string& socket_name) - : content::DevToolsHttpHandler::ServerSocketFactory(socket_name, 0, 1) {} + : socket_name_(socket_name) {} private: - // content::DevToolsHttpHandler::ServerSocketFactory. - virtual scoped_ptr<net::ServerSocket> Create() const override { - return scoped_ptr<net::ServerSocket>( + // devtools_http_handler::DevToolsHttpHandler::ServerSocketFactory. + scoped_ptr<net::ServerSocket> CreateForHttpServer() override { + scoped_ptr<net::ServerSocket> socket( new net::UnixDomainServerSocket( base::Bind(&content::CanUserConnectToDevTools), true /* use_abstract_namespace */)); + if (socket->ListenWithAddressAndPort(socket_name_, 0, kBackLog) != net::OK) + return scoped_ptr<net::ServerSocket>(); + + return socket; } + std::string socket_name_; + DISALLOW_COPY_AND_ASSIGN(UnixDomainServerSocketFactory); }; #else class TCPServerSocketFactory - : public content::DevToolsHttpHandler::ServerSocketFactory { + : public DevToolsHttpHandler::ServerSocketFactory { public: - TCPServerSocketFactory(const std::string& address, int port, int backlog) - : content::DevToolsHttpHandler::ServerSocketFactory( - address, port, backlog) {} + TCPServerSocketFactory(const std::string& address, uint16 port) + : address_(address), port_(port) { + } private: - // content::DevToolsHttpHandler::ServerSocketFactory. - virtual scoped_ptr<net::ServerSocket> Create() const override { - return scoped_ptr<net::ServerSocket>( - new net::TCPServerSocket(NULL, net::NetLog::Source())); + // devtools_http_handler::DevToolsHttpHandler::ServerSocketFactory. + scoped_ptr<net::ServerSocket> CreateForHttpServer() override { + scoped_ptr<net::ServerSocket> socket( + new net::TCPServerSocket(nullptr, net::NetLog::Source())); + if (socket->ListenWithAddressAndPort(address_, port_, kBackLog) != net::OK) + return scoped_ptr<net::ServerSocket>(); + + return socket; } + std::string address_; + uint16 port_; + DISALLOW_COPY_AND_ASSIGN(TCPServerSocketFactory); }; #endif -scoped_ptr<content::DevToolsHttpHandler::ServerSocketFactory> -CreateSocketFactory(int port) { +scoped_ptr<DevToolsHttpHandler::ServerSocketFactory> +CreateSocketFactory(uint16 port) { #if defined(OS_ANDROID) base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); std::string socket_name = "cast_shell_devtools_remote"; @@ -79,11 +98,11 @@ CreateSocketFactory(int port) { socket_name = command_line->GetSwitchValueASCII( switches::kRemoteDebuggingSocketName); } - return scoped_ptr<content::DevToolsHttpHandler::ServerSocketFactory>( + return scoped_ptr<DevToolsHttpHandler::ServerSocketFactory>( new UnixDomainServerSocketFactory(socket_name)); #else - return scoped_ptr<content::DevToolsHttpHandler::ServerSocketFactory>( - new TCPServerSocketFactory("0.0.0.0", port, 1)); + return scoped_ptr<DevToolsHttpHandler::ServerSocketFactory>( + new TCPServerSocketFactory("0.0.0.0", port)); #endif } @@ -93,12 +112,10 @@ std::string GetFrontendUrl() { } // namespace -RemoteDebuggingServer::RemoteDebuggingServer() - : devtools_http_handler_(NULL), - port_(0) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); +RemoteDebuggingServer::RemoteDebuggingServer() : port_(0) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); pref_port_.Init(prefs::kRemoteDebuggingPort, - ChromecastConfig::GetInstance()->pref_service(), + CastBrowserProcess::GetInstance()->pref_service(), base::Bind(&RemoteDebuggingServer::OnPortChanged, base::Unretained(this))); @@ -115,10 +132,7 @@ RemoteDebuggingServer::~RemoteDebuggingServer() { } void RemoteDebuggingServer::OnPortChanged() { - int new_port = *pref_port_; - if (new_port < 0) { - new_port = 0; - } + uint16 new_port = static_cast<uint16>(std::max(*pref_port_, 0)); VLOG(1) << "OnPortChanged called: old_port=" << port_ << ", new_port=" << new_port; @@ -129,18 +143,19 @@ void RemoteDebuggingServer::OnPortChanged() { if (devtools_http_handler_) { LOG(INFO) << "Stop old devtools: port=" << port_; - // Note: Stop destroys devtools_http_handler_. - devtools_http_handler_->Stop(); - devtools_http_handler_ = NULL; + devtools_http_handler_.reset(); } port_ = new_port; if (port_ > 0) { - devtools_http_handler_ = content::DevToolsHttpHandler::Start( + devtools_http_handler_.reset(new DevToolsHttpHandler( CreateSocketFactory(port_), GetFrontendUrl(), new CastDevToolsDelegate(), - base::FilePath()); + base::FilePath(), + base::FilePath(), + std::string(), + GetUserAgent())); LOG(INFO) << "Devtools started: port=" << port_; } } diff --git a/chromium/chromecast/browser/devtools/remote_debugging_server.h b/chromium/chromecast/browser/devtools/remote_debugging_server.h index 3248bb77828..399b34449f9 100644 --- a/chromium/chromecast/browser/devtools/remote_debugging_server.h +++ b/chromium/chromecast/browser/devtools/remote_debugging_server.h @@ -5,15 +5,18 @@ #ifndef CHROMECAST_BROWSER_DEVTOOLS_REMOTE_DEBUGGING_SERVER_H_ #define CHROMECAST_BROWSER_DEVTOOLS_REMOTE_DEBUGGING_SERVER_H_ +#include "base/memory/scoped_ptr.h" #include "base/prefs/pref_member.h" -namespace content { +namespace devtools_http_handler { class DevToolsHttpHandler; -} // namespace content +} namespace chromecast { namespace shell { +class CastDevToolsManagerDelegate; + class RemoteDebuggingServer { public: RemoteDebuggingServer(); @@ -27,10 +30,10 @@ class RemoteDebuggingServer { // on device startup. bool ShouldStartImmediately(); - content::DevToolsHttpHandler* devtools_http_handler_; + scoped_ptr<devtools_http_handler::DevToolsHttpHandler> devtools_http_handler_; IntegerPrefMember pref_port_; - int port_; + uint16 port_; DISALLOW_COPY_AND_ASSIGN(RemoteDebuggingServer); }; diff --git a/chromium/chromecast/browser/geolocation/cast_access_token_store.h b/chromium/chromecast/browser/geolocation/cast_access_token_store.h index 02bbc7cbafb..27de8b3b2ed 100644 --- a/chromium/chromecast/browser/geolocation/cast_access_token_store.h +++ b/chromium/chromecast/browser/geolocation/cast_access_token_store.h @@ -22,12 +22,11 @@ class CastAccessTokenStore : public content::AccessTokenStore { explicit CastAccessTokenStore(content::BrowserContext* browser_context); private: - virtual ~CastAccessTokenStore(); + ~CastAccessTokenStore() override; // AccessTokenStore implementation: - virtual void LoadAccessTokens( - const LoadAccessTokensCallbackType& callback) override; - virtual void SaveAccessToken( + void LoadAccessTokens(const LoadAccessTokensCallbackType& callback) override; + void SaveAccessToken( const GURL& server_url, const base::string16& access_token) override; void GetRequestContextGetterOnUIThread(); diff --git a/chromium/chromecast/browser/media/cast_browser_cdm_factory.cc b/chromium/chromecast/browser/media/cast_browser_cdm_factory.cc new file mode 100644 index 00000000000..61d80019090 --- /dev/null +++ b/chromium/chromecast/browser/media/cast_browser_cdm_factory.cc @@ -0,0 +1,58 @@ +// 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/browser/media/cast_browser_cdm_factory.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "chromecast/browser/media/cma_message_loop.h" +#include "chromecast/media/cdm/browser_cdm_cast.h" +#include "media/base/bind_to_current_loop.h" +#include "media/base/cdm_key_information.h" + +namespace chromecast { +namespace media { + +scoped_ptr<::media::BrowserCdm> CastBrowserCdmFactory::CreateBrowserCdm( + const std::string& key_system_name, + bool use_hw_secure_codecs, + const ::media::SessionMessageCB& session_message_cb, + const ::media::SessionClosedCB& session_closed_cb, + const ::media::LegacySessionErrorCB& legacy_session_error_cb, + const ::media::SessionKeysChangeCB& session_keys_change_cb, + const ::media::SessionExpirationUpdateCB& session_expiration_update_cb) { + DCHECK(!use_hw_secure_codecs) + << "Chromecast does not use |use_hw_secure_codecs|"; + + CastKeySystem key_system(GetKeySystemByName(key_system_name)); + + scoped_ptr<chromecast::media::BrowserCdmCast> browser_cdm; + if (key_system == chromecast::media::KEY_SYSTEM_CLEAR_KEY) { + // TODO(gunsch): handle ClearKey decryption. See crbug.com/441957 + } else { + browser_cdm = CreatePlatformBrowserCdm(key_system); + } + + if (browser_cdm) { + CmaMessageLoop::GetMessageLoopProxy()->PostTask( + FROM_HERE, + base::Bind(&BrowserCdmCast::Initialize, + base::Unretained(browser_cdm.get()), + ::media::BindToCurrentLoop(session_message_cb), + ::media::BindToCurrentLoop(session_closed_cb), + ::media::BindToCurrentLoop(legacy_session_error_cb), + ::media::BindToCurrentLoop(session_keys_change_cb), + ::media::BindToCurrentLoop(session_expiration_update_cb))); + return make_scoped_ptr( + new BrowserCdmCastUi(browser_cdm.Pass(), + CmaMessageLoop::GetMessageLoopProxy())); + } + + LOG(INFO) << "No matching key system found."; + return scoped_ptr< ::media::BrowserCdm>(); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/browser/media/cast_browser_cdm_factory.h b/chromium/chromecast/browser/media/cast_browser_cdm_factory.h new file mode 100644 index 00000000000..ad59bed81eb --- /dev/null +++ b/chromium/chromecast/browser/media/cast_browser_cdm_factory.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_BROWSER_MEDIA_CAST_BROWSER_CDM_FACTORY_H_ +#define CHROMECAST_BROWSER_MEDIA_CAST_BROWSER_CDM_FACTORY_H_ + +#include "chromecast/media/base/key_systems_common.h" +#include "media/base/browser_cdm.h" +#include "media/base/browser_cdm_factory.h" + +namespace chromecast { +namespace media { + +class BrowserCdmCast; + +class CastBrowserCdmFactory : public ::media::BrowserCdmFactory { + public: + CastBrowserCdmFactory() {} + ~CastBrowserCdmFactory() override {}; + + // ::media::BrowserCdmFactory implementation: + scoped_ptr<::media::BrowserCdm> CreateBrowserCdm( + const std::string& key_system, + bool use_hw_secure_codecs, + const ::media::SessionMessageCB& session_message_cb, + const ::media::SessionClosedCB& session_closed_cb, + const ::media::LegacySessionErrorCB& legacy_session_error_cb, + const ::media::SessionKeysChangeCB& session_keys_change_cb, + const ::media::SessionExpirationUpdateCB& session_expiration_update_cb) + override; + + private: + DISALLOW_COPY_AND_ASSIGN(CastBrowserCdmFactory); +}; + +// Allow platform-specific CDMs to be provided. +scoped_ptr<BrowserCdmCast> CreatePlatformBrowserCdm( + const CastKeySystem& key_system); + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_MEDIA_CAST_BROWSER_CDM_FACTORY_H_ diff --git a/chromium/chromecast/browser/media/cast_browser_cdm_factory_simple.cc b/chromium/chromecast/browser/media/cast_browser_cdm_factory_simple.cc new file mode 100644 index 00000000000..d9ebda176e1 --- /dev/null +++ b/chromium/chromecast/browser/media/cast_browser_cdm_factory_simple.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/browser/media/cast_browser_cdm_factory.h" + +#include "chromecast/media/cdm/browser_cdm_cast.h" + +namespace chromecast { +namespace media { + +scoped_ptr<BrowserCdmCast> CreatePlatformBrowserCdm( + const CastKeySystem& key_system) { + return scoped_ptr<BrowserCdmCast>(); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/browser/media/cast_media_client_android.cc b/chromium/chromecast/browser/media/cast_media_client_android.cc new file mode 100644 index 00000000000..696471e6c0d --- /dev/null +++ b/chromium/chromecast/browser/media/cast_media_client_android.cc @@ -0,0 +1,44 @@ +// Copyright 2015 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/browser/media/cast_media_client_android.h" + +#include "chromecast/media/base/key_systems_common.h" + +namespace chromecast { +namespace media { + +CastMediaClientAndroid::CastMediaClientAndroid() { +} + +CastMediaClientAndroid::~CastMediaClientAndroid() { +} + +void CastMediaClientAndroid::AddKeySystemUUIDMappings(KeySystemUuidMap* map) { + // Note: MediaDrmBridge adds the Widevine UUID mapping automatically. +#if defined(PLAYREADY_CDM_AVAILABLE) + (*map)[kChromecastPlayreadyKeySystem] = playready_delegate_.GetUUID(); +#endif + + auto platform_mappings = GetPlatformKeySystemUUIDMappings(); + for (const auto& mapping : platform_mappings) + map->insert(mapping); +} + +::media::MediaDrmBridgeDelegate* +CastMediaClientAndroid::GetMediaDrmBridgeDelegate( + const ::media::UUID& scheme_uuid) { +#if defined(PLAYREADY_CDM_AVAILABLE) + if (scheme_uuid == playready_delegate_.GetUUID()) + return &playready_delegate_; +#endif + + if (scheme_uuid == widevine_delegate_.GetUUID()) + return &widevine_delegate_; + + return nullptr; +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/browser/media/cast_media_client_android.h b/chromium/chromecast/browser/media/cast_media_client_android.h new file mode 100644 index 00000000000..a7aaa1d9b2e --- /dev/null +++ b/chromium/chromecast/browser/media/cast_media_client_android.h @@ -0,0 +1,41 @@ +// Copyright 2015 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_BROWSER_MEDIA_CAST_MEDIA_CLIENT_ANDROID_H_ +#define CHROMECAST_BROWSER_MEDIA_CAST_MEDIA_CLIENT_ANDROID_H_ + +#include <map> + +#include "base/macros.h" +#include "chromecast/media/cdm/playready_drm_delegate_android.h" +#include "components/cdm/browser/widevine_drm_delegate_android.h" +#include "media/base/android/media_client_android.h" + +namespace chromecast { +namespace media { + +class CastMediaClientAndroid : public ::media::MediaClientAndroid { + public: + CastMediaClientAndroid(); + ~CastMediaClientAndroid() override; + + private: + // ::media::MediaClientAndroid implementation: + void AddKeySystemUUIDMappings(KeySystemUuidMap* map) override; + ::media::MediaDrmBridgeDelegate* GetMediaDrmBridgeDelegate( + const ::media::UUID& scheme_uuid) override; + +#if defined(PLAYREADY_CDM_AVAILABLE) + PlayreadyDrmDelegateAndroid playready_delegate_; +#endif + + cdm::WidevineDrmDelegateAndroid widevine_delegate_; + + DISALLOW_COPY_AND_ASSIGN(CastMediaClientAndroid); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_MEDIA_CAST_MEDIA_CLIENT_ANDROID_H_ diff --git a/chromium/chromecast/browser/media/cma_message_filter_host.cc b/chromium/chromecast/browser/media/cma_message_filter_host.cc new file mode 100644 index 00000000000..d3664c915a6 --- /dev/null +++ b/chromium/chromecast/browser/media/cma_message_filter_host.cc @@ -0,0 +1,540 @@ +// 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/browser/media/cma_message_filter_host.h" + +#include <utility> + +#include "base/lazy_instance.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/sync_socket.h" +#include "chromecast/browser/media/cma_message_loop.h" +#include "chromecast/browser/media/media_pipeline_host.h" +#include "chromecast/common/media/cma_messages.h" +#include "chromecast/media/cdm/browser_cdm_cast.h" +#include "chromecast/media/cma/backend/video_plane.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" +#include "chromecast/media/cma/pipeline/media_pipeline_client.h" +#include "chromecast/media/cma/pipeline/video_pipeline_client.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" +#include "media/base/bind_to_current_loop.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/quad_f.h" +#include "ui/gfx/geometry/size.h" + +namespace chromecast { +namespace media { + +#define FORWARD_CALL(arg_pipeline, arg_fn, ...) \ + task_runner_->PostTask( \ + FROM_HERE, \ + base::Bind(&MediaPipelineHost::arg_fn, \ + base::Unretained(arg_pipeline), __VA_ARGS__)) + +namespace { + +const size_t kMaxSharedMem = 8 * 1024 * 1024; + +typedef std::map<uint64_t, MediaPipelineHost*> MediaPipelineCmaMap; + +// Map of MediaPipelineHost instances that is accessed only from the CMA thread. +// The existence of a MediaPipelineHost* in this map implies that the instance +// is still valid. +base::LazyInstance<MediaPipelineCmaMap> g_pipeline_map_cma = + LAZY_INSTANCE_INITIALIZER; + +uint64_t GetPipelineCmaId(int process_id, int media_id) { + return (static_cast<uint64>(process_id) << 32) + + static_cast<uint64>(media_id); +} + +MediaPipelineHost* GetMediaPipeline(int process_id, int media_id) { + DCHECK(CmaMessageLoop::GetTaskRunner()->BelongsToCurrentThread()); + MediaPipelineCmaMap::iterator it = + g_pipeline_map_cma.Get().find(GetPipelineCmaId(process_id, media_id)); + if (it == g_pipeline_map_cma.Get().end()) + return nullptr; + return it->second; +} + +void SetMediaPipeline(int process_id, int media_id, MediaPipelineHost* host) { + DCHECK(CmaMessageLoop::GetTaskRunner()->BelongsToCurrentThread()); + std::pair<MediaPipelineCmaMap::iterator, bool> ret = + g_pipeline_map_cma.Get().insert( + std::make_pair(GetPipelineCmaId(process_id, media_id), host)); + + // Check there is no other entry with the same ID. + DCHECK(ret.second != false); +} + +void DestroyMediaPipeline(int process_id, + int media_id, + scoped_ptr<MediaPipelineHost> media_pipeline) { + DCHECK(CmaMessageLoop::GetTaskRunner()->BelongsToCurrentThread()); + MediaPipelineCmaMap::iterator it = + g_pipeline_map_cma.Get().find(GetPipelineCmaId(process_id, media_id)); + if (it != g_pipeline_map_cma.Get().end()) + g_pipeline_map_cma.Get().erase(it); +} + +void SetCdmOnCmaThread(int render_process_id, int media_id, + BrowserCdmCast* cdm) { + MediaPipelineHost* pipeline = GetMediaPipeline(render_process_id, media_id); + if (!pipeline) { + LOG(WARNING) << "MediaPipelineHost not alive: " << render_process_id << "," + << media_id; + return; + } + + pipeline->SetCdm(cdm); +} + +// BrowserCdm instance must be retrieved/accessed on the UI thread, then +// passed to MediaPipelineHost on CMA thread. +void SetCdmOnUiThread( + int render_process_id, + int render_frame_id, + int media_id, + int cdm_id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + content::RenderProcessHost* host = + content::RenderProcessHost::FromID(render_process_id); + if (!host) { + LOG(ERROR) << "RenderProcessHost not alive for ID: " << render_process_id; + return; + } + + ::media::BrowserCdm* cdm = host->GetBrowserCdm(render_frame_id, cdm_id); + if (!cdm) { + LOG(WARNING) << "Could not find BrowserCdm (" << render_frame_id << "," + << cdm_id << ")"; + return; + } + + BrowserCdmCast* browser_cdm_cast = + static_cast<BrowserCdmCastUi*>(cdm)->browser_cdm_cast(); + CmaMessageLoop::GetTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&SetCdmOnCmaThread, + render_process_id, + media_id, + browser_cdm_cast)); +} + +void UpdateVideoSurfaceHost(int surface_id, const gfx::QuadF& quad) { + // Currently supports only one video plane. + CHECK_EQ(surface_id, 0); + + VideoPlane* video_plane = GetVideoPlane(); + video_plane->SetGeometry( + quad, + VideoPlane::COORDINATE_TYPE_GRAPHICS_PLANE); +} + +} // namespace + +CmaMessageFilterHost::CmaMessageFilterHost(int render_process_id) + : content::BrowserMessageFilter(CastMediaMsgStart), + process_id_(render_process_id), + task_runner_(CmaMessageLoop::GetMessageLoopProxy()), + weak_factory_(this) { + weak_this_ = weak_factory_.GetWeakPtr(); +} + +CmaMessageFilterHost::~CmaMessageFilterHost() { + DCHECK(media_pipelines_.empty()); +} + +void CmaMessageFilterHost::OnChannelClosing() { + content::BrowserMessageFilter::OnChannelClosing(); + DeleteEntries(); +} + +void CmaMessageFilterHost::OnDestruct() const { + content::BrowserThread::DeleteOnIOThread::Destruct(this); +} + +bool CmaMessageFilterHost::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(CmaMessageFilterHost, message) + IPC_MESSAGE_HANDLER(CmaHostMsg_CreateMedia, CreateMedia) + IPC_MESSAGE_HANDLER(CmaHostMsg_DestroyMedia, DestroyMedia) + IPC_MESSAGE_HANDLER(CmaHostMsg_SetCdm, SetCdm) + IPC_MESSAGE_HANDLER(CmaHostMsg_CreateAvPipe, CreateAvPipe) + IPC_MESSAGE_HANDLER(CmaHostMsg_AudioInitialize, AudioInitialize) + IPC_MESSAGE_HANDLER(CmaHostMsg_VideoInitialize, VideoInitialize) + IPC_MESSAGE_HANDLER(CmaHostMsg_StartPlayingFrom, StartPlayingFrom) + IPC_MESSAGE_HANDLER(CmaHostMsg_Flush, Flush) + IPC_MESSAGE_HANDLER(CmaHostMsg_Stop, Stop) + IPC_MESSAGE_HANDLER(CmaHostMsg_SetPlaybackRate, SetPlaybackRate) + IPC_MESSAGE_HANDLER(CmaHostMsg_SetVolume, SetVolume) + IPC_MESSAGE_HANDLER(CmaHostMsg_NotifyPipeWrite, NotifyPipeWrite) + IPC_MESSAGE_HANDLER(CmaHostMsg_NotifyExternalSurface, + NotifyExternalSurface) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +void CmaMessageFilterHost::DeleteEntries() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + for (MediaPipelineMap::iterator it = media_pipelines_.begin(); + it != media_pipelines_.end(); ) { + scoped_ptr<MediaPipelineHost> media_pipeline(it->second); + media_pipelines_.erase(it++); + task_runner_->PostTask( + FROM_HERE, + base::Bind(&DestroyMediaPipeline, process_id_, it->first, + base::Passed(&media_pipeline))); + } +} + +MediaPipelineHost* CmaMessageFilterHost::LookupById(int media_id) { + MediaPipelineMap::iterator it = media_pipelines_.find(media_id); + if (it == media_pipelines_.end()) + return NULL; + return it->second; +} + + +// *** Handle incoming messages *** + +void CmaMessageFilterHost::CreateMedia(int media_id, LoadType load_type) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + scoped_ptr<MediaPipelineHost> media_pipeline_host(new MediaPipelineHost()); + MediaPipelineClient client; + client.time_update_cb = ::media::BindToCurrentLoop(base::Bind( + &CmaMessageFilterHost::OnTimeUpdate, weak_this_, media_id)); + client.buffering_state_cb = ::media::BindToCurrentLoop(base::Bind( + &CmaMessageFilterHost::OnBufferingNotification, weak_this_, media_id)); + client.error_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnPlaybackError, + weak_this_, media_id, media::kNoTrackId)); + task_runner_->PostTask( + FROM_HERE, + base::Bind(&SetMediaPipeline, + process_id_, media_id, media_pipeline_host.get())); + task_runner_->PostTask( + FROM_HERE, + base::Bind(&MediaPipelineHost::Initialize, + base::Unretained(media_pipeline_host.get()), + load_type, client)); + std::pair<MediaPipelineMap::iterator, bool> ret = + media_pipelines_.insert( + std::make_pair(media_id, media_pipeline_host.release())); + + // Check there is no other entry with the same ID. + DCHECK(ret.second != false); +} + +void CmaMessageFilterHost::DestroyMedia(int media_id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + MediaPipelineMap::iterator it = media_pipelines_.find(media_id); + if (it == media_pipelines_.end()) + return; + + scoped_ptr<MediaPipelineHost> media_pipeline(it->second); + media_pipelines_.erase(it); + task_runner_->PostTask( + FROM_HERE, + base::Bind(&DestroyMediaPipeline, process_id_, media_id, + base::Passed(&media_pipeline))); +} + +void CmaMessageFilterHost::SetCdm(int media_id, + int render_frame_id, + int cdm_id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + MediaPipelineHost* media_pipeline = LookupById(media_id); + if (!media_pipeline) + return; + + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&SetCdmOnUiThread, + process_id_, render_frame_id, media_id, cdm_id)); +} + + +void CmaMessageFilterHost::CreateAvPipe( + int media_id, TrackId track_id, size_t shared_mem_size) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + base::FileDescriptor foreign_socket_handle; + base::SharedMemoryHandle foreign_memory_handle; + + // A few sanity checks before allocating resources. + MediaPipelineHost* media_pipeline = LookupById(media_id); + if (!media_pipeline || !PeerHandle() || shared_mem_size > kMaxSharedMem) { + Send(new CmaMsg_AvPipeCreated( + media_id, track_id, false, + foreign_memory_handle, foreign_socket_handle)); + return; + } + + // Create the local/foreign sockets to signal media message + // consune/feed events. + // Use CancelableSyncSocket so that write is always non-blocking. + scoped_ptr<base::CancelableSyncSocket> local_socket( + new base::CancelableSyncSocket()); + scoped_ptr<base::CancelableSyncSocket> foreign_socket( + new base::CancelableSyncSocket()); + if (!base::CancelableSyncSocket::CreatePair(local_socket.get(), + foreign_socket.get()) || + foreign_socket->handle() == -1) { + Send(new CmaMsg_AvPipeCreated( + media_id, track_id, false, + foreign_memory_handle, foreign_socket_handle)); + return; + } + + // Shared memory used to convey media messages. + scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); + if (!shared_memory->CreateAndMapAnonymous(shared_mem_size) || + !shared_memory->ShareToProcess(PeerHandle(), &foreign_memory_handle)) { + Send(new CmaMsg_AvPipeCreated( + media_id, track_id, false, + foreign_memory_handle, foreign_socket_handle)); + return; + } + + // Note: the IPC message can be sent only once the pipe has been fully + // configured. Part of this configuration is done in + // |MediaPipelineHost::SetAvPipe|. + // TODO(erickung): investigate possible memory leak here. + // If the weak pointer in |av_pipe_set_cb| gets invalidated, + // then |foreign_memory_handle| leaks. + base::Closure pipe_read_activity_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnPipeReadActivity, weak_this_, + media_id, track_id)); + base::Closure av_pipe_set_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnAvPipeSet, weak_this_, + media_id, track_id, + foreign_memory_handle, base::Passed(&foreign_socket))); + task_runner_->PostTask( + FROM_HERE, + base::Bind(&MediaPipelineHost::SetAvPipe, + base::Unretained(media_pipeline), + track_id, + base::Passed(&shared_memory), + pipe_read_activity_cb, + av_pipe_set_cb)); +} + +void CmaMessageFilterHost::OnAvPipeSet( + int media_id, + TrackId track_id, + base::SharedMemoryHandle foreign_memory_handle, + scoped_ptr<base::CancelableSyncSocket> foreign_socket) { + base::FileDescriptor foreign_socket_handle; + foreign_socket_handle.fd = foreign_socket->handle(); + foreign_socket_handle.auto_close = false; + + // This message can only be set once the pipe has fully been configured + // by |MediaPipelineHost|. + Send(new CmaMsg_AvPipeCreated( + media_id, track_id, true, foreign_memory_handle, foreign_socket_handle)); +} + +void CmaMessageFilterHost::AudioInitialize( + int media_id, TrackId track_id, const ::media::AudioDecoderConfig& config) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + MediaPipelineHost* media_pipeline = LookupById(media_id); + if (!media_pipeline) { + Send(new CmaMsg_TrackStateChanged( + media_id, track_id, ::media::PIPELINE_ERROR_ABORT)); + return; + } + + AvPipelineClient client; + client.eos_cb = ::media::BindToCurrentLoop(base::Bind( + &CmaMessageFilterHost::OnEos, weak_this_, media_id, track_id)); + client.playback_error_cb = ::media::BindToCurrentLoop(base::Bind( + &CmaMessageFilterHost::OnPlaybackError, weak_this_, media_id, track_id)); + client.statistics_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnStatisticsUpdated, weak_this_, + media_id, track_id)); + + ::media::PipelineStatusCB pipeline_status_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnTrackStateChanged, weak_this_, + media_id, track_id)); + task_runner_->PostTask( + FROM_HERE, + base::Bind(&MediaPipelineHost::AudioInitialize, + base::Unretained(media_pipeline), + track_id, client, config, pipeline_status_cb)); +} + +void CmaMessageFilterHost::VideoInitialize( + int media_id, TrackId track_id, const ::media::VideoDecoderConfig& config) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + MediaPipelineHost* media_pipeline = LookupById(media_id); + if (!media_pipeline) { + Send(new CmaMsg_TrackStateChanged( + media_id, track_id, ::media::PIPELINE_ERROR_ABORT)); + return; + } + + VideoPipelineClient client; + client.av_pipeline_client.eos_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnEos, weak_this_, + media_id, track_id)); + client.av_pipeline_client.playback_error_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnPlaybackError, weak_this_, + media_id, track_id)); + client.av_pipeline_client.statistics_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnStatisticsUpdated, weak_this_, + media_id, track_id)); + client.natural_size_changed_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnNaturalSizeChanged, weak_this_, + media_id, track_id)); + + ::media::PipelineStatusCB pipeline_status_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnTrackStateChanged, weak_this_, + media_id, track_id)); + task_runner_->PostTask( + FROM_HERE, + base::Bind(&MediaPipelineHost::VideoInitialize, + base::Unretained(media_pipeline), + track_id, client, config, pipeline_status_cb)); +} + +void CmaMessageFilterHost::StartPlayingFrom( + int media_id, base::TimeDelta time) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + MediaPipelineHost* media_pipeline = LookupById(media_id); + if (!media_pipeline) + return; + FORWARD_CALL(media_pipeline, StartPlayingFrom, time); +} + +void CmaMessageFilterHost::Flush(int media_id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + MediaPipelineHost* media_pipeline = LookupById(media_id); + if (!media_pipeline) { + Send(new CmaMsg_MediaStateChanged( + media_id, ::media::PIPELINE_ERROR_ABORT)); + return; + } + ::media::PipelineStatusCB pipeline_status_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaMessageFilterHost::OnMediaStateChanged, weak_this_, + media_id)); + FORWARD_CALL(media_pipeline, Flush, pipeline_status_cb); +} + +void CmaMessageFilterHost::Stop(int media_id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + MediaPipelineHost* media_pipeline = LookupById(media_id); + if (!media_pipeline) + return; + task_runner_->PostTask( + FROM_HERE, + base::Bind(&MediaPipelineHost::Stop, + base::Unretained(media_pipeline))); +} + +void CmaMessageFilterHost::SetPlaybackRate( + int media_id, double playback_rate) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + MediaPipelineHost* media_pipeline = LookupById(media_id); + if (!media_pipeline) + return; + FORWARD_CALL(media_pipeline, SetPlaybackRate, playback_rate); +} + +void CmaMessageFilterHost::SetVolume( + int media_id, TrackId track_id, float volume) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + MediaPipelineHost* media_pipeline = LookupById(media_id); + if (!media_pipeline) + return; + FORWARD_CALL(media_pipeline, SetVolume, track_id, volume); +} + +void CmaMessageFilterHost::NotifyPipeWrite(int media_id, TrackId track_id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + MediaPipelineHost* media_pipeline = LookupById(media_id); + if (!media_pipeline) + return; + FORWARD_CALL(media_pipeline, NotifyPipeWrite, track_id); +} + +void CmaMessageFilterHost::NotifyExternalSurface( + int surface_id, + const gfx::PointF& p0, const gfx::PointF& p1, + const gfx::PointF& p2, const gfx::PointF& p3) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + task_runner_->PostTask( + FROM_HERE, + base::Bind(&UpdateVideoSurfaceHost, surface_id, + gfx::QuadF(p0, p1, p2, p3))); +} + +// *** Browser to renderer messages *** + +void CmaMessageFilterHost::OnMediaStateChanged( + int media_id, ::media::PipelineStatus status) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + Send(new CmaMsg_MediaStateChanged(media_id, status)); +} + +void CmaMessageFilterHost::OnTrackStateChanged( + int media_id, TrackId track_id, ::media::PipelineStatus status) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + Send(new CmaMsg_TrackStateChanged(media_id, track_id, status)); +} + +void CmaMessageFilterHost::OnPipeReadActivity(int media_id, TrackId track_id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + Send(new CmaMsg_NotifyPipeRead(media_id, track_id)); +} + +void CmaMessageFilterHost::OnTimeUpdate( + int media_id, + base::TimeDelta media_time, + base::TimeDelta max_media_time, + base::TimeTicks stc) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + Send(new CmaMsg_TimeUpdate(media_id, + media_time, max_media_time, stc)); +} + +void CmaMessageFilterHost::OnBufferingNotification( + int media_id, ::media::BufferingState state) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + Send(new CmaMsg_BufferingNotification(media_id, state)); +} + +void CmaMessageFilterHost::OnEos(int media_id, TrackId track_id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + Send(new CmaMsg_Eos(media_id, track_id)); +} + +void CmaMessageFilterHost::OnPlaybackError( + int media_id, TrackId track_id, ::media::PipelineStatus status) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + Send(new CmaMsg_PlaybackError(media_id, track_id, status)); +} + +void CmaMessageFilterHost::OnStatisticsUpdated( + int media_id, TrackId track_id, const ::media::PipelineStatistics& stats) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + Send(new CmaMsg_PlaybackStatistics(media_id, track_id, stats)); +} + +void CmaMessageFilterHost::OnNaturalSizeChanged( + int media_id, TrackId track_id, const gfx::Size& size) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + Send(new CmaMsg_NaturalSizeChanged(media_id, track_id, size)); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/browser/media/cma_message_filter_host.h b/chromium/chromecast/browser/media/cma_message_filter_host.h new file mode 100644 index 00000000000..e00057466f5 --- /dev/null +++ b/chromium/chromecast/browser/media/cma_message_filter_host.h @@ -0,0 +1,126 @@ +// 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_BROWSER_MEDIA_CMA_MESSAGE_FILTER_HOST_H_ +#define CHROMECAST_BROWSER_MEDIA_CMA_MESSAGE_FILTER_HOST_H_ + +#include <map> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/shared_memory.h" +#include "base/memory/weak_ptr.h" +#include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/media/cma/pipeline/load_type.h" +#include "content/public/browser/browser_message_filter.h" +#include "content/public/browser/browser_thread.h" +#include "media/base/buffering_state.h" +#include "media/base/pipeline_status.h" + +namespace base { +class CancelableSyncSocket; +class SingleThreadTaskRunner; +} + +namespace gfx { +class PointF; +class Size; +} + +namespace media { +class AudioDecoderConfig; +class BrowserCdm; +class VideoDecoderConfig; +} + +namespace chromecast { +namespace media { + +class MediaPipelineHost; + +class CmaMessageFilterHost + : public content::BrowserMessageFilter { + public: + explicit CmaMessageFilterHost(int render_process_id); + + // content::BrowserMessageFilter implementation. + void OnChannelClosing() override; + void OnDestruct() const override; + bool OnMessageReceived(const IPC::Message& message) override; + + private: + typedef std::map<int, MediaPipelineHost*> MediaPipelineMap; + + friend class content::BrowserThread; + friend class base::DeleteHelper<CmaMessageFilterHost>; + ~CmaMessageFilterHost() override; + + void DeleteEntries(); + MediaPipelineHost* LookupById(int media_id); + + // Handling of incoming IPC messages. + void CreateMedia(int media_id, LoadType load_type); + void DestroyMedia(int media_id); + void SetCdm(int media_id, int render_frame_id, int cdm_id); + void CreateAvPipe(int media_id, TrackId track_id, size_t shared_mem_size); + void OnAvPipeSet(int media_id, + TrackId track_id, + base::SharedMemoryHandle foreign_memory_handle, + scoped_ptr<base::CancelableSyncSocket> foreign_socket); + void AudioInitialize(int media_id, + TrackId track_id, + const ::media::AudioDecoderConfig& config); + void VideoInitialize(int media_id, + TrackId track_id, + const ::media::VideoDecoderConfig& config); + void StartPlayingFrom(int media_id, base::TimeDelta time); + void Flush(int media_id); + void Stop(int media_id); + void SetPlaybackRate(int media_id, double playback_rate); + void SetVolume(int media_id, TrackId track_id, float volume); + void NotifyPipeWrite(int media_id, TrackId track_id); + void NotifyExternalSurface(int surface_id, + const gfx::PointF& p0, const gfx::PointF& p1, + const gfx::PointF& p2, const gfx::PointF& p3); + + // Audio/Video callbacks. + void OnMediaStateChanged(int media_id, + ::media::PipelineStatus status); + void OnTrackStateChanged(int media_id, + TrackId track_id, + ::media::PipelineStatus status); + void OnPipeReadActivity(int media_id, TrackId track_id); + void OnTimeUpdate(int media_id, + base::TimeDelta media_time, + base::TimeDelta max_media_time, + base::TimeTicks stc); + void OnBufferingNotification(int media_id, ::media::BufferingState state); + void OnEos(int media_id, TrackId track_id); + void OnPlaybackError(int media_id, TrackId track_id, + ::media::PipelineStatus status); + void OnStatisticsUpdated(int media_id, + TrackId track_id, + const ::media::PipelineStatistics& stats); + void OnNaturalSizeChanged(int media_id, + TrackId track_id, + const gfx::Size& size); + + // Render process ID correponding to this message filter. + const int process_id_; + + // List of media pipeline and message loop media pipelines are running on. + MediaPipelineMap media_pipelines_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + + base::WeakPtr<CmaMessageFilterHost> weak_this_; + base::WeakPtrFactory<CmaMessageFilterHost> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(CmaMessageFilterHost); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_MEDIA_CMA_MESSAGE_FILTER_HOST_H_ + diff --git a/chromium/chromecast/browser/media/cma_message_loop.cc b/chromium/chromecast/browser/media/cma_message_loop.cc new file mode 100644 index 00000000000..8ba93e01c99 --- /dev/null +++ b/chromium/chromecast/browser/media/cma_message_loop.cc @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/browser/media/cma_message_loop.h" + +#include "base/threading/thread.h" + +namespace chromecast { +namespace media { + +// static +scoped_refptr<base::MessageLoopProxy> CmaMessageLoop::GetMessageLoopProxy() { + return GetInstance()->thread_->message_loop_proxy(); +} + +// static +scoped_refptr<base::SingleThreadTaskRunner> CmaMessageLoop::GetTaskRunner() { + return GetInstance()->thread_->task_runner(); +} + +// static +CmaMessageLoop* CmaMessageLoop::GetInstance() { + return Singleton<CmaMessageLoop>::get(); +} + +CmaMessageLoop::CmaMessageLoop() + : thread_(new base::Thread("CmaThread")) { + thread_->Start(); +} + +CmaMessageLoop::~CmaMessageLoop() { + // This will automatically shutdown the thread. +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/browser/media/cma_message_loop.h b/chromium/chromecast/browser/media/cma_message_loop.h new file mode 100644 index 00000000000..5ce2cd40c83 --- /dev/null +++ b/chromium/chromecast/browser/media/cma_message_loop.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_BROWSER_MEDIA_CMA_MESSAGE_LOOP_H_ +#define CHROMECAST_BROWSER_MEDIA_CMA_MESSAGE_LOOP_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/singleton.h" + +namespace base { +class MessageLoopProxy; +class SingleThreadTaskRunner; +class Thread; +} + +namespace chromecast { +namespace media { + +class CmaMessageLoop { + public: + // TODO(gunsch): clean up references to deprecated Message*Loop*Proxy. + static scoped_refptr<base::MessageLoopProxy> GetMessageLoopProxy(); + static scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(); + + private: + friend struct DefaultSingletonTraits<CmaMessageLoop>; + friend class Singleton<CmaMessageLoop>; + + static CmaMessageLoop* GetInstance(); + + CmaMessageLoop(); + ~CmaMessageLoop(); + + scoped_ptr<base::Thread> thread_; + + DISALLOW_COPY_AND_ASSIGN(CmaMessageLoop); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_MEDIA_CMA_MESSAGE_LOOP_H_ diff --git a/chromium/chromecast/browser/media/media_pipeline_host.cc b/chromium/chromecast/browser/media/media_pipeline_host.cc new file mode 100644 index 00000000000..ff3eb6926f9 --- /dev/null +++ b/chromium/chromecast/browser/media/media_pipeline_host.cc @@ -0,0 +1,174 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/browser/media/media_pipeline_host.h" + +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/memory/shared_memory.h" +#include "base/single_thread_task_runner.h" +#include "chromecast/common/media/shared_memory_chunk.h" +#include "chromecast/media/cma/backend/media_pipeline_device.h" +#include "chromecast/media/cma/backend/media_pipeline_device_params.h" +#include "chromecast/media/cma/ipc/media_message_fifo.h" +#include "chromecast/media/cma/ipc_streamer/coded_frame_provider_host.h" +#include "chromecast/media/cma/pipeline/audio_pipeline_impl.h" +#include "chromecast/media/cma/pipeline/media_pipeline_impl.h" +#include "chromecast/media/cma/pipeline/video_pipeline_impl.h" + +namespace chromecast { +namespace media { + +struct MediaPipelineHost::MediaTrackHost { + MediaTrackHost(); + ~MediaTrackHost(); + + base::Closure pipe_write_cb; +}; + +MediaPipelineHost::MediaTrackHost::MediaTrackHost() { +} + +MediaPipelineHost::MediaTrackHost::~MediaTrackHost() { +} + +MediaPipelineHost::MediaPipelineHost() { + thread_checker_.DetachFromThread(); +} + +MediaPipelineHost::~MediaPipelineHost() { + DCHECK(thread_checker_.CalledOnValidThread()); + + for (MediaTrackMap::iterator it = media_track_map_.begin(); + it != media_track_map_.end(); ++it) { + scoped_ptr<MediaTrackHost> media_track(it->second); + } + media_track_map_.clear(); +} + +void MediaPipelineHost::Initialize( + LoadType load_type, + const MediaPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + media_pipeline_.reset(new MediaPipelineImpl()); + MediaPipelineDeviceParams default_parameters; + if (load_type == kLoadTypeMediaStream) + default_parameters.sync_type = MediaPipelineDeviceParams::kModeIgnorePts; + media_pipeline_->Initialize( + load_type, + CreateMediaPipelineDevice(default_parameters).Pass()); + media_pipeline_->SetClient(client); +} + +void MediaPipelineHost::SetAvPipe( + TrackId track_id, + scoped_ptr<base::SharedMemory> shared_mem, + const base::Closure& pipe_read_activity_cb, + const base::Closure& av_pipe_set_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + CHECK(track_id == kAudioTrackId || track_id == kVideoTrackId); + + size_t shared_mem_size = shared_mem->requested_size(); + scoped_ptr<MediaMemoryChunk> shared_memory_chunk( + new SharedMemoryChunk(shared_mem.Pass(), shared_mem_size)); + scoped_ptr<MediaMessageFifo> media_message_fifo( + new MediaMessageFifo(shared_memory_chunk.Pass(), shared_mem_size)); + media_message_fifo->ObserveReadActivity(pipe_read_activity_cb); + scoped_ptr<CodedFrameProviderHost> frame_provider_host( + new CodedFrameProviderHost(media_message_fifo.Pass())); + + MediaTrackMap::iterator it = media_track_map_.find(track_id); + MediaTrackHost* media_track_host; + if (it == media_track_map_.end()) { + media_track_host = new MediaTrackHost(); + media_track_map_.insert( + std::pair<TrackId, MediaTrackHost*>(track_id, media_track_host)); + } else { + media_track_host = it->second; + } + media_track_host->pipe_write_cb = frame_provider_host->GetFifoWriteEventCb(); + + scoped_ptr<CodedFrameProvider> frame_provider(frame_provider_host.release()); + if (track_id == kAudioTrackId) { + media_pipeline_->GetAudioPipelineImpl()->SetCodedFrameProvider( + frame_provider.Pass()); + } else { + media_pipeline_->GetVideoPipelineImpl()->SetCodedFrameProvider( + frame_provider.Pass()); + } + av_pipe_set_cb.Run(); +} + +void MediaPipelineHost::AudioInitialize( + TrackId track_id, + const AvPipelineClient& client, + const ::media::AudioDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + CHECK(track_id == kAudioTrackId); + media_pipeline_->GetAudioPipeline()->SetClient(client); + media_pipeline_->InitializeAudio( + config, scoped_ptr<CodedFrameProvider>(), status_cb); +} + +void MediaPipelineHost::VideoInitialize( + TrackId track_id, + const VideoPipelineClient& client, + const ::media::VideoDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + CHECK(track_id == kVideoTrackId); + media_pipeline_->GetVideoPipeline()->SetClient(client); + media_pipeline_->InitializeVideo( + config, scoped_ptr<CodedFrameProvider>(), status_cb); +} + +void MediaPipelineHost::StartPlayingFrom(base::TimeDelta time) { + DCHECK(thread_checker_.CalledOnValidThread()); + media_pipeline_->StartPlayingFrom(time); +} + +void MediaPipelineHost::Flush(const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + media_pipeline_->Flush(status_cb); +} + +void MediaPipelineHost::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + media_pipeline_->Stop(); +} + +void MediaPipelineHost::SetPlaybackRate(double playback_rate) { + DCHECK(thread_checker_.CalledOnValidThread()); + media_pipeline_->SetPlaybackRate(playback_rate); +} + +void MediaPipelineHost::SetVolume(TrackId track_id, float volume) { + DCHECK(thread_checker_.CalledOnValidThread()); + CHECK(track_id == kAudioTrackId); + media_pipeline_->GetAudioPipeline()->SetVolume(volume); +} + +void MediaPipelineHost::SetCdm(BrowserCdmCast* cdm) { + DCHECK(thread_checker_.CalledOnValidThread()); + media_pipeline_->SetCdm(cdm); +} + +void MediaPipelineHost::NotifyPipeWrite(TrackId track_id) { + DCHECK(thread_checker_.CalledOnValidThread()); + MediaTrackMap::iterator it = media_track_map_.find(track_id); + if (it == media_track_map_.end()) + return; + + MediaTrackHost* media_track_host = it->second; + if (!media_track_host->pipe_write_cb.is_null()) + media_track_host->pipe_write_cb.Run(); +} + +} // namespace media +} // namespace chromecast + diff --git a/chromium/chromecast/browser/media/media_pipeline_host.h b/chromium/chromecast/browser/media/media_pipeline_host.h new file mode 100644 index 00000000000..dc15db94240 --- /dev/null +++ b/chromium/chromecast/browser/media/media_pipeline_host.h @@ -0,0 +1,86 @@ +// 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_BROWSER_MEDIA_MEDIA_PIPELINE_HOST_H_ +#define CHROMECAST_BROWSER_MEDIA_MEDIA_PIPELINE_HOST_H_ + +#include <map> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/media/cma/pipeline/load_type.h" +#include "media/base/pipeline_status.h" + +namespace base { +class SharedMemory; +class SingleThreadTaskRunner; +} + +namespace media { +class AudioDecoderConfig; +class VideoDecoderConfig; +} + +namespace chromecast { +namespace media { +struct AvPipelineClient; +class BrowserCdmCast; +struct MediaPipelineClient; +class MediaPipelineImpl; +struct VideoPipelineClient; + +class MediaPipelineHost { + public: + MediaPipelineHost(); + ~MediaPipelineHost(); + + void Initialize(LoadType load_type, + const MediaPipelineClient& client); + + void SetAvPipe(TrackId track_id, + scoped_ptr<base::SharedMemory> shared_mem, + const base::Closure& pipe_read_activity_cb, + const base::Closure& av_pipe_set_cb); + void AudioInitialize(TrackId track_id, + const AvPipelineClient& client, + const ::media::AudioDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb); + void VideoInitialize(TrackId track_id, + const VideoPipelineClient& client, + const ::media::VideoDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb); + void StartPlayingFrom(base::TimeDelta time); + void Flush(const ::media::PipelineStatusCB& status_cb); + void Stop(); + + void SetPlaybackRate(double playback_rate); + void SetVolume(TrackId track_id, float playback_rate); + void SetCdm(BrowserCdmCast* cdm); + + void NotifyPipeWrite(TrackId track_id); + + private: + base::ThreadChecker thread_checker_; + + scoped_ptr<MediaPipelineImpl> media_pipeline_; + + // The shared memory for a track id must be valid until Stop is invoked on + // that track id. + struct MediaTrackHost; + typedef std::map<TrackId, MediaTrackHost*> MediaTrackMap; + MediaTrackMap media_track_map_; + + DISALLOW_COPY_AND_ASSIGN(MediaPipelineHost); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_MEDIA_MEDIA_PIPELINE_HOST_H_ + diff --git a/chromium/chromecast/browser/metrics/cast_metrics_service_client.cc b/chromium/chromecast/browser/metrics/cast_metrics_service_client.cc index feb0fa6022c..a367322da4e 100644 --- a/chromium/chromecast/browser/metrics/cast_metrics_service_client.cc +++ b/chromium/chromecast/browser/metrics/cast_metrics_service_client.cc @@ -5,19 +5,24 @@ #include "chromecast/browser/metrics/cast_metrics_service_client.h" #include "base/command_line.h" +#include "base/guid.h" #include "base/i18n/rtl.h" +#include "base/prefs/pref_service.h" #include "chromecast/browser/metrics/cast_stability_metrics_provider.h" #include "chromecast/browser/metrics/platform_metrics_providers.h" -#include "chromecast/common/chromecast_config.h" #include "chromecast/common/chromecast_switches.h" +#include "chromecast/common/pref_names.h" #include "components/metrics/client_info.h" #include "components/metrics/gpu/gpu_metrics_provider.h" +#include "components/metrics/metrics_pref_names.h" #include "components/metrics/metrics_provider.h" #include "components/metrics/metrics_service.h" #include "components/metrics/metrics_state_manager.h" #include "components/metrics/net/net_metrics_log_uploader.h" #include "components/metrics/net/network_metrics_provider.h" #include "components/metrics/profiler/profiler_metrics_provider.h" +#include "components/metrics/url_constants.h" +#include "content/public/common/content_switches.h" #if defined(OS_LINUX) #include "chromecast/browser/metrics/external_metrics.h" @@ -27,32 +32,72 @@ namespace chromecast { namespace metrics { namespace { - -void StoreClientInfo(const ::metrics::ClientInfo& client_info) { -} - -scoped_ptr<::metrics::ClientInfo> LoadClientInfo() { - return scoped_ptr<::metrics::ClientInfo>(); -} - +const int kStandardUploadIntervalMinutes = 5; } // namespace // static -CastMetricsServiceClient* CastMetricsServiceClient::Create( +scoped_ptr<CastMetricsServiceClient> CastMetricsServiceClient::Create( base::TaskRunner* io_task_runner, PrefService* pref_service, net::URLRequestContextGetter* request_context) { - return new CastMetricsServiceClient(io_task_runner, - pref_service, - request_context); + return make_scoped_ptr(new CastMetricsServiceClient(io_task_runner, + pref_service, + request_context)); } void CastMetricsServiceClient::SetMetricsClientId( const std::string& client_id) { + client_id_ = client_id; LOG(INFO) << "Metrics client ID set: " << client_id; - PlatformSetClientID(client_id); + PlatformSetClientID(cast_service_, client_id); +} + +void CastMetricsServiceClient::OnRecordingDisabled() { +} + +void CastMetricsServiceClient::StoreClientInfo( + const ::metrics::ClientInfo& client_info) { + const std::string& client_id = client_info.client_id; + DCHECK(client_id.empty() || base::IsValidGUID(client_id)); + // backup client_id or reset to empty. + SetMetricsClientId(client_id); } +scoped_ptr< ::metrics::ClientInfo> CastMetricsServiceClient::LoadClientInfo() { + scoped_ptr< ::metrics::ClientInfo> client_info(new ::metrics::ClientInfo); + + // kMetricsIsNewClientID would be missing if either the device was just + // FDR'ed, or it is on pre-v1.2 build. + if (!pref_service_->GetBoolean(prefs::kMetricsIsNewClientID)) { + // If the old client id exists, the device must be on pre-v1.2 build, + // instead of just being FDR'ed. + if (!pref_service_->GetString(::metrics::prefs::kMetricsOldClientID) + .empty()) { + // Force old client id to be regenerated. See b/9487011. + client_info->client_id = base::GenerateGUID(); + pref_service_->SetBoolean(prefs::kMetricsIsNewClientID, true); + return client_info.Pass(); + } + // else the device was just FDR'ed, pass through. + } + + const std::string client_id(GetPlatformClientID(cast_service_)); + if (!client_id.empty() && base::IsValidGUID(client_id)) { + client_info->client_id = client_id; + return client_info.Pass(); + } else { + if (client_id.empty()) { + LOG(WARNING) << "Empty client id from platform," + << " assuming this is the first boot up of a new device."; + } else { + LOG(ERROR) << "Invalid client id " << client_id << " from platform."; + } + return scoped_ptr< ::metrics::ClientInfo>(); + } +} + + + bool CastMetricsServiceClient::IsOffTheRecordSessionActive() { // Chromecast behaves as "off the record" w/r/t recording browsing state, // but this value is about not disabling metrics because of it. @@ -73,15 +118,15 @@ bool CastMetricsServiceClient::GetBrand(std::string* brand_code) { } ::metrics::SystemProfileProto::Channel CastMetricsServiceClient::GetChannel() { - return GetPlatformReleaseChannel(); + return GetPlatformReleaseChannel(cast_service_); } std::string CastMetricsServiceClient::GetVersionString() { - return GetPlatformVersionString(); + return GetPlatformVersionString(cast_service_); } void CastMetricsServiceClient::OnLogUploadComplete() { - PlatformOnLogUploadComplete(); + PlatformOnLogUploadComplete(cast_service_); } void CastMetricsServiceClient::StartGatheringMetrics( @@ -96,10 +141,8 @@ void CastMetricsServiceClient::CollectFinalMetrics( scoped_ptr< ::metrics::MetricsLogUploader> CastMetricsServiceClient::CreateUploader( - const std::string& server_url, - const std::string& mime_type, const base::Callback<void(int)>& on_upload_complete) { - std::string uma_server_url(server_url); + std::string uma_server_url(::metrics::kDefaultMetricsServerUrl); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kOverrideMetricsUploadUrl)) { uma_server_url.assign( @@ -110,10 +153,14 @@ CastMetricsServiceClient::CreateUploader( new ::metrics::NetMetricsLogUploader( request_context_, uma_server_url, - mime_type, + ::metrics::kDefaultMetricsMimeType, on_upload_complete)); } +base::TimeDelta CastMetricsServiceClient::GetStandardUploadInterval() { + return base::TimeDelta::FromMinutes(kStandardUploadIntervalMinutes); +} + void CastMetricsServiceClient::EnableMetricsService(bool enabled) { if (!metrics_service_loop_->BelongsToCurrentThread()) { metrics_service_loop_->PostTask( @@ -135,18 +182,40 @@ CastMetricsServiceClient::CastMetricsServiceClient( base::TaskRunner* io_task_runner, PrefService* pref_service, net::URLRequestContextGetter* request_context) - : metrics_state_manager_(::metrics::MetricsStateManager::Create( - pref_service, - base::Bind(&CastMetricsServiceClient::IsReportingEnabled, - base::Unretained(this)), - base::Bind(&StoreClientInfo), - base::Bind(&LoadClientInfo))), - metrics_service_(new ::metrics::MetricsService( - metrics_state_manager_.get(), - this, - pref_service)), + : io_task_runner_(io_task_runner), + pref_service_(pref_service), + cast_service_(NULL), +#if !defined(OS_ANDROID) + external_metrics_(NULL), +#endif // !defined(OS_ANDROID) metrics_service_loop_(base::MessageLoopProxy::current()), request_context_(request_context) { +} + +CastMetricsServiceClient::~CastMetricsServiceClient() { +#if !defined(OS_ANDROID) + DCHECK(!external_metrics_); +#endif // !defined(OS_ANDROID) +} + +void CastMetricsServiceClient::Initialize(CastService* cast_service) { + DCHECK(cast_service); + DCHECK(!cast_service_); + cast_service_ = cast_service; + + metrics_state_manager_ = ::metrics::MetricsStateManager::Create( + pref_service_, + base::Bind(&CastMetricsServiceClient::IsReportingEnabled, + base::Unretained(this)), + base::Bind(&CastMetricsServiceClient::StoreClientInfo, + base::Unretained(this)), + base::Bind(&CastMetricsServiceClient::LoadClientInfo, + base::Unretained(this))); + metrics_service_.reset(new ::metrics::MetricsService( + metrics_state_manager_.get(), + this, + pref_service_)); + // Always create a client id as it may also be used by crash reporting, // (indirectly) included in feedback, and can be queried during setup. // For UMA and crash reporting, associated opt-in settings will control @@ -160,18 +229,26 @@ CastMetricsServiceClient::CastMetricsServiceClient( new CastStabilityMetricsProvider(metrics_service_.get()); metrics_service_->RegisterMetricsProvider( scoped_ptr< ::metrics::MetricsProvider>(stability_provider)); + + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (!command_line->HasSwitch(switches::kDisableGpu)) { + metrics_service_->RegisterMetricsProvider( + scoped_ptr< ::metrics::MetricsProvider>( + new ::metrics::GPUMetricsProvider)); + } metrics_service_->RegisterMetricsProvider( scoped_ptr< ::metrics::MetricsProvider>( - new ::metrics::GPUMetricsProvider)); - metrics_service_->RegisterMetricsProvider( - scoped_ptr< ::metrics::MetricsProvider>( - new ::metrics::NetworkMetricsProvider(io_task_runner))); + new ::metrics::NetworkMetricsProvider(io_task_runner_))); metrics_service_->RegisterMetricsProvider( scoped_ptr< ::metrics::MetricsProvider>( new ::metrics::ProfilerMetricsProvider)); - RegisterPlatformMetricsProviders(metrics_service_.get()); + RegisterPlatformMetricsProviders(metrics_service_.get(), cast_service_); metrics_service_->InitializeMetricsRecordingState(); +#if !defined(OS_ANDROID) + // Reset clean_shutdown bit after InitializeMetricsRecordingState(). + metrics_service_->LogNeedForCleanShutdown(); +#endif // !defined(OS_ANDROID) if (IsReportingEnabled()) metrics_service_->Start(); @@ -179,16 +256,27 @@ CastMetricsServiceClient::CastMetricsServiceClient( // Start external metrics collection, which feeds data from external // processes into the main external metrics. #if defined(OS_LINUX) - external_metrics_.reset(new ExternalMetrics(stability_provider)); + external_metrics_ = new ExternalMetrics(stability_provider); external_metrics_->Start(); #endif // defined(OS_LINUX) } -CastMetricsServiceClient::~CastMetricsServiceClient() { +void CastMetricsServiceClient::Finalize() { +#if !defined(OS_ANDROID) + // Set clean_shutdown bit. + metrics_service_->RecordCompletedSessionEnd(); +#endif // !defined(OS_ANDROID) + + // Stop metrics service cleanly before destructing CastMetricsServiceClient. +#if defined(OS_LINUX) + external_metrics_->StopAndDestroy(); + external_metrics_ = NULL; +#endif // defined(OS_LINUX) + metrics_service_->Stop(); } bool CastMetricsServiceClient::IsReportingEnabled() { - return PlatformIsReportingEnabled(); + return PlatformIsReportingEnabled(cast_service_); } } // namespace metrics diff --git a/chromium/chromecast/browser/metrics/cast_metrics_service_client.h b/chromium/chromecast/browser/metrics/cast_metrics_service_client.h index 5cba73c0d27..abc2b57311c 100644 --- a/chromium/chromecast/browser/metrics/cast_metrics_service_client.h +++ b/chromium/chromecast/browser/metrics/cast_metrics_service_client.h @@ -20,6 +20,7 @@ class TaskRunner; } namespace metrics { +struct ClientInfo; class MetricsService; class MetricsStateManager; } // namespace metrics @@ -29,38 +30,48 @@ class URLRequestContextGetter; } // namespace net namespace chromecast { + +class CastService; + namespace metrics { + class ExternalMetrics; class CastMetricsServiceClient : public ::metrics::MetricsServiceClient { public: - virtual ~CastMetricsServiceClient(); + ~CastMetricsServiceClient() override; - static CastMetricsServiceClient* Create( + static scoped_ptr<CastMetricsServiceClient> Create( base::TaskRunner* io_task_runner, PrefService* pref_service, net::URLRequestContextGetter* request_context); + void Initialize(CastService* cast_service); + void Finalize(); + // metrics::MetricsServiceClient implementation: - virtual void SetMetricsClientId(const std::string& client_id) override; - virtual bool IsOffTheRecordSessionActive() override; - virtual int32_t GetProduct() override; - virtual std::string GetApplicationLocale() override; - virtual bool GetBrand(std::string* brand_code) override; - virtual ::metrics::SystemProfileProto::Channel GetChannel() override; - virtual std::string GetVersionString() override; - virtual void OnLogUploadComplete() override; - virtual void StartGatheringMetrics( - const base::Closure& done_callback) override; - virtual void CollectFinalMetrics(const base::Closure& done_callback) override; - virtual scoped_ptr< ::metrics::MetricsLogUploader> CreateUploader( - const std::string& server_url, - const std::string& mime_type, + void SetMetricsClientId(const std::string& client_id) override; + void OnRecordingDisabled() override; + bool IsOffTheRecordSessionActive() override; + int32_t GetProduct() override; + std::string GetApplicationLocale() override; + bool GetBrand(std::string* brand_code) override; + ::metrics::SystemProfileProto::Channel GetChannel() override; + std::string GetVersionString() override; + void OnLogUploadComplete() override; + void StartGatheringMetrics(const base::Closure& done_callback) override; + void CollectFinalMetrics(const base::Closure& done_callback) override; + scoped_ptr< ::metrics::MetricsLogUploader> CreateUploader( const base::Callback<void(int)>& on_upload_complete) override; + base::TimeDelta GetStandardUploadInterval() override; // Starts/stops the metrics service. void EnableMetricsService(bool enabled); + std::string client_id() const { + return client_id_; + } + private: CastMetricsServiceClient( base::TaskRunner* io_task_runner, @@ -70,13 +81,21 @@ class CastMetricsServiceClient : public ::metrics::MetricsServiceClient { // Returns whether or not metrics reporting is enabled. bool IsReportingEnabled(); + scoped_ptr< ::metrics::ClientInfo> LoadClientInfo(); + void StoreClientInfo(const ::metrics::ClientInfo& client_info); + + base::TaskRunner* const io_task_runner_; + PrefService* const pref_service_; + CastService* cast_service_; + std::string client_id_; + #if defined(OS_LINUX) - scoped_ptr<ExternalMetrics> external_metrics_; + ExternalMetrics* external_metrics_; #endif // defined(OS_LINUX) + const scoped_refptr<base::MessageLoopProxy> metrics_service_loop_; scoped_ptr< ::metrics::MetricsStateManager> metrics_state_manager_; scoped_ptr< ::metrics::MetricsService> metrics_service_; - scoped_refptr<base::MessageLoopProxy> metrics_service_loop_; - net::URLRequestContextGetter* request_context_; + net::URLRequestContextGetter* const request_context_; DISALLOW_COPY_AND_ASSIGN(CastMetricsServiceClient); }; diff --git a/chromium/chromecast/browser/metrics/cast_metrics_service_client_unittest.cc b/chromium/chromecast/browser/metrics/cast_metrics_service_client_unittest.cc deleted file mode 100644 index 31537864d81..00000000000 --- a/chromium/chromecast/browser/metrics/cast_metrics_service_client_unittest.cc +++ /dev/null @@ -1,45 +0,0 @@ -// 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/browser/metrics/cast_metrics_service_client.h" - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/prefs/testing_pref_service.h" -#include "components/metrics/metrics_service.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace chromecast { - -class CastMetricsTest : public testing::Test { - public: - CastMetricsTest() {} - virtual ~CastMetricsTest() {} - - protected: - virtual void SetUp() override { - message_loop_.reset(new base::MessageLoop()); - prefs_.reset(new TestingPrefServiceSimple()); - ::metrics::MetricsService::RegisterPrefs(prefs_->registry()); - } - - base::TaskRunner* task_runner() { - return message_loop_->task_runner().get(); - } - TestingPrefServiceSimple* prefs() { return prefs_.get(); } - - private: - scoped_ptr<base::MessageLoop> message_loop_; - scoped_ptr<TestingPrefServiceSimple> prefs_; - - DISALLOW_COPY_AND_ASSIGN(CastMetricsTest); -}; - -TEST_F(CastMetricsTest, CreateMetricsServiceClient) { - // Create and expect this to not crash. - metrics::CastMetricsServiceClient::Create(task_runner(), prefs(), NULL); -} - -} // namespace chromecast diff --git a/chromium/chromecast/browser/metrics/cast_stability_metrics_provider.cc b/chromium/chromecast/browser/metrics/cast_stability_metrics_provider.cc index b7ec5096327..62150a3af3f 100644 --- a/chromium/chromecast/browser/metrics/cast_stability_metrics_provider.cc +++ b/chromium/chromecast/browser/metrics/cast_stability_metrics_provider.cc @@ -11,8 +11,8 @@ #include "base/metrics/sparse_histogram.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" +#include "chromecast/browser/cast_browser_process.h" #include "chromecast/browser/metrics/cast_metrics_service_client.h" -#include "chromecast/common/chromecast_config.h" #include "chromecast/common/pref_names.h" #include "components/metrics/metrics_service.h" #include "components/metrics/proto/system_profile.pb.h" @@ -21,7 +21,6 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host.h" -#include "content/public/browser/user_metrics.h" namespace chromecast { namespace metrics { @@ -29,7 +28,7 @@ namespace metrics { namespace { void IncrementPrefValue(const char* path) { - PrefService* pref = ChromecastConfig::GetInstance()->pref_service(); + PrefService* pref = shell::CastBrowserProcess::GetInstance()->pref_service(); DCHECK(pref); int value = pref->GetInteger(path); pref->SetInteger(path, value + 1); @@ -75,7 +74,7 @@ void CastStabilityMetricsProvider::OnRecordingDisabled() { void CastStabilityMetricsProvider::ProvideStabilityMetrics( ::metrics::SystemProfileProto* system_profile_proto) { - PrefService* pref = ChromecastConfig::GetInstance()->pref_service(); + PrefService* pref = shell::CastBrowserProcess::GetInstance()->pref_service(); ::metrics::SystemProfileProto_Stability* stability_proto = system_profile_proto->mutable_stability(); @@ -141,7 +140,8 @@ void CastStabilityMetricsProvider::Observe( } void CastStabilityMetricsProvider::BrowserChildProcessCrashed( - const content::ChildProcessData& data) { + const content::ChildProcessData& data, + int exit_code) { IncrementPrefValue(prefs::kStabilityChildProcessCrashCount); } diff --git a/chromium/chromecast/browser/metrics/cast_stability_metrics_provider.h b/chromium/chromecast/browser/metrics/cast_stability_metrics_provider.h index eea85054057..b29d2a4dc35 100644 --- a/chromium/chromecast/browser/metrics/cast_stability_metrics_provider.h +++ b/chromium/chromecast/browser/metrics/cast_stability_metrics_provider.h @@ -6,7 +6,6 @@ #define CHROMECAST_BROWSER_METRICS_CAST_STABILITY_METRICS_PROVIDER_H_ #include "base/basictypes.h" -#include "base/metrics/user_metrics.h" #include "base/process/kill.h" #include "components/metrics/metrics_provider.h" #include "content/public/browser/browser_child_process_observer.h" @@ -39,12 +38,12 @@ class CastStabilityMetricsProvider explicit CastStabilityMetricsProvider( ::metrics::MetricsService* metrics_service); - virtual ~CastStabilityMetricsProvider(); + ~CastStabilityMetricsProvider() override; // metrics::MetricsDataProvider implementation: - virtual void OnRecordingEnabled() override; - virtual void OnRecordingDisabled() override; - virtual void ProvideStabilityMetrics( + void OnRecordingEnabled() override; + void OnRecordingDisabled() override; + void ProvideStabilityMetrics( ::metrics::SystemProfileProto* system_profile_proto) override; // Logs an external crash, presumably from the ExternalMetrics service. @@ -52,13 +51,14 @@ class CastStabilityMetricsProvider private: // content::NotificationObserver implementation: - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) override; + void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) override; // content::BrowserChildProcessObserver implementation: - virtual void BrowserChildProcessCrashed( - const content::ChildProcessData& data) override; + void BrowserChildProcessCrashed( + const content::ChildProcessData& data, + int exit_code) override; // Records a renderer process crash. void LogRendererCrash(content::RenderProcessHost* host, diff --git a/chromium/chromecast/browser/metrics/external_metrics.cc b/chromium/chromecast/browser/metrics/external_metrics.cc index 18e7ec0ff0f..f26e0ccd960 100644 --- a/chromium/chromecast/browser/metrics/external_metrics.cc +++ b/chromium/chromecast/browser/metrics/external_metrics.cc @@ -14,12 +14,12 @@ #include "base/metrics/statistics_recorder.h" #include "base/timer/elapsed_timer.h" #include "chromecast/base/metrics/cast_histograms.h" +#include "chromecast/base/metrics/cast_metrics_helper.h" #include "chromecast/browser/metrics/cast_stability_metrics_provider.h" #include "components/metrics/metrics_service.h" #include "components/metrics/serialization/metric_sample.h" #include "components/metrics/serialization/serialization_utils.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/user_metrics.h" namespace chromecast { namespace metrics { @@ -58,17 +58,16 @@ ExternalMetrics::ExternalMetrics( DCHECK(stability_provider); } -ExternalMetrics::~ExternalMetrics() {} +ExternalMetrics::~ExternalMetrics() { +} -void ExternalMetrics::Start() { - ScheduleCollector(); +void ExternalMetrics::StopAndDestroy() { + content::BrowserThread::DeleteSoon( + content::BrowserThread::FILE, FROM_HERE, this); } -void ExternalMetrics::RecordAction(const std::string& action) { - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::Bind(&content::RecordComputedAction, action)); +void ExternalMetrics::Start() { + ScheduleCollector(); } void ExternalMetrics::RecordCrash(const std::string& crash_kind) { @@ -102,7 +101,7 @@ int ExternalMetrics::CollectEvents() { RecordCrash(sample.name()); break; case ::metrics::MetricSample::USER_ACTION: - RecordAction(sample.name()); + CastMetricsHelper::GetInstance()->RecordSimpleAction(sample.name()); break; case ::metrics::MetricSample::HISTOGRAM: if (!CheckValues(sample.name(), sample.min(), sample.max(), diff --git a/chromium/chromecast/browser/metrics/external_metrics.h b/chromium/chromecast/browser/metrics/external_metrics.h index 2b4bc5381a5..dd125e211b6 100644 --- a/chromium/chromecast/browser/metrics/external_metrics.h +++ b/chromium/chromecast/browser/metrics/external_metrics.h @@ -24,19 +24,22 @@ class CastStabilityMetricsProvider; class ExternalMetrics { public: explicit ExternalMetrics(CastStabilityMetricsProvider* stability_provider); - ~ExternalMetrics(); // Begins external data collection. Calls to RecordAction originate in the // File thread but are executed in the UI thread. void Start(); + // Destroys itself in appropriate thread. + void StopAndDestroy(); + private: + friend class base::DeleteHelper<ExternalMetrics>; + + ~ExternalMetrics(); + // The max length of a message (name-value pair, plus header) static const int kMetricsMessageMaxLength = 1024; // be generous - // Passes an action event to the UMA service. - void RecordAction(const std::string& action_name); - // Records an external crash of the given string description. void RecordCrash(const std::string& crash_kind); diff --git a/chromium/chromecast/browser/metrics/platform_metrics_providers.h b/chromium/chromecast/browser/metrics/platform_metrics_providers.h index da1130bc5f4..ea28f679eac 100644 --- a/chromium/chromecast/browser/metrics/platform_metrics_providers.h +++ b/chromium/chromecast/browser/metrics/platform_metrics_providers.h @@ -12,26 +12,35 @@ class MetricsService; } namespace chromecast { + +class CastService; + namespace metrics { // Build-level hook for different platforms to provide data to MetricsService. void RegisterPlatformMetricsProviders( - ::metrics::MetricsService* metrics_service); + ::metrics::MetricsService* metrics_service, + CastService* cast_servce); + +// Returns UMA client ID persisted in the platform. +const std::string GetPlatformClientID(CastService* cast_servce); // Returns the current release channel. -::metrics::SystemProfileProto::Channel GetPlatformReleaseChannel(); +::metrics::SystemProfileProto::Channel GetPlatformReleaseChannel( + CastService* cast_servce); // Returns a string representing this build's version. -std::string GetPlatformVersionString(); +std::string GetPlatformVersionString(CastService* cast_servce); // Returns whether or not metrics reporting should be on. -bool PlatformIsReportingEnabled(); +bool PlatformIsReportingEnabled(CastService* cast_servce); // Called when the UMA client ID has been set. -void PlatformSetClientID(const std::string& client_id); +void PlatformSetClientID(CastService* cast_servce, + const std::string& client_id); // Called when an upload has completed. -void PlatformOnLogUploadComplete(); +void PlatformOnLogUploadComplete(CastService* cast_servce); } // namespace metrics } // namespace chromecast diff --git a/chromium/chromecast/browser/metrics/platform_metrics_providers_simple.cc b/chromium/chromecast/browser/metrics/platform_metrics_providers_simple.cc index 7bc4c52b31c..f92a4790178 100644 --- a/chromium/chromecast/browser/metrics/platform_metrics_providers_simple.cc +++ b/chromium/chromecast/browser/metrics/platform_metrics_providers_simple.cc @@ -8,25 +8,32 @@ namespace chromecast { namespace metrics { void RegisterPlatformMetricsProviders( - ::metrics::MetricsService* metrics_service) { + ::metrics::MetricsService* metrics_service, + CastService* cast_servce) { } -::metrics::SystemProfileProto::Channel GetPlatformReleaseChannel() { +const std::string GetPlatformClientID(CastService* cast_servce) { + return ""; +} + +::metrics::SystemProfileProto::Channel GetPlatformReleaseChannel( + CastService* cast_servce) { return ::metrics::SystemProfileProto::CHANNEL_STABLE; } -std::string GetPlatformVersionString() { +std::string GetPlatformVersionString(CastService* cast_servce) { return ""; } -bool PlatformIsReportingEnabled() { +bool PlatformIsReportingEnabled(CastService* cast_service) { return false; } -void PlatformSetClientID(const std::string& client_id) { +void PlatformSetClientID(CastService* cast_servce, + const std::string& client_id) { } -void PlatformOnLogUploadComplete() { +void PlatformOnLogUploadComplete(CastService* cast_servce) { } } // namespace metrics diff --git a/chromium/chromecast/browser/pref_service_helper.cc b/chromium/chromecast/browser/pref_service_helper.cc new file mode 100644 index 00000000000..3f6f5c607c3 --- /dev/null +++ b/chromium/chromecast/browser/pref_service_helper.cc @@ -0,0 +1,76 @@ +// 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/browser/pref_service_helper.h" + +#include <string> + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/prefs/json_pref_store.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service_factory.h" +#include "base/prefs/pref_store.h" +#include "chromecast/base/cast_paths.h" +#include "chromecast/common/pref_names.h" +#include "content/public/browser/browser_thread.h" + +namespace chromecast { +namespace shell { + +namespace { + +void UserPrefsLoadError(PersistentPrefStore::PrefReadError* error_val, + PersistentPrefStore::PrefReadError error) { + DCHECK(error_val); + *error_val = error; +} + +base::FilePath GetConfigPath() { + base::FilePath config_path; + CHECK(PathService::Get(FILE_CAST_CONFIG, &config_path)); + return config_path; +} + +} // namespace + +// static +scoped_ptr<PrefService> PrefServiceHelper::CreatePrefService( + PrefRegistrySimple* registry) { + const base::FilePath config_path(GetConfigPath()); + VLOG(1) << "Loading config from " << config_path.value(); + + registry->RegisterBooleanPref(prefs::kMetricsIsNewClientID, false); + registry->RegisterIntegerPref(prefs::kRemoteDebuggingPort, 0); + + RegisterPlatformPrefs(registry); + + base::PrefServiceFactory prefServiceFactory; + scoped_refptr<base::SequencedTaskRunner> task_runner = + JsonPrefStore::GetTaskRunnerForFile( + config_path, + content::BrowserThread::GetBlockingPool()); + prefServiceFactory.SetUserPrefsFile(config_path, task_runner.get()); + prefServiceFactory.set_async(false); + + PersistentPrefStore::PrefReadError prefs_read_error = + PersistentPrefStore::PREF_READ_ERROR_NONE; + prefServiceFactory.set_read_error_callback( + base::Bind(&UserPrefsLoadError, &prefs_read_error)); + + scoped_ptr<PrefService> pref_service(prefServiceFactory.Create(registry)); + if (prefs_read_error != PersistentPrefStore::PREF_READ_ERROR_NONE) { + LOG(ERROR) << "Cannot initialize chromecast config: " + << config_path.value() + << ", pref_error=" << prefs_read_error; + } + + OnPrefsLoaded(pref_service.get()); + return pref_service.Pass(); +} + +} // namespace shell +} // namespace chromecast diff --git a/chromium/chromecast/browser/pref_service_helper.h b/chromium/chromecast/browser/pref_service_helper.h new file mode 100644 index 00000000000..cf41a925f36 --- /dev/null +++ b/chromium/chromecast/browser/pref_service_helper.h @@ -0,0 +1,43 @@ +// 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. +// +// Helper to initialize PrefService for cast shell. + +#ifndef CHROMECAST_BROWSER_PREF_SERVICE_HELPER_H_ +#define CHROMECAST_BROWSER_PREF_SERVICE_HELPER_H_ + +#include <string> + +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/pref_service.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/threading/thread_checker.h" + +class PrefRegistrySimple; + +namespace chromecast { +namespace shell { + +// It uses JsonPrefStore internally and/so the format of config file is same to +// that of JsonPrefStore. +class PrefServiceHelper { + public: + // Loads configs from config file. Returns true if successful. + static scoped_ptr<PrefService> CreatePrefService( + PrefRegistrySimple* registry); + + private: + // Registers any needed preferences for the current platform. + static void RegisterPlatformPrefs(PrefRegistrySimple* registry); + + // Called after the pref file has been loaded. + static void OnPrefsLoaded(PrefService* pref_service); +}; + +} // namespace shell +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_PREF_SERVICE_HELPER_H_ diff --git a/chromium/chromecast/browser/pref_service_helper_simple.cc b/chromium/chromecast/browser/pref_service_helper_simple.cc new file mode 100644 index 00000000000..0357ca79f11 --- /dev/null +++ b/chromium/chromecast/browser/pref_service_helper_simple.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/browser/pref_service_helper.h" + +namespace chromecast { +namespace shell { + +void PrefServiceHelper::RegisterPlatformPrefs(PrefRegistrySimple* registry) { +} + +void PrefServiceHelper::OnPrefsLoaded(PrefService* pref_service) { +} + +} // namespace shell +} // namespace chromecast diff --git a/chromium/chromecast/browser/service/cast_service.cc b/chromium/chromecast/browser/service/cast_service.cc index 87c9ee264e2..d725abe2397 100644 --- a/chromium/chromecast/browser/service/cast_service.cc +++ b/chromium/chromecast/browser/service/cast_service.cc @@ -5,15 +5,18 @@ #include "chromecast/browser/service/cast_service.h" #include "base/logging.h" +#include "base/run_loop.h" #include "base/threading/thread_checker.h" namespace chromecast { CastService::CastService( content::BrowserContext* browser_context, - const OptInStatsChangedCallback& opt_in_stats_callback) + PrefService* pref_service, + metrics::CastMetricsServiceClient* metrics_service_client) : browser_context_(browser_context), - opt_in_stats_callback_(opt_in_stats_callback), + pref_service_(pref_service), + metrics_service_client_(metrics_service_client), stopped_(true), thread_checker_(new base::ThreadChecker()) { } @@ -23,10 +26,18 @@ CastService::~CastService() { DCHECK(stopped_); } -void CastService::Start() { +void CastService::Initialize() { DCHECK(thread_checker_->CalledOnValidThread()); + InitializeInternal(); +} - Initialize(); +void CastService::Finalize() { + DCHECK(thread_checker_->CalledOnValidThread()); + FinalizeInternal(); +} + +void CastService::Start() { + DCHECK(thread_checker_->CalledOnValidThread()); stopped_ = false; StartInternal(); } @@ -34,6 +45,9 @@ void CastService::Start() { void CastService::Stop() { DCHECK(thread_checker_->CalledOnValidThread()); StopInternal(); + // Consume any pending tasks which should be done before destroying in-process + // renderer process, for example, destroying web_contents. + base::RunLoop().RunUntilIdle(); stopped_ = true; } diff --git a/chromium/chromecast/browser/service/cast_service.h b/chromium/chromecast/browser/service/cast_service.h index 7e7d94c00b3..e10ac39f254 100644 --- a/chromium/chromecast/browser/service/cast_service.h +++ b/chromium/chromecast/browser/service/cast_service.h @@ -9,6 +9,8 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +class PrefService; + namespace base { class ThreadChecker; } @@ -23,46 +25,68 @@ class URLRequestContextGetter; namespace chromecast { +namespace metrics { +class CastMetricsServiceClient; +} + class CastService { public: - // A callback that will be invoked when the user changes the opt-in stats - // value. - typedef base::Callback<void(bool)> OptInStatsChangedCallback; - // Create() takes a separate url request context getter because the request // context getter obtained through the browser context might not be // appropriate for the url requests made by the cast service/reciever. // For example, on Chromecast, it is needed to pass in a system url request // context getter that would set the request context for NSS, which the main // getter doesn't do. - static CastService* Create( + static scoped_ptr<CastService> Create( content::BrowserContext* browser_context, - net::URLRequestContextGetter* request_context_getter, - const OptInStatsChangedCallback& opt_in_stats_callback); + PrefService* pref_service, + metrics::CastMetricsServiceClient* metrics_service_client, + net::URLRequestContextGetter* request_context_getter); virtual ~CastService(); - // Start/stop the cast service. + // Initializes/finalizes the cast service. + void Initialize(); + void Finalize(); + + // Starts/stops the cast service. void Start(); void Stop(); protected: CastService(content::BrowserContext* browser_context, - const OptInStatsChangedCallback& opt_in_stats_callback); - virtual void Initialize() = 0; - - // Implementation-specific start/stop behavior. + PrefService* pref_service, + metrics::CastMetricsServiceClient* metrics_service_client); + + // Implementation-specific initialization. Initialization of cast service's + // sub-components, and anything that requires IO operations should go here. + // Anything that should happen before cast service is started but doesn't need + // the sub-components to finish initializing should also go here. + virtual void InitializeInternal() = 0; + + // Implementation-specific finalization. Any initializations done by + // InitializeInternal() should be finalized here. + virtual void FinalizeInternal() = 0; + + // Implementation-specific start behavior. It basically starts the + // sub-component services and does additional initialization that cannot be + // done in the InitializationInternal(). virtual void StartInternal() = 0; + + // Implementation-specific stop behavior. Any initializations done by + // StartInternal() should be finalized here. virtual void StopInternal() = 0; content::BrowserContext* browser_context() const { return browser_context_; } - const OptInStatsChangedCallback& opt_in_stats_callback() const { - return opt_in_stats_callback_; + PrefService* pref_service() const { return pref_service_; } + metrics::CastMetricsServiceClient* metrics_service_client() const { + return metrics_service_client_; } private: content::BrowserContext* const browser_context_; - const OptInStatsChangedCallback opt_in_stats_callback_; + PrefService* const pref_service_; + metrics::CastMetricsServiceClient* const metrics_service_client_; bool stopped_; const scoped_ptr<base::ThreadChecker> thread_checker_; diff --git a/chromium/chromecast/browser/service/cast_service_android.cc b/chromium/chromecast/browser/service/cast_service_android.cc index 7a4b563c1b5..8822c83e7b8 100644 --- a/chromium/chromecast/browser/service/cast_service_android.cc +++ b/chromium/chromecast/browser/service/cast_service_android.cc @@ -4,30 +4,44 @@ #include "chromecast/browser/service/cast_service_android.h" +#include "base/bind.h" #include "chromecast/android/chromecast_config_android.h" +#include "chromecast/browser/metrics/cast_metrics_service_client.h" namespace chromecast { // static -CastService* CastService::Create( +scoped_ptr<CastService> CastService::Create( content::BrowserContext* browser_context, - net::URLRequestContextGetter* request_context_getter, - const OptInStatsChangedCallback& opt_in_stats_callback) { - return new CastServiceAndroid(browser_context, opt_in_stats_callback); + PrefService* pref_service, + metrics::CastMetricsServiceClient* metrics_service_client, + net::URLRequestContextGetter* request_context_getter) { + return scoped_ptr<CastService>( + new CastServiceAndroid(browser_context, + pref_service, + metrics_service_client)); } CastServiceAndroid::CastServiceAndroid( content::BrowserContext* browser_context, - const OptInStatsChangedCallback& opt_in_stats_callback) - : CastService(browser_context, opt_in_stats_callback) { + PrefService* pref_service, + metrics::CastMetricsServiceClient* metrics_service_client) + : CastService(browser_context, pref_service, metrics_service_client) { } CastServiceAndroid::~CastServiceAndroid() { } -void CastServiceAndroid::Initialize() { +void CastServiceAndroid::InitializeInternal() { android::ChromecastConfigAndroid::GetInstance()-> - SetSendUsageStatsChangedCallback(opt_in_stats_callback()); + SetSendUsageStatsChangedCallback( + base::Bind(&metrics::CastMetricsServiceClient::EnableMetricsService, + base::Unretained(metrics_service_client()))); +} + +void CastServiceAndroid::FinalizeInternal() { + android::ChromecastConfigAndroid::GetInstance()-> + SetSendUsageStatsChangedCallback(base::Callback<void(bool)>()); } void CastServiceAndroid::StartInternal() { diff --git a/chromium/chromecast/browser/service/cast_service_android.h b/chromium/chromecast/browser/service/cast_service_android.h index 22670c6f2bb..91baf349960 100644 --- a/chromium/chromecast/browser/service/cast_service_android.h +++ b/chromium/chromecast/browser/service/cast_service_android.h @@ -13,14 +13,16 @@ namespace chromecast { class CastServiceAndroid : public CastService { public: CastServiceAndroid(content::BrowserContext* browser_context, - const OptInStatsChangedCallback& opt_in_stats_callback); - virtual ~CastServiceAndroid(); + PrefService* pref_service, + metrics::CastMetricsServiceClient* metrics_service_client); + ~CastServiceAndroid() override; protected: // CastService implementation. - virtual void Initialize() override; - virtual void StartInternal() override; - virtual void StopInternal() override; + void InitializeInternal() override; + void FinalizeInternal() override; + void StartInternal() override; + void StopInternal() override; private: DISALLOW_COPY_AND_ASSIGN(CastServiceAndroid); diff --git a/chromium/chromecast/browser/service/cast_service_simple.cc b/chromium/chromecast/browser/service/cast_service_simple.cc index 9ac90b040e7..2a40b3e16df 100644 --- a/chromium/chromecast/browser/service/cast_service_simple.cc +++ b/chromium/chromecast/browser/service/cast_service_simple.cc @@ -5,12 +5,12 @@ #include "chromecast/browser/service/cast_service_simple.h" #include "base/command_line.h" +#include "base/files/file_util.h" #include "chromecast/browser/cast_content_window.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "net/base/filename_util.h" #include "net/url_request/url_request_context_getter.h" -#include "url/gurl.h" namespace chromecast { @@ -27,29 +27,38 @@ GURL GetStartupURL() { if (url.is_valid() && url.has_scheme()) return url; - return net::FilePathToFileURL(base::FilePath(args[0])); + return net::FilePathToFileURL( + base::MakeAbsoluteFilePath(base::FilePath(args[0]))); } } // namespace // static -CastService* CastService::Create( +scoped_ptr<CastService> CastService::Create( content::BrowserContext* browser_context, - net::URLRequestContextGetter* request_context_getter, - const OptInStatsChangedCallback& opt_in_stats_callback) { - return new CastServiceSimple(browser_context, opt_in_stats_callback); + PrefService* pref_service, + metrics::CastMetricsServiceClient* metrics_service_client, + net::URLRequestContextGetter* request_context_getter) { + return scoped_ptr<CastService>(new CastServiceSimple(browser_context, + pref_service, + metrics_service_client)); } CastServiceSimple::CastServiceSimple( content::BrowserContext* browser_context, - const OptInStatsChangedCallback& opt_in_stats_callback) - : CastService(browser_context, opt_in_stats_callback) { + PrefService* pref_service, + metrics::CastMetricsServiceClient* metrics_service_client) + : CastService(browser_context, pref_service, metrics_service_client) { } CastServiceSimple::~CastServiceSimple() { } -void CastServiceSimple::Initialize() { +void CastServiceSimple::InitializeInternal() { + startup_url_ = GetStartupURL(); +} + +void CastServiceSimple::FinalizeInternal() { } void CastServiceSimple::StartInternal() { @@ -57,16 +66,16 @@ void CastServiceSimple::StartInternal() { gfx::Size initial_size(1280, 720); window_.reset(new CastContentWindow); - web_contents_ = window_->Create(initial_size, browser_context()); + web_contents_ = window_->CreateWebContents(initial_size, browser_context()); + window_->CreateWindowTree(initial_size, web_contents_.get()); - web_contents_->GetController().LoadURL(GetStartupURL(), - content::Referrer(), + web_contents_->GetController().LoadURL(startup_url_, content::Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); } void CastServiceSimple::StopInternal() { - web_contents_->GetRenderViewHost()->ClosePage(); + web_contents_->ClosePage(); web_contents_.reset(); window_.reset(); } diff --git a/chromium/chromecast/browser/service/cast_service_simple.h b/chromium/chromecast/browser/service/cast_service_simple.h index f1fb5c8a127..8ce5a13484a 100644 --- a/chromium/chromecast/browser/service/cast_service_simple.h +++ b/chromium/chromecast/browser/service/cast_service_simple.h @@ -7,6 +7,7 @@ #include "base/memory/scoped_ptr.h" #include "chromecast/browser/service/cast_service.h" +#include "url/gurl.h" namespace content { class WebContents; @@ -18,18 +19,21 @@ class CastContentWindow; class CastServiceSimple : public CastService { public: CastServiceSimple(content::BrowserContext* browser_context, - const OptInStatsChangedCallback& opt_in_stats_callback); - virtual ~CastServiceSimple(); + PrefService* pref_service, + metrics::CastMetricsServiceClient* metrics_service_client); + ~CastServiceSimple() override; protected: // CastService implementation: - virtual void Initialize() override; - virtual void StartInternal() override; - virtual void StopInternal() override; + void InitializeInternal() override; + void FinalizeInternal() override; + void StartInternal() override; + void StopInternal() override; private: scoped_ptr<CastContentWindow> window_; scoped_ptr<content::WebContents> web_contents_; + GURL startup_url_; DISALLOW_COPY_AND_ASSIGN(CastServiceSimple); }; diff --git a/chromium/chromecast/browser/url_request_context_factory.cc b/chromium/chromecast/browser/url_request_context_factory.cc index 339546c51e1..9143071fb8e 100644 --- a/chromium/chromecast/browser/url_request_context_factory.cc +++ b/chromium/chromecast/browser/url_request_context_factory.cc @@ -10,20 +10,20 @@ #include "base/threading/worker_pool.h" #include "chromecast/browser/cast_http_user_agent_settings.h" #include "chromecast/browser/cast_network_delegate.h" +#include "chromecast/common/chromecast_switches.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/cookie_store_factory.h" #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "net/cert/cert_verifier.h" +#include "net/cert_net/nss_ocsp.h" #include "net/cookies/cookie_store.h" #include "net/dns/host_resolver.h" #include "net/http/http_auth_handler_factory.h" -#include "net/http/http_cache.h" #include "net/http/http_network_layer.h" #include "net/http/http_server_properties_impl.h" #include "net/http/http_stream_factory.h" -#include "net/ocsp/nss_ocsp.h" #include "net/proxy/proxy_service.h" #include "net/socket/next_proto.h" #include "net/ssl/channel_id_service.h" @@ -58,29 +58,29 @@ class URLRequestContextFactory::URLRequestContextGetter factory_(factory) { } - virtual net::URLRequestContext* GetURLRequestContext() override { + net::URLRequestContext* GetURLRequestContext() override { if (!request_context_) { if (is_media_) { request_context_.reset(factory_->CreateMediaRequestContext()); } else { request_context_.reset(factory_->CreateSystemRequestContext()); -#if defined(USE_NSS) +#if defined(USE_NSS_CERTS) // Set request context used by NSS for Crl requests. net::SetURLRequestContextForNSSHttpIO(request_context_.get()); -#endif // defined(USE_NSS) +#endif // defined(USE_NSS_CERTS) } } return request_context_.get(); } - virtual scoped_refptr<base::SingleThreadTaskRunner> + scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() const override { return content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::IO); } private: - virtual ~URLRequestContextGetter() {} + ~URLRequestContextGetter() override {} const bool is_media_; URLRequestContextFactory* const factory_; @@ -105,7 +105,7 @@ class URLRequestContextFactory::MainURLRequestContextGetter std::swap(protocol_handlers_, *protocol_handlers); } - virtual net::URLRequestContext* GetURLRequestContext() override { + net::URLRequestContext* GetURLRequestContext() override { if (!request_context_) { request_context_.reset(factory_->CreateMainRequestContext( browser_context_, &protocol_handlers_, request_interceptors_.Pass())); @@ -114,14 +114,14 @@ class URLRequestContextFactory::MainURLRequestContextGetter return request_context_.get(); } - virtual scoped_refptr<base::SingleThreadTaskRunner> + scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() const override { return content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::IO); } private: - virtual ~MainURLRequestContextGetter() {} + ~MainURLRequestContextGetter() override {} content::BrowserContext* const browser_context_; URLRequestContextFactory* const factory_; @@ -144,7 +144,7 @@ URLRequestContextFactory::~URLRequestContextFactory() { } void URLRequestContextFactory::InitializeOnUIThread() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Cast http user agent settings must be initialized in UI thread // because it registers itself to pref notification observer which is not // thread safe. @@ -243,15 +243,17 @@ void URLRequestContextFactory::InitializeMainContextDependencies( url::kDataScheme, new net::DataProtocolHandler); DCHECK(set_protocol); -#if defined(OS_ANDROID) - set_protocol = job_factory->SetProtocolHandler( - url::kFileScheme, - new net::FileProtocolHandler( - content::BrowserThread::GetBlockingPool()-> - GetTaskRunnerWithShutdownBehavior( - base::SequencedWorkerPool::SKIP_ON_SHUTDOWN))); - DCHECK(set_protocol); -#endif // defined(OS_ANDROID) + + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableLocalFileAccesses)) { + set_protocol = job_factory->SetProtocolHandler( + url::kFileScheme, + new net::FileProtocolHandler( + content::BrowserThread::GetBlockingPool()-> + GetTaskRunnerWithShutdownBehavior( + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN))); + DCHECK(set_protocol); + } // Set up interceptors in the reverse order. scoped_ptr<net::URLRequestJobFactory> top_job_factory = job_factory.Pass(); @@ -298,7 +300,7 @@ void URLRequestContextFactory::PopulateNetworkSessionParams( } net::URLRequestContext* URLRequestContextFactory::CreateSystemRequestContext() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); InitializeSystemContextDependencies(); net::HttpNetworkSession::Params system_params; PopulateNetworkSessionParams(false, &system_params); @@ -330,7 +332,7 @@ net::URLRequestContext* URLRequestContextFactory::CreateSystemRequestContext() { } net::URLRequestContext* URLRequestContextFactory::CreateMediaRequestContext() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); DCHECK(main_getter_.get()) << "Getting MediaRequestContext before MainRequestContext"; net::URLRequestContext* main_context = main_getter_->GetURLRequestContext(); @@ -352,14 +354,11 @@ net::URLRequestContext* URLRequestContextFactory::CreateMainRequestContext( content::BrowserContext* browser_context, content::ProtocolHandlerMap* protocol_handlers, content::URLRequestInterceptorScopedVector request_interceptors) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); InitializeSystemContextDependencies(); - net::HttpCache::BackendFactory* main_backend = - net::HttpCache::DefaultBackend::InMemory(16 * 1024 * 1024); - bool ignore_certificate_errors = false; - CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); if (cmd_line->HasSwitch(switches::kIgnoreCertificateErrors)) { ignore_certificate_errors = true; } @@ -367,7 +366,8 @@ net::URLRequestContext* URLRequestContextFactory::CreateMainRequestContext( PopulateNetworkSessionParams(ignore_certificate_errors, &network_session_params); InitializeMainContextDependencies( - new net::HttpCache(network_session_params, main_backend), + new net::HttpNetworkLayer( + new net::HttpNetworkSession(network_session_params)), protocol_handlers, request_interceptors.Pass()); diff --git a/chromium/chromecast/browser/url_request_context_factory.h b/chromium/chromecast/browser/url_request_context_factory.h index 0b9a4e955e0..5ae43cb7a4a 100644 --- a/chromium/chromecast/browser/url_request_context_factory.h +++ b/chromium/chromecast/browser/url_request_context_factory.h @@ -48,6 +48,10 @@ class URLRequestContextFactory { return app_network_delegate_.get(); } + net::HostResolver* host_resolver() const { + return host_resolver_.get(); + } + // Initialize the CastNetworkDelegate objects. This needs to be done // after the CastService is created, but before any URL requests are made. void InitializeNetworkDelegates(); diff --git a/chromium/chromecast/browser/webui/webui_cast.h b/chromium/chromecast/browser/webui/webui_cast.h deleted file mode 100644 index 0172dc5420e..00000000000 --- a/chromium/chromecast/browser/webui/webui_cast.h +++ /dev/null @@ -1,18 +0,0 @@ -// 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_BROWSER_WEBUI_WEBUI_CAST_H_ -#define CHROMECAST_BROWSER_WEBUI_WEBUI_CAST_H_ - -namespace chromecast { -namespace shell { - -// Initializes all WebUIs needed for the Chromecast shell. This should be -// implemented on a per-product basis. -void InitializeWebUI(); - -} // namespace shell -} // namespace chromecast - -#endif // CHROMECAST_BROWSER_UI_WEBUI_CAST_H_ diff --git a/chromium/chromecast/build/args.gn b/chromium/chromecast/build/args.gn new file mode 100644 index 00000000000..1624b0a9a6b --- /dev/null +++ b/chromium/chromecast/build/args.gn @@ -0,0 +1,14 @@ +# Build arguments for Chromecast. +# Copy the contents of this file to gn args. + +root_extra_deps = [ "//chromecast" ] + +# Flags set from build/common.gypi +enable_mpeg2ts_stream_parser = true +ffmpeg_branding = "ChromeOS" +proprietary_codecs = true +enable_browser_cdms = true +# TODO(halliwell): look into supporting Cast Ozone with GN. +# ozone_platform_cast = 1 +# TODO(gyp): Add support for blink_logging_always_on and enable it. +# blink_logging_always_on = 1 diff --git a/chromium/chromecast/build/tests/test_list.gypi b/chromium/chromecast/build/tests/test_list.gypi new file mode 100644 index 00000000000..93373898388 --- /dev/null +++ b/chromium/chromecast/build/tests/test_list.gypi @@ -0,0 +1,54 @@ +# 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': { + 'variables': { + 'variables': { + # Note: priority can be set from 1 to 9 to indicate precedence for + # filters (larger overrides smaller). + 'priority%': 1, + 'test_output_folder': '<(SHARED_INTERMEDIATE_DIR)/chromecast/tests', + }, + # Copy from previous level. + 'test_output_folder': '<(test_output_folder)', + + 'test_output_file_prefix': + '<(test_output_folder)/<(priority)-<(_target_name)', + }, + # Copy from previous level. + 'test_output_folder': '<(test_output_folder)', + + 'test_filters_output_file': '<(test_output_file_prefix).filters', + 'test_generator_py': '<(DEPTH)/chromecast/tools/build/generate_test_lists.py', + 'test_list_output_file': '<(test_output_file_prefix).tests', + 'filters%': [], + }, + 'actions': [ + { + 'action_name': 'generate_test_list', + 'inputs': ['<(test_generator_py)'], + 'outputs': ['<(test_list_output_file)'], + 'message': 'Generating test list from <(_target_name)', + 'action': [ + 'python', '<@(_inputs)', + '-o', '<(test_list_output_file)', + 'create_list', + '<@(_dependencies)' + ], + }, + { + 'action_name': 'generate_filter_list', + 'inputs': ['<(test_generator_py)'], + 'outputs': ['<(test_filters_output_file)'], + 'message': 'Generating filter list from <(_target_name)', + 'action': [ + 'python', '<@(_inputs)', + '-o', '<(test_filters_output_file)', + 'create_list', + '<@(filters)' + ], + }, + ], +} diff --git a/chromium/chromecast/chromecast.gni b/chromium/chromecast/chromecast.gni new file mode 100644 index 00000000000..b6f7307bcce --- /dev/null +++ b/chromium/chromecast/chromecast.gni @@ -0,0 +1,10 @@ +# Copyright 2015 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. + +declare_args() { + # is_chromecast_chrome_branded is set to true for an official build + is_chromecast_chrome_branded = false + + use_playready = false +} diff --git a/chromium/chromecast/chromecast.gyp b/chromium/chromecast/chromecast.gyp index 22a8085c6aa..ed8261946da 100644 --- a/chromium/chromecast/chromecast.gyp +++ b/chromium/chromecast/chromecast.gyp @@ -6,15 +6,53 @@ 'variables': { 'android_support_v13_target%': '../third_party/android_tools/android_tools.gyp:android_support_v13_javalib', + 'cast_build_release': 'internal/build/cast_build_release', + 'cast_is_debug_build%': 0, + # Refers to enum CastProductType in components/metrics/proto/cast_logs.proto + 'cast_product_type%': 0, # CAST_PRODUCT_TYPE_UNKNOWN 'chromium_code': 1, 'chromecast_branding%': 'Chromium', + 'disable_display%': 0, + 'enable_default_cast_graphics%': 1, + 'ozone_platform_cast%': 0, + 'use_chromecast_webui%': 0, }, + 'includes': [ + 'chromecast_tests.gypi', + ], 'target_defaults': { 'include_dirs': [ '..', # Root of Chromium checkout ], + 'conditions': [ + ['disable_display==1', { + 'defines': ['DISABLE_DISPLAY'], + }], + ], }, 'targets': [ + # Public API target for OEM partners to replace shlibs. + { + 'target_name': 'cast_public_api', + 'type': '<(component)', + 'sources': [ + 'public/cast_egl_platform.h', + 'public/cast_egl_platform_shlib.h', + 'public/cast_media_shlib.h', + 'public/cast_sys_info.h', + 'public/chromecast_export.h', + 'public/graphics_properties_shlib.h', + 'public/graphics_types.h', + 'public/media/decoder_config.h', + 'public/osd_plane.h', + 'public/osd_plane_shlib.h', + 'public/osd_surface.h', + ], + }, + # TODO(gunsch): Remove this fake target once it's either added or no + # longer referenced from internal code. + {'target_name': 'cast_media_audio', 'type': 'none'}, + { 'target_name': 'cast_base', 'type': '<(component)', @@ -22,19 +60,60 @@ '../base/base.gyp:base', ], 'sources': [ + 'base/cast_paths.cc', + 'base/cast_paths.h', 'base/metrics/cast_histograms.h', 'base/metrics/cast_metrics_helper.cc', 'base/metrics/cast_metrics_helper.h', + 'base/metrics/grouped_histogram.cc', + 'base/metrics/grouped_histogram.h', + 'base/serializers.cc', + 'base/serializers.h' ], }, # end of target 'cast_base' { + 'target_name': 'cast_crash_client', + 'type': '<(component)', + 'dependencies': [ + '../breakpad/breakpad.gyp:breakpad_client', + '../components/components.gyp:crash_component', + ], + 'sources': [ + 'crash/cast_crash_keys.cc', + 'crash/cast_crash_keys.h', + 'crash/cast_crash_reporter_client.cc', + 'crash/cast_crash_reporter_client.h', + ], + 'conditions': [ + ['chromecast_branding=="Chrome"', { + 'dependencies': [ + 'internal/chromecast_internal.gyp:crash_internal', + ], + }, { + 'sources': [ + 'crash/cast_crash_reporter_client_simple.cc', + ], + }], + ] + }, # end of target 'cast_crash_client' + { 'target_name': 'cast_net', 'type': '<(component)', 'sources': [ - 'net/network_change_notifier_cast.cc', - 'net/network_change_notifier_cast.h', - 'net/network_change_notifier_factory_cast.cc', - 'net/network_change_notifier_factory_cast.h', + 'net/connectivity_checker.cc', + 'net/connectivity_checker.h', + 'net/net_switches.cc', + 'net/net_switches.h', + 'net/net_util_cast.cc', + 'net/net_util_cast.h', + ], + 'conditions': [ + ['OS!="android"', { + 'sources': [ + 'net/network_change_notifier_factory_cast.cc', + 'net/network_change_notifier_factory_cast.h', + ], + }], ], }, { @@ -69,10 +148,11 @@ ], 'actions': [ { - 'action_name': 'repack_cast_shell_pack', + 'action_name': 'repack_cast_shell_pak', 'variables': { 'pak_inputs': [ '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources.pak', + '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_image_resources_100_percent.pak', '<(SHARED_INTERMEDIATE_DIR)/chromecast/shell_resources.pak', '<(SHARED_INTERMEDIATE_DIR)/content/content_resources.pak', '<(SHARED_INTERMEDIATE_DIR)/content/app/resources/content_resources_100_percent.pak', @@ -83,12 +163,28 @@ '<(SHARED_INTERMEDIATE_DIR)/ui/strings/app_locale_settings_en-US.pak', '<(SHARED_INTERMEDIATE_DIR)/ui/strings/ui_strings_en-US.pak', ], + 'conditions': [ + ['chromecast_branding=="Chrome" and use_chromecast_webui==1', { + 'pak_inputs': [ + '<(SHARED_INTERMEDIATE_DIR)/chromecast/app_resources.pak', + '<(SHARED_INTERMEDIATE_DIR)/chromecast/cast_webui_resources.pak', + ], + }], + ], 'pak_output': '<(PRODUCT_DIR)/assets/cast_shell.pak', }, 'includes': [ '../build/repack_action.gypi' ], }, ], - }, + 'conditions': [ + ['chromecast_branding=="Chrome" and use_chromecast_webui==1', { + 'dependencies': [ + 'internal/chromecast_resources.gyp:chromecast_app_resources', + 'internal/chromecast_resources.gyp:chromecast_webui_resources', + ], + }], + ], + }, # end of target 'cast_shell_pak' # This target contains all content-embedder implementation that is # non-platform-specific. { @@ -96,21 +192,31 @@ 'type': '<(component)', 'dependencies': [ 'cast_base', + 'cast_crash_client', + 'cast_net', 'cast_shell_pak', 'cast_shell_resources', + 'cast_sys_info', 'cast_version_header', 'chromecast_locales.gyp:chromecast_locales_pak', 'chromecast_locales.gyp:chromecast_settings', 'media/media.gyp:media_base', + 'media/media.gyp:media_cdm', '../base/base.gyp:base', + '../components/components.gyp:breakpad_host', '../components/components.gyp:cdm_renderer', '../components/components.gyp:component_metrics_proto', + '../components/components.gyp:crash_component', + '../components/components.gyp:devtools_discovery', + '../components/components.gyp:devtools_http_handler', + '../components/components.gyp:network_hints_browser', + '../components/components.gyp:network_hints_renderer', '../components/components.gyp:metrics', '../components/components.gyp:metrics_gpu', '../components/components.gyp:metrics_net', '../components/components.gyp:metrics_profiler', '../content/content.gyp:content', - '../content/content.gyp:content_app_browser', + '../content/content.gyp:content_app_both', '../skia/skia.gyp:skia', '../third_party/WebKit/public/blink.gyp:blink', '../third_party/widevine/cdm/widevine_cdm.gyp:widevine_cdm_version_h', @@ -134,12 +240,20 @@ 'browser/cast_http_user_agent_settings.h', 'browser/cast_network_delegate.cc', 'browser/cast_network_delegate.h', + 'browser/cast_permission_manager.cc', + 'browser/cast_permission_manager.h', + 'browser/cast_quota_permission_context.cc', + 'browser/cast_quota_permission_context.h', + 'browser/cast_resource_dispatcher_host_delegate.cc', + 'browser/cast_resource_dispatcher_host_delegate.h', 'browser/devtools/cast_dev_tools_delegate.cc', 'browser/devtools/cast_dev_tools_delegate.h', 'browser/devtools/remote_debugging_server.cc', 'browser/devtools/remote_debugging_server.h', 'browser/geolocation/cast_access_token_store.cc', 'browser/geolocation/cast_access_token_store.h', + 'browser/media/cast_media_client_android.cc', + 'browser/media/cast_media_client_android.h', 'browser/metrics/cast_metrics_prefs.cc', 'browser/metrics/cast_metrics_prefs.h', 'browser/metrics/cast_metrics_service_client.cc', @@ -147,42 +261,49 @@ 'browser/metrics/cast_stability_metrics_provider.cc', 'browser/metrics/cast_stability_metrics_provider.h', 'browser/metrics/platform_metrics_providers.h', + 'browser/pref_service_helper.cc', + 'browser/pref_service_helper.h', 'browser/service/cast_service.cc', 'browser/service/cast_service.h', 'browser/url_request_context_factory.cc', 'browser/url_request_context_factory.h', - 'browser/webui/webui_cast.h', 'common/cast_content_client.cc', 'common/cast_content_client.h', - 'common/cast_paths.cc', - 'common/cast_paths.h', 'common/cast_resource_delegate.cc', 'common/cast_resource_delegate.h', - 'common/chromecast_config.cc', - 'common/chromecast_config.h', 'common/chromecast_switches.cc', 'common/chromecast_switches.h', + 'common/media/cast_messages.h', + 'common/media/cast_message_generator.cc', + 'common/media/cast_message_generator.h', 'common/platform_client_auth.h', 'common/pref_names.cc', 'common/pref_names.h', 'renderer/cast_content_renderer_client.cc', 'renderer/cast_content_renderer_client.h', + 'renderer/cast_media_load_deferrer.cc', + 'renderer/cast_media_load_deferrer.h', + 'renderer/cast_render_process_observer.cc', + 'renderer/cast_render_process_observer.h', 'renderer/key_systems_cast.cc', 'renderer/key_systems_cast.h', + 'renderer/media/capabilities_message_filter.cc', + 'renderer/media/capabilities_message_filter.h', ], 'conditions': [ ['chromecast_branding=="Chrome"', { 'dependencies': [ - '<(cast_internal_gyp):cast_shell_internal', + 'internal/chromecast_internal.gyp:cast_shell_internal', ], }, { 'sources': [ + 'browser/cast_content_browser_client_simple.cc', 'browser/cast_network_delegate_simple.cc', 'browser/devtools/remote_debugging_server_simple.cc', 'browser/metrics/platform_metrics_providers_simple.cc', - 'browser/webui/webui_cast_simple.cc', - 'common/chromecast_config_simple.cc', + 'browser/pref_service_helper_simple.cc', 'common/platform_client_auth_simple.cc', + 'renderer/cast_content_renderer_client_simple.cc', 'renderer/key_systems_cast_simple.cc', ], 'conditions': [ @@ -193,6 +314,7 @@ ], }, { 'sources': [ + 'browser/media/cast_browser_cdm_factory_simple.cc', 'browser/service/cast_service_simple.cc', 'browser/service/cast_service_simple.h', ], @@ -209,32 +331,36 @@ ], 'dependencies': [ '../components/components.gyp:metrics_serialization', + '../ui/aura/aura.gyp:aura_test_support', + ], + }], + ['OS=="android"', { + 'dependencies': [ + '../components/components.gyp:cdm_browser', ], }], ], }, { - 'target_name': 'cast_shell_unittests', - 'type': '<(gtest_target_type)', + 'target_name': 'cast_sys_info', + 'type': '<(component)', 'dependencies': [ - 'cast_shell_common', - '../base/base.gyp:base_prefs_test_support', - '../base/base.gyp:run_all_unittests', - '../base/base.gyp:test_support_base', - '../components/components.gyp:component_metrics_proto', - '../testing/gtest.gyp:gtest', + 'cast_public_api', + '../base/base.gyp:base', ], 'sources': [ - 'browser/metrics/cast_metrics_service_client_unittest.cc', + 'base/cast_sys_info_util.h', + 'base/cast_sys_info_dummy.cc', + 'base/cast_sys_info_dummy.h', ], 'conditions': [ - ['use_allocator!="none"', { - 'dependencies': [ - '../base/allocator/allocator.gyp:allocator', + ['chromecast_branding!="Chrome"', { + 'sources': [ + 'base/cast_sys_info_util_simple.cc', ], }], - ] - }, # end of target 'cast_metrics_unittests' + ], + }, # end of target 'cast_sys_info' { 'target_name': 'cast_version_header', 'type': 'none', @@ -258,9 +384,14 @@ 'python', '<(version_py_path)', '-e', 'VERSION_FULL="<(version_full)"', - # Revision is taken from buildbot if available; otherwise, a dev string is used. - '-e', 'CAST_BUILD_REVISION="<!(echo ${BUILD_NUMBER:="local.${USER}"})"', - '-e', 'CAST_IS_DEBUG_BUILD=1 if "<(CONFIGURATION_NAME)" == "Debug" else 0', + # CAST_BUILD_INCREMENTAL is taken from buildbot if available; + # otherwise, a dev string is used. + '-e', 'CAST_BUILD_INCREMENTAL="<!(echo ${CAST_BUILD_INCREMENTAL:="<!(date +%Y%m%d.%H%M%S)"})"', + # CAST_BUILD_RELEASE is taken from cast_build_release file if exist; + # otherwise, a dev string is used. + '-e', 'CAST_BUILD_RELEASE="<!(if test -f <(cast_build_release); then cat <(cast_build_release); else echo eng.${USER}; fi)"', + '-e', 'CAST_IS_DEBUG_BUILD=1 if "<(CONFIGURATION_NAME)" == "Debug" or <(cast_is_debug_build) == 1 else 0', + '-e', 'CAST_PRODUCT_TYPE=<(cast_product_type)', 'common/version.h.in', '<@(_outputs)', ], @@ -270,24 +401,6 @@ }, ], }, - { - 'target_name': 'cast_metrics_test_support', - 'type': '<(component)', - 'dependencies': [ - 'cast_base', - ], - 'sources': [ - 'base/metrics/cast_metrics_test_helper.cc', - 'base/metrics/cast_metrics_test_helper.h', - ], - }, # end of target 'cast_metrics_test_support' - { - 'target_name': 'cast_tests', - 'type': 'none', - 'dependencies': [ - 'media/media.gyp:cast_media_unittests', - ], - }, ], # end of targets # Targets for Android receiver. @@ -295,18 +408,35 @@ ['OS=="android"', { 'targets': [ { + 'target_name': 'cast_shell_icudata', + 'type': 'none', + 'dependencies': [ + '../third_party/icu/icu.gyp:icudata', + '../v8/tools/gyp/v8.gyp:v8_external_snapshot', + ], + 'copies': [{ + 'destination': '<(PRODUCT_DIR)/assets', + 'files': [ + '<(PRODUCT_DIR)/icudtl.dat', + '<(PRODUCT_DIR)/natives_blob.bin', + '<(PRODUCT_DIR)/snapshot_blob.bin', + ], + }], + }, + { 'target_name': 'libcast_shell_android', 'type': 'shared_library', 'dependencies': [ 'cast_jni_headers', 'cast_shell_common', + 'cast_shell_icudata', 'cast_shell_pak', 'cast_version_header', '../base/base.gyp:base', '../breakpad/breakpad.gyp:breakpad_client', '../components/components.gyp:breakpad_host', '../components/components.gyp:crash_component', - '../content/content.gyp:content_app_browser', + '../components/components.gyp:external_video_surface', '../content/content.gyp:content', '../skia/skia.gyp:skia', '../ui/gfx/gfx.gyp:gfx', @@ -326,8 +456,6 @@ 'browser/android/cast_window_android.h', 'browser/android/cast_window_manager.cc', 'browser/android/cast_window_manager.h', - 'browser/android/external_video_surface_container_impl.cc', - 'browser/android/external_video_surface_container_impl.h', 'crash/android/cast_crash_reporter_client_android.cc', 'crash/android/cast_crash_reporter_client_android.h', 'crash/android/crash_handler.cc', @@ -336,7 +464,7 @@ 'conditions': [ ['chromecast_branding=="Chrome"', { 'dependencies': [ - '<(cast_internal_gyp):cast_shell_android_internal' + 'internal/chromecast_internal.gyp:cast_shell_android_internal' ], }, { 'sources': [ @@ -366,6 +494,15 @@ 'includes': ['../build/java.gypi'], }, # end of target 'cast_shell_java' { + 'target_name': 'cast_shell_manifest', + 'type': 'none', + 'variables': { + 'jinja_inputs': ['browser/android/apk/AndroidManifest.xml.jinja2'], + 'jinja_output': '<(SHARED_INTERMEDIATE_DIR)/cast_shell_manifest/AndroidManifest.xml', + }, + 'includes': [ '../build/android/jinja_template.gypi' ], + }, + { 'target_name': 'cast_shell_apk', 'type': 'none', 'dependencies': [ @@ -382,7 +519,7 @@ # if the actual Java path is used. # This will hopefully be removable after the great GN migration. 'java_in_dir': 'android', - 'android_manifest_path': 'browser/android/apk/AndroidManifest.xml', + 'android_manifest_path': '<(SHARED_INTERMEDIATE_DIR)/cast_shell_manifest/AndroidManifest.xml', 'package_name': 'org.chromium.chromecast.shell', 'native_lib_target': 'libcast_shell_android', 'asset_location': '<(PRODUCT_DIR)/assets', @@ -397,7 +534,6 @@ 'browser/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java', 'browser/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java', 'browser/android/apk/src/org/chromium/chromecast/shell/CastWindowManager.java', - 'browser/android/apk/src/org/chromium/chromecast/shell/ExternalVideoSurfaceContainer.java', ], 'direct_dependent_settings': { 'include_dirs': [ @@ -412,6 +548,46 @@ ], # end of targets }, { # OS != "android" 'targets': [ + { + 'target_name': 'cast_shell_media', + 'type': '<(component)', + 'dependencies': [ + 'media/media.gyp:cast_media', + '../content/content.gyp:content', + '../ipc/ipc.gyp:ipc', + '../media/media.gyp:media', + ], + 'sources': [ + 'browser/media/cast_browser_cdm_factory.cc', + 'browser/media/cast_browser_cdm_factory.h', + 'browser/media/cma_message_filter_host.cc', + 'browser/media/cma_message_filter_host.h', + 'browser/media/cma_message_loop.cc', + 'browser/media/cma_message_loop.h', + 'browser/media/media_pipeline_host.cc', + 'browser/media/media_pipeline_host.h', + 'common/media/cma_ipc_common.h', + 'common/media/cma_messages.h', + 'common/media/cma_message_generator.cc', + 'common/media/cma_message_generator.h', + 'common/media/cma_param_traits.cc', + 'common/media/cma_param_traits.h', + 'common/media/shared_memory_chunk.cc', + 'common/media/shared_memory_chunk.h', + 'renderer/media/audio_pipeline_proxy.cc', + 'renderer/media/audio_pipeline_proxy.h', + 'renderer/media/chromecast_media_renderer_factory.cc', + 'renderer/media/chromecast_media_renderer_factory.h', + 'renderer/media/cma_message_filter_proxy.cc', + 'renderer/media/cma_message_filter_proxy.h', + 'renderer/media/media_channel_proxy.cc', + 'renderer/media/media_channel_proxy.h', + 'renderer/media/media_pipeline_proxy.cc', + 'renderer/media/media_pipeline_proxy.h', + 'renderer/media/video_pipeline_proxy.cc', + 'renderer/media/video_pipeline_proxy.h', + ], + }, # end of target 'cast_shell_media' # This target contains all of the primary code of |cast_shell|, except # for |main|. This allows end-to-end tests using |cast_shell|. # This also includes all targets that cannot be built on Android. @@ -419,17 +595,12 @@ 'target_name': 'cast_shell_core', 'type': '<(component)', 'dependencies': [ - 'cast_net', + 'cast_shell_media', 'cast_shell_common', 'media/media.gyp:cast_media', - '../ui/aura/aura.gyp:aura_test_support', ], 'conditions': [ - ['chromecast_branding=="Chrome"', { - 'dependencies': [ - '<(cast_internal_gyp):cast_gfx_internal', - ], - }, { + ['ozone_platform_egltest==1', { 'dependencies': [ '../ui/ozone/ozone.gyp:eglplatform_shim_x11', ], @@ -445,39 +616,34 @@ 'sources': [ 'app/cast_main.cc', ], - }, - { - 'target_name': 'cast_shell_browser_test', - 'type': '<(gtest_target_type)', - 'dependencies': [ - 'cast_shell_test_support', - '../testing/gtest.gyp:gtest', - ], - 'defines': [ - 'HAS_OUT_OF_PROC_TEST_RUNNER', - ], - 'sources': [ - 'browser/test/chromecast_shell_browser_test.cc', + 'ldflags': [ + # Allow OEMs to override default libraries that are shipped with + # cast receiver package by installed OEM-specific libraries in + # /oem_cast_shlib. + '-Wl,-rpath=/oem_cast_shlib', + # TODO(dougsteed): remove when Chromecast moves to boringssl. + # Allow the cast shell to find the NSS module in the same + # directory. + '-Wl,-rpath=\$$ORIGIN' ], }, + ], # end of targets + }], + ['enable_default_cast_graphics==1', { + 'targets': [ { - 'target_name': 'cast_shell_test_support', - 'type': '<(component)', - 'defines': [ - 'HAS_OUT_OF_PROC_TEST_RUNNER', - ], + 'target_name': 'libcast_graphics_1.0', + 'type': 'shared_library', 'dependencies': [ - 'cast_shell_core', - '../content/content_shell_and_tests.gyp:content_browser_test_support', - '../testing/gtest.gyp:gtest', + 'cast_public_api' ], 'sources': [ - 'browser/test/chromecast_browser_test.cc', - 'browser/test/chromecast_browser_test.h', - 'browser/test/chromecast_browser_test_runner.cc', + 'graphics/cast_egl_platform_default.cc', + 'graphics/graphics_properties_default.cc', + 'graphics/osd_plane_default.cc' ], - }, - ], # end of targets + } + ] }], ], # end of conditions } diff --git a/chromium/chromecast/chromecast_tests.gypi b/chromium/chromecast/chromecast_tests.gypi new file mode 100644 index 00000000000..a5a7204fcc4 --- /dev/null +++ b/chromium/chromecast/chromecast_tests.gypi @@ -0,0 +1,286 @@ +# Copyright (c) 2014 Google Inc. All Rights Reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'chromium_code': 1 + }, + 'targets': [ + { + 'target_name': 'cast_base_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'chromecast.gyp:cast_base', + '../base/base.gyp:run_all_unittests', + '../testing/gtest.gyp:gtest', + ], + 'sources': [ + 'base/serializers_unittest.cc', + ], + }, + { + 'target_name': 'cast_tests', + 'type': 'none', + 'dependencies': [ + 'cast_test_generator', + ], + 'conditions': [ + ['chromecast_branding=="Chrome"', { + 'dependencies': [ + 'internal/chromecast_internal.gyp:cast_tests_internal', + ], + }], + ], + }, + # This target only depends on targets that generate test binaries. + { + 'target_name': 'cast_test_generator', + 'type': 'none', + 'dependencies': [ + 'cast_base_unittests', + '../base/base.gyp:base_unittests', + '../content/content_shell_and_tests.gyp:content_unittests', + '../crypto/crypto.gyp:crypto_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../jingle/jingle.gyp:jingle_unittests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../net/net.gyp:net_unittests', + '../sandbox/sandbox.gyp:sandbox_linux_unittests', + '../sql/sql.gyp:sql_unittests', + '../sync/sync.gyp:sync_unit_tests', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', + '../ui/base/ui_base_tests.gyp:ui_base_unittests', + '../url/url.gyp:url_unittests', + ], + 'conditions': [ + ['target_arch=="arm" and OS!="android"', { + 'variables': { + 'filters': [ + # Run net_unittests first to avoid random failures due to slow python startup + # KeygenHandlerTest.SmokeTest and KeygenHandlerTest.ConcurrencyTest fail due to + # readonly certdb (b/8153161) + # URLRequestTestHTTP.GetTest_ManyCookies takes roughly 55s to run. Increase + # timeout to 75s from 45s to allow it to pass (b/19821476) + # ProxyScriptFetcherImplTest.HttpMimeType is flaking (b/19848784) + 'net_unittests --gtest_filter=-KeygenHandlerTest.SmokeTest:KeygenHandlerTest.ConcurrencyTest:ProxyScriptFetcherImplTest.HttpMimeType --test-launcher-timeout=75000', + # Disable ProcessMetricsTest.GetNumberOfThreads (b/15610509) + # Disable ProcessUtilTest.* (need to define OS_ANDROID) + # Disable StackContainer.BufferAlignment (don't support 16-byte alignment) + # Disable SystemMetrics2Test.GetSystemMemoryInfo (buffers>0 can't be guaranteed) + 'base_unittests --gtest_filter=-ProcessMetricsTest.GetNumberOfThreads:ProcessUtilTest.*:StackContainer.BufferAlignment:SystemMetrics2Test.GetSystemMemoryInfo', + # DesktopCaptureDeviceTest.*: No capture device on Eureka + # Disable PepperGamepadHostTest.WaitForReply (pepper not supported on Eureka) + # Disable GpuDataManagerImplPrivateTest.SetGLStrings and + # RenderWidgetHostTest.Background because we disable the blacklist to enable WebGL (b/16142554) + 'content_unittests --gtest_filter=-DOMStorageDatabaseTest.TestCanOpenAndReadWebCoreDatabase:DesktopCaptureDeviceTest.Capture:GamepadProviderTest.PollingAccess:GpuDataManagerImplPrivateTest.SetGLStrings:PepperGamepadHostTest.WaitForReply:RenderWidgetHostTest.Background', + # Disable VP9 related tests (b/18593324) + # PipelineIntegrationTest.BasicPlayback_MediaSource_VP9_WebM + # PipelineIntegrationTest.BasicPlayback_VideoOnly_VP9_WebM + # PipelineIntegrationTest.BasicPlayback_VP9* + # PipelineIntegrationTest.P444_VP9_WebM + # Disable VP8A tests (b/18593324) + # PipelineIntegrationTest.BasicPlayback_VP8A* + # Disable OpusAudioDecoderTest/AudioDecoderTest.ProduceAudioSamples/0 (unit + # test fails when Opus decoder uses fixed-point) + # Due to b/16456550, disable the following four test cases: + # AudioOutputControllerTest.PlayDivertSwitchDeviceRevertClose + # AudioOutputControllerTest.PlaySwitchDeviceClose + # AudioStreamHandlerTest.Play + # SoundsManagerTest.Play + # Disable AudioStreamHandlerTest.ConsecutivePlayRequests (b/16539293) + 'media_unittests --gtest_filter=-AudioOutputControllerTest.PlayDivertSwitchDeviceRevertClose:AudioOutputControllerTest.PlaySwitchDeviceClose:AudioStreamHandlerTest.Play:AudioStreamHandlerTest.ConsecutivePlayRequests:PipelineIntegrationTest.BasicPlayback_MediaSource_VP9_WebM:PipelineIntegrationTest.BasicPlayback_VideoOnly_VP9_WebM:PipelineIntegrationTest.BasicPlayback_VP9*:PipelineIntegrationTest.P444_VP9_WebM:PipelineIntegrationTest.BasicPlayback_VP8A*:OpusAudioDecoderTest/AudioDecoderTest.ProduceAudioSamples/0:SoundsManagerTest.Play', + 'sync_unit_tests --gtest_filter=-SyncHttpBridgeTest.*', + # DoAppendUTF8Invalid fails because of dcheck_always_on flag in Eng builds + 'url_unittests --gtest_filter=-URLCanonTest.DoAppendUTF8Invalid', + ], + }, + }, { # else "x86" or "android" + 'variables': { + 'filters': [ + # Disable PipelineIntegrationTest.BasicPlayback_MediaSource_VP9_WebM (not supported) + 'media_unittests --gtest_filter=-PipelineIntegrationTest.BasicPlayback_MediaSource_VP9_WebM', + ], + } + }], + ['disable_display==0', { + 'dependencies': [ + '../gpu/gpu.gyp:gpu_unittests', + ], + }], + ['OS!="android"', { + 'dependencies': [ + 'cast_shell_browser_test', + 'cast_shell_media_unittests', + 'media/media.gyp:cast_media_unittests', + ], + 'variables': { + 'filters': [ + 'cast_shell_browser_test --no-sandbox --disable-gpu', + ], + }, + }], + ], + 'includes': ['build/tests/test_list.gypi'], + }, + # Builds all tests and the output lists of build/run targets for those tests. + # Note: producing a predetermined list of dependent inputs on which to + # regenerate this output is difficult with GYP. This file is not + # guaranteed to be regenerated outside of a clean build. + { + 'target_name': 'cast_test_lists', + 'type': 'none', + 'dependencies': [ + 'cast_tests', + ], + 'variables': { + 'test_generator_py': '<(DEPTH)/chromecast/tools/build/generate_test_lists.py', + 'test_inputs_dir': '<(SHARED_INTERMEDIATE_DIR)/chromecast/tests', + 'test_additional_options': '--ozone-platform=test' + }, + 'actions': [ + { + 'action_name': 'generate_combined_test_build_list', + 'message': 'Generating combined test build list', + 'inputs': ['<(test_generator_py)'], + 'outputs': ['<(PRODUCT_DIR)/tests/build_test_list.txt'], + 'action': [ + 'python', '<(test_generator_py)', + '-t', '<(test_inputs_dir)', + '-o', '<@(_outputs)', + 'pack_build', + ], + }, + { + 'action_name': 'generate_combined_test_run_list', + 'message': 'Generating combined test run list', + 'inputs': ['<(test_generator_py)'], + 'outputs': ['<(PRODUCT_DIR)/tests/run_test_list.txt'], + 'action': [ + 'python', '<(test_generator_py)', + '-t', '<(test_inputs_dir)', + '-o', '<@(_outputs)', + '-a', '<(test_additional_options)', + 'pack_run', + ], + } + ], + }, + { + 'target_name': 'cast_metrics_test_support', + 'type': '<(component)', + 'dependencies': [ + 'cast_base', + ], + 'sources': [ + 'base/metrics/cast_metrics_test_helper.cc', + 'base/metrics/cast_metrics_test_helper.h', + ], + }, # end of target 'cast_metrics_test_support' + ], # end of targets + 'conditions': [ + ['OS=="android"', { + 'targets': [ + { + 'target_name': 'cast_android_tests', + 'type': 'none', + 'dependencies': [ + '../base/base.gyp:base_unittests_apk', + '../cc/cc_tests.gyp:cc_unittests_apk', + '../ipc/ipc.gyp:ipc_tests_apk', + '../media/media.gyp:media_unittests_apk', + '../media/midi/midi.gyp:midi_unittests_apk', + '../net/net.gyp:net_unittests_apk', + '../sandbox/sandbox.gyp:sandbox_linux_jni_unittests_apk', + '../sql/sql.gyp:sql_unittests_apk', + '../sync/sync.gyp:sync_unit_tests_apk', + '../ui/events/events.gyp:events_unittests_apk', + '../ui/gfx/gfx_tests.gyp:gfx_unittests_apk', + ], + 'includes': ['build/tests/test_list.gypi'], + }, + { + 'target_name': 'cast_android_test_lists', + 'type': 'none', + 'dependencies': [ + 'cast_android_tests', + ], + 'variables': { + 'test_generator_py': '<(DEPTH)/chromecast/tools/build/generate_test_lists.py', + 'test_inputs_dir': '<(SHARED_INTERMEDIATE_DIR)/chromecast/tests', + }, + 'actions': [ + { + 'action_name': 'generate_combined_test_build_list', + 'message': 'Generating combined test build list', + 'inputs': ['<(test_generator_py)'], + 'outputs': ['<(PRODUCT_DIR)/tests/build_test_list_android.txt'], + 'action': [ + 'python', '<(test_generator_py)', + '-t', '<(test_inputs_dir)', + '-o', '<@(_outputs)', + 'pack_build', + ], + }, + ], + }, + ], # end of targets + }, { # OS!="android" + 'targets': [ + { + 'target_name': 'cast_shell_media_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'cast_metrics_test_support', + 'cast_shell_media', + 'media/media.gyp:cast_media', + '../base/base.gyp:base', + '../base/base.gyp:test_support_base', + '../ipc/ipc.gyp:test_support_ipc', + '../media/media.gyp:media_test_support', + '../testing/gmock.gyp:gmock', + '../testing/gtest.gyp:gtest', + '../testing/gtest.gyp:gtest_main', + ], + 'sources': [ + 'renderer/media/cma_renderer_unittest.cc', + 'media/cma/test/run_all_unittests.cc', + ], + }, + { + 'target_name': 'cast_shell_test_support', + 'type': '<(component)', + 'defines': [ + 'HAS_OUT_OF_PROC_TEST_RUNNER', + ], + 'dependencies': [ + 'cast_shell_core', + '../content/content_shell_and_tests.gyp:content_browser_test_support', + '../testing/gtest.gyp:gtest', + '../third_party/mojo/mojo_public.gyp:mojo_cpp_bindings', + ], + 'sources': [ + 'browser/test/chromecast_browser_test.cc', + 'browser/test/chromecast_browser_test.h', + 'browser/test/chromecast_browser_test_runner.cc', + ], + }, # end of target 'cast_shell_test_support' + { + 'target_name': 'cast_shell_browser_test', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'cast_shell_test_support', + '../testing/gtest.gyp:gtest', + ], + 'defines': [ + 'HAS_OUT_OF_PROC_TEST_RUNNER', + ], + 'sources': [ + 'browser/test/chromecast_shell_browser_test.cc', + ], + }, + ], # end of targets + }], + ], # end of conditions +} diff --git a/chromium/chromecast/common/cast_content_client.cc b/chromium/chromecast/common/cast_content_client.cc index e93b7d2a51b..4772fdd5c06 100644 --- a/chromium/chromecast/common/cast_content_client.cc +++ b/chromium/chromecast/common/cast_content_client.cc @@ -4,6 +4,8 @@ #include "chromecast/common/cast_content_client.h" +#include "base/strings/stringprintf.h" +#include "base/sys_info.h" #include "chromecast/common/version.h" #include "content/public/common/user_agent.h" #include "ui/base/l10n/l10n_util.h" @@ -12,9 +14,56 @@ namespace chromecast { namespace shell { +namespace { + +#if defined(OS_ANDROID) +std::string BuildAndroidOsInfo() { + int32 os_major_version = 0; + int32 os_minor_version = 0; + int32 os_bugfix_version = 0; + base::SysInfo::OperatingSystemVersionNumbers(&os_major_version, + &os_minor_version, + &os_bugfix_version); + + std::string android_version_str; + base::StringAppendF( + &android_version_str, "%d.%d", os_major_version, os_minor_version); + if (os_bugfix_version != 0) + base::StringAppendF(&android_version_str, ".%d", os_bugfix_version); + + std::string android_info_str; + // Append the build ID. + std::string android_build_id = base::SysInfo::GetAndroidBuildID(); + if (android_build_id.size() > 0) + android_info_str += "; Build/" + android_build_id; + + std::string os_info; + base::StringAppendF( + &os_info, + "Android %s%s", + android_version_str.c_str(), + android_info_str.c_str()); + return os_info; +} +#endif + +} // namespace + std::string GetUserAgent() { std::string product = "Chrome/" PRODUCT_VERSION; - return content::BuildUserAgentFromProduct(product) + + std::string os_info; + base::StringAppendF( + &os_info, + "%s%s", +#if defined(OS_ANDROID) + "Linux; ", + BuildAndroidOsInfo().c_str() +#else + "X11; ", + content::BuildOSCpuInfo().c_str() +#endif + ); + return content::BuildUserAgentFromOSAndProduct(os_info, product) + " CrKey/" CAST_BUILD_REVISION; } diff --git a/chromium/chromecast/common/cast_content_client.h b/chromium/chromecast/common/cast_content_client.h index 3a4d61d71f7..8289f41b34f 100644 --- a/chromium/chromecast/common/cast_content_client.h +++ b/chromium/chromecast/common/cast_content_client.h @@ -14,17 +14,17 @@ std::string GetUserAgent(); class CastContentClient : public content::ContentClient { public: - virtual ~CastContentClient(); + ~CastContentClient() override; // content::ContentClient implementation: - virtual std::string GetUserAgent() const override; - virtual base::string16 GetLocalizedString(int message_id) const override; - virtual base::StringPiece GetDataResource( + std::string GetUserAgent() const override; + base::string16 GetLocalizedString(int message_id) const override; + base::StringPiece GetDataResource( int resource_id, ui::ScaleFactor scale_factor) const override; - virtual base::RefCountedStaticMemory* GetDataResourceBytes( + base::RefCountedStaticMemory* GetDataResourceBytes( int resource_id) const override; - virtual gfx::Image& GetNativeImageNamed(int resource_id) const override; + gfx::Image& GetNativeImageNamed(int resource_id) const override; }; } // namespace shell diff --git a/chromium/chromecast/common/cast_resource_delegate.h b/chromium/chromecast/common/cast_resource_delegate.h index 8e248c3453a..3e4cac2e4cb 100644 --- a/chromium/chromecast/common/cast_resource_delegate.h +++ b/chromium/chromecast/common/cast_resource_delegate.h @@ -30,29 +30,27 @@ class CastResourceDelegate : public ui::ResourceBundle::Delegate { static CastResourceDelegate* GetInstance(); CastResourceDelegate(); - virtual ~CastResourceDelegate(); + ~CastResourceDelegate() override; // ui:ResourceBundle::Delegate implementation: - virtual base::FilePath GetPathForResourcePack( + base::FilePath GetPathForResourcePack( const base::FilePath& pack_path, ui::ScaleFactor scale_factor) override; - virtual base::FilePath GetPathForLocalePack( + base::FilePath GetPathForLocalePack( const base::FilePath& pack_path, const std::string& locale) override; - virtual gfx::Image GetImageNamed(int resource_id) override; - virtual gfx::Image GetNativeImageNamed( + gfx::Image GetImageNamed(int resource_id) override; + gfx::Image GetNativeImageNamed( int resource_id, ui::ResourceBundle::ImageRTL rtl) override; - virtual base::RefCountedStaticMemory* LoadDataResourceBytes( + base::RefCountedStaticMemory* LoadDataResourceBytes( int resource_id, ui::ScaleFactor scale_factor) override; - virtual bool GetRawDataResource(int resource_id, - ui::ScaleFactor scale_factor, - base::StringPiece* value) override; - virtual bool GetLocalizedString(int message_id, - base::string16* value) override; - virtual scoped_ptr<gfx::Font> GetFont( - ui::ResourceBundle::FontStyle style) override; + bool GetRawDataResource(int resource_id, + ui::ScaleFactor scale_factor, + base::StringPiece* value) override; + bool GetLocalizedString(int message_id, base::string16* value) override; + scoped_ptr<gfx::Font> GetFont(ui::ResourceBundle::FontStyle style) override; // Adds/removes/clears extra localized strings. void AddExtraLocalizedString(int resource_id, diff --git a/chromium/chromecast/common/chromecast_config.cc b/chromium/chromecast/common/chromecast_config.cc deleted file mode 100644 index 4b5e6bf8053..00000000000 --- a/chromium/chromecast/common/chromecast_config.cc +++ /dev/null @@ -1,144 +0,0 @@ -// 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/common/chromecast_config.h" - -#include <string> - -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/prefs/json_pref_store.h" -#include "base/prefs/pref_registry_simple.h" -#include "base/prefs/pref_service_factory.h" -#include "base/prefs/pref_store.h" -#include "base/strings/string_number_conversions.h" -#include "chromecast/common/cast_paths.h" -#include "chromecast/common/pref_names.h" - -namespace chromecast { - -namespace { - -// Config file IO worker constants. -const int kNumOfConfigFileIOWorkers = 1; -const char kNameOfConfigFileIOWorkers[] = "ConfigFileIO"; - -void UserPrefsLoadError( - PersistentPrefStore::PrefReadError* error_val, - PersistentPrefStore::PrefReadError error) { - DCHECK(error_val); - *error_val = error; -} - -base::FilePath GetConfigPath() { - base::FilePath config_path; - CHECK(PathService::Get(FILE_CAST_CONFIG, &config_path)); - return config_path; -} - -} // namespace - -// static -ChromecastConfig* ChromecastConfig::g_instance_ = NULL; - -// static -void ChromecastConfig::Create(PrefRegistrySimple* registry) { - DCHECK(g_instance_ == NULL); - g_instance_ = new ChromecastConfig(); - g_instance_->Load(registry); -} - -// static -ChromecastConfig* ChromecastConfig::GetInstance() { - DCHECK(g_instance_ != NULL); - return g_instance_; -} - -ChromecastConfig::ChromecastConfig() - : config_path_(GetConfigPath()), - worker_pool_(new base::SequencedWorkerPool(kNumOfConfigFileIOWorkers, - kNameOfConfigFileIOWorkers)) { -} - -ChromecastConfig::~ChromecastConfig() { - // Explict writing before worker_pool shutdown. - pref_service_->CommitPendingWrite(); - worker_pool_->Shutdown(); -} - -bool ChromecastConfig::Load(PrefRegistrySimple* registry) { - DCHECK(thread_checker_.CalledOnValidThread()); - VLOG(1) << "Loading config from " << config_path_.value(); - registry->RegisterIntegerPref(prefs::kRemoteDebuggingPort, 0); - - RegisterPlatformPrefs(registry); - - PersistentPrefStore::PrefReadError prefs_read_error = - PersistentPrefStore::PREF_READ_ERROR_NONE; - base::PrefServiceFactory prefServiceFactory; - scoped_refptr<base::SequencedTaskRunner> task_runner = - JsonPrefStore::GetTaskRunnerForFile(config_path_, worker_pool_.get()); - prefServiceFactory.SetUserPrefsFile(config_path_, task_runner.get()); - prefServiceFactory.set_async(false); - prefServiceFactory.set_read_error_callback( - base::Bind(&UserPrefsLoadError, &prefs_read_error)); - pref_service_ = prefServiceFactory.Create(registry); - - if (prefs_read_error == PersistentPrefStore::PREF_READ_ERROR_NONE) { - return true; - } else { - LOG(ERROR) << "Cannot initialize chromecast config: " - << config_path_.value() - << ", pref_error=" << prefs_read_error; - return false; - } -} - -void ChromecastConfig::Save() const { - DCHECK(thread_checker_.CalledOnValidThread()); - VLOG(1) << "Saving config to: " << config_path_.value(); - pref_service_->CommitPendingWrite(); -} - -const std::string ChromecastConfig::GetValue(const std::string& key) const { - DCHECK(thread_checker_.CalledOnValidThread()); - return pref_service_->GetString(key.c_str()); -} - -const int ChromecastConfig::GetIntValue(const std::string& key) const { - return pref_service_->GetInteger(key.c_str()); -} - -void ChromecastConfig::SetValue( - const std::string& key, - const std::string& value) const { - DCHECK(thread_checker_.CalledOnValidThread()); - if (pref_service_->IsUserModifiablePreference(key.c_str())) { - VLOG(1) << "Set config: key=" << key << ", value=" << value; - pref_service_->SetString(key.c_str(), value); - } else { - LOG(ERROR) << "Cannot set read-only config: key=" << key - << ", value=" << value; - } -} - -void ChromecastConfig::SetIntValue(const std::string& key, int value) const { - DCHECK(thread_checker_.CalledOnValidThread()); - if (pref_service_->IsUserModifiablePreference(key.c_str())) { - VLOG(1) << "Set config: key=" << key << ", value=" << value; - pref_service_->SetInteger(key.c_str(), value); - } else { - LOG(ERROR) << "Cannot set read-only config: key=" << key - << ", value=" << value; - } -} - -bool ChromecastConfig::HasValue(const std::string& key) const { - DCHECK(thread_checker_.CalledOnValidThread()); - return pref_service_->HasPrefPath(key.c_str()); -} - -} // namespace chromecast diff --git a/chromium/chromecast/common/chromecast_config.h b/chromium/chromecast/common/chromecast_config.h deleted file mode 100644 index 23482c6be4d..00000000000 --- a/chromium/chromecast/common/chromecast_config.h +++ /dev/null @@ -1,85 +0,0 @@ -// 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. -// -// Chromecast-specific configurations retrieved from and stored into a given -// configuration file. - -#ifndef CHROMECAST_COMMON_CHROMECAST_CONFIG_H_ -#define CHROMECAST_COMMON_CHROMECAST_CONFIG_H_ - -#include <string> - -#include "base/files/file_path.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/prefs/pref_service.h" -#include "base/threading/sequenced_worker_pool.h" -#include "base/threading/thread_checker.h" - -class PrefRegistrySimple; - -namespace chromecast { - -// Manages configuration for Chromecast via PrefService. -// It uses JsonPrefStore internally and/so the format of config file is same to -// that of JsonPrefStore. -// It is NOT thread-safe; all functions must be run on the same thread as -// the object is created. -class ChromecastConfig { - public: - // Creates new singleton instance of ChromecastConfig. - static void Create(PrefRegistrySimple* registry); - - // Returns the singleton instance of ChromecastConfig. - static ChromecastConfig* GetInstance(); - - // Saves configs into configuration file. - void Save() const; - - // Returns string value for |key|, if present. - const std::string GetValue(const std::string& key) const; - - // Returns integer value for |key|, if present. - const int GetIntValue(const std::string& key) const; - - // Sets new string value for |key|. - void SetValue(const std::string& key, const std::string& value) const; - - // Sets new int value for |key|. - void SetIntValue(const std::string& key, int value) const; - - // Whether or not a value has been set for |key|. - bool HasValue(const std::string& key) const; - - scoped_refptr<base::SequencedWorkerPool> worker_pool() const { - return worker_pool_; - } - - PrefService* pref_service() const { return pref_service_.get(); } - - private: - ChromecastConfig(); - ~ChromecastConfig(); - - // Loads configs from config file. Returns true if successful. - bool Load(PrefRegistrySimple* registry); - - // Registers any needed preferences for the current platform. - void RegisterPlatformPrefs(PrefRegistrySimple* registry); - - // Global singleton instance. - static ChromecastConfig* g_instance_; - - const base::FilePath config_path_; - const scoped_refptr<base::SequencedWorkerPool> worker_pool_; - scoped_ptr<PrefService> pref_service_; - - base::ThreadChecker thread_checker_; - - DISALLOW_COPY_AND_ASSIGN(ChromecastConfig); -}; - -} // namespace chromecast - -#endif // CHROMECAST_COMMON_CHROMECAST_CONFIG_H_ diff --git a/chromium/chromecast/common/chromecast_switches.cc b/chromium/chromecast/common/chromecast_switches.cc index 0ccbf7bc0cf..435a35abdf9 100644 --- a/chromium/chromecast/common/chromecast_switches.cc +++ b/chromium/chromecast/common/chromecast_switches.cc @@ -6,12 +6,27 @@ namespace switches { -#if defined(OS_ANDROID) -// Enable file accesses for debug. +// Enable the CMA media pipeline. +const char kEnableCmaMediaPipeline[] = "enable-cma-media-pipeline"; + +// The bitmask of codecs (media_caps.h) supported by the current HDMI sink. +const char kHdmiSinkSupportedCodecs[] = "hdmi-sink-supported-codecs"; + +// Enable file accesses. It should not be enabled for most Cast devices. const char kEnableLocalFileAccesses[] = "enable-local-file-accesses"; -#endif // defined(OS_ANDROID) // Override the URL to which metrics logs are sent for debugging. const char kOverrideMetricsUploadUrl[] = "override-metrics-upload-url"; +// Disable features that require WiFi management. +const char kNoWifi[] = "no-wifi"; + +// Pass the app id information to the renderer process, to be used for logging. +// last-launched-app should be the app that just launched and is spawning the +// renderer. +const char kLastLaunchedApp[] = "last-launched-app"; +// previous-app should be the app that was running when last-launched-app +// started. +const char kPreviousApp[] = "previous-app"; + } // namespace switches diff --git a/chromium/chromecast/common/chromecast_switches.h b/chromium/chromecast/common/chromecast_switches.h index 4fc0e715892..697a8a15763 100644 --- a/chromium/chromecast/common/chromecast_switches.h +++ b/chromium/chromecast/common/chromecast_switches.h @@ -9,14 +9,23 @@ namespace switches { -#if defined(OS_ANDROID) +// Media switches +extern const char kEnableCmaMediaPipeline[]; +extern const char kHdmiSinkSupportedCodecs[]; + // Content-implementation switches extern const char kEnableLocalFileAccesses[]; -#endif // defined(OS_ANDROID) // Metrics switches extern const char kOverrideMetricsUploadUrl[]; +// Network switches +extern const char kNoWifi[]; + +// Switches to communicate app state information +extern const char kLastLaunchedApp[]; +extern const char kPreviousApp[]; + } // namespace switches #endif // CHROMECAST_COMMON_CHROMECAST_SWITCHES_H_ diff --git a/chromium/chromecast/common/media/DEPS b/chromium/chromecast/common/media/DEPS new file mode 100644 index 00000000000..b1e00c87d24 --- /dev/null +++ b/chromium/chromecast/common/media/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+chromecast/media", + "+media/base", +] diff --git a/chromium/chromecast/common/media/OWNERS b/chromium/chromecast/common/media/OWNERS new file mode 100644 index 00000000000..b4e262d45b4 --- /dev/null +++ b/chromium/chromecast/common/media/OWNERS @@ -0,0 +1,25 @@ +# Changes to IPC messages require a security review to avoid introducing +# new sandbox escapes. +per-file *_message*.h=set noparent +per-file *_message*.h=dcheng@chromium.org +per-file *_message*.h=inferno@chromium.org +per-file *_message*.h=jln@chromium.org +per-file *_message*.h=jschuh@chromium.org +per-file *_message*.h=kenrb@chromium.org +per-file *_message*.h=mkwst@chromium.org +per-file *_message*.h=nasko@chromium.org +per-file *_message*.h=palmer@chromium.org +per-file *_message*.h=tsepez@chromium.org +per-file *_message*.h=wfh@chromium.org + +per-file *_messages.cc=set noparent +per-file *_messages.cc=dcheng@chromium.org +per-file *_messages.cc=inferno@chromium.org +per-file *_messages.cc=jln@chromium.org +per-file *_messages.cc=jschuh@chromium.org +per-file *_messages.cc=kenrb@chromium.org +per-file *_messages.cc=mkwst@chromium.org +per-file *_messages.cc=nasko@chromium.org +per-file *_messages.cc=palmer@chromium.org +per-file *_messages.cc=tsepez@chromium.org +per-file *_messages.cc=wfh@chromium.org diff --git a/chromium/chromecast/common/media/cast_message_generator.cc b/chromium/chromecast/common/media/cast_message_generator.cc new file mode 100644 index 00000000000..ee6c73e2de9 --- /dev/null +++ b/chromium/chromecast/common/media/cast_message_generator.cc @@ -0,0 +1,15 @@ +// Copyright 2015 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. + +// Get basic type definitions. +#define IPC_MESSAGE_IMPL +#include "chromecast/common/media/cast_message_generator.h" + +// Generate constructors. +#include "ipc/struct_constructor_macros.h" +#include "chromecast/common/media/cast_message_generator.h" + +// Generate destructors. +#include "ipc/struct_destructor_macros.h" +#include "chromecast/common/media/cast_message_generator.h" diff --git a/chromium/chromecast/common/media/cast_message_generator.h b/chromium/chromecast/common/media/cast_message_generator.h new file mode 100644 index 00000000000..ac6c0041c50 --- /dev/null +++ b/chromium/chromecast/common/media/cast_message_generator.h @@ -0,0 +1,8 @@ +// Copyright 2015 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. + +// Multiply-included file, hence no include guard. + +#include "chromecast/common/media/cast_messages.h" + diff --git a/chromium/chromecast/common/media/cast_messages.h b/chromium/chromecast/common/media/cast_messages.h new file mode 100644 index 00000000000..3d9cc2a88c4 --- /dev/null +++ b/chromium/chromecast/common/media/cast_messages.h @@ -0,0 +1,17 @@ +// Copyright 2015 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. + +// IPC messages related to media on Chromecast. +// Multiply-included message file, hence no include guard. + +#include "ipc/ipc_message_macros.h" + +#undef IPC_MESSAGE_EXPORT +#define IPC_MESSAGE_EXPORT +#define IPC_MESSAGE_START CastMediaMsgStart + +// Messages sent from the browser to the renderer process. + +IPC_MESSAGE_CONTROL1(CmaMsg_UpdateSupportedHdmiSinkCodecs, + int /* Codec support, bitmask of media_caps.h values */) diff --git a/chromium/chromecast/common/media/cma_ipc_common.h b/chromium/chromecast/common/media/cma_ipc_common.h new file mode 100644 index 00000000000..343110451f2 --- /dev/null +++ b/chromium/chromecast/common/media/cma_ipc_common.h @@ -0,0 +1,21 @@ +// 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_COMMON_MEDIA_CMA_IPC_COMMON_H_ +#define CHROMECAST_COMMON_MEDIA_CMA_IPC_COMMON_H_ + +namespace chromecast { +namespace media { + +enum TrackId { + kNoTrackId = -1, + kAudioTrackId = 0, + kVideoTrackId = 1, +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_COMMON_MEDIA_CMA_IPC_COMMON_H_ + diff --git a/chromium/chromecast/common/media/cma_message_generator.cc b/chromium/chromecast/common/media/cma_message_generator.cc new file mode 100644 index 00000000000..c6dd826dbbf --- /dev/null +++ b/chromium/chromecast/common/media/cma_message_generator.cc @@ -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. + +// Get basic type definitions. +#define IPC_MESSAGE_IMPL +#include "chromecast/common/media/cma_message_generator.h" + +// Generate constructors. +#include "ipc/struct_constructor_macros.h" +#include "chromecast/common/media/cma_message_generator.h" + +// Generate destructors. +#include "ipc/struct_destructor_macros.h" +#include "chromecast/common/media/cma_message_generator.h" + +// Generate param traits write methods. +#include "ipc/param_traits_write_macros.h" +namespace IPC { +#undef CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_MACROS_H_ +#include "chromecast/common/media/cma_message_generator.h" +#include "chromecast/common/media/cma_param_traits_macros.h" +} // namespace IPC + +// Generate param traits read methods. +#include "ipc/param_traits_read_macros.h" +namespace IPC { +#undef CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_MACROS_H_ +#include "chromecast/common/media/cma_message_generator.h" +#include "chromecast/common/media/cma_param_traits_macros.h" +} // namespace IPC + +// Generate param traits log methods. +#include "ipc/param_traits_log_macros.h" +namespace IPC { +#undef CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_MACROS_H_ +#include "chromecast/common/media/cma_message_generator.h" +#include "chromecast/common/media/cma_param_traits_macros.h" +} // namespace IPC diff --git a/chromium/chromecast/common/media/cma_message_generator.h b/chromium/chromecast/common/media/cma_message_generator.h new file mode 100644 index 00000000000..805066dc36c --- /dev/null +++ b/chromium/chromecast/common/media/cma_message_generator.h @@ -0,0 +1,8 @@ +// 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. + +// Multiply-included file, hence no include guard. + +#include "chromecast/common/media/cma_messages.h" + diff --git a/chromium/chromecast/common/media/cma_messages.h b/chromium/chromecast/common/media/cma_messages.h new file mode 100644 index 00000000000..3fbed3dbad7 --- /dev/null +++ b/chromium/chromecast/common/media/cma_messages.h @@ -0,0 +1,115 @@ +// 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. + +// IPC messages for the Cast Media Acceleration (CMA) pipeline. +// Multiply-included message file, hence no include guard. + +#include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/common/media/cma_param_traits.h" +#include "chromecast/common/media/cma_param_traits_macros.h" +#include "chromecast/media/cma/pipeline/load_type.h" +#include "content/public/common/common_param_traits.h" +#include "ipc/ipc_message_macros.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/buffering_state.h" +#include "media/base/pipeline_status.h" +#include "media/base/video_decoder_config.h" +#include "ui/gfx/ipc/gfx_param_traits.h" + +#undef IPC_MESSAGE_EXPORT +#define IPC_MESSAGE_EXPORT +#define IPC_MESSAGE_START CastMediaMsgStart + +// Messages sent from the renderer to the browser process. + +IPC_MESSAGE_CONTROL2(CmaHostMsg_CreateMedia, + int /* Media pipeline ID */, + chromecast::media::LoadType /* Load type */) +IPC_MESSAGE_CONTROL1(CmaHostMsg_DestroyMedia, + int /* Media pipeline ID */) +IPC_MESSAGE_CONTROL3(CmaHostMsg_SetCdm, + int /* Media pipeline ID */, + int /* render_frame_id */, + int /* cdm_id */) +IPC_MESSAGE_CONTROL2(CmaHostMsg_StartPlayingFrom, + int /* Media pipeline ID */, + base::TimeDelta /* Timestamp */) +IPC_MESSAGE_CONTROL1(CmaHostMsg_Flush, + int /* Media pipeline ID */) +IPC_MESSAGE_CONTROL1(CmaHostMsg_Stop, + int /* Media pipeline ID */) +IPC_MESSAGE_CONTROL2(CmaHostMsg_SetPlaybackRate, + int /* Media pipeline ID */, + double /* Playback rate */) + +IPC_MESSAGE_CONTROL3(CmaHostMsg_CreateAvPipe, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */, + size_t /* Fifo size */) +IPC_MESSAGE_CONTROL3(CmaHostMsg_AudioInitialize, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */, + media::AudioDecoderConfig /* Audio config */) +IPC_MESSAGE_CONTROL3(CmaHostMsg_VideoInitialize, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */, + media::VideoDecoderConfig /* Video config */) +IPC_MESSAGE_CONTROL3(CmaHostMsg_SetVolume, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */, + float /* Volume */) +IPC_MESSAGE_CONTROL2(CmaHostMsg_NotifyPipeWrite, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */) + +IPC_MESSAGE_CONTROL5(CmaHostMsg_NotifyExternalSurface, + int /* Surface ID */, + gfx::PointF /* Quad video top left */, + gfx::PointF /* Quad video top right */, + gfx::PointF /* Quad video bottim right */, + gfx::PointF /* Quad video bottom left */) + +// Messages from the browser to the renderer process. + +IPC_MESSAGE_CONTROL2(CmaMsg_MediaStateChanged, + int /* Media pipeline ID */, + media::PipelineStatus /* Status */) +IPC_MESSAGE_CONTROL4(CmaMsg_TimeUpdate, + int /* Media pipeline ID */, + base::TimeDelta /* Media time */, + base::TimeDelta /* Max media time */, + base::TimeTicks /* STC */) +IPC_MESSAGE_CONTROL2(CmaMsg_BufferingNotification, + int /* Media pipeline ID */, + media::BufferingState /* Buffering state */) + +IPC_MESSAGE_CONTROL5(CmaMsg_AvPipeCreated, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */, + bool /* Status */, + base::SharedMemoryHandle /* Shared memory */, + base::FileDescriptor /* socket handle */) +IPC_MESSAGE_CONTROL3(CmaMsg_TrackStateChanged, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */, + media::PipelineStatus /* Status */) +IPC_MESSAGE_CONTROL2(CmaMsg_NotifyPipeRead, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */) + +IPC_MESSAGE_CONTROL2(CmaMsg_Eos, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */) +IPC_MESSAGE_CONTROL3(CmaMsg_PlaybackError, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */, + media::PipelineStatus /* status */) +IPC_MESSAGE_CONTROL3(CmaMsg_PlaybackStatistics, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */, + media::PipelineStatistics /* status */) +IPC_MESSAGE_CONTROL3(CmaMsg_NaturalSizeChanged, + int /* Media pipeline ID */, + chromecast::media::TrackId /* Track ID */, + gfx::Size /* Size */)
\ No newline at end of file diff --git a/chromium/chromecast/common/media/cma_param_traits.cc b/chromium/chromecast/common/media/cma_param_traits.cc new file mode 100644 index 00000000000..33accb04672 --- /dev/null +++ b/chromium/chromecast/common/media/cma_param_traits.cc @@ -0,0 +1,135 @@ +// 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/common/media/cma_param_traits.h" + +#include <vector> + +#include "chromecast/common/media/cma_param_traits_macros.h" +#include "content/public/common/common_param_traits.h" +#include "ipc/ipc_message_macros.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/video_decoder_config.h" +#include "ui/gfx/ipc/gfx_param_traits.h" + +// Note(gunsch): these are currently defined in content/, but not declared in +// content/public/. These headers need to be forward-declared for chromecast/, +// but without new implementations linked in. +// The correct long-term fix is to use Mojo instead of the content/ IPCs. +IPC_ENUM_TRAITS_MIN_MAX_VALUE(media::ChannelLayout, + media::ChannelLayout::CHANNEL_LAYOUT_NONE, + media::ChannelLayout::CHANNEL_LAYOUT_MAX) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(media::VideoCodecProfile, + media::VIDEO_CODEC_PROFILE_MIN, + media::VIDEO_CODEC_PROFILE_MAX) +IPC_ENUM_TRAITS_MAX_VALUE(media::VideoFrame::Format, + media::VideoFrame::FORMAT_MAX) + +namespace IPC { + +void ParamTraits<media::AudioDecoderConfig>::Write( + Message* m, const media::AudioDecoderConfig& p) { + ParamTraits<media::AudioCodec>::Write(m, p.codec()); + ParamTraits<media::SampleFormat>::Write(m, p.sample_format()); + ParamTraits<media::ChannelLayout>::Write(m, p.channel_layout()); + ParamTraits<int>::Write(m, p.samples_per_second()); + ParamTraits<bool>::Write(m, p.is_encrypted()); + std::vector<uint8> extra_data; + if (p.extra_data_size() > 0) { + extra_data = + std::vector<uint8>(p.extra_data(), + p.extra_data() + p.extra_data_size()); + } + ParamTraits<std::vector<uint8> >::Write(m, extra_data); +} + +bool ParamTraits<media::AudioDecoderConfig>::Read( + const Message* m, PickleIterator* iter, + media::AudioDecoderConfig* r) { + media::AudioCodec codec; + media::SampleFormat sample_format; + media::ChannelLayout channel_layout; + int samples_per_second; + bool is_encrypted; + if (!ParamTraits<media::AudioCodec>::Read(m, iter, &codec) || + !ParamTraits<media::SampleFormat>::Read(m, iter, &sample_format) || + !ParamTraits<media::ChannelLayout>::Read(m, iter, &channel_layout) || + !ParamTraits<int>::Read(m, iter, &samples_per_second) || + !ParamTraits<bool>::Read(m, iter, &is_encrypted)) { + return false; + } + std::vector<uint8> extra_data; + if (!ParamTraits<std::vector<uint8> >::Read(m, iter, &extra_data)) + return false; + const uint8* extra_data_ptr = NULL; + if (extra_data.size() > 0) + extra_data_ptr = &extra_data[0]; + *r = media::AudioDecoderConfig(codec, sample_format, channel_layout, + samples_per_second, + extra_data_ptr, extra_data.size(), + is_encrypted); + return true; +} + +void ParamTraits<media::AudioDecoderConfig>::Log( + const media::AudioDecoderConfig& p, std::string* l) { + l->append(base::StringPrintf("<AudioDecoderConfig>")); +} + +void ParamTraits<media::VideoDecoderConfig>::Write( + Message* m, const media::VideoDecoderConfig& p) { + ParamTraits<media::VideoCodec>::Write(m, p.codec()); + ParamTraits<media::VideoCodecProfile>::Write(m, p.profile()); + ParamTraits<media::VideoFrame::Format>::Write(m, p.format()); + ParamTraits<gfx::Size>::Write(m, p.coded_size()); + ParamTraits<gfx::Rect>::Write(m, p.visible_rect()); + ParamTraits<gfx::Size>::Write(m, p.natural_size()); + ParamTraits<bool>::Write(m, p.is_encrypted()); + std::vector<uint8> extra_data; + if (p.extra_data_size() > 0) { + extra_data = + std::vector<uint8>(p.extra_data(), + p.extra_data() + p.extra_data_size()); + } + ParamTraits<std::vector<uint8> >::Write(m, extra_data); +} + +bool ParamTraits<media::VideoDecoderConfig>::Read( + const Message* m, PickleIterator* iter, + media::VideoDecoderConfig* r) { + 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; + if (!ParamTraits<media::VideoCodec>::Read(m, iter, &codec) || + !ParamTraits<media::VideoCodecProfile>::Read(m, iter, &profile) || + !ParamTraits<media::VideoFrame::Format>::Read(m, iter, &format) || + !ParamTraits<gfx::Size>::Read(m, iter, &coded_size) || + !ParamTraits<gfx::Rect>::Read(m, iter, &visible_rect) || + !ParamTraits<gfx::Size>::Read(m, iter, &natural_size) || + !ParamTraits<bool>::Read(m, iter, &is_encrypted)) { + return false; + } + std::vector<uint8> extra_data; + if (!ParamTraits<std::vector<uint8> >::Read(m, iter, &extra_data)) + return false; + const uint8* extra_data_ptr = NULL; + if (extra_data.size() > 0) + extra_data_ptr = &extra_data[0]; + *r = media::VideoDecoderConfig(codec, profile, format, + coded_size, visible_rect, natural_size, + extra_data_ptr, extra_data.size(), + is_encrypted); + return true; +} + +void ParamTraits<media::VideoDecoderConfig>::Log( + const media::VideoDecoderConfig& p, std::string* l) { + l->append(base::StringPrintf("<VideoDecoderConfig>")); +} + +} // namespace IPC diff --git a/chromium/chromecast/common/media/cma_param_traits.h b/chromium/chromecast/common/media/cma_param_traits.h new file mode 100644 index 00000000000..0bebac35c2a --- /dev/null +++ b/chromium/chromecast/common/media/cma_param_traits.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_COMMON_MEDIA_CMA_PARAM_TRAITS_H_ +#define CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_H_ + +#include "ipc/ipc_message_utils.h" + +namespace media { +class AudioDecoderConfig; +class VideoDecoderConfig; +} + +namespace IPC { + +template <> +struct ParamTraits<media::AudioDecoderConfig> { + typedef media::AudioDecoderConfig param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<media::VideoDecoderConfig> { + typedef media::VideoDecoderConfig param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +} // namespace IPC + +#endif // CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_H_ diff --git a/chromium/chromecast/common/media/cma_param_traits_macros.h b/chromium/chromecast/common/media/cma_param_traits_macros.h new file mode 100644 index 00000000000..72682000ef4 --- /dev/null +++ b/chromium/chromecast/common/media/cma_param_traits_macros.h @@ -0,0 +1,54 @@ +// 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. + +// Singly or Multiply-included shared traits file depending on circumstances. +// This allows the use of IPC serialization macros in more than one IPC message +// file. +#ifndef CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_MACROS_H_ +#define CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_MACROS_H_ + +#include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/media/cma/pipeline/load_type.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/param_traits_macros.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/buffering_state.h" +#include "media/base/channel_layout.h" +#include "media/base/pipeline_status.h" +#include "media/base/sample_format.h" +#include "media/base/video_decoder_config.h" +#include "media/base/video_frame.h" + +IPC_ENUM_TRAITS_MIN_MAX_VALUE(chromecast::media::LoadType, + chromecast::media::kLoadTypeURL, + chromecast::media::kLoadTypeMediaStream) + +IPC_ENUM_TRAITS_MIN_MAX_VALUE(chromecast::media::TrackId, + chromecast::media::kNoTrackId, + chromecast::media::kVideoTrackId) + +IPC_ENUM_TRAITS_MIN_MAX_VALUE(media::AudioCodec, + media::AudioCodec::kUnknownAudioCodec, + media::AudioCodec::kAudioCodecMax) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(media::BufferingState, + media::BUFFERING_HAVE_NOTHING, + media::BUFFERING_HAVE_ENOUGH) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(media::SampleFormat, + media::SampleFormat::kUnknownSampleFormat, + media::SampleFormat::kSampleFormatMax) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(media::PipelineStatus, + media::PIPELINE_OK, + media::PIPELINE_STATUS_MAX) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(media::VideoCodec, + media::VideoCodec::kUnknownVideoCodec, + media::VideoCodec::kVideoCodecMax) + +IPC_STRUCT_TRAITS_BEGIN(media::PipelineStatistics) + IPC_STRUCT_TRAITS_MEMBER(audio_bytes_decoded) + IPC_STRUCT_TRAITS_MEMBER(video_bytes_decoded) + IPC_STRUCT_TRAITS_MEMBER(video_frames_decoded) + IPC_STRUCT_TRAITS_MEMBER(video_frames_dropped) +IPC_STRUCT_TRAITS_END() + +#endif // CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_MACROS_H_
\ No newline at end of file diff --git a/chromium/chromecast/common/media/shared_memory_chunk.cc b/chromium/chromecast/common/media/shared_memory_chunk.cc new file mode 100644 index 00000000000..405339502c5 --- /dev/null +++ b/chromium/chromecast/common/media/shared_memory_chunk.cc @@ -0,0 +1,36 @@ +// 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/common/media/shared_memory_chunk.h" + +#include "base/memory/shared_memory.h" + +namespace chromecast { +namespace media { + +SharedMemoryChunk::SharedMemoryChunk( + scoped_ptr<base::SharedMemory> shared_mem, + size_t size) + : shared_mem_(shared_mem.Pass()), + size_(size) { +} + +SharedMemoryChunk::~SharedMemoryChunk() { +} + +void* SharedMemoryChunk::data() const { + return shared_mem_->memory(); +} + +size_t SharedMemoryChunk::size() const { + return size_; +} + +bool SharedMemoryChunk::valid() const { + return true; +} + +} // namespace media +} // namespace chromecast + diff --git a/chromium/chromecast/common/media/shared_memory_chunk.h b/chromium/chromecast/common/media/shared_memory_chunk.h new file mode 100644 index 00000000000..a8b71c2a12b --- /dev/null +++ b/chromium/chromecast/common/media/shared_memory_chunk.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_COMMON_MEDIA_SHARED_MEMORY_CHUNK_H_ +#define CHROMECAST_COMMON_MEDIA_SHARED_MEMORY_CHUNK_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "chromecast/media/cma/ipc/media_memory_chunk.h" + +namespace base { +class SharedMemory; +} + +namespace chromecast { +namespace media { + +class SharedMemoryChunk : public MediaMemoryChunk { + public: + SharedMemoryChunk(scoped_ptr<base::SharedMemory> shared_mem, + size_t size); + ~SharedMemoryChunk() override; + + // MediaMemoryChunk implementation. + void* data() const override; + size_t size() const override; + bool valid() const override; + + private: + scoped_ptr<base::SharedMemory> shared_mem_; + size_t size_; + + DISALLOW_COPY_AND_ASSIGN(SharedMemoryChunk); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_COMMON_MEDIA_SHARED_MEMORY_CHUNK_H_
\ No newline at end of file diff --git a/chromium/chromecast/common/pref_names.cc b/chromium/chromecast/common/pref_names.cc index 45d0bc1780d..0b885f50080 100644 --- a/chromium/chromecast/common/pref_names.cc +++ b/chromium/chromecast/common/pref_names.cc @@ -6,6 +6,10 @@ namespace prefs { +// Boolean that specifies whether or not the client_id has been regenerated +// due to bug b/9487011. +const char kMetricsIsNewClientID[] = "user_experience_metrics.is_new_client_id"; + // Port on which to host the remote debugging server. A value of 0 indicates // that remote debugging is disabled. const char kRemoteDebuggingPort[] = "remote_debugging_port"; diff --git a/chromium/chromecast/common/pref_names.h b/chromium/chromecast/common/pref_names.h index fe9f6ce1aec..6caa2cc6eea 100644 --- a/chromium/chromecast/common/pref_names.h +++ b/chromium/chromecast/common/pref_names.h @@ -7,6 +7,7 @@ namespace prefs { +extern const char kMetricsIsNewClientID[]; extern const char kRemoteDebuggingPort[]; extern const char kStabilityChildProcessCrashCount[]; extern const char kStabilityKernelCrashCount[]; diff --git a/chromium/chromecast/common/version.h.in b/chromium/chromecast/common/version.h.in index 628cb4c217f..918401f42ae 100644 --- a/chromium/chromecast/common/version.h.in +++ b/chromium/chromecast/common/version.h.in @@ -8,7 +8,9 @@ #define CHROMECAST_COMMON_VERSION_INFO_H_ #define PRODUCT_VERSION "@VERSION_FULL@" -#define CAST_BUILD_REVISION "@CAST_BUILD_REVISION@" -#define CAST_IS_DEBUG_BUILD @CAST_IS_DEBUG_BUILD@ +#define CAST_BUILD_INCREMENTAL "@CAST_BUILD_INCREMENTAL@" +#define CAST_BUILD_REVISION "@CAST_BUILD_RELEASE@.@CAST_BUILD_INCREMENTAL@" +#define CAST_IS_DEBUG_BUILD() @CAST_IS_DEBUG_BUILD@ +#define CAST_PRODUCT_TYPE @CAST_PRODUCT_TYPE@ #endif // CHROMECAST_COMMON_VERSION_INFO_H_ diff --git a/chromium/chromecast/crash/android/cast_crash_reporter_client_android.cc b/chromium/chromecast/crash/android/cast_crash_reporter_client_android.cc index 2137faeea9b..ae4063745e5 100644 --- a/chromium/chromecast/crash/android/cast_crash_reporter_client_android.cc +++ b/chromium/chromecast/crash/android/cast_crash_reporter_client_android.cc @@ -11,6 +11,7 @@ #include "chromecast/android/chromecast_config_android.h" #include "chromecast/common/global_descriptors.h" #include "chromecast/common/version.h" +#include "chromecast/crash/cast_crash_keys.h" #include "content/public/common/content_switches.h" namespace chromecast { @@ -26,7 +27,7 @@ void CastCrashReporterClientAndroid::GetProductNameAndVersion( const char** version) { *product_name = "media_shell"; *version = PRODUCT_VERSION -#if CAST_IS_DEBUG_BUILD +#if CAST_IS_DEBUG_BUILD() ".debug" #endif "." CAST_BUILD_REVISION; @@ -55,6 +56,10 @@ bool CastCrashReporterClientAndroid::GetCrashDumpLocation( return true; } +size_t CastCrashReporterClientAndroid::RegisterCrashKeys() { + return crash_keys::RegisterCastCrashKeys(); +} + bool CastCrashReporterClientAndroid::GetCollectStatsConsent() { return android::ChromecastConfigAndroid::GetInstance()->CanSendUsageStats(); } diff --git a/chromium/chromecast/crash/android/cast_crash_reporter_client_android.h b/chromium/chromecast/crash/android/cast_crash_reporter_client_android.h index 4524ae70a26..e98eef72605 100644 --- a/chromium/chromecast/crash/android/cast_crash_reporter_client_android.h +++ b/chromium/chromecast/crash/android/cast_crash_reporter_client_android.h @@ -14,17 +14,18 @@ class CastCrashReporterClientAndroid : public crash_reporter::CrashReporterClient { public: CastCrashReporterClientAndroid(); - virtual ~CastCrashReporterClientAndroid(); + ~CastCrashReporterClientAndroid() override; // crash_reporter::CrashReporterClient implementation: - virtual void GetProductNameAndVersion(const char** product_name, - const char** version) override; - virtual base::FilePath GetReporterLogFilename() override; - virtual bool GetCrashDumpLocation(base::FilePath* crash_dir) override; - virtual int GetAndroidMinidumpDescriptor() override; - virtual bool GetCollectStatsConsent() override; - virtual bool EnableBreakpadForProcess( + void GetProductNameAndVersion(const char** product_name, + const char** version) override; + base::FilePath GetReporterLogFilename() override; + bool GetCrashDumpLocation(base::FilePath* crash_dir) override; + int GetAndroidMinidumpDescriptor() override; + bool GetCollectStatsConsent() override; + bool EnableBreakpadForProcess( const std::string& process_type) override; + size_t RegisterCrashKeys() override; private: DISALLOW_COPY_AND_ASSIGN(CastCrashReporterClientAndroid); diff --git a/chromium/chromecast/crash/android/crash_handler.cc b/chromium/chromecast/crash/android/crash_handler.cc index a73e31352b7..1c42dfa931a 100644 --- a/chromium/chromecast/crash/android/crash_handler.cc +++ b/chromium/chromecast/crash/android/crash_handler.cc @@ -5,12 +5,14 @@ #include "chromecast/crash/android/crash_handler.h" #include <jni.h> +#include <stdlib.h> #include <string> #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/files/file_path.h" #include "base/logging.h" +#include "base/strings/string_number_conversions.h" #include "breakpad/src/client/linux/handler/exception_handler.h" #include "breakpad/src/client/linux/handler/minidump_descriptor.h" #include "chromecast/common/version.h" @@ -24,22 +26,29 @@ namespace { chromecast::CrashHandler* g_crash_handler = NULL; -// ExceptionHandler requires a HandlerCallback as a function pointer. This -// function exists to proxy into the global CrashHandler instance. -bool HandleCrash(const void* /* crash_context */, - size_t /* crash_context_size */, - void* /* context */) { +bool HandleCrash(void* /* crash_context */) { DCHECK(g_crash_handler); - if (g_crash_handler->CanUploadCrashDump()) { - g_crash_handler->AttemptUploadCrashDump(); - } else { - g_crash_handler->RemoveCrashDumps(); - } + g_crash_handler->UploadCrashDumps(); - // Let the exception continue to propagate up to the system. + // TODO(gunsch): clean up the ATV crash handling code. + // Don't write another minidump. Chrome's default ExceptionHandler has already + // written a minidump by this point in the crash handling sequence. return false; } +// Debug builds: always to crash-staging +// Release builds: only to crash-staging for local/invalid build numbers +bool UploadCrashToStaging() { +#if CAST_IS_DEBUG_BUILD() + return true; +#else + int build_number; + if (base::StringToInt(CAST_BUILD_INCREMENTAL, &build_number)) + return build_number == 0; + return true; +#endif +} + } // namespace namespace chromecast { @@ -79,67 +88,56 @@ CrashHandler::~CrashHandler() { } void CrashHandler::Initialize(const std::string& process_type) { - if (process_type != switches::kZygoteProcess) { - if (process_type.empty()) { - // ExceptionHandlers are called on crash in reverse order of - // instantiation. This ExceptionHandler will attempt to upload crashes - // and the log file written out by the main process. - - // Dummy MinidumpDescriptor just to start up another ExceptionHandler. - google_breakpad::MinidumpDescriptor dummy(crash_dump_path_.value()); - crash_uploader_.reset(new google_breakpad::ExceptionHandler( - dummy, NULL, NULL, NULL, true, -1)); - crash_uploader_->set_crash_handler(&::HandleCrash); - - breakpad::InitCrashReporter(process_type); - } else { - breakpad::InitNonBrowserCrashReporterForAndroid(process_type); - } - } + if (process_type.empty()) { + InitializeUploader(); - UploadCrashDumpsAsync(); -} + // ExceptionHandlers are called on crash in reverse order of + // instantiation. This ExceptionHandler will attempt to upload crashes + // and the log file written out by the main process. -bool CrashHandler::CanUploadCrashDump() { - DCHECK(crash_reporter_client_); - return crash_reporter_client_->GetCollectStatsConsent(); + // Dummy MinidumpDescriptor just to start up another ExceptionHandler. + google_breakpad::MinidumpDescriptor dummy(crash_dump_path_.value()); + crash_uploader_.reset(new google_breakpad::ExceptionHandler( + dummy, &HandleCrash, NULL, NULL, true, -1)); + + breakpad::InitCrashReporter(process_type); + + return; + } + + if (process_type != switches::kZygoteProcess) { + breakpad::InitNonBrowserCrashReporterForAndroid(process_type); + } } -void CrashHandler::AttemptUploadCrashDump() { - VLOG(1) << "Attempting to upload current process crash"; +void CrashHandler::InitializeUploader() { JNIEnv* env = base::android::AttachCurrentThread(); - // Crash dump location base::android::ScopedJavaLocalRef<jstring> crash_dump_path_java = base::android::ConvertUTF8ToJavaString(env, crash_dump_path_.value()); - // Current log file location - base::android::ScopedJavaLocalRef<jstring> log_file_path_java = - base::android::ConvertUTF8ToJavaString(env, log_file_path_.value()); - Java_CastCrashHandler_uploadCurrentProcessDumpSync( - env, - crash_dump_path_java.obj(), - log_file_path_java.obj(), - CAST_IS_DEBUG_BUILD); + Java_CastCrashHandler_initializeUploader( + env, crash_dump_path_java.obj(), UploadCrashToStaging()); } -void CrashHandler::UploadCrashDumpsAsync() { - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::ScopedJavaLocalRef<jstring> crash_dump_path_java = - base::android::ConvertUTF8ToJavaString(env, - crash_dump_path_.value()); - Java_CastCrashHandler_uploadCrashDumpsAsync(env, - crash_dump_path_java.obj(), - CAST_IS_DEBUG_BUILD); +bool CrashHandler::CanUploadCrashDump() { + DCHECK(crash_reporter_client_); + return crash_reporter_client_->GetCollectStatsConsent(); } -void CrashHandler::RemoveCrashDumps() { - VLOG(1) << "Removing crash dumps instead of uploading"; - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::ScopedJavaLocalRef<jstring> crash_dump_path_java = - base::android::ConvertUTF8ToJavaString(env, - crash_dump_path_.value()); - Java_CastCrashHandler_removeCrashDumpsSync( - env, crash_dump_path_java.obj(), CAST_IS_DEBUG_BUILD); +void CrashHandler::UploadCrashDumps() { + VLOG(1) << "Attempting to upload current process crash"; + + if (CanUploadCrashDump()) { + JNIEnv* env = base::android::AttachCurrentThread(); + // Current log file location + base::android::ScopedJavaLocalRef<jstring> log_file_path_java = + base::android::ConvertUTF8ToJavaString(env, log_file_path_.value()); + Java_CastCrashHandler_uploadCrashDumps(env, log_file_path_java.obj()); + } else { + VLOG(1) << "Removing crash dumps instead of uploading"; + JNIEnv* env = base::android::AttachCurrentThread(); + Java_CastCrashHandler_removeCrashDumps(env); + } } } // namespace chromecast diff --git a/chromium/chromecast/crash/android/crash_handler.h b/chromium/chromecast/crash/android/crash_handler.h index 246c5bb8c66..853e7f50973 100644 --- a/chromium/chromecast/crash/android/crash_handler.h +++ b/chromium/chromecast/crash/android/crash_handler.h @@ -36,14 +36,7 @@ class CrashHandler { // Returns whether or not the user has allowed for uploading crash dumps. bool CanUploadCrashDump(); - // Callback with which to create a breakpad::ExceptionHandler that will - // attempt synchronously uploading crash dumps and logs at crash time. - void AttemptUploadCrashDump(); - - // Callback for breakpad::ExceptionHandler to delete crash dumps created by - // the Chrome crash component. Chrome's crash component does not query - // for user consent after initializing breakpad. - void RemoveCrashDumps(); + void UploadCrashDumps(); private: CrashHandler(const base::FilePath& log_file_path); @@ -51,9 +44,8 @@ class CrashHandler { void Initialize(const std::string& process_type); - // Starts a background thread to look for any past crash dumps and upload them - // to the crash server. - void UploadCrashDumpsAsync(); + // Starts a thread to periodically check for uploads + void InitializeUploader(); // Path to the current process's log file. base::FilePath log_file_path_; diff --git a/chromium/chromecast/crash/cast_crash_keys.cc b/chromium/chromecast/crash/cast_crash_keys.cc new file mode 100644 index 00000000000..a33a257b585 --- /dev/null +++ b/chromium/chromecast/crash/cast_crash_keys.cc @@ -0,0 +1,53 @@ +// Copyright 2015 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/crash/cast_crash_keys.h" + +// TODO(kjoswiak): Potentially refactor chunk size info as well as non-cast +// specific keys out and make shared with chrome/common/crash_keys.cc. +namespace { + +// A small crash key, guaranteed to never be split into multiple pieces. +const size_t kSmallSize = 63; + +// A medium crash key, which will be chunked on certain platforms but not +// others. Guaranteed to never be more than four chunks. +const size_t kMediumSize = kSmallSize * 4; + +// A large crash key, which will be chunked on all platforms. This should be +// used sparingly. +const size_t kLargeSize = kSmallSize * 16; + +// The maximum lengths specified by breakpad include the trailing NULL, so +// the actual length of the string is one less. +static const size_t kSingleChunkLength = 63; + +} + +namespace chromecast { +namespace crash_keys { + +const char kLastApp[] = "last_app"; +const char kCurrentApp[] = "current_app"; +const char kPreviousApp[] = "previous_app"; + +size_t RegisterCastCrashKeys() { + const base::debug::CrashKey fixed_keys[] = { + { kLastApp, kSmallSize }, + { kCurrentApp, kSmallSize }, + { kPreviousApp, kSmallSize }, + // content/: + { "discardable-memory-allocated", kSmallSize }, + { "discardable-memory-free", kSmallSize }, + { "ppapi_path", kMediumSize }, + { "subresource_url", kLargeSize }, + { "total-discardable-memory-allocated", kSmallSize }, + }; + + return base::debug::InitCrashKeys(fixed_keys, arraysize(fixed_keys), + kSingleChunkLength); +} + +} // namespace chromecast +} // namespace crash_keys diff --git a/chromium/chromecast/crash/cast_crash_keys.h b/chromium/chromecast/crash/cast_crash_keys.h new file mode 100644 index 00000000000..2f1af20ee8e --- /dev/null +++ b/chromium/chromecast/crash/cast_crash_keys.h @@ -0,0 +1,22 @@ +// Copyright 2015 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_CRASH_CAST_CRASH_KEYS_H_ +#define CHROMECAST_CRASH_CAST_CRASH_KEYS_H_ + +#include "base/debug/crash_logging.h" + +namespace chromecast { +namespace crash_keys { + +size_t RegisterCastCrashKeys(); + +extern const char kCurrentApp[]; +extern const char kLastApp[]; +extern const char kPreviousApp[]; + +} // namespace chromecast +} // namespace crash_keys + +#endif // CHROMECAST_CRASH_CAST_CRASH_KEYS_H_ diff --git a/chromium/chromecast/crash/cast_crash_reporter_client.cc b/chromium/chromecast/crash/cast_crash_reporter_client.cc new file mode 100644 index 00000000000..8edbfa6b7b2 --- /dev/null +++ b/chromium/chromecast/crash/cast_crash_reporter_client.cc @@ -0,0 +1,58 @@ +// 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/crash/cast_crash_reporter_client.h" + +#include "base/time/time.h" +#include "components/crash/app/breakpad_linux.h" +#include "content/public/common/content_switches.h" + +namespace chromecast { + +namespace { + +char* g_process_type = NULL; +uint64_t g_process_start_time = 0; + +} // namespace + +// static +void CastCrashReporterClient::InitCrashReporter( + const std::string& process_type) { + g_process_start_time = (base::TimeTicks::Now() - + base::TimeTicks()).InMilliseconds(); + + // Save the process type (leaked). + // Note: "browser" process is identified by empty process type string. + const std::string& named_process_type( + process_type.empty() ? "browser" : process_type); + const size_t process_type_len = named_process_type.size() + 1; + g_process_type = new char[process_type_len]; + strncpy(g_process_type, named_process_type.c_str(), process_type_len); + + // Start up breakpad for this process, if applicable. + breakpad::InitCrashReporter(process_type); +} + +// static +char* CastCrashReporterClient::GetProcessType() { + return g_process_type; +} + +// static +uint64_t CastCrashReporterClient::GetProcessStartTime() { + return g_process_start_time; +} + +CastCrashReporterClient::CastCrashReporterClient() {} +CastCrashReporterClient::~CastCrashReporterClient() {} + +bool CastCrashReporterClient::EnableBreakpadForProcess( + const std::string& process_type) { + return process_type == switches::kRendererProcess || + process_type == switches::kZygoteProcess || + process_type == switches::kGpuProcess; +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/cast_crash_reporter_client.h b/chromium/chromecast/crash/cast_crash_reporter_client.h new file mode 100644 index 00000000000..a0916a041ea --- /dev/null +++ b/chromium/chromecast/crash/cast_crash_reporter_client.h @@ -0,0 +1,36 @@ +// 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_CRASH_CAST_CRASH_REPORTER_CLIENT_H_ +#define CHROMECAST_CRASH_CAST_CRASH_REPORTER_CLIENT_H_ + +#include <string> + +#include "base/macros.h" +#include "components/crash/app/crash_reporter_client.h" + +namespace chromecast { + +class CastCrashReporterClient : public crash_reporter::CrashReporterClient { + public: + static void InitCrashReporter(const std::string& process_type); + + CastCrashReporterClient(); + ~CastCrashReporterClient() override; + + // crash_reporter::CrashReporterClient implementation: + bool EnableBreakpadForProcess( + const std::string& process_type) override; + bool HandleCrashDump(const char* crashdump_filename) override; + + private: + static char* GetProcessType(); + static uint64_t GetProcessStartTime(); + + DISALLOW_COPY_AND_ASSIGN(CastCrashReporterClient); +}; + +} // namespace chromecast + +#endif // CHROMECAST_CRASH_CAST_CRASH_REPORTER_CLIENT_H_ diff --git a/chromium/chromecast/crash/cast_crash_reporter_client_simple.cc b/chromium/chromecast/crash/cast_crash_reporter_client_simple.cc new file mode 100644 index 00000000000..aef4add13c0 --- /dev/null +++ b/chromium/chromecast/crash/cast_crash_reporter_client_simple.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/crash/cast_crash_reporter_client.h" + +#include "base/logging.h" + +namespace chromecast { + +bool CastCrashReporterClient::HandleCrashDump(const char* crashdump_filename) { + LOG(INFO) << "Process " << GetProcessType() << " crashed; minidump in " + << crashdump_filename; + return true; +} + +} // namespace chromecast diff --git a/chromium/chromecast/graphics/DEPS b/chromium/chromecast/graphics/DEPS new file mode 100644 index 00000000000..ada2e660c17 --- /dev/null +++ b/chromium/chromecast/graphics/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+chromecast/public", +] diff --git a/chromium/chromecast/graphics/cast_egl_platform_default.cc b/chromium/chromecast/graphics/cast_egl_platform_default.cc new file mode 100644 index 00000000000..3cb9a1b8c32 --- /dev/null +++ b/chromium/chromecast/graphics/cast_egl_platform_default.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/public/cast_egl_platform.h" +#include "chromecast/public/cast_egl_platform_shlib.h" + +namespace chromecast { +namespace { + +// Default/stub CastEglPlatform implementation so that we can link +// successfully. +class EglPlatformDefault : public CastEglPlatform { + public: + ~EglPlatformDefault() override {} + const int* GetEGLSurfaceProperties(const int* desired) override { + return desired; + } + bool InitializeHardware() override { return true; } + void ShutdownHardware() override {} + void* GetEglLibrary() override { return nullptr; } + void* GetGles2Library() override { return nullptr; } + GLGetProcAddressProc GetGLProcAddressProc() override { return nullptr; } + NativeDisplayType CreateDisplayType(const Size& size) override { + return nullptr; + } + void DestroyDisplayType(NativeDisplayType display_type) override {} + NativeWindowType CreateWindow(NativeDisplayType display_type, + const Size& size) override { + return nullptr; + } + void DestroyWindow(NativeWindowType window) override {} +}; + +} // namespace + +CastEglPlatform* CastEglPlatformShlib::Create( + const std::vector<std::string>& argv) { + return new EglPlatformDefault(); +} + +} // namespace chromecast diff --git a/chromium/chromecast/graphics/graphics_properties_default.cc b/chromium/chromecast/graphics/graphics_properties_default.cc new file mode 100644 index 00000000000..200c6cc4b3a --- /dev/null +++ b/chromium/chromecast/graphics/graphics_properties_default.cc @@ -0,0 +1,15 @@ +// Copyright 2015 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/public/graphics_properties_shlib.h" + +namespace chromecast { + +bool GraphicsPropertiesShlib::IsSupported( + Resolution resolution, + const std::vector<std::string>& argv) { + return true; +} + +} // namespace chromecast diff --git a/chromium/chromecast/graphics/osd_plane_default.cc b/chromium/chromecast/graphics/osd_plane_default.cc new file mode 100644 index 00000000000..2c73edc531a --- /dev/null +++ b/chromium/chromecast/graphics/osd_plane_default.cc @@ -0,0 +1,73 @@ +// Copyright 2015 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/memory/scoped_ptr.h" +#include "chromecast/public/graphics_types.h" +#include "chromecast/public/osd_plane.h" +#include "chromecast/public/osd_plane_shlib.h" +#include "chromecast/public/osd_surface.h" + +namespace chromecast { +namespace { + +// Default no-op OsdSurface implementation +class OsdSurfaceDefault : public OsdSurface { + public: + OsdSurfaceDefault(const Size& size) : size_(size) {} + + // OsdSurface implementation: + void Blit(OsdSurface* src_surface, + const Rect& src_rect, + const Point& dst_point) override {} + void Composite(OsdSurface* src_surface, + const Rect& src_rect, + const Point& dst_point) override {} + void CopyBitmap(char* src_bitmap, + const Rect& src_rect, + const Rect& damage_rect, + const Point& dst_point) override {} + void Fill(const Rect& rect, int argb) override {} + + const Size& size() const override { return size_; } + + private: + const Size size_; + + DISALLOW_COPY_AND_ASSIGN(OsdSurfaceDefault); +}; + +// Default no-op OsdPlane implementation +class OsdPlaneDefault : public OsdPlane { + public: + OsdPlaneDefault() : size_(0, 0) {} + + // OsdPlane implementation: + OsdSurface* CreateSurface(const Size& size) override { + return new OsdSurfaceDefault(size); + } + void SetClipRectangle(const Rect& rect) override { + size_ = Size(rect.width, rect.height); + } + OsdSurface* GetBackBuffer() override { + if (!back_buffer_) + back_buffer_.reset(new OsdSurfaceDefault(size_)); + return back_buffer_.get(); + } + + void Flip() override {} + + private: + scoped_ptr<OsdSurface> back_buffer_; + Size size_; + + DISALLOW_COPY_AND_ASSIGN(OsdPlaneDefault); +}; + +} // namespace + +OsdPlane* OsdPlaneShlib::Create(const std::vector<std::string>& argv) { + return new OsdPlaneDefault; +} + +} // namespace chromecast diff --git a/chromium/chromecast/media/BUILD.gn b/chromium/chromecast/media/BUILD.gn new file mode 100644 index 00000000000..6a8c6a6a3df --- /dev/null +++ b/chromium/chromecast/media/BUILD.gn @@ -0,0 +1,53 @@ +# Copyright 2015 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. + +import("//testing/test.gni") + +group("media") { + deps = [ + "//chromecast/media/base", + "//chromecast/media/cdm", + "//chromecast/media/cma", + ] +} + +test("unittests") { + sources = [ + "//media/base", + "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/pipeline/audio_video_pipeline_impl_unittest.cc", + "cma/test/frame_generator_for_test.cc", + "cma/test/frame_generator_for_test.h", + "cma/test/frame_segmenter_for_test.cc", + "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", + ] + + deps = [ + ":media", + "//base", + "//base:i18n", + "//base/test:test_support", + "//chromecast/base/metrics:test_support", + "//media", + "//media/base:test_support", + "//testing/gmock", + "//testing/gtest", + ] + + configs += [ "//chromecast:config" ] +} diff --git a/chromium/chromecast/media/DEPS b/chromium/chromecast/media/DEPS index 1891d1afe50..f2506bb5ad6 100644 --- a/chromium/chromecast/media/DEPS +++ b/chromium/chromecast/media/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+media/base", "+media/cdm", + "+media/renderers", ] diff --git a/chromium/chromecast/media/base/BUILD.gn b/chromium/chromecast/media/base/BUILD.gn new file mode 100644 index 00000000000..64992f331f2 --- /dev/null +++ b/chromium/chromecast/media/base/BUILD.gn @@ -0,0 +1,38 @@ +# Copyright 2015 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. + +import("//build/config/crypto.gni") +import("//chromecast/chromecast.gni") + +source_set("base") { + sources = [ + "decrypt_context.cc", + "decrypt_context.h", + "decrypt_context_clearkey.cc", + "decrypt_context_clearkey.h", + "key_systems_common.cc", + "key_systems_common.h", + "media_caps.cc", + "media_caps.h", + "switching_media_renderer.cc", + "switching_media_renderer.h", + ] + + deps = [ + "//base", + "//crypto", + "//crypto:platform", + "//third_party/widevine/cdm:version_h", + ] + + configs += [ "//chromecast:config" ] + + if (is_chromecast_chrome_branded) { + deps += [ + # TODO(gyp): add dependency on internal/chromecast_internal:media_base_internal + ] + } else { + sources += [ "key_systems_common_simple.cc" ] + } +} diff --git a/chromium/chromecast/media/base/cast_media_default.cc b/chromium/chromecast/media/base/cast_media_default.cc new file mode 100644 index 00000000000..0cc03d38705 --- /dev/null +++ b/chromium/chromecast/media/base/cast_media_default.cc @@ -0,0 +1,17 @@ +// Copyright 2015 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/public/cast_media_shlib.h" + +namespace chromecast { +namespace media { + +void CastMediaShlib::Initialize(const std::vector<std::string>& argv) { +} + +void CastMediaShlib::Finalize() { +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/base/decrypt_context_clearkey.h b/chromium/chromecast/media/base/decrypt_context_clearkey.h index 770cea08144..a4e56d0ba25 100644 --- a/chromium/chromecast/media/base/decrypt_context_clearkey.h +++ b/chromium/chromecast/media/base/decrypt_context_clearkey.h @@ -17,10 +17,10 @@ class DecryptContextClearKey : public DecryptContext { explicit DecryptContextClearKey(crypto::SymmetricKey* key); // DecryptContext implementation. - virtual crypto::SymmetricKey* GetKey() const override; + crypto::SymmetricKey* GetKey() const override; private: - virtual ~DecryptContextClearKey(); + ~DecryptContextClearKey() override; crypto::SymmetricKey* const key_; @@ -30,4 +30,4 @@ class DecryptContextClearKey : public DecryptContext { } // namespace media } // namespace chromecast -#endif // CHROMECAST_MEDIA_BASE_DECRYPT_CONTEXT_CLEARKEY_H_
\ No newline at end of file +#endif // CHROMECAST_MEDIA_BASE_DECRYPT_CONTEXT_CLEARKEY_H_ diff --git a/chromium/chromecast/media/base/key_systems_common.h b/chromium/chromecast/media/base/key_systems_common.h index 4ab8ea75dec..1537afd36ab 100644 --- a/chromium/chromecast/media/base/key_systems_common.h +++ b/chromium/chromecast/media/base/key_systems_common.h @@ -6,6 +6,11 @@ #define CHROMECAST_MEDIA_BASE_KEY_SYSTEMS_COMMON_H_ #include <string> +#include <utility> +#include <vector> + +#include "base/compiler_specific.h" +#include "media/base/android/media_client_android.h" namespace chromecast { namespace media { @@ -29,6 +34,13 @@ CastKeySystem GetKeySystemByName(const std::string& key_system_name); // TODO(gunsch): Remove when prefixed EME is removed. CastKeySystem GetPlatformKeySystemByName(const std::string& key_system_name); +// Translates a platform-specific key system string into a CastKeySystem. +// TODO(gunsch): Remove when prefixed EME is removed. +#if defined(OS_ANDROID) +std::vector<::media::MediaClientAndroid::KeySystemUuidMap::value_type> +GetPlatformKeySystemUUIDMappings(); +#endif + } // namespace media } // namespace chromecast diff --git a/chromium/chromecast/media/base/key_systems_common_simple.cc b/chromium/chromecast/media/base/key_systems_common_simple.cc index e6dbd028902..671ce037730 100644 --- a/chromium/chromecast/media/base/key_systems_common_simple.cc +++ b/chromium/chromecast/media/base/key_systems_common_simple.cc @@ -11,5 +11,13 @@ CastKeySystem GetPlatformKeySystemByName(const std::string& key_system_name) { return KEY_SYSTEM_NONE; } +#if defined(OS_ANDROID) +std::vector<::media::MediaClientAndroid::KeySystemUuidMap::value_type> +GetPlatformKeySystemUUIDMappings() { + return std::vector< + ::media::MediaClientAndroid::KeySystemUuidMap::value_type>(); +} +#endif + } // namespace media } // namespace chromecast diff --git a/chromium/chromecast/media/base/media_caps.cc b/chromium/chromecast/media/base/media_caps.cc new file mode 100644 index 00000000000..29248828a03 --- /dev/null +++ b/chromium/chromecast/media/base/media_caps.cc @@ -0,0 +1,38 @@ +// Copyright 2015 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/media_caps.h" + +namespace media { + +namespace { +int g_hdmi_codecs = 0; +} // namespace + +void SetHdmiSinkCodecs(int codecs_mask) { + g_hdmi_codecs = codecs_mask; +} + +bool HdmiSinkSupportsAC3() { + return g_hdmi_codecs & HdmiSinkCodec::kSinkCodecAc3; +} + +bool HdmiSinkSupportsDTS() { + return g_hdmi_codecs & HdmiSinkCodec::kSinkCodecDts; +} + +bool HdmiSinkSupportsDTSHD() { + return g_hdmi_codecs & HdmiSinkCodec::kSinkCodecDtsHd; +} + +bool HdmiSinkSupportsEAC3() { + return g_hdmi_codecs & HdmiSinkCodec::kSinkCodecEac3; +} + +bool HdmiSinkSupportsPcmSurroundSound() { + return g_hdmi_codecs & HdmiSinkCodec::kSinkCodecPcmSurroundSound; +} + +} // namespace media + diff --git a/chromium/chromecast/media/base/media_caps.h b/chromium/chromecast/media/base/media_caps.h new file mode 100644 index 00000000000..8ae2c7bcec0 --- /dev/null +++ b/chromium/chromecast/media/base/media_caps.h @@ -0,0 +1,30 @@ +// 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_MEDIA_CAPS_ +#define CHROMECAST_MEDIA_BASE_MEDIA_CAPS_ + +namespace media { + +enum HdmiSinkCodec { + kSinkCodecAc3 = 1, + kSinkCodecDts = 1 << 1, + kSinkCodecDtsHd = 1 << 2, + kSinkCodecEac3 = 1 << 3, + kSinkCodecPcmSurroundSound = 1 << 4, +}; + +// Records the known supported codecs for the current HDMI sink, as a bit mask +// of HdmiSinkCodec values. +void SetHdmiSinkCodecs(int codecs_mask); + +bool HdmiSinkSupportsAC3(); +bool HdmiSinkSupportsDTS(); +bool HdmiSinkSupportsDTSHD(); +bool HdmiSinkSupportsEAC3(); +bool HdmiSinkSupportsPcmSurroundSound(); + +} // namespace media + +#endif // CHROMECAST_MEDIA_BASE_MEDIA_CAPS_ diff --git a/chromium/chromecast/media/base/media_codec_support.cc b/chromium/chromecast/media/base/media_codec_support.cc new file mode 100644 index 00000000000..b04a077961e --- /dev/null +++ b/chromium/chromecast/media/base/media_codec_support.cc @@ -0,0 +1,16 @@ +// Copyright 2015 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/media_codec_support.h" + +// TODO(gunsch/servolk): delete this file once a solution exists upstream. + +namespace net { + +bool DefaultIsCodecSupported(const std::string&) { + return true; +} + +} // namespace net + diff --git a/chromium/chromecast/media/base/media_codec_support.h b/chromium/chromecast/media/base/media_codec_support.h new file mode 100644 index 00000000000..e961177ecb0 --- /dev/null +++ b/chromium/chromecast/media/base/media_codec_support.h @@ -0,0 +1,34 @@ +// Copyright 2015 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_MEDIA_CODEC_SUPPORT_H_ +#define CHROMECAST_MEDIA_BASE_MEDIA_CODEC_SUPPORT_H_ + +#include <string> + +#include "base/callback.h" +#include "net/base/mime_util.h" + +// TODO(gunsch/servolk): remove when this definition exists upstream. + +namespace net { +typedef base::Callback<bool(const std::string&)> IsCodecSupportedCB; +bool DefaultIsCodecSupported(const std::string&); +} + +namespace chromecast { +namespace media { + +// This function should return a callback capable of deciding whether a given +// codec (passed in as a string representation of the codec id conforming to +// RFC 6381) is supported or not. The implementation of this function is +// expected to be provided somewhere in the vendor platform-specific libraries +// that will get linked with cast_shell_common target. +net::IsCodecSupportedCB GetIsCodecSupportedOnChromecastCB(); + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_BASE_MEDIA_CODEC_SUPPORT_H_ + diff --git a/chromium/chromecast/media/base/media_codec_support_simple.cc b/chromium/chromecast/media/base/media_codec_support_simple.cc new file mode 100644 index 00000000000..24e83be69dc --- /dev/null +++ b/chromium/chromecast/media/base/media_codec_support_simple.cc @@ -0,0 +1,21 @@ +// Copyright 2015 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/media_codec_support.h" + +#include "base/bind.h" + +namespace chromecast { +namespace media { + +// This is a default implementation of GetIsCodecSupportedOnChromecastCB that is +// going to be used for brandings other than 'Chrome'. Most platforms will want +// to use their own custom implementations of the IsCodecSupportedCB callback. +net::IsCodecSupportedCB GetIsCodecSupportedOnChromecastCB() { + return base::Bind(&net::DefaultIsCodecSupported); +} + +} // namespace media +} // namespace chromecast + diff --git a/chromium/chromecast/media/base/switching_media_renderer.cc b/chromium/chromecast/media/base/switching_media_renderer.cc new file mode 100644 index 00000000000..b0a12c3a040 --- /dev/null +++ b/chromium/chromecast/media/base/switching_media_renderer.cc @@ -0,0 +1,102 @@ +// Copyright 2015 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/switching_media_renderer.h" + +#include "base/logging.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/demuxer_stream.h" +#include "media/base/demuxer_stream_provider.h" + +namespace chromecast { +namespace media { + +SwitchingMediaRenderer::SwitchingMediaRenderer( + scoped_ptr<::media::Renderer> default_renderer, + scoped_ptr<::media::Renderer> cma_renderer) + : default_renderer_(default_renderer.Pass()), + cma_renderer_(cma_renderer.Pass()) { + DCHECK(default_renderer_); + DCHECK(cma_renderer_); +} + +SwitchingMediaRenderer::~SwitchingMediaRenderer() { +} + +void SwitchingMediaRenderer::Initialize( + ::media::DemuxerStreamProvider* demuxer_stream_provider, + const ::media::PipelineStatusCB& init_cb, + const ::media::StatisticsCB& statistics_cb, + const ::media::BufferingStateCB& buffering_state_cb, + const base::Closure& ended_cb, + const ::media::PipelineStatusCB& error_cb, + const base::Closure& waiting_for_decryption_key_cb) { + // At this point the DemuxerStreamProvider should be fully initialized, so we + // have enough information to decide which renderer to use. + demuxer_stream_provider_ = demuxer_stream_provider; + DCHECK(demuxer_stream_provider_); + ::media::DemuxerStream* audio_stream = + demuxer_stream_provider_->GetStream(::media::DemuxerStream::AUDIO); + ::media::DemuxerStream* video_stream = + demuxer_stream_provider_->GetStream(::media::DemuxerStream::VIDEO); + if (audio_stream && !video_stream && + (audio_stream->audio_decoder_config().codec() == ::media::kCodecFLAC || + audio_stream->audio_decoder_config().codec() == ::media::kCodecOpus)) { + // We'll use the default Chrome media renderer with software audio decoding + cma_renderer_.reset(); + } else { + // We'll use the CMA-based rendering with hardware decoding + default_renderer_.reset(); + } + + return GetRenderer()->Initialize( + demuxer_stream_provider, init_cb, statistics_cb, buffering_state_cb, + ended_cb, error_cb, waiting_for_decryption_key_cb); +} + +::media::Renderer* SwitchingMediaRenderer::GetRenderer() const { + DCHECK(default_renderer_ || cma_renderer_); + if (cma_renderer_) + return cma_renderer_.get(); + + DCHECK(default_renderer_); + return default_renderer_.get(); +} + +void SwitchingMediaRenderer::SetCdm( + ::media::CdmContext* cdm_context, + const ::media::CdmAttachedCB& cdm_attached_cb) { + GetRenderer()->SetCdm(cdm_context, cdm_attached_cb); +} + +void SwitchingMediaRenderer::Flush(const base::Closure& flush_cb) { + GetRenderer()->Flush(flush_cb); +} + +void SwitchingMediaRenderer::StartPlayingFrom(base::TimeDelta time) { + GetRenderer()->StartPlayingFrom(time); +} + +void SwitchingMediaRenderer::SetPlaybackRate(double playback_rate) { + GetRenderer()->SetPlaybackRate(playback_rate); +} + +void SwitchingMediaRenderer::SetVolume(float volume) { + GetRenderer()->SetVolume(volume); +} + +base::TimeDelta SwitchingMediaRenderer::GetMediaTime() { + return GetRenderer()->GetMediaTime(); +} + +bool SwitchingMediaRenderer::HasAudio() { + return GetRenderer()->HasAudio(); +} + +bool SwitchingMediaRenderer::HasVideo() { + return GetRenderer()->HasVideo(); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/base/switching_media_renderer.h b/chromium/chromecast/media/base/switching_media_renderer.h new file mode 100644 index 00000000000..c32b1d438d5 --- /dev/null +++ b/chromium/chromecast/media/base/switching_media_renderer.h @@ -0,0 +1,66 @@ +// Copyright 2015 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_SWITCHING_MEDIA_RENDERER_H_ +#define CHROMECAST_MEDIA_BASE_SWITCHING_MEDIA_RENDERER_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/renderer.h" + +namespace media { +class DemuxerStreamProvider; +} + +namespace chromecast { +namespace media { + +// Chromecast's custom media renderer which is capable of selecting an +// appropriate media renderer at runtime depending on the content. +// We'll look at media types of media streams present in DemuxerStreamProvider +// and will use either CMA-based renderer (for media types that are supported by +// our hardware decoder - H264 and VP8 video, AAC and Vorbis audio) or the +// default Chrome media renderer (this will allow us to support audio codecs +// like FLAC and Opus, which are decoded in software). +class SwitchingMediaRenderer : public ::media::Renderer { + public: + SwitchingMediaRenderer( + scoped_ptr<::media::Renderer> default_renderer, + scoped_ptr<::media::Renderer> cma_renderer); + ~SwitchingMediaRenderer() override; + + // ::media::Renderer implementation: + void Initialize( + ::media::DemuxerStreamProvider* demuxer_stream_provider, + const ::media::PipelineStatusCB& init_cb, + const ::media::StatisticsCB& statistics_cb, + const ::media::BufferingStateCB& buffering_state_cb, + const base::Closure& ended_cb, + const ::media::PipelineStatusCB& error_cb, + const base::Closure& waiting_for_decryption_key_cb) override; + void SetCdm(::media::CdmContext* cdm_context, + const ::media::CdmAttachedCB& cdm_attached_cb) override; + void Flush(const base::Closure& flush_cb) override; + void StartPlayingFrom(base::TimeDelta time) override; + void SetPlaybackRate(double playback_rate) override; + void SetVolume(float volume) override; + base::TimeDelta GetMediaTime() override; + bool HasAudio() override; + bool HasVideo() override; + + private: + // Returns the pointer to the actual renderer being used + ::media::Renderer* GetRenderer() const; + + ::media::DemuxerStreamProvider* demuxer_stream_provider_; + scoped_ptr<::media::Renderer> default_renderer_; + scoped_ptr<::media::Renderer> cma_renderer_; + + DISALLOW_COPY_AND_ASSIGN(SwitchingMediaRenderer); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_BASE_SWITCHING_MEDIA_RENDERER_H_ diff --git a/chromium/chromecast/media/cdm/BUILD.gn b/chromium/chromecast/media/cdm/BUILD.gn new file mode 100644 index 00000000000..1a929d07b33 --- /dev/null +++ b/chromium/chromecast/media/cdm/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright 2015 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. + +source_set("cdm") { + sources = [ + "browser_cdm_cast.cc", + "browser_cdm_cast.h", + ] + + deps = [ + "//base", + "//chromecast/media/base", + "//media", + ] + + configs += [ "//chromecast:config" ] +} diff --git a/chromium/chromecast/media/cdm/browser_cdm_cast.cc b/chromium/chromecast/media/cdm/browser_cdm_cast.cc new file mode 100644 index 00000000000..b62df02cb8b --- /dev/null +++ b/chromium/chromecast/media/cdm/browser_cdm_cast.cc @@ -0,0 +1,209 @@ +// 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/cdm/browser_cdm_cast.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "media/base/cdm_key_information.h" +#include "media/base/cdm_promise.h" +#include "media/cdm/player_tracker_impl.h" + +namespace chromecast { +namespace media { + +BrowserCdmCast::BrowserCdmCast() { + thread_checker_.DetachFromThread(); +} + +BrowserCdmCast::~BrowserCdmCast() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(player_tracker_impl_.get()); + player_tracker_impl_->NotifyCdmUnset(); +} + +void BrowserCdmCast::Initialize( + const ::media::SessionMessageCB& session_message_cb, + const ::media::SessionClosedCB& session_closed_cb, + const ::media::LegacySessionErrorCB& legacy_session_error_cb, + const ::media::SessionKeysChangeCB& session_keys_change_cb, + const ::media::SessionExpirationUpdateCB& session_expiration_update_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + + player_tracker_impl_.reset(new ::media::PlayerTrackerImpl); + + session_message_cb_ = session_message_cb; + session_closed_cb_ = session_closed_cb; + legacy_session_error_cb_ = legacy_session_error_cb; + session_keys_change_cb_ = session_keys_change_cb; + session_expiration_update_cb_ = session_expiration_update_cb; + + InitializeInternal(); +} + +int BrowserCdmCast::RegisterPlayer(const base::Closure& new_key_cb, + const base::Closure& cdm_unset_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + return player_tracker_impl_->RegisterPlayer(new_key_cb, cdm_unset_cb); +} + +void BrowserCdmCast::UnregisterPlayer(int registration_id) { + DCHECK(thread_checker_.CalledOnValidThread()); + player_tracker_impl_->UnregisterPlayer(registration_id); +} + +void BrowserCdmCast::LoadSession( + ::media::MediaKeys::SessionType session_type, + const std::string& session_id, + scoped_ptr<::media::NewSessionCdmPromise> promise) { + NOTREACHED() << "LoadSession not supported"; + legacy_session_error_cb_.Run( + session_id, ::media::MediaKeys::Exception::NOT_SUPPORTED_ERROR, 0, + std::string()); +} + +::media::CdmContext* BrowserCdmCast::GetCdmContext() { + NOTREACHED(); + return nullptr; +} + +void BrowserCdmCast::OnSessionMessage(const std::string& session_id, + const std::vector<uint8_t>& message, + const GURL& destination_url) { + // Note: Message type is not supported in Chromecast. Do our best guess here. + ::media::MediaKeys::MessageType message_type = + destination_url.is_empty() ? ::media::MediaKeys::LICENSE_REQUEST + : ::media::MediaKeys::LICENSE_RENEWAL; + session_message_cb_.Run(session_id, + message_type, + message, + destination_url); +} + +void BrowserCdmCast::OnSessionClosed(const std::string& session_id) { + session_closed_cb_.Run(session_id); +} + +void BrowserCdmCast::OnSessionKeysChange( + const std::string& session_id, + const ::media::KeyIdAndKeyPairs& keys) { + ::media::CdmKeysInfo cdm_keys_info; + for (const std::pair<std::string, std::string>& key : keys) { + scoped_ptr< ::media::CdmKeyInformation> cdm_key_information( + new ::media::CdmKeyInformation()); + cdm_key_information->key_id.assign(key.first.begin(), key.first.end()); + cdm_keys_info.push_back(cdm_key_information.release()); + } + session_keys_change_cb_.Run(session_id, true, cdm_keys_info.Pass()); + + player_tracker_impl_->NotifyNewKey(); +} + +// A macro runs current member function on |cdm_loop_| thread. +#define FORWARD_ON_CDM_THREAD(param_fn, ...) \ + cdm_loop_->PostTask( \ + FROM_HERE, \ + base::Bind(&BrowserCdmCast::param_fn, \ + base::Unretained(browser_cdm_cast_.get()), ##__VA_ARGS__)) + + +BrowserCdmCastUi::BrowserCdmCastUi( + scoped_ptr<BrowserCdmCast> browser_cdm_cast, + const scoped_refptr<base::MessageLoopProxy>& cdm_loop) + : browser_cdm_cast_(browser_cdm_cast.Pass()), + cdm_loop_(cdm_loop) { +} + +BrowserCdmCastUi::~BrowserCdmCastUi() { + DCHECK(thread_checker_.CalledOnValidThread()); + cdm_loop_->DeleteSoon(FROM_HERE, browser_cdm_cast_.release()); +} + +int BrowserCdmCastUi::RegisterPlayer(const base::Closure& new_key_cb, + const base::Closure& cdm_unset_cb) { + NOTREACHED() << "RegisterPlayer should be called on BrowserCdmCast"; + return -1; +} + +void BrowserCdmCastUi::UnregisterPlayer(int registration_id) { + NOTREACHED() << "UnregisterPlayer should be called on BrowserCdmCast"; +} + +BrowserCdmCast* BrowserCdmCastUi::browser_cdm_cast() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return browser_cdm_cast_.get(); +} + +void BrowserCdmCastUi::SetServerCertificate( + const std::vector<uint8_t>& certificate, + scoped_ptr<::media::SimpleCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD( + SetServerCertificate, + certificate, + base::Passed(&promise)); +} + +void BrowserCdmCastUi::CreateSessionAndGenerateRequest( + ::media::MediaKeys::SessionType session_type, + ::media::EmeInitDataType init_data_type, + const std::vector<uint8_t>& init_data, + scoped_ptr<::media::NewSessionCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD( + CreateSessionAndGenerateRequest, + session_type, + init_data_type, + init_data, + base::Passed(&promise)); +} + +void BrowserCdmCastUi::LoadSession( + ::media::MediaKeys::SessionType session_type, + const std::string& session_id, + scoped_ptr<::media::NewSessionCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD( + LoadSession, session_type, session_id, base::Passed(&promise)); +} + +void BrowserCdmCastUi::UpdateSession( + const std::string& session_id, + const std::vector<uint8_t>& response, + scoped_ptr<::media::SimpleCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD( + UpdateSession, + session_id, + response, + base::Passed(&promise)); +} + +void BrowserCdmCastUi::CloseSession( + const std::string& session_id, + scoped_ptr<::media::SimpleCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD(CloseSession, session_id, base::Passed(&promise)); +} + +void BrowserCdmCastUi::RemoveSession( + const std::string& session_id, + scoped_ptr<::media::SimpleCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD(RemoveSession, session_id, base::Passed(&promise)); +} + +::media::CdmContext* BrowserCdmCastUi::GetCdmContext() { + NOTREACHED(); + return nullptr; +} + +// A default empty implementation for subclasses that don't need to provide +// any key system specific initialization. +void BrowserCdmCast::InitializeInternal() { +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cdm/browser_cdm_cast.h b/chromium/chromecast/media/cdm/browser_cdm_cast.h new file mode 100644 index 00000000000..7a83a08dcc8 --- /dev/null +++ b/chromium/chromecast/media/cdm/browser_cdm_cast.h @@ -0,0 +1,147 @@ +// 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_CDM_BROWSER_CDM_CAST_H_ +#define CHROMECAST_MEDIA_CDM_BROWSER_CDM_CAST_H_ + +#include <stdint.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "media/base/browser_cdm.h" +#include "media/cdm/json_web_key.h" + +namespace base { +class MessageLoopProxy; +} + +namespace media { +class PlayerTrackerImpl; +} + +namespace chromecast { +namespace media { +class DecryptContext; + +// BrowserCdmCast is an extension of BrowserCdm that provides common +// functionality across CDM implementations. +// All these additional functions are synchronous so: +// - either both the CDM and the media pipeline must be running on the same +// thread, +// - or BrowserCdmCast implementations must use some locks. +// +class BrowserCdmCast : public ::media::BrowserCdm { + public: + BrowserCdmCast(); + ~BrowserCdmCast() override; + + void Initialize( + const ::media::SessionMessageCB& session_message_cb, + const ::media::SessionClosedCB& session_closed_cb, + const ::media::LegacySessionErrorCB& legacy_session_error_cb, + const ::media::SessionKeysChangeCB& session_keys_change_cb, + const ::media::SessionExpirationUpdateCB& session_expiration_update_cb); + + // PlayerTracker implementation. + int RegisterPlayer(const base::Closure& new_key_cb, + const base::Closure& cdm_unset_cb) override; + void UnregisterPlayer(int registration_id) override; + + // ::media::BrowserCdm implementation: + void LoadSession(::media::MediaKeys::SessionType session_type, + const std::string& session_id, + scoped_ptr<::media::NewSessionCdmPromise> promise) override; + ::media::CdmContext* GetCdmContext() override; + + // Returns the decryption context needed to decrypt frames encrypted with + // |key_id|. + // Returns null if |key_id| is not available. + virtual scoped_refptr<DecryptContext> GetDecryptContext( + const std::string& key_id) const = 0; + + protected: + void OnSessionMessage(const std::string& session_id, + const std::vector<uint8_t>& message, + const GURL& destination_url); + void OnSessionClosed(const std::string& session_id); + void OnSessionKeysChange(const std::string& session_id, + const ::media::KeyIdAndKeyPairs& keys); + + private: + friend class BrowserCdmCastUi; + + // Allow subclasses to override to provide key sysytem specific + // initialization. + virtual void InitializeInternal(); + + ::media::SessionMessageCB session_message_cb_; + ::media::SessionClosedCB session_closed_cb_; + ::media::LegacySessionErrorCB legacy_session_error_cb_; + ::media::SessionKeysChangeCB session_keys_change_cb_; + ::media::SessionExpirationUpdateCB session_expiration_update_cb_; + + scoped_ptr<::media::PlayerTrackerImpl> player_tracker_impl_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(BrowserCdmCast); +}; + +// BrowserCdm implementation that lives on the UI thread and forwards all calls +// to a BrowserCdmCast instance on the CMA thread. This is used to simplify the +// UI-CMA threading interaction. +class BrowserCdmCastUi : public ::media::BrowserCdm { + public: + BrowserCdmCastUi( + scoped_ptr<BrowserCdmCast> browser_cdm_cast, + const scoped_refptr<base::MessageLoopProxy>& cdm_loop); + ~BrowserCdmCastUi() override; + + // PlayerTracker implementation: + int RegisterPlayer(const base::Closure& new_key_cb, + const base::Closure& cdm_unset_cb) override; + void UnregisterPlayer(int registration_id) override; + + BrowserCdmCast* browser_cdm_cast() const; + + private: + // ::media::MediaKeys implementation: + void SetServerCertificate( + const std::vector<uint8_t>& certificate, + scoped_ptr<::media::SimpleCdmPromise> promise) override; + void CreateSessionAndGenerateRequest( + ::media::MediaKeys::SessionType session_type, + ::media::EmeInitDataType init_data_type, + const std::vector<uint8_t>& init_data, + scoped_ptr<::media::NewSessionCdmPromise> promise) override; + void LoadSession(::media::MediaKeys::SessionType session_type, + const std::string& session_id, + scoped_ptr<::media::NewSessionCdmPromise> promise) override; + void UpdateSession(const std::string& session_id, + const std::vector<uint8_t>& response, + scoped_ptr<::media::SimpleCdmPromise> promise) override; + void CloseSession(const std::string& session_id, + scoped_ptr<::media::SimpleCdmPromise> promise) override; + void RemoveSession(const std::string& session_id, + scoped_ptr<::media::SimpleCdmPromise> promise) override; + ::media::CdmContext* GetCdmContext() override; + + scoped_ptr<BrowserCdmCast> browser_cdm_cast_; + scoped_refptr<base::MessageLoopProxy> cdm_loop_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(BrowserCdmCastUi); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CDM_BROWSER_CDM_CAST_H_ diff --git a/chromium/chromecast/media/cdm/playready_drm_delegate_android.cc b/chromium/chromecast/media/cdm/playready_drm_delegate_android.cc new file mode 100644 index 00000000000..135fc44bd52 --- /dev/null +++ b/chromium/chromecast/media/cdm/playready_drm_delegate_android.cc @@ -0,0 +1,85 @@ +// Copyright 2015 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/cdm/playready_drm_delegate_android.h" + +#include "base/logging.h" +#include "media/base/bit_reader.h" + +namespace chromecast { +namespace media { + +const uint8_t kPlayreadyUuid[16] = { + 0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, + 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95}; + +const uint8_t kPlayreadyCustomDataUuid[] = { + 0x2b, 0xf8, 0x66, 0x80, 0xc6, 0xe5, 0x4e, 0x24, + 0xbe, 0x23, 0x0f, 0x81, 0x5a, 0x60, 0x6e, 0xb2}; + +// ASCII "uuid" as an 4-byte integer +const uint32_t kBoxTypeUuid = 1970628964; + +PlayreadyDrmDelegateAndroid::PlayreadyDrmDelegateAndroid() { +} + +PlayreadyDrmDelegateAndroid::~PlayreadyDrmDelegateAndroid() { +} + +const ::media::UUID PlayreadyDrmDelegateAndroid::GetUUID() const { + return ::media::UUID(kPlayreadyUuid, + kPlayreadyUuid + arraysize(kPlayreadyUuid)); +} + +bool PlayreadyDrmDelegateAndroid::OnCreateSession( + const ::media::EmeInitDataType init_data_type, + const std::vector<uint8_t>& init_data, + std::vector<uint8_t>* /* init_data_out */, + std::vector<std::string>* optional_parameters_out) { + if (init_data_type == ::media::EmeInitDataType::CENC) { + ::media::BitReader reader(&init_data[0], init_data.size()); + while (reader.bits_available() > 64) { + uint32_t box_size; + uint32_t box_type; + reader.ReadBits(32, &box_size); + reader.ReadBits(32, &box_type); + int bytes_read = 8; + + if (box_type != kBoxTypeUuid) { + if (box_size < 8 + sizeof(kPlayreadyCustomDataUuid)) { + break; + } + // Box size includes the bytes already consumed + reader.SkipBits((box_size - bytes_read) * 8); + continue; + } + + // "uuid" was found, look for custom data format as per b/10246367 + reader.SkipBits(128); + bytes_read += 16; + if (!memcmp(&init_data[0] + reader.bits_read() / 8, + kPlayreadyCustomDataUuid, 16)) { + reader.SkipBits((box_size - bytes_read) * 8); + continue; + } + + int custom_data_size = box_size - bytes_read; + DCHECK(reader.bits_read() % 8 == 0); + int total_bytes_read = reader.bits_read() / 8; + + optional_parameters_out->clear(); + optional_parameters_out->push_back("PRCustomData"); + optional_parameters_out->push_back( + std::string(&init_data[0] + total_bytes_read, + &init_data[0] + total_bytes_read + custom_data_size)); + reader.SkipBits(custom_data_size * 8); + LOG(INFO) << "Including " << custom_data_size + << " bytes of custom PlayReady data"; + } + } + return true; +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cdm/playready_drm_delegate_android.h b/chromium/chromecast/media/cdm/playready_drm_delegate_android.h new file mode 100644 index 00000000000..3a934c456d2 --- /dev/null +++ b/chromium/chromecast/media/cdm/playready_drm_delegate_android.h @@ -0,0 +1,34 @@ +// Copyright 2015 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_CDM_PLAYREADY_DRM_DELEGATE_ANDROID_H_ +#define CHROMECAST_MEDIA_CDM_PLAYREADY_DRM_DELEGATE_ANDROID_H_ + +#include "base/macros.h" +#include "media/base/android/media_drm_bridge_delegate.h" + +namespace chromecast { +namespace media { + +class PlayreadyDrmDelegateAndroid : public ::media::MediaDrmBridgeDelegate { + public: + PlayreadyDrmDelegateAndroid(); + ~PlayreadyDrmDelegateAndroid() override; + + // ::media::MediaDrmBridgeDelegate implementation: + const ::media::UUID GetUUID() const override; + bool OnCreateSession( + const ::media::EmeInitDataType init_data_type, + const std::vector<uint8_t>& init_data, + std::vector<uint8_t>* init_data_out, + std::vector<std::string>* optional_parameters_out) override; + + private: + DISALLOW_COPY_AND_ASSIGN(PlayreadyDrmDelegateAndroid); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CDM_PLAYREADY_DRM_DELEGATE_ANDROID_H_ diff --git a/chromium/chromecast/media/cma/BUILD.gn b/chromium/chromecast/media/cma/BUILD.gn new file mode 100644 index 00000000000..c9da6296f0e --- /dev/null +++ b/chromium/chromecast/media/cma/BUILD.gn @@ -0,0 +1,14 @@ +# Copyright 2015 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. + +group("cma") { + deps = [ + "//chromecast/media/cma/backend", + "//chromecast/media/cma/base", + "//chromecast/media/cma/filters", + "//chromecast/media/cma/ipc", + "//chromecast/media/cma/ipc_streamer", + "//chromecast/media/cma/pipeline", + ] +} diff --git a/chromium/chromecast/media/cma/backend/BUILD.gn b/chromium/chromecast/media/cma/backend/BUILD.gn new file mode 100644 index 00000000000..6526a1f746b --- /dev/null +++ b/chromium/chromecast/media/cma/backend/BUILD.gn @@ -0,0 +1,37 @@ +# Copyright 2015 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. + +source_set("backend") { + sources = [ + "audio_pipeline_device.cc", + "audio_pipeline_device.h", + "media_clock_device.cc", + "media_clock_device.h", + "media_component_device.cc", + "media_component_device.h", + "media_pipeline_device.cc", + "media_pipeline_device.h", + "media_pipeline_device_fake.cc", + "media_pipeline_device_fake.h", + "media_pipeline_device_fake_factory.cc", + "media_pipeline_device_params.cc", + "media_pipeline_device_params.h", + "video_pipeline_device.cc", + "video_pipeline_device.h", + "video_plane.cc", + "video_plane.h", + "video_plane_fake.cc", + "video_plane_fake.h", + "video_plane_fake_factory.cc", + ] + + deps = [ + "//base", + "//chromecast/media/base", + "//chromecast/media/cma/base", + "//media", + ] + + configs += [ "//chromecast:config" ] +} diff --git a/chromium/chromecast/media/cma/backend/audio_pipeline_device.h b/chromium/chromecast/media/cma/backend/audio_pipeline_device.h index 3fbf5731dbe..84c1af1fe1d 100644 --- a/chromium/chromecast/media/cma/backend/audio_pipeline_device.h +++ b/chromium/chromecast/media/cma/backend/audio_pipeline_device.h @@ -8,24 +8,21 @@ #include "base/macros.h" #include "chromecast/media/cma/backend/media_component_device.h" -namespace media { -class AudioDecoderConfig; -} - namespace chromecast { namespace media { class AudioPipelineDeviceClient; +struct AudioConfig; class AudioPipelineDevice : public MediaComponentDevice { public: AudioPipelineDevice(); - virtual ~AudioPipelineDevice(); + ~AudioPipelineDevice() override; // 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; + virtual bool SetConfig(const AudioConfig& config) = 0; // Sets the volume multiplier. // The multiplier must be in the range [0.0, 1.0]. 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 index cf1daca9ab0..972d20af679 100644 --- a/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc +++ b/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc @@ -6,6 +6,7 @@ #include "base/basictypes.h" #include "base/bind.h" +#include "base/command_line.h" #include "base/files/file_path.h" #include "base/files/memory_mapped_file.h" #include "base/logging.h" @@ -25,8 +26,11 @@ #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/base/decoder_config_adapter.h" #include "chromecast/media/cma/test/frame_segmenter_for_test.h" #include "chromecast/media/cma/test/media_component_device_feeder_for_test.h" +#include "chromecast/public/cast_media_shlib.h" +#include "chromecast/public/media/decoder_config.h" #include "media/base/audio_decoder_config.h" #include "media/base/buffers.h" #include "media/base/decoder_buffer.h" @@ -67,7 +71,16 @@ class AudioVideoPipelineDeviceTest : public testing::Test { }; AudioVideoPipelineDeviceTest(); - virtual ~AudioVideoPipelineDeviceTest(); + ~AudioVideoPipelineDeviceTest() override; + + void SetUp() override { + CastMediaShlib::Initialize( + base::CommandLine::ForCurrentProcess()->argv()); + } + + void TearDown() override { + CastMediaShlib::Finalize(); + } void ConfigureForFile(std::string filename); void ConfigureForAudioOnly(std::string filename); @@ -154,7 +167,8 @@ void AudioVideoPipelineDeviceTest::LoadAudioStream(std::string filename) { AudioPipelineDevice* audio_pipeline_device = media_pipeline_device_->GetAudioPipelineDevice(); - bool success = audio_pipeline_device->SetConfig(demux_result.audio_config); + bool success = audio_pipeline_device->SetConfig( + DecoderConfigAdapter::ToCastAudioConfig(demux_result.audio_config)); ASSERT_TRUE(success); VLOG(2) << "Got " << frames.size() << " audio input frames"; @@ -174,7 +188,7 @@ void AudioVideoPipelineDeviceTest::LoadAudioStream(std::string filename) { void AudioVideoPipelineDeviceTest::LoadVideoStream(std::string filename, bool raw_h264) { BufferList frames; - ::media::VideoDecoderConfig video_config; + VideoConfig video_config; if (raw_h264) { base::FilePath file_path = GetTestDataFilePath(filename); @@ -183,26 +197,18 @@ void AudioVideoPipelineDeviceTest::LoadVideoStream(std::string filename, << "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); + // TODO(erickung): Either pull data from stream or make caller specify value + video_config.codec = kCodecH264; + video_config.profile = kH264Main; + video_config.additional_config = NULL; + video_config.is_encrypted = 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; + video_config = + DecoderConfigAdapter::ToCastVideoConfig(demux_result.video_config); } VideoPipelineDevice* video_pipeline_device = @@ -230,7 +236,7 @@ void AudioVideoPipelineDeviceTest::Start() { pause_time_ = base::TimeDelta(); pause_pattern_idx_ = 0; - for (int i = 0; i < component_device_feeders_.size(); i++) { + for (size_t i = 0; i < component_device_feeders_.size(); i++) { base::MessageLoopProxy::current()->PostTask( FROM_HERE, base::Bind(&MediaComponentDeviceFeederForTest::Feed, diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc index c075191e62f..60d9576764a 100644 --- a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc +++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc @@ -5,6 +5,7 @@ #include "chromecast/media/cma/backend/media_pipeline_device_fake.h" #include <list> +#include <vector> #include "base/bind.h" #include "base/callback_helpers.h" @@ -17,9 +18,8 @@ #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 "chromecast/public/media/decoder_config.h" #include "media/base/buffers.h" -#include "media/base/video_decoder_config.h" namespace chromecast { namespace media { @@ -27,14 +27,14 @@ namespace media { class MediaClockDeviceFake : public MediaClockDevice { public: MediaClockDeviceFake(); - virtual ~MediaClockDeviceFake(); + ~MediaClockDeviceFake() override; // 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; + State GetState() const override; + bool SetState(State new_state) override; + bool ResetTimeline(base::TimeDelta time) override; + bool SetRate(float rate) override; + base::TimeDelta GetTime() override; private: State state_; @@ -130,20 +130,20 @@ const size_t kMaxFrameCount = 20; class MediaComponentDeviceFake : public MediaComponentDevice { public: explicit MediaComponentDeviceFake(MediaClockDeviceFake* media_clock_device); - virtual ~MediaComponentDeviceFake(); + ~MediaComponentDeviceFake() override; // 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( + void SetClient(const Client& client) override; + State GetState() const override; + bool SetState(State new_state) override; + bool SetStartPts(base::TimeDelta time) override; + 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; + base::TimeDelta GetRenderingTime() const override; + base::TimeDelta GetRenderingDelay() const override; + bool GetStatistics(Statistics* stats) const override; private: struct FakeDecoderBuffer { @@ -358,27 +358,28 @@ bool MediaComponentDeviceFake::GetStatistics(Statistics* stats) const { class AudioPipelineDeviceFake : public AudioPipelineDevice { public: explicit AudioPipelineDeviceFake(MediaClockDeviceFake* media_clock_device); - virtual ~AudioPipelineDeviceFake(); + ~AudioPipelineDeviceFake() override; // 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( + void SetClient(const Client& client) override; + State GetState() const override; + bool SetState(State new_state) override; + bool SetStartPts(base::TimeDelta time) override; + 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; + base::TimeDelta GetRenderingTime() const override; + base::TimeDelta GetRenderingDelay() const override; + bool SetConfig(const AudioConfig& config) override; + void SetStreamVolumeMultiplier(float multiplier) override; + bool GetStatistics(Statistics* stats) const override; private: scoped_ptr<MediaComponentDeviceFake> fake_pipeline_; - ::media::AudioDecoderConfig config_; + AudioConfig config_; + std::vector<uint8_t> config_extra_data_; DISALLOW_COPY_AND_ASSIGN(AudioPipelineDeviceFake); }; @@ -406,10 +407,10 @@ bool AudioPipelineDeviceFake::SetState(State new_state) { return false; if (new_state == kStateIdle) { - DCHECK(config_.IsValidConfig()); + DCHECK(IsValidConfig(config_)); } if (new_state == kStateUninitialized) { - config_ = ::media::AudioDecoderConfig(); + config_ = AudioConfig(); } return true; } @@ -433,12 +434,16 @@ base::TimeDelta AudioPipelineDeviceFake::GetRenderingDelay() const { return fake_pipeline_->GetRenderingDelay(); } -bool AudioPipelineDeviceFake::SetConfig( - const ::media::AudioDecoderConfig& config) { +bool AudioPipelineDeviceFake::SetConfig(const AudioConfig& config) { DCHECK(CalledOnValidThread()); - if (!config.IsValidConfig()) + if (!IsValidConfig(config)) return false; config_ = config; + if (config.extra_data_size > 0) + config_extra_data_.assign(config.extra_data, + config.extra_data + config.extra_data_size); + else + config_extra_data_.clear(); return true; } @@ -454,27 +459,28 @@ bool AudioPipelineDeviceFake::GetStatistics(Statistics* stats) const { class VideoPipelineDeviceFake : public VideoPipelineDevice { public: explicit VideoPipelineDeviceFake(MediaClockDeviceFake* media_clock_device); - virtual ~VideoPipelineDeviceFake(); + ~VideoPipelineDeviceFake() override; // 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( + void SetClient(const Client& client) override; + State GetState() const override; + bool SetState(State new_state) override; + bool SetStartPts(base::TimeDelta time) override; + 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; + base::TimeDelta GetRenderingTime() const override; + base::TimeDelta GetRenderingDelay() const override; + void SetVideoClient(const VideoClient& client) override; + bool SetConfig(const VideoConfig& config) override; + bool GetStatistics(Statistics* stats) const override; private: scoped_ptr<MediaComponentDeviceFake> fake_pipeline_; - ::media::VideoDecoderConfig config_; + VideoConfig config_; + std::vector<uint8_t> config_extra_data_; DISALLOW_COPY_AND_ASSIGN(VideoPipelineDeviceFake); }; @@ -502,10 +508,10 @@ bool VideoPipelineDeviceFake::SetState(State new_state) { return false; if (new_state == kStateIdle) { - DCHECK(config_.IsValidConfig()); + DCHECK(IsValidConfig(config_)); } if (new_state == kStateUninitialized) { - config_ = ::media::VideoDecoderConfig(); + config_ = VideoConfig(); } return true; } @@ -532,12 +538,16 @@ base::TimeDelta VideoPipelineDeviceFake::GetRenderingDelay() const { void VideoPipelineDeviceFake::SetVideoClient(const VideoClient& client) { } -bool VideoPipelineDeviceFake::SetConfig( - const ::media::VideoDecoderConfig& config) { +bool VideoPipelineDeviceFake::SetConfig(const VideoConfig& config) { DCHECK(CalledOnValidThread()); - if (!config.IsValidConfig()) + if (!IsValidConfig(config)) return false; config_ = config; + if (config.extra_data_size > 0) + config_extra_data_.assign(config.extra_data, + config.extra_data + config.extra_data_size); + else + config_extra_data_.clear(); return true; } diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h index 0534515bf9f..248c9a4322e 100644 --- a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h +++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h @@ -19,12 +19,12 @@ class VideoPipelineDeviceFake; class MediaPipelineDeviceFake : public MediaPipelineDevice { public: MediaPipelineDeviceFake(); - virtual ~MediaPipelineDeviceFake(); + ~MediaPipelineDeviceFake() override; // MediaPipelineDevice implementation. - virtual AudioPipelineDevice* GetAudioPipelineDevice() const override; - virtual VideoPipelineDevice* GetVideoPipelineDevice() const override; - virtual MediaClockDevice* GetMediaClockDevice() const override; + AudioPipelineDevice* GetAudioPipelineDevice() const override; + VideoPipelineDevice* GetVideoPipelineDevice() const override; + MediaClockDevice* GetMediaClockDevice() const override; private: scoped_ptr<MediaClockDeviceFake> media_clock_device_; diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_params.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device_params.cc index 531c76884f9..1f4e2ebed58 100644 --- a/chromium/chromecast/media/cma/backend/media_pipeline_device_params.cc +++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_params.cc @@ -8,7 +8,7 @@ namespace chromecast { namespace media { MediaPipelineDeviceParams::MediaPipelineDeviceParams() - : disable_synchronization(false) { + : sync_type(kModeSyncPts) { } MediaPipelineDeviceParams::~MediaPipelineDeviceParams() { diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_params.h b/chromium/chromecast/media/cma/backend/media_pipeline_device_params.h index 9bf9c044f16..d13e4e3a188 100644 --- a/chromium/chromecast/media/cma/backend/media_pipeline_device_params.h +++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_params.h @@ -12,17 +12,25 @@ namespace media { class MediaPipelineDeviceParams { public: + enum MediaSyncType { + // Default operation, synchronize playback using PTS with higher latency. + kModeSyncPts = 0, + // With this mode, 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. + kModeIgnorePts = 1, + // In addition to the constraints above, also do not wait for vsync. + kModeIgnorePtsAndVSync = 2, + }; + 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; + MediaSyncType sync_type; }; } // namespace media diff --git a/chromium/chromecast/media/cma/backend/video_pipeline_device.h b/chromium/chromecast/media/cma/backend/video_pipeline_device.h index eda48e206f4..ee7dff4c0d1 100644 --- a/chromium/chromecast/media/cma/backend/video_pipeline_device.h +++ b/chromium/chromecast/media/cma/backend/video_pipeline_device.h @@ -13,13 +13,10 @@ namespace gfx { class Size; } -namespace media { -class VideoDecoderConfig; -} - namespace chromecast { namespace media { class DecoderBufferBase; +struct VideoConfig; // VideoPipelineDevice - // @@ -42,7 +39,7 @@ class VideoPipelineDevice : public MediaComponentDevice { }; VideoPipelineDevice(); - virtual ~VideoPipelineDevice(); + ~VideoPipelineDevice() override; // Registers |client| as the video specific event handler. virtual void SetVideoClient(const VideoClient& client) = 0; @@ -51,7 +48,7 @@ class VideoPipelineDevice : public MediaComponentDevice { // 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; + virtual bool SetConfig(const VideoConfig& config) = 0; private: DISALLOW_COPY_AND_ASSIGN(VideoPipelineDevice); diff --git a/chromium/chromecast/media/cma/backend/video_plane.cc b/chromium/chromecast/media/cma/backend/video_plane.cc new file mode 100644 index 00000000000..5d8097f2a62 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/video_plane.cc @@ -0,0 +1,54 @@ +// 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_plane.h" + +#include "base/memory/singleton.h" + +namespace chromecast { +namespace media { + +VideoPlane::VideoPlane() { +} + +VideoPlane::~VideoPlane() { +} + +class VideoPlaneRegistry { + public: + static VideoPlaneRegistry* GetInstance() { + return Singleton<VideoPlaneRegistry>::get(); + } + + VideoPlane* GetVideoPlane(); + + private: + friend struct DefaultSingletonTraits<VideoPlaneRegistry>; + friend class Singleton<VideoPlaneRegistry>; + + VideoPlaneRegistry(); + virtual ~VideoPlaneRegistry(); + + scoped_ptr<VideoPlane> video_plane_; + + DISALLOW_COPY_AND_ASSIGN(VideoPlaneRegistry); +}; + +VideoPlaneRegistry::VideoPlaneRegistry() : + video_plane_(CreateVideoPlane()) { +} + +VideoPlaneRegistry::~VideoPlaneRegistry() { +} + +VideoPlane* VideoPlaneRegistry::GetVideoPlane() { + return video_plane_.get(); +} + +VideoPlane* GetVideoPlane() { + return VideoPlaneRegistry::GetInstance()->GetVideoPlane(); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/video_plane.h b/chromium/chromecast/media/cma/backend/video_plane.h new file mode 100644 index 00000000000..a5f864c6d50 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/video_plane.h @@ -0,0 +1,62 @@ +// 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_PLANE_H_ +#define CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_PLANE_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" + +namespace gfx { +class QuadF; +class Size; +} + +namespace chromecast { +namespace media { + +class VideoPlane { + public: + enum CoordinateType { + // Graphics plane as coordinate type. + COORDINATE_TYPE_GRAPHICS_PLANE = 0, + // Output display screen as coordinate type. + COORDINATE_TYPE_SCREEN_RESOLUTION = 1, + }; + + VideoPlane(); + virtual ~VideoPlane(); + + // Gets output screen resolution. + virtual gfx::Size GetScreenResolution() = 0; + + // Updates the video plane geometry. + // |quad.p1()| corresponds to the top left of the original video, + // |quad.p2()| to the top right of the original video, + // and so on. + // Depending on the underlying hardware, the exact geometry + // might not be honored. + // |coordinate_type| indicates what coordinate type |quad| refers to. + virtual void SetGeometry(const gfx::QuadF& quad, + CoordinateType coordinate_type) = 0; + + // Should be invoked whenever screen resolution changes (e.g. when a device is + // plugged into a new HDMI port and a new HDMI EDID is received). + // VideoPlane should reposition itself according to the new screen resolution. + virtual void OnScreenResolutionChanged(const gfx::Size& screen_res) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(VideoPlane); +}; + +// Factory to create a VideoPlane. +scoped_ptr<VideoPlane> CreateVideoPlane(); + +// Global accessor to the video plane. +VideoPlane* GetVideoPlane(); + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_PLANE_H_ diff --git a/chromium/chromecast/media/cma/backend/video_plane_fake.cc b/chromium/chromecast/media/cma/backend/video_plane_fake.cc new file mode 100644 index 00000000000..73fa39ef5b6 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/video_plane_fake.cc @@ -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. + +#include "chromecast/media/cma/backend/video_plane_fake.h" + +#include "base/logging.h" +#include "ui/gfx/geometry/quad_f.h" +#include "ui/gfx/geometry/size.h" + +namespace chromecast { +namespace media { + +VideoPlaneFake::VideoPlaneFake() { +} + +VideoPlaneFake::~VideoPlaneFake() { +} + +gfx::Size VideoPlaneFake::GetScreenResolution() { + return gfx::Size(1920, 1080); +} + +void VideoPlaneFake::SetGeometry(const gfx::QuadF& quad, + CoordinateType coordinate_type) { + // Nothing to be done. +} + +void VideoPlaneFake::OnScreenResolutionChanged(const gfx::Size& screen_res) { +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/video_plane_fake.h b/chromium/chromecast/media/cma/backend/video_plane_fake.h new file mode 100644 index 00000000000..5fe04e6accb --- /dev/null +++ b/chromium/chromecast/media/cma/backend/video_plane_fake.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_VIDEO_PLANE_FAKE_H_ +#define CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_PLANE_FAKE_H_ + +#include "base/macros.h" +#include "chromecast/media/cma/backend/video_plane.h" + +namespace chromecast { +namespace media { + +class VideoPlaneFake : public VideoPlane { + public: + VideoPlaneFake(); + ~VideoPlaneFake() override; + + // VideoPlane implementation. + gfx::Size GetScreenResolution() override; + void SetGeometry(const gfx::QuadF& quad, + CoordinateType coordinate_type) override; + void OnScreenResolutionChanged(const gfx::Size& screen_res) override; + + DISALLOW_COPY_AND_ASSIGN(VideoPlaneFake); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_PLANE_FAKE_H_ diff --git a/chromium/chromecast/media/cma/backend/video_plane_fake_factory.cc b/chromium/chromecast/media/cma/backend/video_plane_fake_factory.cc new file mode 100644 index 00000000000..310c8bc878a --- /dev/null +++ b/chromium/chromecast/media/cma/backend/video_plane_fake_factory.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/cma/backend/video_plane_fake.h" + +namespace chromecast { +namespace media { + +scoped_ptr<VideoPlane> CreateVideoPlane() { + return make_scoped_ptr(new VideoPlaneFake()); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/base/BUILD.gn b/chromium/chromecast/media/cma/base/BUILD.gn new file mode 100644 index 00000000000..b919439cde6 --- /dev/null +++ b/chromium/chromecast/media/cma/base/BUILD.gn @@ -0,0 +1,35 @@ +# Copyright 2015 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. + +source_set("base") { + sources = [ + "balanced_media_task_runner_factory.cc", + "balanced_media_task_runner_factory.h", + "buffering_controller.cc", + "buffering_controller.h", + "buffering_defs.cc", + "buffering_defs.h", + "buffering_frame_provider.cc", + "buffering_frame_provider.h", + "buffering_state.cc", + "buffering_state.h", + "cma_logging.h", + "coded_frame_provider.cc", + "coded_frame_provider.h", + "decoder_buffer_adapter.cc", + "decoder_buffer_adapter.h", + "decoder_buffer_base.cc", + "decoder_buffer_base.h", + "media_task_runner.cc", + "media_task_runner.h", + ] + + deps = [ + "//base", + "//chromecast/base", + "//media", + ] + + configs += [ "//chromecast:config" ] +} 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 index 48413c0a9e2..f365534e457 100644 --- a/chromium/chromecast/media/cma/base/balanced_media_task_runner_factory.cc +++ b/chromium/chromecast/media/cma/base/balanced_media_task_runner_factory.cc @@ -32,13 +32,13 @@ class MediaTaskRunnerWithNotification : public MediaTaskRunner { const base::Closure& shutdown_cb); // MediaTaskRunner implementation. - virtual bool PostMediaTask( + bool PostMediaTask( const tracked_objects::Location& from_here, const base::Closure& task, base::TimeDelta timestamp) override; private: - virtual ~MediaTaskRunnerWithNotification(); + ~MediaTaskRunnerWithNotification() override; scoped_refptr<MediaTaskRunner> const media_task_runner_; @@ -93,13 +93,13 @@ class BalancedMediaTaskRunner base::TimeDelta GetMediaTimestamp() const; // MediaTaskRunner implementation. - virtual bool PostMediaTask( + bool PostMediaTask( const tracked_objects::Location& from_here, const base::Closure& task, base::TimeDelta timestamp) override; private: - virtual ~BalancedMediaTaskRunner(); + ~BalancedMediaTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> const task_runner_; 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 index e3448f67cac..b572e500247 100644 --- a/chromium/chromecast/media/cma/base/balanced_media_task_runner_unittest.cc +++ b/chromium/chromecast/media/cma/base/balanced_media_task_runner_unittest.cc @@ -45,7 +45,7 @@ MediaTaskRunnerTestContext::~MediaTaskRunnerTestContext() { class BalancedMediaTaskRunnerTest : public testing::Test { public: BalancedMediaTaskRunnerTest(); - virtual ~BalancedMediaTaskRunnerTest(); + ~BalancedMediaTaskRunnerTest() override; void SetupTest(base::TimeDelta max_delta, const std::vector<std::vector<int> >& timestamps_in_ms, diff --git a/chromium/chromecast/media/cma/base/buffering_controller.cc b/chromium/chromecast/media/cma/base/buffering_controller.cc index 183b12628d2..9e7cb6c9c01 100644 --- a/chromium/chromecast/media/cma/base/buffering_controller.cc +++ b/chromium/chromecast/media/cma/base/buffering_controller.cc @@ -63,11 +63,13 @@ void BufferingController::UpdateHighLevelThreshold( OnBufferingStateChanged(false, false); } -scoped_refptr<BufferingState> BufferingController::AddStream() { +scoped_refptr<BufferingState> BufferingController::AddStream( + const std::string& stream_id) { DCHECK(thread_checker_.CalledOnValidThread()); // Add a new stream to the list of streams being monitored. scoped_refptr<BufferingState> buffering_state(new BufferingState( + stream_id, config_, base::Bind(&BufferingController::OnBufferingStateChanged, weak_this_, false, false), @@ -195,7 +197,6 @@ bool BufferingController::IsLowBufferLevel() { } void BufferingController::DumpState() const { - CMALOG(kLogControl) << __FUNCTION__; for (StreamList::const_iterator it = stream_list_.begin(); it != stream_list_.end(); ++it) { CMALOG(kLogControl) << (*it)->ToString(); diff --git a/chromium/chromecast/media/cma/base/buffering_controller.h b/chromium/chromecast/media/cma/base/buffering_controller.h index bfc2c5cb9aa..b0ccecee1a1 100644 --- a/chromium/chromecast/media/cma/base/buffering_controller.h +++ b/chromium/chromecast/media/cma/base/buffering_controller.h @@ -6,6 +6,7 @@ #define CHROMECAST_MEDIA_CMA_BASE_BUFFERING_CONTROLLER_H #include <list> +#include <string> #include "base/callback.h" #include "base/macros.h" @@ -36,7 +37,7 @@ class 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(); + scoped_refptr<BufferingState> AddStream(const std::string& stream_id); // Sets the playback time. void SetMediaTime(base::TimeDelta time); diff --git a/chromium/chromecast/media/cma/base/buffering_controller_unittest.cc b/chromium/chromecast/media/cma/base/buffering_controller_unittest.cc index 75eaed9ce1a..e0b45c5b377 100644 --- a/chromium/chromecast/media/cma/base/buffering_controller_unittest.cc +++ b/chromium/chromecast/media/cma/base/buffering_controller_unittest.cc @@ -39,7 +39,7 @@ MockBufferingControllerClient::~MockBufferingControllerClient() { class BufferingControllerTest : public testing::Test { public: BufferingControllerTest(); - virtual ~BufferingControllerTest(); + ~BufferingControllerTest() override; protected: scoped_ptr<BufferingController> buffering_controller_; @@ -83,7 +83,7 @@ BufferingControllerTest::~BufferingControllerTest() { TEST_F(BufferingControllerTest, OneStream_Typical) { EXPECT_CALL(client_, OnBufferingNotification(true)).Times(1); scoped_refptr<BufferingState> buffering_state = - buffering_controller_->AddStream(); + buffering_controller_->AddStream("test"); buffering_state->SetMediaTime(base::TimeDelta()); // Simulate pre-buffering. @@ -121,7 +121,7 @@ TEST_F(BufferingControllerTest, OneStream_Typical) { TEST_F(BufferingControllerTest, OneStream_LeaveBufferingOnEos) { EXPECT_CALL(client_, OnBufferingNotification(true)).Times(1); scoped_refptr<BufferingState> buffering_state = - buffering_controller_->AddStream(); + buffering_controller_->AddStream("test"); buffering_state->SetMediaTime(base::TimeDelta()); EXPECT_CALL(client_, OnBufferingNotification(false)).Times(1); diff --git a/chromium/chromecast/media/cma/base/buffering_defs.cc b/chromium/chromecast/media/cma/base/buffering_defs.cc new file mode 100644 index 00000000000..3afd0f8087e --- /dev/null +++ b/chromium/chromecast/media/cma/base/buffering_defs.cc @@ -0,0 +1,14 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/cma/base/buffering_defs.h" + +namespace chromecast { +namespace media { + +const size_t kAppAudioBufferSize = 64 * 1024; +const size_t kAppVideoBufferSize = 4 * 1024 * 1024; + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/base/buffering_defs.h b/chromium/chromecast/media/cma/base/buffering_defs.h new file mode 100644 index 00000000000..a56a9a679e9 --- /dev/null +++ b/chromium/chromecast/media/cma/base/buffering_defs.h @@ -0,0 +1,19 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_BASE_BUFFERING_DEFS_H_ +#define CHROMECAST_MEDIA_CMA_BASE_BUFFERING_DEFS_H_ + +#include "base/basictypes.h" + +namespace chromecast { +namespace media { + +extern const size_t kAppAudioBufferSize; +extern const size_t kAppVideoBufferSize; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BASE_BUFFERING_DEFS_H_ diff --git a/chromium/chromecast/media/cma/base/buffering_frame_provider.h b/chromium/chromecast/media/cma/base/buffering_frame_provider.h index 0397676bca0..0cb99e2c591 100644 --- a/chromium/chromecast/media/cma/base/buffering_frame_provider.h +++ b/chromium/chromecast/media/cma/base/buffering_frame_provider.h @@ -40,11 +40,11 @@ class BufferingFrameProvider : public CodedFrameProvider { size_t max_buffer_size, size_t max_frame_size, const FrameBufferedCB& frame_buffered_cb); - virtual ~BufferingFrameProvider(); + ~BufferingFrameProvider() override; // CodedFrameProvider implementation. - virtual void Read(const ReadCB& read_cb) override; - virtual void Flush(const base::Closure& flush_cb) override; + void Read(const ReadCB& read_cb) override; + void Flush(const base::Closure& flush_cb) override; private: class BufferWithConfig { diff --git a/chromium/chromecast/media/cma/base/buffering_frame_provider_unittest.cc b/chromium/chromecast/media/cma/base/buffering_frame_provider_unittest.cc index c24862f4932..8e027365ef7 100644 --- a/chromium/chromecast/media/cma/base/buffering_frame_provider_unittest.cc +++ b/chromium/chromecast/media/cma/base/buffering_frame_provider_unittest.cc @@ -27,7 +27,7 @@ namespace media { class BufferingFrameProviderTest : public testing::Test { public: BufferingFrameProviderTest(); - virtual ~BufferingFrameProviderTest(); + ~BufferingFrameProviderTest() override; // Setup the test. void Configure( diff --git a/chromium/chromecast/media/cma/base/buffering_state.cc b/chromium/chromecast/media/cma/base/buffering_state.cc index e1fc49fe188..dec0cb42095 100644 --- a/chromium/chromecast/media/cma/base/buffering_state.cc +++ b/chromium/chromecast/media/cma/base/buffering_state.cc @@ -24,10 +24,12 @@ BufferingConfig::~BufferingConfig() { BufferingState::BufferingState( + const std::string& stream_id, const scoped_refptr<BufferingConfig>& config, const base::Closure& state_changed_cb, const HighLevelBufferCB& high_level_buffer_cb) - : config_(config), + : stream_id_(stream_id), + config_(config), state_changed_cb_(state_changed_cb), high_level_buffer_cb_(high_level_buffer_cb), state_(kLowLevel), @@ -94,7 +96,7 @@ void BufferingState::NotifyMaxCapacity(base::TimeDelta buffered_time) { std::string BufferingState::ToString() const { std::ostringstream s; - s << "state=" << state_ + s << stream_id_ << " state=" << state_ << " media_time_ms=" << media_time_.InMilliseconds() << " buffered_time_ms=" << buffered_time_.InMilliseconds() << " low_level_ms=" << config_->low_level().InMilliseconds() diff --git a/chromium/chromecast/media/cma/base/buffering_state.h b/chromium/chromecast/media/cma/base/buffering_state.h index ced8206d8a1..3e1fe5d811c 100644 --- a/chromium/chromecast/media/cma/base/buffering_state.h +++ b/chromium/chromecast/media/cma/base/buffering_state.h @@ -57,7 +57,8 @@ class BufferingState // |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, + BufferingState(const std::string& stream_id, + const scoped_refptr<BufferingConfig>& config, const base::Closure& state_changed_cb, const HighLevelBufferCB& high_level_buffer_cb); @@ -105,6 +106,7 @@ class BufferingState // Updates the state to |new_state|. void UpdateState(State new_state); + std::string const stream_id_; scoped_refptr<BufferingConfig> const config_; // Callback invoked each time there is a change of state. diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc index 236505b9950..45095da169b 100644 --- a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc +++ b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc @@ -29,7 +29,7 @@ uint8* DecoderBufferAdapter::writable_data() const { return buffer_->writable_data(); } -int DecoderBufferAdapter::data_size() const { +size_t DecoderBufferAdapter::data_size() const { return buffer_->data_size(); } diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h index 1967fbebd67..054e60ee59e 100644 --- a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h +++ b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h @@ -24,15 +24,15 @@ class DecoderBufferAdapter : public DecoderBufferBase { 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; + base::TimeDelta timestamp() const override; + const uint8* data() const override; + uint8* writable_data() const override; + size_t data_size() const override; + const ::media::DecryptConfig* decrypt_config() const override; + bool end_of_stream() const override; private: - virtual ~DecoderBufferAdapter(); + ~DecoderBufferAdapter() override; scoped_refptr< ::media::DecoderBuffer> const buffer_; diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_base.h b/chromium/chromecast/media/cma/base/decoder_buffer_base.h index 9143104f14a..099b44d7a9e 100644 --- a/chromium/chromecast/media/cma/base/decoder_buffer_base.h +++ b/chromium/chromecast/media/cma/base/decoder_buffer_base.h @@ -33,7 +33,7 @@ class DecoderBufferBase virtual uint8* writable_data() const = 0; // Returns the size of the frame in bytes. - virtual int data_size() const = 0; + virtual size_t data_size() const = 0; // Returns the decrypt configuration. // Returns NULL if the buffer has no decrypt info. diff --git a/chromium/chromecast/media/cma/base/decoder_config_adapter.cc b/chromium/chromecast/media/cma/base/decoder_config_adapter.cc new file mode 100644 index 00000000000..793395c9c14 --- /dev/null +++ b/chromium/chromecast/media/cma/base/decoder_config_adapter.cc @@ -0,0 +1,126 @@ +// Copyright 2015 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_config_adapter.h" + +#include "base/logging.h" +#include "media/base/channel_layout.h" + +namespace chromecast { +namespace media { + +namespace { + +// Converts ::media::AudioCodec to chromecast::media::AudioCodec. Any unknown or +// unsupported codec will be converted to chromecast::media::kCodecUnknown. +AudioCodec ToAudioCodec(const ::media::AudioCodec audio_codec) { + switch (audio_codec) { + case ::media::kCodecAAC: + return kCodecAAC; + case ::media::kCodecMP3: + return kCodecMP3; + case ::media::kCodecPCM: + return kCodecPCM; + case ::media::kCodecPCM_S16BE: + return kCodecPCM_S16BE; + case ::media::kCodecVorbis: + return kCodecVorbis; + default: + LOG(ERROR) << "Unsupported audio codec " << audio_codec; + } + return kAudioCodecUnknown; +} + +// Converts ::media::VideoCodec to chromecast::media::VideoCodec. Any unknown or +// unsupported codec will be converted to chromecast::media::kCodecUnknown. +VideoCodec ToVideoCodec(const ::media::VideoCodec video_codec) { + switch (video_codec) { + case ::media::kCodecH264: + return kCodecH264; + case ::media::kCodecVP8: + return kCodecVP8; + case ::media::kCodecVP9: + return kCodecVP9; + default: + LOG(ERROR) << "Unsupported video codec " << video_codec; + } + return kVideoCodecUnknown; +} + +// Converts ::media::VideoCodecProfile to chromecast::media::VideoProfile. +VideoProfile ToVideoProfile(const ::media::VideoCodecProfile codec_profile) { + switch(codec_profile) { + case ::media::H264PROFILE_BASELINE: + return kH264Baseline; + case ::media::H264PROFILE_MAIN: + return kH264Main; + case ::media::H264PROFILE_EXTENDED: + return kH264Extended; + case ::media::H264PROFILE_HIGH: + return kH264High; + case ::media::H264PROFILE_HIGH10PROFILE: + return kH264High10; + case ::media::H264PROFILE_HIGH422PROFILE: + return kH264High422; + case ::media::H264PROFILE_HIGH444PREDICTIVEPROFILE: + return kH264High444Predictive; + case ::media::H264PROFILE_SCALABLEBASELINE: + return kH264ScalableBaseline; + case ::media::H264PROFILE_SCALABLEHIGH: + return kH264ScalableHigh; + case ::media::H264PROFILE_STEREOHIGH: + return kH264Stereohigh; + case ::media::H264PROFILE_MULTIVIEWHIGH: + return kH264MultiviewHigh; + case ::media::VP8PROFILE_ANY: + return kVP8ProfileAny; + case ::media::VP9PROFILE_ANY: + return kVP9ProfileAny; + default: + LOG(INFO) << "Unsupported video codec profile " << codec_profile; + } + return kVideoProfileUnknown; +} + +} // namespace + +// static +AudioConfig DecoderConfigAdapter::ToCastAudioConfig( + const ::media::AudioDecoderConfig& config) { + AudioConfig audio_config; + if (!config.IsValidConfig()) { + return audio_config; + } + + audio_config.codec = ToAudioCodec(config.codec()); + audio_config.bytes_per_channel = config.bytes_per_channel(); + audio_config.channel_number = + ::media::ChannelLayoutToChannelCount(config.channel_layout()), + audio_config.samples_per_second = config.samples_per_second(); + audio_config.extra_data = (config.extra_data_size() > 0) ? + config.extra_data() : nullptr; + audio_config.extra_data_size = config.extra_data_size(); + audio_config.is_encrypted = config.is_encrypted(); + return audio_config; +} + +// static +VideoConfig DecoderConfigAdapter::ToCastVideoConfig( + const ::media::VideoDecoderConfig& config) { + VideoConfig video_config; + if (!config.IsValidConfig()) { + return video_config; + } + + video_config.codec = ToVideoCodec(config.codec()); + video_config.profile = ToVideoProfile(config.profile()); + video_config.extra_data = (config.extra_data_size() > 0) ? + config.extra_data() : nullptr; + video_config.extra_data_size = config.extra_data_size(); + video_config.is_encrypted = config.is_encrypted(); + return video_config; +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/base/decoder_config_adapter.h b/chromium/chromecast/media/cma/base/decoder_config_adapter.h new file mode 100644 index 00000000000..f04c1c2f1d3 --- /dev/null +++ b/chromium/chromecast/media/cma/base/decoder_config_adapter.h @@ -0,0 +1,29 @@ +// Copyright 2015 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_CONFIG_ADAPTER_H_ +#define CHROMECAST_MEDIA_CMA_BASE_DECODER_CONFIG_ADAPTER_H_ + +#include "chromecast/public/media/decoder_config.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/video_decoder_config.h" + +namespace chromecast { +namespace media { + +class DecoderConfigAdapter { + public: + // Converts ::media::AudioDecoderConfig to chromecast::media::AudioConfig. + static AudioConfig ToCastAudioConfig( + const ::media::AudioDecoderConfig& config); + + // Converts ::media::VideoDecoderConfig to chromecast::media::VideoConfig. + static VideoConfig ToCastVideoConfig( + const ::media::VideoDecoderConfig& config); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BASE_DECODER_CONFIG_ADAPTER_H_ diff --git a/chromium/chromecast/media/cma/filters/BUILD.gn b/chromium/chromecast/media/cma/filters/BUILD.gn new file mode 100644 index 00000000000..605075533f9 --- /dev/null +++ b/chromium/chromecast/media/cma/filters/BUILD.gn @@ -0,0 +1,20 @@ +# Copyright 2015 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. + +source_set("filters") { + sources = [ + "cma_renderer.cc", + "cma_renderer.h", + "demuxer_stream_adapter.cc", + "demuxer_stream_adapter.h", + ] + + deps = [ + "//base", + "//chromecast/media/cma/base", + "//media", + ] + + configs += [ "//chromecast:config" ] +} diff --git a/chromium/chromecast/media/cma/filters/cma_renderer.cc b/chromium/chromecast/media/cma/filters/cma_renderer.cc new file mode 100644 index 00000000000..b8e7b42ba9a --- /dev/null +++ b/chromium/chromecast/media/cma/filters/cma_renderer.cc @@ -0,0 +1,472 @@ +// 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/cma_renderer.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "chromecast/media/cma/base/balanced_media_task_runner_factory.h" +#include "chromecast/media/cma/base/cma_logging.h" +#include "chromecast/media/cma/filters/demuxer_stream_adapter.h" +#include "chromecast/media/cma/pipeline/audio_pipeline.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" +#include "chromecast/media/cma/pipeline/media_pipeline.h" +#include "chromecast/media/cma/pipeline/media_pipeline_client.h" +#include "chromecast/media/cma/pipeline/video_pipeline.h" +#include "chromecast/media/cma/pipeline/video_pipeline_client.h" +#include "media/base/bind_to_current_loop.h" +#include "media/base/demuxer_stream_provider.h" +#include "media/base/pipeline_status.h" +#include "media/base/time_delta_interpolator.h" +#include "media/base/video_frame.h" +#include "media/base/video_renderer_sink.h" +#include "ui/gfx/geometry/size.h" + +namespace chromecast { +namespace media { + +namespace { + +// Maximum difference between audio frame PTS and video frame PTS +// for frames read from the DemuxerStream. +const base::TimeDelta kMaxDeltaFetcher( + base::TimeDelta::FromMilliseconds(2000)); + +} // namespace + +CmaRenderer::CmaRenderer(scoped_ptr<MediaPipeline> media_pipeline, + ::media::VideoRendererSink* video_renderer_sink) + : media_task_runner_factory_( + new BalancedMediaTaskRunnerFactory(kMaxDeltaFetcher)), + media_pipeline_(media_pipeline.Pass()), + audio_pipeline_(media_pipeline_->GetAudioPipeline()), + video_pipeline_(media_pipeline_->GetVideoPipeline()), + video_renderer_sink_(video_renderer_sink), + state_(kUninitialized), + is_pending_transition_(false), + has_audio_(false), + has_video_(false), + received_audio_eos_(false), + received_video_eos_(false), + initial_natural_size_(gfx::Size()), + initial_video_hole_created_(false), + time_interpolator_( + new ::media::TimeDeltaInterpolator(&default_tick_clock_)), + playback_rate_(1.0), + weak_factory_(this) { + weak_this_ = weak_factory_.GetWeakPtr(); + thread_checker_.DetachFromThread(); + + time_interpolator_->SetUpperBound(base::TimeDelta()); +} + +CmaRenderer::~CmaRenderer() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!init_cb_.is_null()) + base::ResetAndReturn(&init_cb_).Run(::media::PIPELINE_ERROR_ABORT); + else if (!flush_cb_.is_null()) + base::ResetAndReturn(&flush_cb_).Run(); + + if (has_audio_ || has_video_) + media_pipeline_->Stop(); +} + +void CmaRenderer::Initialize( + ::media::DemuxerStreamProvider* demuxer_stream_provider, + const ::media::PipelineStatusCB& init_cb, + const ::media::StatisticsCB& statistics_cb, + const ::media::BufferingStateCB& buffering_state_cb, + const base::Closure& ended_cb, + const ::media::PipelineStatusCB& error_cb, + const base::Closure& waiting_for_decryption_key_cb) { + CMALOG(kLogControl) << __FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(state_, kUninitialized) << state_; + DCHECK(!init_cb.is_null()); + DCHECK(!statistics_cb.is_null()); + DCHECK(!ended_cb.is_null()); + DCHECK(!error_cb.is_null()); + DCHECK(!buffering_state_cb.is_null()); + DCHECK(!waiting_for_decryption_key_cb.is_null()); + DCHECK(demuxer_stream_provider->GetStream(::media::DemuxerStream::AUDIO) || + demuxer_stream_provider->GetStream(::media::DemuxerStream::VIDEO)); + + BeginStateTransition(); + + demuxer_stream_provider_ = demuxer_stream_provider; + statistics_cb_ = statistics_cb; + buffering_state_cb_ = buffering_state_cb; + ended_cb_ = ended_cb; + error_cb_ = error_cb; + // TODO(erickung): wire up waiting_for_decryption_key_cb. + waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb; + + MediaPipelineClient media_pipeline_client; + media_pipeline_client.error_cb = error_cb_; + media_pipeline_client.buffering_state_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnBufferingNotification, weak_this_)); + media_pipeline_client.time_update_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnPlaybackTimeUpdated, weak_this_)); + media_pipeline_->SetClient(media_pipeline_client); + + init_cb_ = init_cb; + InitializeAudioPipeline(); +} + +void CmaRenderer::Flush(const base::Closure& flush_cb) { + CMALOG(kLogControl) << __FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); + BeginStateTransition(); + + DCHECK(flush_cb_.is_null()); + flush_cb_ = flush_cb; + + if (state_ == kError) { + OnError(::media::PIPELINE_ERROR_ABORT); + return; + } + + DCHECK_EQ(state_, kPlaying) << state_; + media_pipeline_->Flush( + ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnFlushDone, weak_this_))); + + { + base::AutoLock auto_lock(time_interpolator_lock_); + time_interpolator_->StopInterpolating(); + } +} + +void CmaRenderer::StartPlayingFrom(base::TimeDelta time) { + CMALOG(kLogControl) << __FUNCTION__ << ": " << time.InMilliseconds(); + DCHECK(thread_checker_.CalledOnValidThread()); + BeginStateTransition(); + + if (state_ == kError) { + error_cb_.Run(::media::PIPELINE_ERROR_ABORT); + CompleteStateTransition(kError); + return; + } + +#if defined(VIDEO_HOLE) + // Create a video hole frame just before starting playback. + // Note that instead of creating the video hole frame in Initialize(), we do + // it here because paint_cb_ (which eventually calls OnOpacityChanged) + // expects the current state to not be HaveNothing. And the place where + // the ready state is changed to HaveMetadata (OnPipelineMetadata) is + // right before the pipeline calls StartPlayingFrom (in + // Pipeline::StateTransitionTask). + if (!initial_video_hole_created_) { + initial_video_hole_created_ = true; + video_renderer_sink_->PaintFrameUsingOldRenderingPath( + ::media::VideoFrame::CreateHoleFrame(initial_natural_size_)); + } +#endif + + { + base::AutoLock auto_lock(time_interpolator_lock_); + time_interpolator_.reset( + new ::media::TimeDeltaInterpolator(&default_tick_clock_)); + time_interpolator_->SetPlaybackRate(playback_rate_); + time_interpolator_->SetBounds(time, time); + time_interpolator_->StartInterpolating(); + } + + received_audio_eos_ = false; + received_video_eos_ = false; + + DCHECK_EQ(state_, kFlushed) << state_; + // Immediately update transition to playing. + // Error case will be handled on response from host. + media_pipeline_->StartPlayingFrom(time); + CompleteStateTransition(kPlaying); +} + +void CmaRenderer::SetPlaybackRate(double playback_rate) { + CMALOG(kLogControl) << __FUNCTION__ << ": " << playback_rate; + DCHECK(thread_checker_.CalledOnValidThread()); + media_pipeline_->SetPlaybackRate(playback_rate); + playback_rate_ = playback_rate; + + { + base::AutoLock auto_lock(time_interpolator_lock_); + time_interpolator_->SetPlaybackRate(playback_rate); + } +} + +void CmaRenderer::SetVolume(float volume) { + CMALOG(kLogControl) << __FUNCTION__ << ": " << volume; + DCHECK(thread_checker_.CalledOnValidThread()); + audio_pipeline_->SetVolume(volume); +} + +base::TimeDelta CmaRenderer::GetMediaTime() { + base::AutoLock auto_lock(time_interpolator_lock_); + return time_interpolator_->GetInterpolatedTime(); +} + +bool CmaRenderer::HasAudio() { + DCHECK(thread_checker_.CalledOnValidThread()); + return has_audio_; +} + +bool CmaRenderer::HasVideo() { + DCHECK(thread_checker_.CalledOnValidThread()); + return has_video_; +} + +void CmaRenderer::SetCdm(::media::CdmContext* cdm_context, + const ::media::CdmAttachedCB& cdm_attached_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); +#if defined(ENABLE_BROWSER_CDMS) + media_pipeline_->SetCdm(cdm_context->GetCdmId()); +#endif + cdm_attached_cb.Run(true); +} + +void CmaRenderer::InitializeAudioPipeline() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(state_, kUninitialized) << state_; + DCHECK(!init_cb_.is_null()); + + ::media::PipelineStatusCB audio_initialization_done_cb = + ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnAudioPipelineInitializeDone, weak_this_)); + + ::media::DemuxerStream* stream = + demuxer_stream_provider_->GetStream(::media::DemuxerStream::AUDIO); + if (!stream) { + audio_initialization_done_cb.Run(::media::PIPELINE_OK); + return; + } + + // Receive events from the audio pipeline. + AvPipelineClient av_pipeline_client; + av_pipeline_client.eos_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnEosReached, weak_this_, true)); + av_pipeline_client.playback_error_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnError, weak_this_)); + av_pipeline_client.statistics_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnStatisticsUpdated, weak_this_)); + audio_pipeline_->SetClient(av_pipeline_client); + + scoped_ptr<CodedFrameProvider> frame_provider( + new DemuxerStreamAdapter( + base::MessageLoopProxy::current(), + media_task_runner_factory_, + stream)); + + const ::media::AudioDecoderConfig& config = stream->audio_decoder_config(); + if (config.codec() == ::media::kCodecAAC) + stream->EnableBitstreamConverter(); + + media_pipeline_->InitializeAudio( + config, frame_provider.Pass(), audio_initialization_done_cb); +} + +void CmaRenderer::OnAudioPipelineInitializeDone( + ::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // OnError() may be fired at any time, even before initialization is complete. + if (state_ == kError) + return; + + DCHECK_EQ(state_, kUninitialized) << state_; + DCHECK(!init_cb_.is_null()); + if (status != ::media::PIPELINE_OK) { + base::ResetAndReturn(&init_cb_).Run(status); + return; + } + has_audio_ = true; + + InitializeVideoPipeline(); +} + +void CmaRenderer::InitializeVideoPipeline() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(state_, kUninitialized) << state_; + DCHECK(!init_cb_.is_null()); + + ::media::PipelineStatusCB video_initialization_done_cb = + ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnVideoPipelineInitializeDone, weak_this_)); + + ::media::DemuxerStream* stream = + demuxer_stream_provider_->GetStream(::media::DemuxerStream::VIDEO); + if (!stream) { + video_initialization_done_cb.Run(::media::PIPELINE_OK); + return; + } + + // Receive events from the video pipeline. + VideoPipelineClient client; + client.av_pipeline_client.eos_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnEosReached, weak_this_, false)); + client.av_pipeline_client.playback_error_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnError, weak_this_)); + client.av_pipeline_client.statistics_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnStatisticsUpdated, weak_this_)); + client.natural_size_changed_cb = ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnNaturalSizeChanged, weak_this_)); + video_pipeline_->SetClient(client); + + scoped_ptr<CodedFrameProvider> frame_provider( + new DemuxerStreamAdapter( + base::MessageLoopProxy::current(), + media_task_runner_factory_, + stream)); + + const ::media::VideoDecoderConfig& config = stream->video_decoder_config(); + if (config.codec() == ::media::kCodecH264) + stream->EnableBitstreamConverter(); + + initial_natural_size_ = config.natural_size(); + + media_pipeline_->InitializeVideo( + config, + frame_provider.Pass(), + video_initialization_done_cb); +} + +void CmaRenderer::OnVideoPipelineInitializeDone( + ::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // OnError() may be fired at any time, even before initialization is complete. + if (state_ == kError) + return; + + DCHECK_EQ(state_, kUninitialized) << state_; + DCHECK(!init_cb_.is_null()); + if (status != ::media::PIPELINE_OK) { + base::ResetAndReturn(&init_cb_).Run(status); + return; + } + has_video_ = true; + + CompleteStateTransition(kFlushed); + base::ResetAndReturn(&init_cb_).Run(::media::PIPELINE_OK); +} + +void CmaRenderer::OnEosReached(bool is_audio) { + DCHECK(thread_checker_.CalledOnValidThread()); + CMALOG(kLogControl) << __FUNCTION__; + if (state_ != kPlaying) { + LOG(WARNING) << "Ignoring a late EOS event"; + return; + } + + if (is_audio) { + DCHECK(!received_audio_eos_); + received_audio_eos_ = true; + } else { + DCHECK(!received_video_eos_); + received_video_eos_ = true; + } + + bool audio_finished = !has_audio_ || received_audio_eos_; + bool video_finished = !has_video_ || received_video_eos_; + if (audio_finished && video_finished) + ended_cb_.Run(); +} + +void CmaRenderer::OnStatisticsUpdated( + const ::media::PipelineStatistics& stats) { + DCHECK(thread_checker_.CalledOnValidThread()); + statistics_cb_.Run(stats); +} + +void CmaRenderer::OnNaturalSizeChanged(const gfx::Size& size) { + DCHECK(thread_checker_.CalledOnValidThread()); +#if defined(VIDEO_HOLE) + video_renderer_sink_->PaintFrameUsingOldRenderingPath( + ::media::VideoFrame::CreateHoleFrame(size)); +#endif +} + +void CmaRenderer::OnPlaybackTimeUpdated( + base::TimeDelta time, + base::TimeDelta max_time, + base::TimeTicks capture_time) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (state_ != kPlaying) { + LOG(WARNING) << "Ignoring a late time update"; + return; + } + + // TODO(halliwell): arguably, TimeDeltaInterpolator::SetBounds should perform + // this calculation to avoid calling TimeTicks::Now twice (it's slower and has + // potential accuracy problems). + base::TimeDelta lower_bound = + std::min(max_time, time + base::TimeTicks::Now() - capture_time); + + base::AutoLock auto_lock(time_interpolator_lock_); + time_interpolator_->SetBounds(lower_bound, max_time); +} + +void CmaRenderer::OnBufferingNotification( + ::media::BufferingState buffering_state) { + CMALOG(kLogControl) << __FUNCTION__ << ": state=" << state_ + << ", buffering=" << buffering_state; + // TODO(gunsch): WebMediaPlayerImpl currently only handles HAVE_ENOUGH while + // playing. See OnPipelineBufferingStateChanged, http://crbug.com/144683. + if (state_ != kPlaying) { + LOG(WARNING) << "Ignoring buffering notification in state: " << state_; + return; + } + if (buffering_state != ::media::BUFFERING_HAVE_ENOUGH) { + LOG(WARNING) << "Ignoring buffering notification during playing: " + << buffering_state; + return; + } + buffering_state_cb_.Run(buffering_state); +} + +void CmaRenderer::OnFlushDone(::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (status != ::media::PIPELINE_OK) { + OnError(status); + return; + } + + CompleteStateTransition(kFlushed); + base::ResetAndReturn(&flush_cb_).Run(); +} + +void CmaRenderer::OnError(::media::PipelineStatus error) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_NE(::media::PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; + LOG(ERROR) << "CMA error encountered: " << error; + + State old_state = state_; + CompleteStateTransition(kError); + + if (old_state != kError) { + if (!init_cb_.is_null()) { + base::ResetAndReturn(&init_cb_).Run(error); + return; + } + error_cb_.Run(error); + } + + // After OnError() returns, the pipeline may destroy |this|. + if (!flush_cb_.is_null()) + base::ResetAndReturn(&flush_cb_).Run(); +} + +void CmaRenderer::BeginStateTransition() { + DCHECK(!is_pending_transition_) << state_; + is_pending_transition_ = true; +} + +void CmaRenderer::CompleteStateTransition(State new_state) { + state_ = new_state; + is_pending_transition_ = false; +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/filters/cma_renderer.h b/chromium/chromecast/media/cma/filters/cma_renderer.h new file mode 100644 index 00000000000..d33699ea586 --- /dev/null +++ b/chromium/chromecast/media/cma/filters/cma_renderer.h @@ -0,0 +1,151 @@ +// 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_CMA_RENDERER_H_ +#define CHROMECAST_MEDIA_CMA_FILTERS_CMA_RENDERER_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/synchronization/lock.h" +#include "base/threading/thread_checker.h" +#include "base/time/default_tick_clock.h" +#include "media/base/buffering_state.h" +#include "media/base/renderer.h" +#include "ui/gfx/geometry/size.h" + +namespace base { +class MessageLoopProxy; +} + +namespace media { +class DemuxerStreamProvider; +class TimeDeltaInterpolator; +class VideoFrame; +class VideoRendererSink; +} + +namespace chromecast { +namespace media { +class AudioPipeline; +class BalancedMediaTaskRunnerFactory; +class MediaPipeline; +class VideoPipeline; + +class CmaRenderer : public ::media::Renderer { + public: + CmaRenderer(scoped_ptr<MediaPipeline> media_pipeline, + ::media::VideoRendererSink* video_renderer_sink); + ~CmaRenderer() override; + + // ::media::Renderer implementation: + void Initialize( + ::media::DemuxerStreamProvider* demuxer_stream_provider, + const ::media::PipelineStatusCB& init_cb, + const ::media::StatisticsCB& statistics_cb, + const ::media::BufferingStateCB& buffering_state_cb, + const base::Closure& ended_cb, + const ::media::PipelineStatusCB& error_cb, + const base::Closure& waiting_for_decryption_key_cb) override; + void Flush(const base::Closure& flush_cb) override; + void StartPlayingFrom(base::TimeDelta time) override; + void SetPlaybackRate(double playback_rate) override; + void SetVolume(float volume) override; + base::TimeDelta GetMediaTime() override; + bool HasAudio() override; + bool HasVideo() override; + void SetCdm(::media::CdmContext* cdm_context, + const ::media::CdmAttachedCB& cdm_attached_cb) override; + + private: + enum State { + kUninitialized, + kPlaying, + kFlushed, + kError, + }; + + // Asynchronous initialization sequence. These four methods are invoked in + // the order below, with a successful initialization making it to + // OnVideoPipelineInitializeDone, regardless of which streams are present. + void InitializeAudioPipeline(); + void OnAudioPipelineInitializeDone(::media::PipelineStatus status); + void InitializeVideoPipeline(); + void OnVideoPipelineInitializeDone(::media::PipelineStatus status); + + // Callbacks for AvPipelineClient. + void OnEosReached(bool is_audio); + void OnStatisticsUpdated(const ::media::PipelineStatistics& stats); + void OnNaturalSizeChanged(const gfx::Size& size); + + // Callbacks for MediaPipelineClient. + void OnPlaybackTimeUpdated(base::TimeDelta time, + base::TimeDelta max_time, + base::TimeTicks capture_time); + void OnBufferingNotification(::media::BufferingState state); + + void OnFlushDone(::media::PipelineStatus status); + void OnError(::media::PipelineStatus status); + + // Begin a state transition. + // Return true if delayed because of a pending state transition. + void BeginStateTransition(); + void CompleteStateTransition(State new_state); + + base::ThreadChecker thread_checker_; + + scoped_refptr<BalancedMediaTaskRunnerFactory> media_task_runner_factory_; + scoped_ptr<MediaPipeline> media_pipeline_; + AudioPipeline* audio_pipeline_; + VideoPipeline* video_pipeline_; + ::media::VideoRendererSink* video_renderer_sink_; + + ::media::DemuxerStreamProvider* demuxer_stream_provider_; + + // Set of callbacks. + ::media::PipelineStatusCB init_cb_; + ::media::StatisticsCB statistics_cb_; + base::Closure ended_cb_; + ::media::PipelineStatusCB error_cb_; + ::media::BufferingStateCB buffering_state_cb_; + base::Closure flush_cb_; + base::Closure waiting_for_decryption_key_cb_; + + // Renderer state. + // Used mostly for checking that transitions are correct. + State state_; + bool is_pending_transition_; + + bool has_audio_; + bool has_video_; + + bool received_audio_eos_; + bool received_video_eos_; + + // Data members for helping the creation of the initial video hole frame. + gfx::Size initial_natural_size_; + bool initial_video_hole_created_; + + // Lock protecting access to |time_interpolator_|. + base::Lock time_interpolator_lock_; + + // base::TickClock used by |time_interpolator_|. + base::DefaultTickClock default_tick_clock_; + + // Tracks the most recent media time update and provides interpolated values + // as playback progresses. + scoped_ptr< ::media::TimeDeltaInterpolator> time_interpolator_; + + double playback_rate_; + + base::WeakPtr<CmaRenderer> weak_this_; + base::WeakPtrFactory<CmaRenderer> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(CmaRenderer); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_FILTERS_CMA_RENDERER_H_ diff --git a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc index db0d7b70ab2..c2fd5a47861 100644 --- a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc +++ b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc @@ -27,13 +27,13 @@ class DummyMediaTaskRunner : public MediaTaskRunner { const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); // MediaTaskRunner implementation. - virtual bool PostMediaTask( + bool PostMediaTask( const tracked_objects::Location& from_here, const base::Closure& task, base::TimeDelta timestamp) override; private: - virtual ~DummyMediaTaskRunner(); + ~DummyMediaTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> const task_runner_; @@ -183,6 +183,13 @@ void DemuxerStreamAdapter::OnNewBuffer( DCHECK_EQ(status, ::media::DemuxerStream::kOk); + if (input->end_of_stream()) { + // This stream has ended, its media time will stop increasing, but there + // might be other streams that are still playing. Remove the task runner of + // this stream to ensure other streams are not blocked waiting for this one. + ResetMediaTaskRunner(); + } + // Updates the timestamp used for task scheduling. if (!input->end_of_stream() && input->timestamp() != ::media::kNoTimestamp() && diff --git a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.h b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.h index 7d8c8b33702..5e8d817b76c 100644 --- a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.h +++ b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.h @@ -36,11 +36,11 @@ class DemuxerStreamAdapter : public CodedFrameProvider { const scoped_refptr<BalancedMediaTaskRunnerFactory>& media_task_runner_factory, ::media::DemuxerStream* demuxer_stream); - virtual ~DemuxerStreamAdapter(); + ~DemuxerStreamAdapter() override; // CodedFrameProvider implementation. - virtual void Read(const ReadCB& read_cb) override; - virtual void Flush(const base::Closure& flush_cb) override; + void Read(const ReadCB& read_cb) override; + void Flush(const base::Closure& flush_cb) override; private: void ResetMediaTaskRunner(); diff --git a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc index bee63eb4d45..084acd7e3a4 100644 --- a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc +++ b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc @@ -41,15 +41,15 @@ class DummyDemuxerStream : public ::media::DemuxerStream { DummyDemuxerStream(int cycle_count, int delayed_frame_count, const std::list<int>& config_idx); - virtual ~DummyDemuxerStream(); + ~DummyDemuxerStream() override; // ::media::DemuxerStream implementation. - virtual void Read(const ReadCB& read_cb) override; - virtual ::media::AudioDecoderConfig audio_decoder_config() override; - virtual ::media::VideoDecoderConfig video_decoder_config() override; - virtual Type type() override; - virtual bool SupportsConfigChanges() override; - virtual ::media::VideoRotation video_rotation() override; + void Read(const ReadCB& read_cb) override; + ::media::AudioDecoderConfig audio_decoder_config() override; + ::media::VideoDecoderConfig video_decoder_config() override; + Type type() const override; + bool SupportsConfigChanges() override; + ::media::VideoRotation video_rotation() override; bool has_pending_read() const { return has_pending_read_; @@ -127,7 +127,7 @@ void DummyDemuxerStream::Read(const ReadCB& read_cb) { false); } -::media::DemuxerStream::Type DummyDemuxerStream::type() { +::media::DemuxerStream::Type DummyDemuxerStream::type() const { return VIDEO; } @@ -153,7 +153,7 @@ void DummyDemuxerStream::DoRead(const ReadCB& read_cb) { class DemuxerStreamAdapterTest : public testing::Test { public: DemuxerStreamAdapterTest(); - virtual ~DemuxerStreamAdapterTest(); + ~DemuxerStreamAdapterTest() override; void Initialize(::media::DemuxerStream* demuxer_stream); void Start(); @@ -232,7 +232,7 @@ void DemuxerStreamAdapterTest::OnNewFrame( const ::media::AudioDecoderConfig& audio_config, const ::media::VideoDecoderConfig& video_config) { if (video_config.IsValidConfig()) { - ASSERT_GT(config_idx_.size(), 0); + ASSERT_GT(config_idx_.size(), 0u); ASSERT_EQ(frame_received_count_, config_idx_.front()); config_idx_.pop_front(); } diff --git a/chromium/chromecast/media/cma/ipc/BUILD.gn b/chromium/chromecast/media/cma/ipc/BUILD.gn new file mode 100644 index 00000000000..037b3bdd6a7 --- /dev/null +++ b/chromium/chromecast/media/cma/ipc/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2015 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. + +source_set("ipc") { + sources = [ + "media_memory_chunk.cc", + "media_memory_chunk.h", + "media_message.cc", + "media_message.h", + "media_message_fifo.cc", + "media_message_fifo.h", + "media_message_type.h", + ] + + deps = [ + "//base", + ] + + configs += [ "//chromecast:config" ] +} diff --git a/chromium/chromecast/media/cma/ipc/media_message_fifo.cc b/chromium/chromecast/media/cma/ipc/media_message_fifo.cc index 9f8ad001dc8..5e4b239bc5d 100644 --- a/chromium/chromecast/media/cma/ipc/media_message_fifo.cc +++ b/chromium/chromecast/media/cma/ipc/media_message_fifo.cc @@ -60,12 +60,12 @@ class FifoOwnedMemory : public MediaMemoryChunk { FifoOwnedMemory(void* data, size_t size, const scoped_refptr<MediaMessageFlag>& flag, const base::Closure& release_msg_cb); - virtual ~FifoOwnedMemory(); + ~FifoOwnedMemory() override; // 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(); } + void* data() const override { return data_; } + size_t size() const override { return size_; } + bool valid() const override { return flag_->IsValid(); } private: scoped_refptr<base::SingleThreadTaskRunner> task_runner_; @@ -133,7 +133,7 @@ MediaMessageFifo::MediaMessageFifo( } CMALOG(kLogControl) << "MediaMessageFifo:" << " init=" << init << " size=" << size_; - CHECK_GT(size_, 0) << size_; + CHECK_GT(size_, 0u) << size_; weak_this_ = weak_factory_.GetWeakPtr(); thread_checker_.DetachFromThread(); diff --git a/chromium/chromecast/media/cma/ipc/media_message_fifo.h b/chromium/chromecast/media/cma/ipc/media_message_fifo.h index dcc60dbd60b..b20432b5b1f 100644 --- a/chromium/chromecast/media/cma/ipc/media_message_fifo.h +++ b/chromium/chromecast/media/cma/ipc/media_message_fifo.h @@ -82,7 +82,19 @@ class MediaMessageFlag; // // class MediaMessageFifo { + 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; + }; + public: + static const int kDescriptorSize = sizeof(Descriptor); + // Creates a media message fifo using |mem| as the underlying serialized // structure. // If |init| is true, the underlying fifo structure is initialized. @@ -108,15 +120,6 @@ class MediaMessageFifo { 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; @@ -174,7 +177,7 @@ class MediaMessageFifo { typedef base::subtle::Atomic32 AtomicSize; #elif SIZE_MAX == UINT64_MAX typedef base::subtle::Atomic64 AtomicSize; -#elif +#else #error "Unsupported size_t" #endif AtomicSize* rd_offset_; diff --git a/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc b/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc index bc04b70a0cc..ed758182ce4 100644 --- a/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc +++ b/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc @@ -22,11 +22,11 @@ class FifoMemoryChunk : public MediaMemoryChunk { public: FifoMemoryChunk(void* mem, size_t size) : mem_(mem), size_(size) {} - virtual ~FifoMemoryChunk() {} + ~FifoMemoryChunk() override {} - virtual void* data() const override { return mem_; } - virtual size_t size() const override { return size_; } - virtual bool valid() const override { return true; } + void* data() const override { return mem_; } + size_t size() const override { return size_; } + bool valid() const override { return true; } private: void* mem_; diff --git a/chromium/chromecast/media/cma/ipc/media_message_unittest.cc b/chromium/chromecast/media/cma/ipc/media_message_unittest.cc index 6f34f3eaf89..cec0712cfaa 100644 --- a/chromium/chromecast/media/cma/ipc/media_message_unittest.cc +++ b/chromium/chromecast/media/cma/ipc/media_message_unittest.cc @@ -19,12 +19,12 @@ class ExternalMemoryBlock public: ExternalMemoryBlock(void* data, size_t size) : data_(data), size_(size) {} - virtual ~ExternalMemoryBlock() {} + ~ExternalMemoryBlock() override {} // MediaMemoryChunk implementation. - virtual void* data() const override { return data_; } - virtual size_t size() const override { return size_; } - virtual bool valid() const override { return true; } + void* data() const override { return data_; } + size_t size() const override { return size_; } + bool valid() const override { return true; } private: void* const data_; diff --git a/chromium/chromecast/media/cma/ipc_streamer/BUILD.gn b/chromium/chromecast/media/cma/ipc_streamer/BUILD.gn new file mode 100644 index 00000000000..aa87709333e --- /dev/null +++ b/chromium/chromecast/media/cma/ipc_streamer/BUILD.gn @@ -0,0 +1,28 @@ +# Copyright 2015 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. + +source_set("ipc_streamer") { + sources = [ + "audio_decoder_config_marshaller.cc", + "audio_decoder_config_marshaller.h", + "av_streamer_proxy.cc", + "av_streamer_proxy.h", + "coded_frame_provider_host.cc", + "coded_frame_provider_host.h", + "decoder_buffer_base_marshaller.cc", + "decoder_buffer_base_marshaller.h", + "decrypt_config_marshaller.cc", + "decrypt_config_marshaller.h", + "video_decoder_config_marshaller.cc", + "video_decoder_config_marshaller.h", + ] + + deps = [ + "//base", + "//chromecast/media/cma/base", + "//media", + ] + + configs += [ "//chromecast:config" ] +} diff --git a/chromium/chromecast/media/cma/ipc_streamer/av_streamer_unittest.cc b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_unittest.cc index 54a9e14ba07..c8720561c1a 100644 --- a/chromium/chromecast/media/cma/ipc_streamer/av_streamer_unittest.cc +++ b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_unittest.cc @@ -33,11 +33,11 @@ class FifoMemoryChunk : public MediaMemoryChunk { public: FifoMemoryChunk(void* mem, size_t size) : mem_(mem), size_(size) {} - virtual ~FifoMemoryChunk() {} + ~FifoMemoryChunk() override {} - virtual void* data() const override { return mem_; } - virtual size_t size() const override { return size_; } - virtual bool valid() const override { return true; } + void* data() const override { return mem_; } + size_t size() const override { return size_; } + bool valid() const override { return true; } private: void* mem_; @@ -51,7 +51,7 @@ class FifoMemoryChunk : public MediaMemoryChunk { class AvStreamerTest : public testing::Test { public: AvStreamerTest(); - virtual ~AvStreamerTest(); + ~AvStreamerTest() override; // Setups the test. void Configure( 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 index 9dc9471bc5b..738cc8bd769 100644 --- a/chromium/chromecast/media/cma/ipc_streamer/coded_frame_provider_host.h +++ b/chromium/chromecast/media/cma/ipc_streamer/coded_frame_provider_host.h @@ -25,11 +25,11 @@ class CodedFrameProviderHost : public CodedFrameProvider { // during the whole lifetime of this object. explicit CodedFrameProviderHost( scoped_ptr<MediaMessageFifo> media_message_fifo); - virtual ~CodedFrameProviderHost(); + ~CodedFrameProviderHost() override; // CodedFrameProvider implementation. - virtual void Read(const ReadCB& read_cb) override; - virtual void Flush(const base::Closure& flush_cb) override; + void Read(const ReadCB& read_cb) override; + void Flush(const base::Closure& flush_cb) override; // Invoked when some data has been written into the fifo. void OnFifoWriteEvent(); 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 index dc1f8f5714e..a377ae66337 100644 --- a/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.cc +++ b/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.cc @@ -24,15 +24,15 @@ class DecoderBufferFromMsg : public DecoderBufferBase { 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; + base::TimeDelta timestamp() const override; + const uint8* data() const override; + uint8* writable_data() const override; + size_t data_size() const override; + const ::media::DecryptConfig* decrypt_config() const override; + bool end_of_stream() const override; private: - virtual ~DecoderBufferFromMsg(); + ~DecoderBufferFromMsg() override; // Indicates whether this is an end of stream frame. bool is_eos_; @@ -44,7 +44,7 @@ class DecoderBufferFromMsg : public DecoderBufferBase { scoped_ptr< ::media::DecryptConfig> decrypt_config_; // Size of the frame. - int data_size_; + size_t data_size_; // Keeps the message since frame data is not copied. scoped_ptr<MediaMessage> msg_; @@ -55,8 +55,8 @@ class DecoderBufferFromMsg : public DecoderBufferBase { DecoderBufferFromMsg::DecoderBufferFromMsg( scoped_ptr<MediaMessage> msg) - : msg_(msg.Pass()), - is_eos_(true), + : is_eos_(true), + msg_(msg.Pass()), data_(NULL) { CHECK(msg_); } @@ -81,7 +81,7 @@ void DecoderBufferFromMsg::Initialize() { decrypt_config_.reset(DecryptConfigMarshaller::Read(msg_.get()).release()); CHECK(msg_->ReadPod(&data_size_)); - CHECK_GT(data_size_, 0); + CHECK_GT(data_size_, 0u); CHECK_LT(data_size_, kMaxFrameSize); // Get a pointer to the frame data inside the message. @@ -113,7 +113,7 @@ uint8* DecoderBufferFromMsg::writable_data() const { return data_; } -int DecoderBufferFromMsg::data_size() const { +size_t DecoderBufferFromMsg::data_size() const { return data_size_; } diff --git a/chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.cc b/chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.cc index 9e3de954ccb..72eacd6e9ce 100644 --- a/chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.cc +++ b/chromium/chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.cc @@ -20,9 +20,9 @@ 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_GT(config.key_id().size(), 0u); + CHECK_GT(config.iv().size(), 0u); + CHECK_GT(config.subsamples().size(), 0u); CHECK(msg->WritePod(config.key_id().size())); CHECK(msg->WriteBuffer(config.key_id().data(), config.key_id().size())); @@ -40,21 +40,21 @@ 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_GT(key_id_size, 0u); 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_GT(iv_size, 0u); 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_GT(subsample_count, 0u); CHECK_LT(subsample_count, kMaxSubsampleCount); std::vector< ::media::SubsampleEntry> subsamples(subsample_count); for (size_t k = 0; k < subsample_count; k++) { 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 index 645056ce22d..d6ec43e23b0 100644 --- a/chromium/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.cc +++ b/chromium/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.cc @@ -8,8 +8,8 @@ #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" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" namespace chromecast { namespace media { diff --git a/chromium/chromecast/media/cma/pipeline/BUILD.gn b/chromium/chromecast/media/cma/pipeline/BUILD.gn new file mode 100644 index 00000000000..f82a8d9a3f6 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/BUILD.gn @@ -0,0 +1,44 @@ +# Copyright 2015 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. + +source_set("pipeline") { + sources = [ + "audio_pipeline.cc", + "audio_pipeline.h", + "audio_pipeline_impl.cc", + "audio_pipeline_impl.h", + "av_pipeline_client.cc", + "av_pipeline_client.h", + "av_pipeline_impl.cc", + "av_pipeline_impl.h", + "decrypt_util.cc", + "decrypt_util.h", + "load_type.h", + "media_pipeline.h", + "media_pipeline_client.cc", + "media_pipeline_client.h", + "media_pipeline_impl.cc", + "media_pipeline_impl.h", + "video_pipeline.cc", + "video_pipeline.h", + "video_pipeline_client.cc", + "video_pipeline_client.h", + "video_pipeline_impl.cc", + "video_pipeline_impl.h", + ] + + deps = [ + "//base", + "//chromecast/media/cma/backend", + "//chromecast/media/cma/base", + "//chromecast/media/base", + "//chromecast/media/cdm", + "//crypto", + "//crypto:platform", + "//media", + "//third_party/boringssl", + ] + + configs += [ "//chromecast:config" ] +} diff --git a/chromium/chromecast/browser/webui/webui_cast_simple.cc b/chromium/chromecast/media/cma/pipeline/audio_pipeline.cc index 255c1d8de26..7d98e09f2c4 100644 --- a/chromium/chromecast/browser/webui/webui_cast_simple.cc +++ b/chromium/chromecast/media/cma/pipeline/audio_pipeline.cc @@ -2,14 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromecast/browser/webui/webui_cast.h" +#include "chromecast/media/cma/pipeline/audio_pipeline.h" namespace chromecast { -namespace shell { +namespace media { -void InitializeWebUI() { - // Intentional no-op for public cast_shell build. +AudioPipeline::AudioPipeline() { } -} // namespace shell +AudioPipeline::~AudioPipeline() { +} + +} // namespace media } // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/audio_pipeline.h b/chromium/chromecast/media/cma/pipeline/audio_pipeline.h new file mode 100644 index 00000000000..d7284a10d7f --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/audio_pipeline.h @@ -0,0 +1,34 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_AUDIO_PIPELINE_H_ +#define CHROMECAST_MEDIA_CMA_PIPELINE_AUDIO_PIPELINE_H_ + +#include "base/macros.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" + +namespace chromecast { +namespace media { + +class AudioPipeline { + public: + AudioPipeline(); + virtual ~AudioPipeline(); + + // Set the audio client. + virtual void SetClient(const AvPipelineClient& client) = 0; + + // Set the stream volume. + // - A value of 1.0 is the neutral value. + // - |volume|=0.0 mutes the stream. + virtual void SetVolume(float volume) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(AudioPipeline); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_PIPELINE_AUDIO_PIPELINE_H_ diff --git a/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.cc new file mode 100644 index 00000000000..3dbc028fc6c --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.cc @@ -0,0 +1,161 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/cma/pipeline/audio_pipeline_impl.h" + +#include "base/bind.h" +#include "chromecast/media/cma/backend/audio_pipeline_device.h" +#include "chromecast/media/cma/base/buffering_defs.h" +#include "chromecast/media/cma/base/cma_logging.h" +#include "chromecast/media/cma/base/coded_frame_provider.h" +#include "chromecast/media/cma/base/decoder_config_adapter.h" +#include "chromecast/media/cma/pipeline/av_pipeline_impl.h" +#include "chromecast/public/media/decoder_config.h" +#include "media/base/audio_decoder_config.h" + +namespace chromecast { +namespace media { + +namespace { +const size_t kMaxAudioFrameSize = 32 * 1024; +} + +AudioPipelineImpl::AudioPipelineImpl(AudioPipelineDevice* audio_device) + : audio_device_(audio_device), + weak_factory_(this) { + av_pipeline_impl_.reset(new AvPipelineImpl( + audio_device_, + base::Bind(&AudioPipelineImpl::OnUpdateConfig, base::Unretained(this)))); + weak_this_ = weak_factory_.GetWeakPtr(); +} + +AudioPipelineImpl::~AudioPipelineImpl() { +} + +void AudioPipelineImpl::SetCodedFrameProvider( + scoped_ptr<CodedFrameProvider> frame_provider) { + av_pipeline_impl_->SetCodedFrameProvider( + frame_provider.Pass(), kAppAudioBufferSize, kMaxAudioFrameSize); +} + +void AudioPipelineImpl::SetClient(const AvPipelineClient& client) { + audio_client_ = client; + av_pipeline_impl_->SetClient(client); +} + +bool AudioPipelineImpl::StartPlayingFrom( + base::TimeDelta time, + const scoped_refptr<BufferingState>& buffering_state) { + CMALOG(kLogControl) << "AudioPipelineImpl::StartPlayingFrom t0=" + << time.InMilliseconds(); + + // Reset the pipeline statistics. + previous_stats_ = ::media::PipelineStatistics(); + + // Start playing. + if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError) + return false; + DCHECK_EQ(av_pipeline_impl_->GetState(), AvPipelineImpl::kFlushed); + + if (!av_pipeline_impl_->StartPlayingFrom(time, buffering_state)) { + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kError); + return false; + } + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kPlaying); + + return true; +} + +void AudioPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << "AudioPipelineImpl::Flush"; + if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError) { + status_cb.Run(::media::PIPELINE_ERROR_ABORT); + return; + } + DCHECK_EQ(av_pipeline_impl_->GetState(), AvPipelineImpl::kPlaying); + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushing); + av_pipeline_impl_->Flush( + base::Bind(&AudioPipelineImpl::OnFlushDone, weak_this_, status_cb)); +} + +void AudioPipelineImpl::OnFlushDone( + const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << "AudioPipelineImpl::OnFlushDone"; + if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError) { + status_cb.Run(::media::PIPELINE_ERROR_ABORT); + return; + } + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushed); + status_cb.Run(::media::PIPELINE_OK); +} + +void AudioPipelineImpl::Stop() { + CMALOG(kLogControl) << "AudioPipelineImpl::Stop"; + av_pipeline_impl_->Stop(); + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kStopped); +} + +void AudioPipelineImpl::SetCdm(BrowserCdmCast* media_keys) { + av_pipeline_impl_->SetCdm(media_keys); +} + +void AudioPipelineImpl::Initialize( + const ::media::AudioDecoderConfig& audio_config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << "AudioPipelineImpl::Initialize " + << audio_config.AsHumanReadableString(); + if (frame_provider) + SetCodedFrameProvider(frame_provider.Pass()); + + if (!audio_device_->SetConfig( + DecoderConfigAdapter::ToCastAudioConfig(audio_config)) || + !av_pipeline_impl_->Initialize()) { + status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushed); + status_cb.Run(::media::PIPELINE_OK); +} + +void AudioPipelineImpl::SetVolume(float volume) { + audio_device_->SetStreamVolumeMultiplier(volume); +} + +void AudioPipelineImpl::OnUpdateConfig( + const ::media::AudioDecoderConfig& audio_config, + const ::media::VideoDecoderConfig& video_config) { + if (audio_config.IsValidConfig()) { + CMALOG(kLogControl) << "AudioPipelineImpl::OnUpdateConfig " + << audio_config.AsHumanReadableString(); + + bool success = audio_device_->SetConfig( + DecoderConfigAdapter::ToCastAudioConfig(audio_config)); + if (!success && !audio_client_.playback_error_cb.is_null()) + audio_client_.playback_error_cb.Run(::media::PIPELINE_ERROR_DECODE); + } +} + +void AudioPipelineImpl::UpdateStatistics() { + if (audio_client_.statistics_cb.is_null()) + return; + + MediaComponentDevice::Statistics device_stats; + if (!audio_device_->GetStatistics(&device_stats)) + return; + + ::media::PipelineStatistics current_stats; + current_stats.audio_bytes_decoded = device_stats.decoded_bytes; + + ::media::PipelineStatistics delta_stats; + delta_stats.audio_bytes_decoded = + current_stats.audio_bytes_decoded - previous_stats_.audio_bytes_decoded; + + previous_stats_ = current_stats; + + audio_client_.statistics_cb.Run(delta_stats); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.h b/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.h new file mode 100644 index 00000000000..7d2e63a1314 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.h @@ -0,0 +1,79 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_BASE_AUDIO_PIPELINE_IMPL_H_ +#define CHROMECAST_MEDIA_CMA_BASE_AUDIO_PIPELINE_IMPL_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "chromecast/media/cma/pipeline/audio_pipeline.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" + +namespace media { +class AudioDecoderConfig; +class VideoDecoderConfig; +} + +namespace chromecast { +namespace media { +class AudioPipelineDevice; +class AvPipelineImpl; +class BrowserCdmCast; +class BufferingState; +class CodedFrameProvider; + +class AudioPipelineImpl : public AudioPipeline { + public: + // |buffering_controller| can be NULL. + explicit AudioPipelineImpl(AudioPipelineDevice* audio_device); + ~AudioPipelineImpl() override; + + // Input port of the pipeline. + void SetCodedFrameProvider(scoped_ptr<CodedFrameProvider> frame_provider); + + // Provide the CDM to use to decrypt samples. + void SetCdm(BrowserCdmCast* media_keys); + + // Functions to control the state of the audio pipeline. + void Initialize( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb); + bool StartPlayingFrom(base::TimeDelta time, + const scoped_refptr<BufferingState>& buffering_state); + void Flush(const ::media::PipelineStatusCB& status_cb); + void Stop(); + + // Update the playback statistics for this audio stream. + void UpdateStatistics(); + + // AudioPipeline implementation. + void SetClient(const AvPipelineClient& client) override; + void SetVolume(float volume) override; + + private: + void OnFlushDone(const ::media::PipelineStatusCB& status_cb); + void OnUpdateConfig(const ::media::AudioDecoderConfig& audio_config, + const ::media::VideoDecoderConfig& video_config); + + AudioPipelineDevice* audio_device_; + + scoped_ptr<AvPipelineImpl> av_pipeline_impl_; + AvPipelineClient audio_client_; + + ::media::PipelineStatistics previous_stats_; + + base::WeakPtr<AudioPipelineImpl> weak_this_; + base::WeakPtrFactory<AudioPipelineImpl> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(AudioPipelineImpl); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BASE_AUDIO_PIPELINE_IMPL_H_ diff --git a/chromium/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc b/chromium/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc new file mode 100644 index 00000000000..9c17c7b2330 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc @@ -0,0 +1,211 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <vector> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/time/time.h" +#include "chromecast/media/cma/backend/audio_pipeline_device.h" +#include "chromecast/media/cma/backend/media_clock_device.h" +#include "chromecast/media/cma/backend/media_pipeline_device.h" +#include "chromecast/media/cma/backend/media_pipeline_device_fake.h" +#include "chromecast/media/cma/base/buffering_controller.h" +#include "chromecast/media/cma/base/decoder_buffer_base.h" +#include "chromecast/media/cma/pipeline/audio_pipeline_impl.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" +#include "chromecast/media/cma/pipeline/media_pipeline_impl.h" +#include "chromecast/media/cma/pipeline/video_pipeline_client.h" +#include "chromecast/media/cma/pipeline/video_pipeline_impl.h" +#include "chromecast/media/cma/test/frame_generator_for_test.h" +#include "chromecast/media/cma/test/mock_frame_provider.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/buffers.h" +#include "media/base/decoder_buffer.h" +#include "media/base/video_decoder_config.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { +namespace media { + +class AudioVideoPipelineImplTest : public testing::Test { + public: + AudioVideoPipelineImplTest(); + ~AudioVideoPipelineImplTest() override; + + void Initialize(const base::Closure& done_cb, + ::media::PipelineStatus status, + bool is_audio); + void StartPlaying(const base::Closure& done_cb, + ::media::PipelineStatus status); + + void Flush(const base::Closure& done_cb, ::media::PipelineStatus status); + void Stop(const base::Closure& done_cb, ::media::PipelineStatus status); + + void OnEos(); + + base::Closure task_after_eos_cb_; + + private: + scoped_ptr<MediaPipelineImpl> media_pipeline_; + + DISALLOW_COPY_AND_ASSIGN(AudioVideoPipelineImplTest); +}; + +AudioVideoPipelineImplTest::AudioVideoPipelineImplTest() + : media_pipeline_(new MediaPipelineImpl()) { + scoped_ptr<MediaPipelineDevice> media_pipeline_device( + new MediaPipelineDeviceFake()); + media_pipeline_->Initialize(kLoadTypeURL, media_pipeline_device.Pass()); + media_pipeline_->SetPlaybackRate(1.0); +} + +AudioVideoPipelineImplTest::~AudioVideoPipelineImplTest() { +} + +void AudioVideoPipelineImplTest::Initialize( + const base::Closure& done_cb, + ::media::PipelineStatus status, + bool is_audio) { + if (is_audio) { + AvPipelineClient client; + client.eos_cb = + base::Bind(&AudioVideoPipelineImplTest::OnEos, base::Unretained(this)); + media_pipeline_->GetAudioPipeline()->SetClient(client); + } else { + VideoPipelineClient client; + client.av_pipeline_client.eos_cb = + base::Bind(&AudioVideoPipelineImplTest::OnEos, base::Unretained(this)); + media_pipeline_->GetVideoPipeline()->SetClient(client); + } + + ::media::AudioDecoderConfig audio_config( + ::media::kCodecMP3, + ::media::kSampleFormatS16, + ::media::CHANNEL_LAYOUT_STEREO, + 44100, + NULL, 0, false); + ::media::VideoDecoderConfig video_config( + ::media::kCodecH264, + ::media::H264PROFILE_MAIN, + ::media::VideoFrame::I420, + gfx::Size(640, 480), + gfx::Rect(0, 0, 640, 480), + gfx::Size(640, 480), + NULL, 0, false); + + // Frame generation on the producer side. + std::vector<FrameGeneratorForTest::FrameSpec> frame_specs; + frame_specs.resize(100); + for (size_t k = 0; k < frame_specs.size() - 1; k++) { + frame_specs[k].has_config = (k == 0); + frame_specs[k].timestamp = base::TimeDelta::FromMilliseconds(40) * k; + frame_specs[k].size = 512; + frame_specs[k].has_decrypt_config = false; + } + frame_specs[frame_specs.size() - 1].is_eos = true; + + scoped_ptr<FrameGeneratorForTest> frame_generator_provider( + new FrameGeneratorForTest(frame_specs)); + bool provider_delayed_pattern[] = { true, false }; + scoped_ptr<MockFrameProvider> frame_provider(new MockFrameProvider()); + frame_provider->Configure( + std::vector<bool>( + provider_delayed_pattern, + provider_delayed_pattern + arraysize(provider_delayed_pattern)), + frame_generator_provider.Pass()); + + ::media::PipelineStatusCB next_task = + base::Bind(&AudioVideoPipelineImplTest::StartPlaying, + base::Unretained(this), + done_cb); + + scoped_ptr<CodedFrameProvider> frame_provider_base(frame_provider.release()); + base::Closure task = is_audio ? + base::Bind(&MediaPipeline::InitializeAudio, + base::Unretained(media_pipeline_.get()), + audio_config, + base::Passed(&frame_provider_base), + next_task) : + base::Bind(&MediaPipeline::InitializeVideo, + base::Unretained(media_pipeline_.get()), + video_config, + base::Passed(&frame_provider_base), + next_task); + + base::MessageLoopProxy::current()->PostTask(FROM_HERE, task); +} + +void AudioVideoPipelineImplTest::StartPlaying( + const base::Closure& done_cb, ::media::PipelineStatus status) { + base::TimeDelta start_time = base::TimeDelta::FromMilliseconds(0); + + media_pipeline_->StartPlayingFrom(start_time); + if (!done_cb.is_null()) + done_cb.Run(); +} + +void AudioVideoPipelineImplTest::OnEos() { + task_after_eos_cb_.Run(); +} + +void AudioVideoPipelineImplTest::Flush( + const base::Closure& done_cb, ::media::PipelineStatus status) { + ::media::PipelineStatusCB next_task = + base::Bind(&AudioVideoPipelineImplTest::Stop, base::Unretained(this), + done_cb); + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(&MediaPipeline::Flush, + base::Unretained(media_pipeline_.get()), + next_task)); +} + +void AudioVideoPipelineImplTest::Stop( + const base::Closure& done_cb, ::media::PipelineStatus status) { + media_pipeline_->Stop(); + if (!done_cb.is_null()) + done_cb.Run(); + base::MessageLoop::current()->QuitWhenIdle(); +} + + +TEST_F(AudioVideoPipelineImplTest, AudioFullCycleInitToStop) { + bool is_audio = true; + task_after_eos_cb_ = base::Bind( + &AudioVideoPipelineImplTest::Flush, base::Unretained(this), + base::Closure(), ::media::PIPELINE_OK); + + scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); + message_loop->PostTask( + FROM_HERE, + base::Bind(&AudioVideoPipelineImplTest::Initialize, + base::Unretained(this), + base::Closure(), + ::media::PIPELINE_OK, is_audio)); + message_loop->Run(); +}; + +TEST_F(AudioVideoPipelineImplTest, VideoFullCycleInitToStop) { + bool is_audio = false; + task_after_eos_cb_ = base::Bind( + &AudioVideoPipelineImplTest::Flush, base::Unretained(this), + base::Closure(), ::media::PIPELINE_OK); + + scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); + message_loop->PostTask( + FROM_HERE, + base::Bind(&AudioVideoPipelineImplTest::Initialize, + base::Unretained(this), + base::Closure(), + ::media::PIPELINE_OK, is_audio)); + message_loop->Run(); +}; + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/av_pipeline_client.cc b/chromium/chromecast/media/cma/pipeline/av_pipeline_client.cc new file mode 100644 index 00000000000..1845c6cc5c1 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/av_pipeline_client.cc @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" + +namespace chromecast { +namespace media { + +AvPipelineClient::AvPipelineClient() { +} + +AvPipelineClient::~AvPipelineClient() { +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/av_pipeline_client.h b/chromium/chromecast/media/cma/pipeline/av_pipeline_client.h new file mode 100644 index 00000000000..692871603d7 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/av_pipeline_client.h @@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_AV_PIPELINE_CLIENT_H_ +#define CHROMECAST_MEDIA_CMA_PIPELINE_AV_PIPELINE_CLIENT_H_ + +#include "base/callback.h" +#include "base/time/time.h" +#include "media/base/pipeline_status.h" + +namespace chromecast { +namespace media { + +struct AvPipelineClient { + typedef base::Callback<void( + base::TimeDelta, base::TimeDelta, base::TimeTicks)> TimeUpdateCB; + + AvPipelineClient(); + ~AvPipelineClient(); + + // End of stream notification. + base::Closure eos_cb; + + // Asynchronous playback error notification. + ::media::PipelineStatusCB playback_error_cb; + + // Callback used to report the playback statistics. + ::media::StatisticsCB statistics_cb; +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_PIPELINE_AV_PIPELINE_CLIENT_H_ diff --git a/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc new file mode 100644 index 00000000000..9ff88e99225 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc @@ -0,0 +1,387 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/cma/pipeline/av_pipeline_impl.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/strings/string_number_conversions.h" +#include "chromecast/media/base/decrypt_context.h" +#include "chromecast/media/cdm/browser_cdm_cast.h" +#include "chromecast/media/cma/backend/media_clock_device.h" +#include "chromecast/media/cma/backend/media_component_device.h" +#include "chromecast/media/cma/base/buffering_frame_provider.h" +#include "chromecast/media/cma/base/buffering_state.h" +#include "chromecast/media/cma/base/cma_logging.h" +#include "chromecast/media/cma/base/coded_frame_provider.h" +#include "chromecast/media/cma/base/decoder_buffer_base.h" +#include "chromecast/media/cma/pipeline/decrypt_util.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/bind_to_current_loop.h" +#include "media/base/decrypt_config.h" + +namespace chromecast { +namespace media { + +namespace { + +const int kNoCallbackId = -1; + +} // namespace + +AvPipelineImpl::AvPipelineImpl( + MediaComponentDevice* media_component_device, + const UpdateConfigCB& update_config_cb) + : update_config_cb_(update_config_cb), + media_component_device_(media_component_device), + state_(kUninitialized), + buffered_time_(::media::kNoTimestamp()), + playable_buffered_time_(::media::kNoTimestamp()), + enable_feeding_(false), + pending_read_(false), + pending_push_(false), + enable_time_update_(false), + pending_time_update_task_(false), + media_keys_(NULL), + media_keys_callback_id_(kNoCallbackId), + weak_factory_(this) { + DCHECK(media_component_device); + weak_this_ = weak_factory_.GetWeakPtr(); + thread_checker_.DetachFromThread(); +} + +AvPipelineImpl::~AvPipelineImpl() { + DCHECK(thread_checker_.CalledOnValidThread()); + media_component_device_->SetClient(MediaComponentDevice::Client()); + + if (media_keys_ && media_keys_callback_id_ != kNoCallbackId) + media_keys_->UnregisterPlayer(media_keys_callback_id_); +} + +void AvPipelineImpl::TransitionToState(State state) { + DCHECK(thread_checker_.CalledOnValidThread()); + state_ = state; +} + +void AvPipelineImpl::SetCodedFrameProvider( + scoped_ptr<CodedFrameProvider> frame_provider, + size_t max_buffer_size, + size_t max_frame_size) { + DCHECK_EQ(state_, kUninitialized); + DCHECK(frame_provider); + + // Wrap the incoming frame provider to add some buffering capabilities. + frame_provider_.reset( + new BufferingFrameProvider( + frame_provider.Pass(), + max_buffer_size, + max_frame_size, + base::Bind(&AvPipelineImpl::OnFrameBuffered, weak_this_))); +} + +void AvPipelineImpl::SetClient(const AvPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(state_, kUninitialized); + client_ = client; +} + +bool AvPipelineImpl::Initialize() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(state_, kUninitialized); + + MediaComponentDevice::Client client; + client.eos_cb = base::Bind(&AvPipelineImpl::OnEos, weak_this_); + media_component_device_->SetClient(client); + if (!media_component_device_->SetState(MediaComponentDevice::kStateIdle)) + return false; + + return true; +} + +bool AvPipelineImpl::StartPlayingFrom( + base::TimeDelta time, + const scoped_refptr<BufferingState>& buffering_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(state_, kFlushed); + + // Media time where rendering should start + // and switch to a state where the audio device accepts incoming buffers. + if (!media_component_device_->SetStartPts(time) || + !media_component_device_->SetState(MediaComponentDevice::kStatePaused)) { + return false; + } + + // Buffering related initialization. + DCHECK(frame_provider_); + buffering_state_ = buffering_state; + if (buffering_state_.get()) + buffering_state_->SetMediaTime(time); + + if (!media_component_device_->SetState(MediaComponentDevice::kStateRunning)) + return false; + + // Start feeding the pipeline. + enable_feeding_ = true; + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); + + return true; +} + +void AvPipelineImpl::Flush(const base::Closure& done_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(state_, kFlushing); + DCHECK_EQ( + media_component_device_->GetState(), MediaComponentDevice::kStateRunning); + // Note: returning to idle state aborts any pending frame push. + media_component_device_->SetState(MediaComponentDevice::kStateIdle); + pending_push_ = false; + + // Break the feeding loop. + enable_feeding_ = false; + + // Remove any pending buffer. + pending_buffer_ = scoped_refptr<DecoderBufferBase>(); + + // Finally, remove any frames left in the frame provider. + pending_read_ = false; + buffered_time_ = ::media::kNoTimestamp(); + playable_buffered_time_ = ::media::kNoTimestamp(); + non_playable_frames_.clear(); + frame_provider_->Flush(done_cb); +} + +void AvPipelineImpl::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Stop can be called from any state. + if (state_ == kUninitialized || state_ == kStopped) + return; + + // Stop feeding the pipeline. + enable_feeding_ = false; + + // Release hardware resources on Stop. + if (media_component_device_->GetState() == + MediaComponentDevice::kStatePaused || + media_component_device_->GetState() == + MediaComponentDevice::kStateRunning) { + media_component_device_->SetState(MediaComponentDevice::kStateIdle); + } + if (media_component_device_->GetState() == MediaComponentDevice::kStateIdle) { + media_component_device_->SetState( + MediaComponentDevice::kStateUninitialized); + } +} + +void AvPipelineImpl::SetCdm(BrowserCdmCast* media_keys) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(media_keys); + + if (media_keys_ && media_keys_callback_id_ != kNoCallbackId) + media_keys_->UnregisterPlayer(media_keys_callback_id_); + + media_keys_ = media_keys; + media_keys_callback_id_ = media_keys_->RegisterPlayer( + base::Bind(&AvPipelineImpl::OnCdmStateChanged, weak_this_), + base::Bind(&AvPipelineImpl::OnCdmDestroyed, weak_this_)); +} + +void AvPipelineImpl::OnEos() { + DCHECK(thread_checker_.CalledOnValidThread()); + CMALOG(kLogControl) << __FUNCTION__; + if (state_ != kPlaying) + return; + + if (!client_.eos_cb.is_null()) + client_.eos_cb.Run(); +} + +void AvPipelineImpl::FetchBufferIfNeeded() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!enable_feeding_) + return; + + if (pending_read_ || pending_buffer_.get()) + return; + + pending_read_ = true; + frame_provider_->Read( + base::Bind(&AvPipelineImpl::OnNewFrame, weak_this_)); +} + +void AvPipelineImpl::OnNewFrame( + const scoped_refptr<DecoderBufferBase>& buffer, + const ::media::AudioDecoderConfig& audio_config, + const ::media::VideoDecoderConfig& video_config) { + DCHECK(thread_checker_.CalledOnValidThread()); + pending_read_ = false; + + if (audio_config.IsValidConfig() || video_config.IsValidConfig()) + update_config_cb_.Run(audio_config, video_config); + + pending_buffer_ = buffer; + ProcessPendingBuffer(); + + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); +} + +void AvPipelineImpl::ProcessPendingBuffer() { + if (!enable_feeding_) + return; + + // Initiate a read if there isn't already one. + if (!pending_buffer_.get() && !pending_read_) { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); + return; + } + + if (!pending_buffer_.get() || pending_push_) + return; + + // Break the feeding loop when the end of stream is reached. + if (pending_buffer_->end_of_stream()) { + CMALOG(kLogControl) << __FUNCTION__ << ": EOS reached, stopped feeding"; + enable_feeding_ = false; + } + + scoped_refptr<DecryptContext> decrypt_context; + if (!pending_buffer_->end_of_stream() && + pending_buffer_->decrypt_config()) { + // Verify that CDM has the key ID. + // Should not send the frame if the key ID is not available yet. + std::string key_id(pending_buffer_->decrypt_config()->key_id()); + if (!media_keys_) { + CMALOG(kLogControl) << "No CDM for frame: pts=" + << pending_buffer_->timestamp().InMilliseconds(); + return; + } + decrypt_context = media_keys_->GetDecryptContext(key_id); + if (!decrypt_context.get()) { + CMALOG(kLogControl) << "frame(pts=" + << pending_buffer_->timestamp().InMilliseconds() + << "): waiting for key id " + << base::HexEncode(&key_id[0], key_id.size()); + return; + } + + // If we do have the clear key, decrypt the pending buffer + // and reset the decryption context (not needed anymore). + crypto::SymmetricKey* key = decrypt_context->GetKey(); + if (key != NULL) { + pending_buffer_ = DecryptDecoderBuffer(pending_buffer_, key); + decrypt_context = scoped_refptr<DecryptContext>(); + } + } + + if (!pending_buffer_->end_of_stream() && buffering_state_.get()) { + base::TimeDelta timestamp = pending_buffer_->timestamp(); + if (timestamp != ::media::kNoTimestamp()) + buffering_state_->SetMaxRenderingTime(timestamp); + } + + MediaComponentDevice::FrameStatus status = media_component_device_->PushFrame( + decrypt_context, + pending_buffer_, + base::Bind(&AvPipelineImpl::OnFramePushed, weak_this_)); + pending_buffer_ = scoped_refptr<DecoderBufferBase>(); + + pending_push_ = (status == MediaComponentDevice::kFramePending); + if (!pending_push_) + OnFramePushed(status); +} + +void AvPipelineImpl::OnFramePushed(MediaComponentDevice::FrameStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + pending_push_ = false; + if (status == MediaComponentDevice::kFrameFailed) { + LOG(WARNING) << "AvPipelineImpl: PushFrame failed"; + enable_feeding_ = false; + state_ = kError; + return; + } + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(&AvPipelineImpl::ProcessPendingBuffer, weak_this_)); +} + +void AvPipelineImpl::OnCdmStateChanged() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Update the buffering state if needed. + if (buffering_state_.get()) + UpdatePlayableFrames(); + + // Process the pending buffer in case the CDM now has the frame key id. + ProcessPendingBuffer(); +} + +void AvPipelineImpl::OnCdmDestroyed() { + DCHECK(thread_checker_.CalledOnValidThread()); + media_keys_ = NULL; +} + +void AvPipelineImpl::OnFrameBuffered( + const scoped_refptr<DecoderBufferBase>& buffer, + bool is_at_max_capacity) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!buffering_state_.get()) + return; + + if (!buffer->end_of_stream() && + (buffered_time_ == ::media::kNoTimestamp() || + buffered_time_ < buffer->timestamp())) { + buffered_time_ = buffer->timestamp(); + } + + if (is_at_max_capacity) + buffering_state_->NotifyMaxCapacity(buffered_time_); + + // No need to update the list of playable frames, + // if we are already blocking on a frame. + bool update_playable_frames = non_playable_frames_.empty(); + non_playable_frames_.push_back(buffer); + if (update_playable_frames) + UpdatePlayableFrames(); +} + +void AvPipelineImpl::UpdatePlayableFrames() { + while (!non_playable_frames_.empty()) { + const scoped_refptr<DecoderBufferBase>& non_playable_frame = + non_playable_frames_.front(); + + if (non_playable_frame->end_of_stream()) { + buffering_state_->NotifyEos(); + } else { + const ::media::DecryptConfig* decrypt_config = + non_playable_frame->decrypt_config(); + if (decrypt_config && + !(media_keys_ && + media_keys_->GetDecryptContext(decrypt_config->key_id()).get())) { + // The frame is still not playable. All the following are thus not + // playable. + break; + } + + if (playable_buffered_time_ == ::media::kNoTimestamp() || + playable_buffered_time_ < non_playable_frame->timestamp()) { + playable_buffered_time_ = non_playable_frame->timestamp(); + buffering_state_->SetBufferedTime(playable_buffered_time_); + } + } + + // The frame is playable: remove it from the list of non playable frames. + non_playable_frames_.pop_front(); + } +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h new file mode 100644 index 00000000000..20b3fc4feda --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h @@ -0,0 +1,172 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_BASE_AV_PIPELINE_IMPL_H_ +#define CHROMECAST_MEDIA_CMA_BASE_AV_PIPELINE_IMPL_H_ + +#include <list> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "chromecast/media/cma/backend/media_component_device.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" + +namespace media { +class AudioDecoderConfig; +class VideoDecoderConfig; +} + +namespace chromecast { +namespace media { +class BrowserCdmCast; +class BufferingFrameProvider; +class BufferingState; +class CodedFrameProvider; +class DecoderBufferBase; +class MediaComponentDevice; + +class AvPipelineImpl { + public: + // Pipeline states. + enum State { + kUninitialized, + kPlaying, + kFlushing, + kFlushed, + kStopped, + kError, + }; + + typedef base::Callback< + void(const ::media::AudioDecoderConfig&, + const ::media::VideoDecoderConfig&)> UpdateConfigCB; + + AvPipelineImpl( + MediaComponentDevice* media_component_device, + const UpdateConfigCB& update_config_cb); + ~AvPipelineImpl(); + + // Setting the frame provider or the client must be done in the + // |kUninitialized| state. + void SetCodedFrameProvider(scoped_ptr<CodedFrameProvider> frame_provider, + size_t max_buffer_size, + size_t max_frame_size); + void SetClient(const AvPipelineClient& client); + + // Initialize the pipeline. + bool Initialize(); + + // Setup the pipeline and ensure samples are available for the given media + // time, then start rendering samples. + bool StartPlayingFrom(base::TimeDelta time, + const scoped_refptr<BufferingState>& buffering_state); + + // Flush any remaining samples in the pipeline. + // Invoke |done_cb| when flush is completed. + void Flush(const base::Closure& done_cb); + + // Tear down the pipeline and release the hardware resources. + void Stop(); + + State GetState() const { return state_; } + void TransitionToState(State state); + + void SetCdm(BrowserCdmCast* media_keys); + + private: + // Callback invoked when the CDM state has changed in a way that might + // impact media playback. + void OnCdmStateChange(); + + // Callback invoked when playback has reached the end of stream. + void OnEos(); + + // Feed the pipeline, getting the frames from |frame_provider_|. + void FetchBufferIfNeeded(); + + // Callback invoked when receiving a new frame from |frame_provider_|. + void OnNewFrame(const scoped_refptr<DecoderBufferBase>& buffer, + const ::media::AudioDecoderConfig& audio_config, + const ::media::VideoDecoderConfig& video_config); + + // Process a pending buffer. + void ProcessPendingBuffer(); + + void OnFramePushed(MediaComponentDevice::FrameStatus status); + + // Callbacks: + // - when BrowserCdm updated its state. + // - when BrowserCdm has been destroyed. + void OnCdmStateChanged(); + void OnCdmDestroyed(); + + // Callback invoked when a frame has been buffered by |frame_provider_| + // which is a BufferingFrameProvider. + void OnFrameBuffered(const scoped_refptr<DecoderBufferBase>& buffer, + bool is_at_max_capacity); + void UpdatePlayableFrames(); + + base::ThreadChecker thread_checker_; + + UpdateConfigCB update_config_cb_; + + AvPipelineClient client_; + + // Backends. + MediaComponentDevice* media_component_device_; + + // AV pipeline state. + State state_; + + // Buffering state. + // Can be NULL if there is no buffering strategy. + scoped_refptr<BufferingState> buffering_state_; + + // |buffered_time_| is the maximum timestamp of buffered frames. + // |playable_buffered_time_| is the maximum timestamp of buffered and + // playable frames (i.e. the key id is available for those frames). + base::TimeDelta buffered_time_; + base::TimeDelta playable_buffered_time_; + + // List of frames buffered but not playable right away due to a missing + // key id. + std::list<scoped_refptr<DecoderBufferBase> > non_playable_frames_; + + // Buffer provider. + scoped_ptr<BufferingFrameProvider> frame_provider_; + + // Indicate whether the frame fetching process is active. + bool enable_feeding_; + + // Indicate whether there is a pending buffer read. + bool pending_read_; + + // Pending buffer. + scoped_refptr<DecoderBufferBase> pending_buffer_; + + // Indicate if there is a frame being pushed to the audio device. + bool pending_push_; + + // The media time is retrieved at regular intervals. + // Indicate whether time update is enabled. + bool enable_time_update_; + bool pending_time_update_task_; + + // Decryption keys, if available. + BrowserCdmCast* media_keys_; + int media_keys_callback_id_; + + base::WeakPtr<AvPipelineImpl> weak_this_; + base::WeakPtrFactory<AvPipelineImpl> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(AvPipelineImpl); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BASE_AV_PIPELINE_IMPL_H_ diff --git a/chromium/chromecast/media/cma/pipeline/decrypt_util.cc b/chromium/chromecast/media/cma/pipeline/decrypt_util.cc new file mode 100644 index 00000000000..1f1fbe85c62 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/decrypt_util.cc @@ -0,0 +1,128 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/cma/pipeline/decrypt_util.h" + +#include <openssl/aes.h> +#include <string> + +#include "base/logging.h" +#include "chromecast/media/cma/base/decoder_buffer_base.h" +#include "crypto/symmetric_key.h" +#include "media/base/decrypt_config.h" + +namespace chromecast { +namespace media { + +namespace { + +class DecoderBufferClear : public DecoderBufferBase { + public: + explicit DecoderBufferClear(const scoped_refptr<DecoderBufferBase>& buffer); + + // DecoderBufferBase implementation. + base::TimeDelta timestamp() const override; + const uint8* data() const override; + uint8* writable_data() const override; + size_t data_size() const override; + const ::media::DecryptConfig* decrypt_config() const override; + bool end_of_stream() const override; + + private: + ~DecoderBufferClear() override; + + scoped_refptr<DecoderBufferBase> const buffer_; + + DISALLOW_COPY_AND_ASSIGN(DecoderBufferClear); +}; + +DecoderBufferClear::DecoderBufferClear( + const scoped_refptr<DecoderBufferBase>& buffer) + : buffer_(buffer) { +} + +DecoderBufferClear::~DecoderBufferClear() { +} + +base::TimeDelta DecoderBufferClear::timestamp() const { + return buffer_->timestamp(); +} + +const uint8* DecoderBufferClear::data() const { + return buffer_->data(); +} + +uint8* DecoderBufferClear::writable_data() const { + return buffer_->writable_data(); +} + +size_t DecoderBufferClear::data_size() const { + return buffer_->data_size(); +} + +const ::media::DecryptConfig* DecoderBufferClear::decrypt_config() const { + // Buffer is clear so no decryption info. + return NULL; +} + +bool DecoderBufferClear::end_of_stream() const { + return buffer_->end_of_stream(); +} + +} // namespace + +scoped_refptr<DecoderBufferBase> DecryptDecoderBuffer( + const scoped_refptr<DecoderBufferBase>& buffer, + crypto::SymmetricKey* key) { + if (buffer->end_of_stream()) + return buffer; + + const ::media::DecryptConfig* decrypt_config = buffer->decrypt_config(); + if (!decrypt_config || decrypt_config->iv().size() == 0) + return buffer; + + // Get the key. + std::string raw_key; + if (!key->GetRawKey(&raw_key)) { + LOG(ERROR) << "Failed to get the underlying AES key"; + return buffer; + } + DCHECK_EQ(static_cast<int>(raw_key.length()), AES_BLOCK_SIZE); + const uint8* key_u8 = reinterpret_cast<const uint8*>(raw_key.data()); + AES_KEY aes_key; + if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOG(ERROR) << "Failed to set the AES key"; + return buffer; + } + + // Get the IV. + uint8 aes_iv[AES_BLOCK_SIZE]; + DCHECK_EQ(static_cast<int>(decrypt_config->iv().length()), + AES_BLOCK_SIZE); + memcpy(aes_iv, decrypt_config->iv().data(), AES_BLOCK_SIZE); + + // Decryption state. + unsigned int encrypted_byte_offset = 0; + uint8 ecount_buf[AES_BLOCK_SIZE]; + + // Perform the decryption. + const std::vector< ::media::SubsampleEntry>& subsamples = + decrypt_config->subsamples(); + uint8* data = buffer->writable_data(); + uint32 offset = 0; + for (size_t k = 0; k < subsamples.size(); k++) { + offset += subsamples[k].clear_bytes; + uint32 cypher_bytes = subsamples[k].cypher_bytes; + CHECK_LE(static_cast<size_t>(offset + cypher_bytes), buffer->data_size()); + AES_ctr128_encrypt( + data + offset, data + offset, cypher_bytes, &aes_key, + aes_iv, ecount_buf, &encrypted_byte_offset); + offset += cypher_bytes; + } + + return scoped_refptr<DecoderBufferBase>(new DecoderBufferClear(buffer)); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/decrypt_util.h b/chromium/chromecast/media/cma/pipeline/decrypt_util.h new file mode 100644 index 00000000000..5a59afc8648 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/decrypt_util.h @@ -0,0 +1,31 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_DECRYPT_UTIL_H_ +#define CHROMECAST_MEDIA_CMA_PIPELINE_DECRYPT_UTIL_H_ + +#include "base/memory/ref_counted.h" + +namespace crypto { +class SymmetricKey; +} + +namespace chromecast { +namespace media { + +class DecoderBufferBase; + +// Create a new buffer which corresponds to the clear version of |buffer|. +// Note: the memory area corresponding to the ES data of the new buffer +// is the same as the ES data of |buffer| (for efficiency). +// After the function is called, |buffer| is left in a inconsistent state +// in the sense it has some decryption info but the ES data is now in clear. +scoped_refptr<DecoderBufferBase> DecryptDecoderBuffer( + const scoped_refptr<DecoderBufferBase>& buffer, + crypto::SymmetricKey* key); + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_PIPELINE_DECRYPT_UTIL_H_ diff --git a/chromium/chromecast/media/cma/pipeline/load_type.h b/chromium/chromecast/media/cma/pipeline/load_type.h new file mode 100644 index 00000000000..569b2ef33f2 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/load_type.h @@ -0,0 +1,20 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_LOAD_TYPE_H_ +#define CHROMECAST_MEDIA_CMA_PIPELINE_LOAD_TYPE_H_ + +namespace chromecast { +namespace media { + +enum LoadType { + kLoadTypeURL, + kLoadTypeMediaSource, + kLoadTypeMediaStream, +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_PIPELINE_LOAD_TYPE_H_ diff --git a/chromium/chromecast/media/cma/pipeline/media_pipeline.h b/chromium/chromecast/media/cma/pipeline/media_pipeline.h new file mode 100644 index 00000000000..6b127cb276b --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/media_pipeline.h @@ -0,0 +1,69 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_H_ +#define CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_H_ + +#include "base/basictypes.h" +#include "base/macros.h" +#include "base/time/time.h" +#include "media/base/pipeline_status.h" + +namespace media { +class AudioDecoderConfig; +class BrowserCdm; +class VideoDecoderConfig; +} + +namespace chromecast { +namespace media { +class AudioPipeline; +class CodedFrameProvider; +struct MediaPipelineClient; +class VideoPipeline; + +class MediaPipeline { + public: + MediaPipeline() {} + virtual ~MediaPipeline() {} + + // Set the media pipeline client. + virtual void SetClient(const MediaPipelineClient& client) = 0; + + // Set the CDM to use for decryption. + // The CDM is refered by its id. + virtual void SetCdm(int cdm_id) = 0; + + // Return the audio/video pipeline owned by the MediaPipeline. + virtual AudioPipeline* GetAudioPipeline() const = 0; + virtual VideoPipeline* GetVideoPipeline() const = 0; + + // Create an audio/video pipeline. + // MediaPipeline owns the resulting audio/video pipeline. + // Only one audio and one video pipeline can be created. + virtual void InitializeAudio( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) = 0; + virtual void InitializeVideo( + const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) = 0; + + // Control the media pipeline state machine. + virtual void StartPlayingFrom(base::TimeDelta time) = 0; + virtual void Flush(const ::media::PipelineStatusCB& status_cb) = 0; + virtual void Stop() = 0; + + // Set the playback rate. + virtual void SetPlaybackRate(double playback_rate) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(MediaPipeline); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_H_ diff --git a/chromium/chromecast/media/cma/pipeline/media_pipeline_client.cc b/chromium/chromecast/media/cma/pipeline/media_pipeline_client.cc new file mode 100644 index 00000000000..8dbfc8db724 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/media_pipeline_client.cc @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/cma/pipeline/media_pipeline_client.h" + +namespace chromecast { +namespace media { + +MediaPipelineClient::MediaPipelineClient() { +} + +MediaPipelineClient::~MediaPipelineClient() { +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/media_pipeline_client.h b/chromium/chromecast/media/cma/pipeline/media_pipeline_client.h new file mode 100644 index 00000000000..692263c6e6a --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/media_pipeline_client.h @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_CLIENT_H_ +#define CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_CLIENT_H_ + +#include "base/callback.h" +#include "base/time/time.h" +#include "media/base/buffering_state.h" +#include "media/base/pipeline_status.h" + +namespace chromecast { +namespace media { + +struct MediaPipelineClient { + typedef base::Callback<void( + base::TimeDelta, base::TimeDelta, base::TimeTicks)> TimeUpdateCB; + + MediaPipelineClient(); + ~MediaPipelineClient(); + + // Callback used to report a playback error as a ::media::PipelineStatus. + ::media::PipelineStatusCB error_cb; + + // Callback used to report the latest playback time, + // as well as the maximum time available for rendering. + TimeUpdateCB time_update_cb; + + // Callback used to report the buffering status. + ::media::BufferingStateCB buffering_state_cb; +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_CLIENT_H_ diff --git a/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc new file mode 100644 index 00000000000..ced7df14cc0 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc @@ -0,0 +1,373 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/cma/pipeline/media_pipeline_impl.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/time/time.h" +#include "chromecast/media/cdm/browser_cdm_cast.h" +#include "chromecast/media/cma/backend/media_clock_device.h" +#include "chromecast/media/cma/backend/media_pipeline_device.h" +#include "chromecast/media/cma/base/buffering_controller.h" +#include "chromecast/media/cma/base/buffering_state.h" +#include "chromecast/media/cma/base/cma_logging.h" +#include "chromecast/media/cma/base/coded_frame_provider.h" +#include "chromecast/media/cma/pipeline/audio_pipeline_impl.h" +#include "chromecast/media/cma/pipeline/video_pipeline_impl.h" +#include "media/base/buffers.h" + +namespace chromecast { +namespace media { + +namespace { + +// Buffering parameters when load_type is kLoadTypeUrl. +const base::TimeDelta kLowBufferThresholdURL( + base::TimeDelta::FromMilliseconds(2000)); +const base::TimeDelta kHighBufferThresholdURL( + base::TimeDelta::FromMilliseconds(6000)); + +// Buffering parameters when load_type is kLoadTypeMediaSource. +const base::TimeDelta kLowBufferThresholdMediaSource( + base::TimeDelta::FromMilliseconds(0)); +const base::TimeDelta kHighBufferThresholdMediaSource( + base::TimeDelta::FromMilliseconds(300)); + +// Interval between two updates of the media time. +const base::TimeDelta kTimeUpdateInterval( + base::TimeDelta::FromMilliseconds(250)); + +// Interval between two updates of the statistics is equal to: +// kTimeUpdateInterval * kStatisticsUpdatePeriod. +const int kStatisticsUpdatePeriod = 4; + +} // namespace + +MediaPipelineImpl::MediaPipelineImpl() + : has_audio_(false), + has_video_(false), + target_playback_rate_(0.0), + enable_time_update_(false), + pending_time_update_task_(false), + statistics_rolling_counter_(0), + weak_factory_(this) { + CMALOG(kLogControl) << __FUNCTION__; + weak_this_ = weak_factory_.GetWeakPtr(); + thread_checker_.DetachFromThread(); +} + +MediaPipelineImpl::~MediaPipelineImpl() { + CMALOG(kLogControl) << __FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void MediaPipelineImpl::Initialize( + LoadType load_type, + scoped_ptr<MediaPipelineDevice> media_pipeline_device) { + CMALOG(kLogControl) << __FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); + media_pipeline_device_.reset(media_pipeline_device.release()); + clock_device_ = media_pipeline_device_->GetMediaClockDevice(); + + if (load_type == kLoadTypeURL || load_type == kLoadTypeMediaSource) { + base::TimeDelta low_threshold(kLowBufferThresholdURL); + base::TimeDelta high_threshold(kHighBufferThresholdURL); + if (load_type == kLoadTypeMediaSource) { + low_threshold = kLowBufferThresholdMediaSource; + high_threshold = kHighBufferThresholdMediaSource; + } + scoped_refptr<BufferingConfig> buffering_config( + new BufferingConfig(low_threshold, high_threshold)); + buffering_controller_.reset(new BufferingController( + buffering_config, + base::Bind(&MediaPipelineImpl::OnBufferingNotification, weak_this_))); + } + + audio_pipeline_.reset(new AudioPipelineImpl( + media_pipeline_device_->GetAudioPipelineDevice())); + + video_pipeline_.reset(new VideoPipelineImpl( + media_pipeline_device_->GetVideoPipelineDevice())); +} + +void MediaPipelineImpl::SetClient(const MediaPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!client.error_cb.is_null()); + DCHECK(!client.time_update_cb.is_null()); + DCHECK(!client.buffering_state_cb.is_null()); + client_ = client; +} + +void MediaPipelineImpl::SetCdm(int cdm_id) { + CMALOG(kLogControl) << __FUNCTION__ << " cdm_id=" << cdm_id; + DCHECK(thread_checker_.CalledOnValidThread()); + NOTIMPLEMENTED(); + // TODO(gunsch): SetCdm(int) is not implemented. + // One possibility would be a GetCdmByIdCB that's passed in. +} + +void MediaPipelineImpl::SetCdm(BrowserCdmCast* cdm) { + CMALOG(kLogControl) << __FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); + audio_pipeline_->SetCdm(cdm); + video_pipeline_->SetCdm(cdm); +} + +AudioPipeline* MediaPipelineImpl::GetAudioPipeline() const { + return audio_pipeline_.get(); +} + +VideoPipeline* MediaPipelineImpl::GetVideoPipeline() const { + return video_pipeline_.get(); +} + +void MediaPipelineImpl::InitializeAudio( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!has_audio_); + if (clock_device_->GetState() == MediaClockDevice::kStateUninitialized && + !clock_device_->SetState(MediaClockDevice::kStateIdle)) { + status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + has_audio_ = true; + audio_pipeline_->Initialize(config, frame_provider.Pass(), status_cb); +} + +void MediaPipelineImpl::InitializeVideo( + const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!has_video_); + if (clock_device_->GetState() == MediaClockDevice::kStateUninitialized && + !clock_device_->SetState(MediaClockDevice::kStateIdle)) { + status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + has_video_ = true; + video_pipeline_->Initialize(config, frame_provider.Pass(), status_cb); +} + +void MediaPipelineImpl::StartPlayingFrom(base::TimeDelta time) { + CMALOG(kLogControl) << __FUNCTION__ << " t0=" << time.InMilliseconds(); + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(has_audio_ || has_video_); + DCHECK(!pending_callbacks_); + + // Reset the start of the timeline. + DCHECK_EQ(clock_device_->GetState(), MediaClockDevice::kStateIdle); + clock_device_->ResetTimeline(time); + + // Start the clock. If the playback rate is 0, then the clock is started + // but does not increase. + if (!clock_device_->SetState(MediaClockDevice::kStateRunning)) { + OnError(::media::PIPELINE_ERROR_ABORT); + return; + } + + // Enable time updates. + enable_time_update_ = true; + statistics_rolling_counter_ = 0; + if (!pending_time_update_task_) { + pending_time_update_task_ = true; + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_)); + } + + // Setup the audio and video pipeline for the new timeline. + if (has_audio_) { + scoped_refptr<BufferingState> buffering_state; + if (buffering_controller_) + buffering_state = buffering_controller_->AddStream("audio"); + if (!audio_pipeline_->StartPlayingFrom(time, buffering_state)) { + OnError(::media::PIPELINE_ERROR_ABORT); + return; + } + } + if (has_video_) { + scoped_refptr<BufferingState> buffering_state; + if (buffering_controller_) + buffering_state = buffering_controller_->AddStream("video"); + if (!video_pipeline_->StartPlayingFrom(time, buffering_state)) { + OnError(::media::PIPELINE_ERROR_ABORT); + return; + } + } +} + +void MediaPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << __FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(has_audio_ || has_video_); + DCHECK(!pending_callbacks_); + + // No need to update media time anymore. + enable_time_update_ = false; + + buffering_controller_->Reset(); + + // The clock should return to idle. + if (!clock_device_->SetState(MediaClockDevice::kStateIdle)) { + status_cb.Run(::media::PIPELINE_ERROR_ABORT); + return; + } + + // Flush both the audio and video pipeline. + ::media::SerialRunner::Queue bound_fns; + if (has_audio_) { + bound_fns.Push(base::Bind( + &AudioPipelineImpl::Flush, + base::Unretained(audio_pipeline_.get()))); + } + if (has_video_) { + bound_fns.Push(base::Bind( + &VideoPipelineImpl::Flush, + base::Unretained(video_pipeline_.get()))); + } + ::media::PipelineStatusCB transition_cb = + base::Bind(&MediaPipelineImpl::StateTransition, weak_this_, status_cb); + pending_callbacks_ = + ::media::SerialRunner::Run(bound_fns, transition_cb); +} + +void MediaPipelineImpl::Stop() { + CMALOG(kLogControl) << __FUNCTION__; + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(has_audio_ || has_video_); + DCHECK(!pending_callbacks_); + + // No need to update media time anymore. + enable_time_update_ = false; + + // Release hardware resources on Stop. + // Note: Stop can be called from any state. + if (clock_device_->GetState() == MediaClockDevice::kStateRunning) + clock_device_->SetState(MediaClockDevice::kStateIdle); + if (clock_device_->GetState() == MediaClockDevice::kStateIdle) + clock_device_->SetState(MediaClockDevice::kStateUninitialized); + + // Stop both the audio and video pipeline. + if (has_audio_) + audio_pipeline_->Stop(); + if (has_video_) + video_pipeline_->Stop(); +} + +void MediaPipelineImpl::SetPlaybackRate(double rate) { + CMALOG(kLogControl) << __FUNCTION__ << " rate=" << rate; + DCHECK(thread_checker_.CalledOnValidThread()); + target_playback_rate_ = rate; + if (!buffering_controller_ || !buffering_controller_->IsBuffering()) + media_pipeline_device_->GetMediaClockDevice()->SetRate(rate); +} + +AudioPipelineImpl* MediaPipelineImpl::GetAudioPipelineImpl() const { + return audio_pipeline_.get(); +} + +VideoPipelineImpl* MediaPipelineImpl::GetVideoPipelineImpl() const { + return video_pipeline_.get(); +} + +void MediaPipelineImpl::StateTransition( + const ::media::PipelineStatusCB& status_cb, + ::media::PipelineStatus status) { + pending_callbacks_.reset(); + status_cb.Run(status); +} + +void MediaPipelineImpl::OnBufferingNotification(bool is_buffering) { + CMALOG(kLogControl) << __FUNCTION__ << " is_buffering=" << is_buffering; + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(buffering_controller_); + + if (!client_.buffering_state_cb.is_null()) { + ::media::BufferingState buffering_state = is_buffering ? + ::media::BUFFERING_HAVE_NOTHING : ::media::BUFFERING_HAVE_ENOUGH; + client_.buffering_state_cb.Run(buffering_state); + } + + if (media_pipeline_device_->GetMediaClockDevice()->GetState() == + MediaClockDevice::kStateUninitialized) { + return; + } + + if (is_buffering) { + // Do not consume data in a rebuffering phase. + media_pipeline_device_->GetMediaClockDevice()->SetRate(0.0); + } else { + media_pipeline_device_->GetMediaClockDevice()->SetRate( + target_playback_rate_); + } +} + +void MediaPipelineImpl::UpdateMediaTime() { + pending_time_update_task_ = false; + if (!enable_time_update_) + return; + + if (statistics_rolling_counter_ == 0) { + audio_pipeline_->UpdateStatistics(); + video_pipeline_->UpdateStatistics(); + } + statistics_rolling_counter_ = + (statistics_rolling_counter_ + 1) % kStatisticsUpdatePeriod; + + base::TimeDelta media_time(clock_device_->GetTime()); + if (media_time == ::media::kNoTimestamp()) { + pending_time_update_task_ = true; + base::MessageLoopProxy::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_), + kTimeUpdateInterval); + return; + } + base::TimeTicks stc = base::TimeTicks::Now(); + + base::TimeDelta max_rendering_time = media_time; + if (buffering_controller_) { + buffering_controller_->SetMediaTime(media_time); + + // Receiving the same time twice in a row means playback isn't moving, + // so don't interpolate ahead. + if (media_time != last_media_time_) { + max_rendering_time = buffering_controller_->GetMaxRenderingTime(); + if (max_rendering_time == ::media::kNoTimestamp()) + max_rendering_time = media_time; + + // Cap interpolation time to avoid interpolating too far ahead. + max_rendering_time = + std::min(max_rendering_time, media_time + 2 * kTimeUpdateInterval); + } + } + + last_media_time_ = media_time; + if (!client_.time_update_cb.is_null()) + client_.time_update_cb.Run(media_time, max_rendering_time, stc); + + pending_time_update_task_ = true; + base::MessageLoopProxy::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_), + kTimeUpdateInterval); +} + +void MediaPipelineImpl::OnError(::media::PipelineStatus error) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_NE(error, ::media::PIPELINE_OK) << "PIPELINE_OK is not an error!"; + if (!client_.error_cb.is_null()) + client_.error_cb.Run(error); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h new file mode 100644 index 00000000000..eb14a555b98 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h @@ -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. + +#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_IMPL_H_ +#define CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_IMPL_H_ + +#include "base/basictypes.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "chromecast/media/cma/pipeline/load_type.h" +#include "chromecast/media/cma/pipeline/media_pipeline.h" +#include "chromecast/media/cma/pipeline/media_pipeline_client.h" +#include "media/base/serial_runner.h" + +namespace chromecast { +namespace media { +class AudioPipelineImpl; +class BrowserCdmCast; +class BufferingController; +class MediaClockDevice; +class MediaPipelineDevice; +class VideoPipelineImpl; + +class MediaPipelineImpl : public MediaPipeline { + public: + MediaPipelineImpl(); + ~MediaPipelineImpl() override; + + // Initialize the media pipeline: the pipeline is configured based on + // |load_type|. + void Initialize(LoadType load_type, + scoped_ptr<MediaPipelineDevice> media_pipeline_device); + + // MediaPipeline implementation. + void SetClient(const MediaPipelineClient& client) override; + void SetCdm(int cdm_id) override; + AudioPipeline* GetAudioPipeline() const override; + VideoPipeline* GetVideoPipeline() const override; + void InitializeAudio( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) override; + void InitializeVideo( + const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) override; + void StartPlayingFrom(base::TimeDelta time) override; + void Flush(const ::media::PipelineStatusCB& status_cb) override; + void Stop() override; + void SetPlaybackRate(double playback_rate) override; + + AudioPipelineImpl* GetAudioPipelineImpl() const; + VideoPipelineImpl* GetVideoPipelineImpl() const; + + void SetCdm(BrowserCdmCast* cdm); + + private: + void StateTransition(const ::media::PipelineStatusCB& status_cb, + ::media::PipelineStatus status); + + // Invoked to notify about a change of buffering state. + void OnBufferingNotification(bool is_buffering); + + void UpdateMediaTime(); + + void OnError(::media::PipelineStatus error); + + base::ThreadChecker thread_checker_; + + MediaPipelineClient client_; + + scoped_ptr<BufferingController> buffering_controller_; + + // Interface with the underlying hardware media pipeline. + scoped_ptr<MediaPipelineDevice> media_pipeline_device_; + MediaClockDevice* clock_device_; + + bool has_audio_; + bool has_video_; + scoped_ptr<AudioPipelineImpl> audio_pipeline_; + scoped_ptr<VideoPipelineImpl> video_pipeline_; + scoped_ptr< ::media::SerialRunner> pending_callbacks_; + + // Playback rate set by the upper layer. + float target_playback_rate_; + + // Indicate a possible re-buffering phase. + bool is_buffering_; + + // The media time is retrieved at regular intervals. + // Indicate whether time update is enabled. + bool enable_time_update_; + bool pending_time_update_task_; + base::TimeDelta last_media_time_; + + // Used to make the statistics update period a multiplier of the time update + // period. + int statistics_rolling_counter_; + + base::WeakPtr<MediaPipelineImpl> weak_this_; + base::WeakPtrFactory<MediaPipelineImpl> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(MediaPipelineImpl); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_IMPL_H_ diff --git a/chromium/chromecast/common/chromecast_config_simple.cc b/chromium/chromecast/media/cma/pipeline/video_pipeline.cc index f62efe524b8..6b428b5e6e2 100644 --- a/chromium/chromecast/common/chromecast_config_simple.cc +++ b/chromium/chromecast/media/cma/pipeline/video_pipeline.cc @@ -2,11 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromecast/common/chromecast_config.h" +#include "chromecast/media/cma/pipeline/video_pipeline.h" namespace chromecast { +namespace media { -void ChromecastConfig::RegisterPlatformPrefs(PrefRegistrySimple* registry) { +VideoPipeline::VideoPipeline() { } +VideoPipeline::~VideoPipeline() { +} + +} // namespace media } // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/video_pipeline.h b/chromium/chromecast/media/cma/pipeline/video_pipeline.h new file mode 100644 index 00000000000..68a9b8ccbce --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/video_pipeline.h @@ -0,0 +1,28 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_H_ +#define CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_H_ + +#include "base/macros.h" + +namespace chromecast { +namespace media { +struct VideoPipelineClient; + +class VideoPipeline { + public: + VideoPipeline(); + virtual ~VideoPipeline(); + + virtual void SetClient(const VideoPipelineClient& client) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(VideoPipeline); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_H_ diff --git a/chromium/chromecast/media/cma/pipeline/video_pipeline_client.cc b/chromium/chromecast/media/cma/pipeline/video_pipeline_client.cc new file mode 100644 index 00000000000..bb7388e83c3 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/video_pipeline_client.cc @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/cma/pipeline/video_pipeline_client.h" + +namespace chromecast { +namespace media { + +VideoPipelineClient::VideoPipelineClient() { +} + +VideoPipelineClient::~VideoPipelineClient() { +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/video_pipeline_client.h b/chromium/chromecast/media/cma/pipeline/video_pipeline_client.h new file mode 100644 index 00000000000..105b8a2fe11 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/video_pipeline_client.h @@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_CLIENT_H_ +#define CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_CLIENT_H_ + +#include "base/callback.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" + +namespace gfx { +class Size; +} + +namespace chromecast { +namespace media { + +struct VideoPipelineClient { + typedef base::Callback<void( + const gfx::Size& natural_size)> NaturalSizeChangedCB; + + VideoPipelineClient(); + ~VideoPipelineClient(); + + // All the default callbacks. + AvPipelineClient av_pipeline_client; + + // Video resolution change notification. + NaturalSizeChangedCB natural_size_changed_cb; +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_PIPELINE_VIDEO_PIPELINE_CLIENT_H_ diff --git a/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc new file mode 100644 index 00000000000..fda8b445ba4 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc @@ -0,0 +1,178 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/cma/pipeline/video_pipeline_impl.h" + +#include "base/bind.h" +#include "chromecast/media/cma/backend/video_pipeline_device.h" +#include "chromecast/media/cma/base/buffering_defs.h" +#include "chromecast/media/cma/base/cma_logging.h" +#include "chromecast/media/cma/base/coded_frame_provider.h" +#include "chromecast/media/cma/base/decoder_config_adapter.h" +#include "chromecast/media/cma/pipeline/av_pipeline_impl.h" +#include "chromecast/public/media/decoder_config.h" +#include "media/base/video_decoder_config.h" + +namespace chromecast { +namespace media { + +namespace { +const size_t kMaxVideoFrameSize = 1024 * 1024; +} + +VideoPipelineImpl::VideoPipelineImpl(VideoPipelineDevice* video_device) + : video_device_(video_device), + weak_factory_(this) { + weak_this_ = weak_factory_.GetWeakPtr(); + av_pipeline_impl_.reset(new AvPipelineImpl( + video_device_, + base::Bind(&VideoPipelineImpl::OnUpdateConfig, base::Unretained(this)))); +} + +VideoPipelineImpl::~VideoPipelineImpl() { +} + +void VideoPipelineImpl::SetCodedFrameProvider( + scoped_ptr<CodedFrameProvider> frame_provider) { + av_pipeline_impl_->SetCodedFrameProvider( + frame_provider.Pass(), kAppVideoBufferSize, kMaxVideoFrameSize); +} + +bool VideoPipelineImpl::StartPlayingFrom( + base::TimeDelta time, + const scoped_refptr<BufferingState>& buffering_state) { + CMALOG(kLogControl) << "VideoPipelineImpl::StartPlayingFrom t0=" + << time.InMilliseconds(); + + // Reset the pipeline statistics. + previous_stats_ = ::media::PipelineStatistics(); + + // Start playing. + if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError) + return false; + DCHECK_EQ(av_pipeline_impl_->GetState(), AvPipelineImpl::kFlushed); + + if (!av_pipeline_impl_->StartPlayingFrom(time, buffering_state)) { + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kError); + return false; + } + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kPlaying); + + return true; +} + +void VideoPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << "VideoPipelineImpl::Flush"; + if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError) { + status_cb.Run(::media::PIPELINE_ERROR_ABORT); + return; + } + DCHECK_EQ(av_pipeline_impl_->GetState(), AvPipelineImpl::kPlaying); + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushing); + av_pipeline_impl_->Flush( + base::Bind(&VideoPipelineImpl::OnFlushDone, weak_this_, status_cb)); +} + +void VideoPipelineImpl::OnFlushDone( + const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << "VideoPipelineImpl::OnFlushDone"; + if (av_pipeline_impl_->GetState() == AvPipelineImpl::kError) { + status_cb.Run(::media::PIPELINE_ERROR_ABORT); + return; + } + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushed); + status_cb.Run(::media::PIPELINE_OK); +} + +void VideoPipelineImpl::Stop() { + CMALOG(kLogControl) << "VideoPipelineImpl::Stop"; + av_pipeline_impl_->Stop(); + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kStopped); +} + +void VideoPipelineImpl::SetCdm(BrowserCdmCast* media_keys) { + av_pipeline_impl_->SetCdm(media_keys); +} + +void VideoPipelineImpl::SetClient(const VideoPipelineClient& client) { + video_client_ = client; + av_pipeline_impl_->SetClient(client.av_pipeline_client); +} + +void VideoPipelineImpl::Initialize( + const ::media::VideoDecoderConfig& video_config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << "VideoPipelineImpl::Initialize " + << video_config.AsHumanReadableString(); + VideoPipelineDevice::VideoClient client; + client.natural_size_changed_cb = + base::Bind(&VideoPipelineImpl::OnNaturalSizeChanged, weak_this_); + video_device_->SetVideoClient(client); + if (frame_provider) + SetCodedFrameProvider(frame_provider.Pass()); + + if (!video_device_->SetConfig( + DecoderConfigAdapter::ToCastVideoConfig(video_config)) || + !av_pipeline_impl_->Initialize()) { + status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + av_pipeline_impl_->TransitionToState(AvPipelineImpl::kFlushed); + status_cb.Run(::media::PIPELINE_OK); +} + +void VideoPipelineImpl::OnUpdateConfig( + const ::media::AudioDecoderConfig& audio_config, + const ::media::VideoDecoderConfig& video_config) { + if (video_config.IsValidConfig()) { + CMALOG(kLogControl) << "VideoPipelineImpl::OnUpdateConfig " + << video_config.AsHumanReadableString(); + + bool success = video_device_->SetConfig( + DecoderConfigAdapter::ToCastVideoConfig(video_config)); + if (!success && + !video_client_.av_pipeline_client.playback_error_cb.is_null()) { + video_client_.av_pipeline_client.playback_error_cb.Run( + ::media::PIPELINE_ERROR_DECODE); + } + } +} + +void VideoPipelineImpl::OnNaturalSizeChanged(const gfx::Size& size) { + if (av_pipeline_impl_->GetState() != AvPipelineImpl::kPlaying) + return; + + if (!video_client_.natural_size_changed_cb.is_null()) + video_client_.natural_size_changed_cb.Run(size); +} + +void VideoPipelineImpl::UpdateStatistics() { + if (video_client_.av_pipeline_client.statistics_cb.is_null()) + return; + + MediaComponentDevice::Statistics device_stats; + if (!video_device_->GetStatistics(&device_stats)) + return; + + ::media::PipelineStatistics current_stats; + current_stats.video_bytes_decoded = device_stats.decoded_bytes; + current_stats.video_frames_decoded = device_stats.decoded_samples; + current_stats.video_frames_dropped = device_stats.dropped_samples; + + ::media::PipelineStatistics delta_stats; + delta_stats.video_bytes_decoded = + current_stats.video_bytes_decoded - previous_stats_.video_bytes_decoded; + delta_stats.video_frames_decoded = + current_stats.video_frames_decoded - previous_stats_.video_frames_decoded; + delta_stats.video_frames_dropped = + current_stats.video_frames_dropped - previous_stats_.video_frames_dropped; + + previous_stats_ = current_stats; + + video_client_.av_pipeline_client.statistics_cb.Run(delta_stats); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.h b/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.h new file mode 100644 index 00000000000..e065ac1df20 --- /dev/null +++ b/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.h @@ -0,0 +1,82 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CMA_BASE_VIDEO_PIPELINE_IMPL_H_ +#define CHROMECAST_MEDIA_CMA_BASE_VIDEO_PIPELINE_IMPL_H_ + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "chromecast/media/cma/pipeline/video_pipeline.h" +#include "chromecast/media/cma/pipeline/video_pipeline_client.h" + +namespace gfx { +class Size; +} + +namespace media { +class AudioDecoderConfig; +class VideoDecoderConfig; +} + +namespace chromecast { +namespace media { +class AvPipelineImpl; +class BrowserCdmCast; +class BufferingState; +class CodedFrameProvider; +class VideoPipelineDevice; + +class VideoPipelineImpl : public VideoPipeline { + public: + // |buffering_controller| can be NULL. + explicit VideoPipelineImpl(VideoPipelineDevice* video_device); + ~VideoPipelineImpl() override; + + // Input port of the pipeline. + void SetCodedFrameProvider(scoped_ptr<CodedFrameProvider> frame_provider); + + // Provide the CDM to use to decrypt samples. + void SetCdm(BrowserCdmCast* media_keys); + + // Functions to control the state of the audio pipeline. + void Initialize( + const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb); + bool StartPlayingFrom(base::TimeDelta time, + const scoped_refptr<BufferingState>& buffering_state); + void Flush(const ::media::PipelineStatusCB& status_cb); + void Stop(); + + // Update the playback statistics for this video stream. + void UpdateStatistics(); + + // VideoPipeline implementation. + void SetClient(const VideoPipelineClient& client) override; + + private: + void OnFlushDone(const ::media::PipelineStatusCB& status_cb); + void OnUpdateConfig(const ::media::AudioDecoderConfig& audio_config, + const ::media::VideoDecoderConfig& video_config); + void OnNaturalSizeChanged(const gfx::Size& size); + + VideoPipelineDevice* video_device_; + + scoped_ptr<AvPipelineImpl> av_pipeline_impl_; + VideoPipelineClient video_client_; + + ::media::PipelineStatistics previous_stats_; + + base::WeakPtr<VideoPipelineImpl> weak_this_; + base::WeakPtrFactory<VideoPipelineImpl> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(VideoPipelineImpl); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BASE_VIDEO_PIPELINE_IMPL_H_ diff --git a/chromium/chromecast/media/media.gyp b/chromium/chromecast/media/media.gyp index 9eaf8d1be75..20b1dc0c134 100644 --- a/chromium/chromecast/media/media.gyp +++ b/chromium/chromecast/media/media.gyp @@ -4,7 +4,10 @@ { 'variables': { + 'chromium_code': 1, 'chromecast_branding%': 'Chromium', + 'libcast_media_gyp%': '', + 'use_default_libcast_media%': 1, }, 'targets': [ { @@ -14,6 +17,7 @@ '../../base/base.gyp:base', '../../crypto/crypto.gyp:crypto', '../../third_party/widevine/cdm/widevine_cdm.gyp:widevine_cdm_version_h', + '<(libcast_media_gyp):libcast_media_1.0', ], 'sources': [ 'base/decrypt_context.cc', @@ -22,11 +26,17 @@ 'base/decrypt_context_clearkey.h', 'base/key_systems_common.cc', 'base/key_systems_common.h', + 'base/media_caps.cc', + 'base/media_caps.h', + 'base/media_codec_support.cc', + 'base/media_codec_support.h', + 'base/switching_media_renderer.cc', + 'base/switching_media_renderer.h', ], 'conditions': [ ['chromecast_branding=="Chrome"', { 'dependencies': [ - '<(cast_internal_gyp):media_base_internal', + '../internal/chromecast_internal.gyp:media_base_internal', ], }, { 'sources': [ @@ -36,6 +46,27 @@ ], }, { + 'target_name': 'media_cdm', + 'type': '<(component)', + 'dependencies': [ + 'media_base', + '../../base/base.gyp:base', + '../../media/media.gyp:media', + ], + 'sources': [ + 'cdm/browser_cdm_cast.cc', + 'cdm/browser_cdm_cast.h', + ], + 'conditions': [ + ['use_playready==1', { + 'sources': [ + 'cdm/playready_drm_delegate_android.cc', + 'cdm/playready_drm_delegate_android.h', + ], + }], + ], + }, + { 'target_name': 'cma_base', 'type': '<(component)', 'dependencies': [ @@ -51,6 +82,8 @@ 'cma/base/balanced_media_task_runner_factory.h', 'cma/base/buffering_controller.cc', 'cma/base/buffering_controller.h', + 'cma/base/buffering_defs.cc', + 'cma/base/buffering_defs.h', 'cma/base/buffering_frame_provider.cc', 'cma/base/buffering_frame_provider.h', 'cma/base/buffering_state.cc', @@ -62,6 +95,8 @@ 'cma/base/decoder_buffer_adapter.h', 'cma/base/decoder_buffer_base.cc', 'cma/base/decoder_buffer_base.h', + 'cma/base/decoder_config_adapter.cc', + 'cma/base/decoder_config_adapter.h', 'cma/base/media_task_runner.cc', 'cma/base/media_task_runner.h', ], @@ -93,15 +128,20 @@ 'cma/backend/media_pipeline_device_params.h', 'cma/backend/video_pipeline_device.cc', 'cma/backend/video_pipeline_device.h', + 'cma/backend/video_plane.cc', + 'cma/backend/video_plane.h', + 'cma/backend/video_plane_fake.cc', + 'cma/backend/video_plane_fake.h', ], 'conditions': [ ['chromecast_branding=="Chrome"', { 'dependencies': [ - '<(cast_internal_gyp):cma_backend_internal', + '../internal/chromecast_internal.gyp:cma_backend_internal', ], }, { 'sources': [ 'cma/backend/media_pipeline_device_fake_factory.cc', + 'cma/backend/video_plane_fake_factory.cc', ], }], ], @@ -145,6 +185,54 @@ ], }, { + 'target_name': 'cma_pipeline', + 'type': '<(component)', + 'dependencies': [ + 'cma_backend', + 'cma_base', + 'media_base', + 'media_cdm', + '../../base/base.gyp:base', + '../../crypto/crypto.gyp:crypto', + '../../media/media.gyp:media', + ], + 'conditions': [ + ['chromecast_branding=="Chrome"', { + 'dependencies': [ + '../internal/cast_system.gyp:openssl', + ], + }, { + 'dependencies': [ + '../../third_party/boringssl/boringssl.gyp:boringssl', + ], + }], + ], + 'sources': [ + 'cma/pipeline/audio_pipeline.cc', + 'cma/pipeline/audio_pipeline.h', + 'cma/pipeline/audio_pipeline_impl.cc', + 'cma/pipeline/audio_pipeline_impl.h', + 'cma/pipeline/av_pipeline_client.cc', + 'cma/pipeline/av_pipeline_client.h', + 'cma/pipeline/av_pipeline_impl.cc', + 'cma/pipeline/av_pipeline_impl.h', + 'cma/pipeline/decrypt_util.cc', + 'cma/pipeline/decrypt_util.h', + 'cma/pipeline/load_type.h', + 'cma/pipeline/media_pipeline.h', + 'cma/pipeline/media_pipeline_client.cc', + 'cma/pipeline/media_pipeline_client.h', + 'cma/pipeline/media_pipeline_impl.cc', + 'cma/pipeline/media_pipeline_impl.h', + 'cma/pipeline/video_pipeline.cc', + 'cma/pipeline/video_pipeline.h', + 'cma/pipeline/video_pipeline_client.cc', + 'cma/pipeline/video_pipeline_client.h', + 'cma/pipeline/video_pipeline_impl.cc', + 'cma/pipeline/video_pipeline_impl.h', + ], + }, + { 'target_name': 'cma_filters', 'type': '<(component)', 'dependencies': [ @@ -153,6 +241,8 @@ 'cma_base', ], 'sources': [ + 'cma/filters/cma_renderer.cc', + 'cma/filters/cma_renderer.h', 'cma/filters/demuxer_stream_adapter.cc', 'cma/filters/demuxer_stream_adapter.h', ], @@ -166,6 +256,8 @@ 'cma_filters', 'cma_ipc', 'cma_ipc_streamer', + 'cma_pipeline', + 'media_cdm', ], }, { @@ -191,6 +283,7 @@ 'cma/ipc/media_message_fifo_unittest.cc', 'cma/ipc/media_message_unittest.cc', 'cma/ipc_streamer/av_streamer_unittest.cc', + 'cma/pipeline/audio_video_pipeline_impl_unittest.cc', 'cma/test/frame_generator_for_test.cc', 'cma/test/frame_generator_for_test.h', 'cma/test/frame_segmenter_for_test.cc', @@ -204,5 +297,24 @@ 'cma/test/run_all_unittests.cc', ], }, + ], # end of targets + 'conditions': [ + ['use_default_libcast_media==1', { + 'targets': [ + { + 'target_name': 'libcast_media_1.0', + 'type': 'shared_library', + 'dependencies': [ + '../../chromecast/chromecast.gyp:cast_public_api' + ], + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'base/cast_media_default.cc', + ], + } + ] + }], ], } diff --git a/chromium/chromecast/net/connectivity_checker.cc b/chromium/chromecast/net/connectivity_checker.cc new file mode 100644 index 00000000000..de3081291f3 --- /dev/null +++ b/chromium/chromecast/net/connectivity_checker.cc @@ -0,0 +1,184 @@ +// Copyright 2015 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/net/connectivity_checker.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "chromecast/net/net_switches.h" +#include "net/base/request_priority.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_response_info.h" +#include "net/http/http_status_code.h" +#include "net/proxy/proxy_config.h" +#include "net/proxy/proxy_config_service_fixed.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" + +namespace chromecast { + +namespace { + +// How often connectivity checks are performed in seconds +const unsigned int kConnectivityPeriodSeconds = 1; + +// Number of consecutive bad responses received before connectivity status is +// changed to offline +const unsigned int kNumBadResponses = 3; + +// Default url for connectivity checking. +const char kDefaultConnectivityCheckUrl[] = + "https://clients3.google.com/generate_204"; + +} // namespace + +ConnectivityChecker::ConnectivityChecker( + const scoped_refptr<base::MessageLoopProxy>& loop_proxy) + : connectivity_observer_list_( + new ObserverListThreadSafe<ConnectivityObserver>()), + loop_proxy_(loop_proxy), + connected_(false), + bad_responses_(0) { + DCHECK(loop_proxy_.get()); + loop_proxy->PostTask(FROM_HERE, + base::Bind(&ConnectivityChecker::Initialize, this)); +} + +void ConnectivityChecker::Initialize() { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + base::CommandLine::StringType check_url_str = + command_line->GetSwitchValueNative(switches::kConnectivityCheckUrl); + connectivity_check_url_.reset(new GURL( + check_url_str.empty() ? kDefaultConnectivityCheckUrl : check_url_str)); + + net::URLRequestContextBuilder builder; + builder.set_proxy_config_service( + new net::ProxyConfigServiceFixed(net::ProxyConfig::CreateDirect())); + builder.DisableHttpCache(); + url_request_context_.reset(builder.Build()); + + net::NetworkChangeNotifier::AddConnectionTypeObserver(this); + net::NetworkChangeNotifier::AddIPAddressObserver(this); + loop_proxy_->PostTask(FROM_HERE, + base::Bind(&ConnectivityChecker::Check, this)); +} + +ConnectivityChecker::~ConnectivityChecker() { + DCHECK(loop_proxy_.get()); + net::NetworkChangeNotifier::RemoveIPAddressObserver(this); + net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); + loop_proxy_->DeleteSoon(FROM_HERE, url_request_context_.release()); + loop_proxy_->DeleteSoon(FROM_HERE, url_request_.release()); +} + +void ConnectivityChecker::AddConnectivityObserver( + ConnectivityObserver* observer) { + connectivity_observer_list_->AddObserver(observer); +} + +void ConnectivityChecker::RemoveConnectivityObserver( + ConnectivityObserver* observer) { + connectivity_observer_list_->RemoveObserver(observer); +} + +bool ConnectivityChecker::Connected() const { + return connected_; +} + +void ConnectivityChecker::SetConnectivity(bool connected) { + if (connected_ == connected) + return; + + connected_ = connected; + connectivity_observer_list_->Notify( + FROM_HERE, &ConnectivityObserver::OnConnectivityChanged, connected); + LOG(INFO) << "Global connection is: " << (connected ? "Up" : "Down"); +} + +void ConnectivityChecker::Check() { + if (!loop_proxy_->BelongsToCurrentThread()) { + loop_proxy_->PostTask(FROM_HERE, + base::Bind(&ConnectivityChecker::Check, this)); + return; + } + DCHECK(url_request_context_.get()); + + // Don't check connectivity if network is offline, because internet could be + // accessible via netifs ignored. + if (net::NetworkChangeNotifier::IsOffline()) + return; + + // If url_request_ is non-null, there is already a check going on. Don't + // start another. + if (url_request_.get()) + return; + + VLOG(1) << "Connectivity check: url=" << *connectivity_check_url_; + url_request_ = url_request_context_->CreateRequest( + *connectivity_check_url_, net::MAXIMUM_PRIORITY, this); + url_request_->set_method("HEAD"); + url_request_->Start(); +} + +void ConnectivityChecker::OnConnectionTypeChanged( + net::NetworkChangeNotifier::ConnectionType type) { + VLOG(2) << "OnConnectionTypeChanged " << type; + if (type == net::NetworkChangeNotifier::CONNECTION_NONE) + SetConnectivity(false); + + Cancel(); + Check(); +} + +void ConnectivityChecker::OnIPAddressChanged() { + VLOG(2) << "OnIPAddressChanged"; + + Cancel(); + Check(); +} + +void ConnectivityChecker::OnResponseStarted(net::URLRequest* request) { + int http_response_code = + (request->status().is_success() && + request->response_info().headers.get() != NULL) + ? request->response_info().headers->response_code() + : net::HTTP_BAD_REQUEST; + + // Clears resources. + url_request_.reset(NULL); // URLRequest::Cancel() is called in destructor. + + if (http_response_code < 400) { + VLOG(1) << "Connectivity check succeeded"; + bad_responses_ = 0; + SetConnectivity(true); + return; + } + + VLOG(1) << "Connectivity check failed: " << http_response_code; + ++bad_responses_; + if (bad_responses_ > kNumBadResponses) { + bad_responses_ = kNumBadResponses; + SetConnectivity(false); + } + + // Check again + loop_proxy_->PostDelayedTask( + FROM_HERE, base::Bind(&ConnectivityChecker::Check, this), + base::TimeDelta::FromSeconds(kConnectivityPeriodSeconds)); +} + +void ConnectivityChecker::OnReadCompleted(net::URLRequest* request, + int bytes_read) { + NOTREACHED(); +} + +void ConnectivityChecker::Cancel() { + if (url_request_.get()) { + VLOG(2) << "Cancel connectivity check in progress"; + url_request_.reset(NULL); // URLRequest::Cancel() is called in destructor. + } +} + +} // namespace chromecast diff --git a/chromium/chromecast/net/connectivity_checker.h b/chromium/chromecast/net/connectivity_checker.h new file mode 100644 index 00000000000..c52b8a624da --- /dev/null +++ b/chromium/chromecast/net/connectivity_checker.h @@ -0,0 +1,99 @@ +// Copyright 2015 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_NET_CONNECTIVITY_CHECKER_H_ +#define CHROMECAST_NET_CONNECTIVITY_CHECKER_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "net/base/network_change_notifier.h" +#include "net/url_request/url_request.h" + +class GURL; + +namespace base { +class MessageLoopProxy; +} + +namespace net { +class URLRequestContext; +} + +namespace chromecast { + +// Simple class to check network connectivity by sending a HEAD http request +// to given url. +class ConnectivityChecker + : public base::RefCountedThreadSafe<ConnectivityChecker>, + public net::URLRequest::Delegate, + public net::NetworkChangeNotifier::ConnectionTypeObserver, + public net::NetworkChangeNotifier::IPAddressObserver { + public: + class ConnectivityObserver { + public: + // Will be called when internet connectivity changes + virtual void OnConnectivityChanged(bool connected) = 0; + + protected: + ConnectivityObserver() {} + virtual ~ConnectivityObserver() {} + + private: + DISALLOW_COPY_AND_ASSIGN(ConnectivityObserver); + }; + + explicit ConnectivityChecker( + const scoped_refptr<base::MessageLoopProxy>& loop_proxy); + + void AddConnectivityObserver(ConnectivityObserver* observer); + void RemoveConnectivityObserver(ConnectivityObserver* observer); + + // Returns if there is internet connectivity + bool Connected() const; + + // Checks for connectivity + void Check(); + + protected: + ~ConnectivityChecker() override; + + private: + friend class base::RefCountedThreadSafe<ConnectivityChecker>; + + // UrlRequest::Delegate implementation: + void OnResponseStarted(net::URLRequest* request) override; + void OnReadCompleted(net::URLRequest* request, int bytes_read) override; + + // Initializes ConnectivityChecker + void Initialize(); + + // NetworkChangeNotifier::ConnectionTypeObserver implementation: + void OnConnectionTypeChanged( + net::NetworkChangeNotifier::ConnectionType type) override; + + // net::NetworkChangeNotifier::IPAddressObserver implementation: + void OnIPAddressChanged() override; + + // Cancels current connectivity checking in progress. + void Cancel(); + + // Sets connectivity and alerts observers if it has changed + void SetConnectivity(bool connected); + + scoped_ptr<GURL> connectivity_check_url_; + scoped_ptr<net::URLRequestContext> url_request_context_; + scoped_ptr<net::URLRequest> url_request_; + const scoped_refptr<ObserverListThreadSafe<ConnectivityObserver> > + connectivity_observer_list_; + const scoped_refptr<base::MessageLoopProxy> loop_proxy_; + bool connected_; + unsigned int bad_responses_; + + DISALLOW_COPY_AND_ASSIGN(ConnectivityChecker); +}; + +} // namespace chromecast + +#endif // CHROMECAST_NET_CONNECTIVITY_CHECKER_H_ diff --git a/chromium/chromecast/net/net_switches.cc b/chromium/chromecast/net/net_switches.cc new file mode 100644 index 00000000000..d50b1d88b64 --- /dev/null +++ b/chromium/chromecast/net/net_switches.cc @@ -0,0 +1,17 @@ +// Copyright 2015 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/net/net_switches.h" + +namespace switches { + +// Url for network connectivity checking. Default is +// "https://clients3.google.com/generate_204". +const char kConnectivityCheckUrl[] = "connectivity-check-url"; + +// List of network interfaces to ignore. Ignored interfaces will not be used +// for network connectivity. +const char kNetifsToIgnore[] = "netifs-to-ignore"; + +} // namespace switches diff --git a/chromium/chromecast/net/net_switches.h b/chromium/chromecast/net/net_switches.h new file mode 100644 index 00000000000..d8c71f34db5 --- /dev/null +++ b/chromium/chromecast/net/net_switches.h @@ -0,0 +1,15 @@ +// Copyright 2015 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_NET_NET_SWITCHES_H_ +#define CHROMECAST_NET_NET_SWITCHES_H_ + +namespace switches { + +extern const char kConnectivityCheckUrl[]; +extern const char kNetifsToIgnore[]; + +} // namespace switches + +#endif // CHROMECAST_NET_NET_SWITCHES_H_ diff --git a/chromium/chromecast/net/net_util_cast.cc b/chromium/chromecast/net/net_util_cast.cc new file mode 100644 index 00000000000..6e688316c8a --- /dev/null +++ b/chromium/chromecast/net/net_util_cast.cc @@ -0,0 +1,33 @@ +// Copyright 2015 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/net/net_util_cast.h" + +#include "base/command_line.h" +#include "base/strings/string_split.h" +#include "chromecast/base/cast_sys_info_util.h" +#include "chromecast/net/net_switches.h" +#include "chromecast/public/cast_sys_info.h" + +namespace chromecast { + +base::hash_set<std::string> GetIgnoredInterfaces() { + base::hash_set<std::string> ignored_interfaces; + scoped_ptr<CastSysInfo> sys_info = CreateSysInfo(); + if (!sys_info->GetApInterface().empty()) + ignored_interfaces.insert(sys_info->GetApInterface()); + + // Add interfaces from "netif-to-ignore" switch. + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + base::CommandLine::StringType netifs_to_ignore_str = + command_line->GetSwitchValueNative(switches::kNetifsToIgnore); + base::CommandLine::StringVector netifs_to_ignore_vector; + base::SplitString(netifs_to_ignore_str, ',', &netifs_to_ignore_vector); + for (const auto& netif : netifs_to_ignore_vector) + ignored_interfaces.insert(netif); + + return ignored_interfaces; +} + +} // namespace chromecast diff --git a/chromium/chromecast/net/net_util_cast.h b/chromium/chromecast/net/net_util_cast.h new file mode 100644 index 00000000000..2ec95bbd985 --- /dev/null +++ b/chromium/chromecast/net/net_util_cast.h @@ -0,0 +1,20 @@ +// Copyright 2015 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_NET_NET_UTIL_H_ +#define CHROMECAST_NET_NET_UTIL_H_ + +#include <string> + +#include "base/containers/hash_tables.h" + +namespace chromecast { + +// Gets the list of interfaces that should be ignored. The interfaces returned +// by this function will not be used to connect to the internet. +base::hash_set<std::string> GetIgnoredInterfaces(); + +} // namespace chromecast + +#endif // CHROMECAST_NET_NET_UTIL_CAST_H_ diff --git a/chromium/chromecast/net/network_change_notifier_cast.cc b/chromium/chromecast/net/network_change_notifier_cast.cc deleted file mode 100644 index ab6a218b189..00000000000 --- a/chromium/chromecast/net/network_change_notifier_cast.cc +++ /dev/null @@ -1,20 +0,0 @@ -// 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/net/network_change_notifier_cast.h" - -namespace chromecast { - -NetworkChangeNotifierCast::NetworkChangeNotifierCast() { -} - -NetworkChangeNotifierCast::~NetworkChangeNotifierCast() { -} - -net::NetworkChangeNotifier::ConnectionType -NetworkChangeNotifierCast::GetCurrentConnectionType() const { - return net::NetworkChangeNotifier::CONNECTION_NONE; -} - -} // namespace chromecast diff --git a/chromium/chromecast/net/network_change_notifier_cast.h b/chromium/chromecast/net/network_change_notifier_cast.h deleted file mode 100644 index adf85607804..00000000000 --- a/chromium/chromecast/net/network_change_notifier_cast.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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_NET_NETWORK_CHANGE_NOTIFIER_CAST_H_ -#define CHROMECAST_NET_NETWORK_CHANGE_NOTIFIER_CAST_H_ - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "net/base/network_change_notifier.h" - -namespace chromecast { - -// TODO(lcwu): http://crbug.com/391064. This is a place holder for -// cast-specific NetworkChangeNotifier implementation. The actual -// implementation of this class will come in later CLs. -class NetworkChangeNotifierCast : public net::NetworkChangeNotifier { - public: - NetworkChangeNotifierCast(); - virtual ~NetworkChangeNotifierCast(); - - // net::NetworkChangeNotifier implementation: - virtual net::NetworkChangeNotifier::ConnectionType - GetCurrentConnectionType() const override; - - private: - friend class NetworkChangeNotifierCastTest; - - DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierCast); -}; - -} // namespace chromecast - -#endif // CHROMECAST_NET_NETWORK_CHANGE_NOTIFIER_CAST_H_ diff --git a/chromium/chromecast/net/network_change_notifier_factory_cast.cc b/chromium/chromecast/net/network_change_notifier_factory_cast.cc index c446b1e5768..4c1cd604d10 100644 --- a/chromium/chromecast/net/network_change_notifier_factory_cast.cc +++ b/chromium/chromecast/net/network_change_notifier_factory_cast.cc @@ -1,16 +1,17 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2015 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/net/network_change_notifier_factory_cast.h" -#include "chromecast/net/network_change_notifier_cast.h" +#include "chromecast/net/net_util_cast.h" +#include "net/base/network_change_notifier_linux.h" namespace chromecast { net::NetworkChangeNotifier* NetworkChangeNotifierFactoryCast::CreateInstance() { // Caller assumes ownership. - return new NetworkChangeNotifierCast(); + return new net::NetworkChangeNotifierLinux(GetIgnoredInterfaces()); } NetworkChangeNotifierFactoryCast::~NetworkChangeNotifierFactoryCast() { diff --git a/chromium/chromecast/net/network_change_notifier_factory_cast.h b/chromium/chromecast/net/network_change_notifier_factory_cast.h index 281a18d060a..058d5b91c6b 100644 --- a/chromium/chromecast/net/network_change_notifier_factory_cast.h +++ b/chromium/chromecast/net/network_change_notifier_factory_cast.h @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2015 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. @@ -17,12 +17,10 @@ class NetworkChangeNotifierFactoryCast : public net::NetworkChangeNotifierFactory { public: NetworkChangeNotifierFactoryCast() {} - virtual ~NetworkChangeNotifierFactoryCast(); + ~NetworkChangeNotifierFactoryCast() override; // net::NetworkChangeNotifierFactory implementation: - virtual net::NetworkChangeNotifier* CreateInstance() override; - - static NetworkChangeNotifierCast* GetInstance(); + net::NetworkChangeNotifier* CreateInstance() override; private: DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierFactoryCast); diff --git a/chromium/chromecast/public/DEPS b/chromium/chromecast/public/DEPS new file mode 100644 index 00000000000..30d9ea2d336 --- /dev/null +++ b/chromium/chromecast/public/DEPS @@ -0,0 +1,8 @@ +include_rules = [ + # chromecast/public should not depend on anything Chromium specific + "-chromecast", + "-base", + "-content", + "-net", + "-ui", +] diff --git a/chromium/chromecast/public/cast_egl_platform.h b/chromium/chromecast/public/cast_egl_platform.h new file mode 100644 index 00000000000..c1350f6964f --- /dev/null +++ b/chromium/chromecast/public/cast_egl_platform.h @@ -0,0 +1,60 @@ +// Copyright 2015 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_PUBLIC_CAST_EGL_PLATFORM_H_ +#define CHROMECAST_PUBLIC_CAST_EGL_PLATFORM_H_ + +namespace chromecast { + +struct Size; + +// Interface representing all the hardware-specific elements of an Ozone +// implementation for Cast. Supply an implementation of this interface +// to OzonePlatformCast to create a complete Ozone implementation. +class CastEglPlatform { + public: + typedef void* (*GLGetProcAddressProc)(const char* name); + typedef void* NativeDisplayType; + typedef void* NativeWindowType; + + virtual ~CastEglPlatform() {} + + // Returns an array of EGL properties, which can be used in any EGL function + // used to select a display configuration. Note that all properties should be + // immediately followed by the corresponding desired value and array should be + // terminated with EGL_NONE. Ownership of the array is not transferred to + // caller. desired_list contains list of desired EGL properties and values. + virtual const int* GetEGLSurfaceProperties(const int* desired_list) = 0; + + // Initialize/ShutdownHardware are called at most once each over the object's + // lifetime. Initialize will be called before creating display type or + // window. If Initialize fails, return false (Shutdown will still be called). + virtual bool InitializeHardware() = 0; + virtual void ShutdownHardware() = 0; + + // These three are called once after hardware is successfully initialized. + // The implementation must load the libraries containing EGL and GLES2 + // bindings (return the pointer obtained from dlopen). It must also supply + // a function pointer to eglGetProcAddress or equivalent. + virtual void* GetEglLibrary() = 0; + virtual void* GetGles2Library() = 0; + virtual GLGetProcAddressProc GetGLProcAddressProc() = 0; + + // Creates/destroys an EGLNativeDisplayType. These may be called multiple + // times over the object's lifetime, for example to release the display when + // switching to an external application. There will be at most one display + // type at a time. + virtual NativeDisplayType CreateDisplayType(const Size& size) = 0; + virtual void DestroyDisplayType(NativeDisplayType display_type) = 0; + + // Creates/destroys an EGLNativeWindow. There will be at most one window at a + // time, created within a valid display type. + virtual NativeWindowType CreateWindow(NativeDisplayType display_type, + const Size& size) = 0; + virtual void DestroyWindow(NativeWindowType window) = 0; +}; + +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_CAST_EGL_PLATFORM_H_ diff --git a/chromium/chromecast/public/cast_egl_platform_shlib.h b/chromium/chromecast/public/cast_egl_platform_shlib.h new file mode 100644 index 00000000000..4a091406f1a --- /dev/null +++ b/chromium/chromecast/public/cast_egl_platform_shlib.h @@ -0,0 +1,25 @@ +// Copyright 2015 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_PUBLIC_CAST_EGL_PLATFORM_SHLIB_H_ +#define CHROMECAST_PUBLIC_CAST_EGL_PLATFORM_SHLIB_H_ + +#include <string> +#include <vector> + +#include "chromecast_export.h" + +namespace chromecast { + +class CastEglPlatform; + +// Entry point for loading CastEglPlatform from shared library. +class CHROMECAST_EXPORT CastEglPlatformShlib { + public: + static CastEglPlatform* Create(const std::vector<std::string>& argv); +}; + +} + +#endif // CHROMECAST_PUBLIC_CAST_EGL_PLATFORM_SHLIB_H_ diff --git a/chromium/chromecast/public/cast_media_shlib.h b/chromium/chromecast/public/cast_media_shlib.h new file mode 100644 index 00000000000..6f2d60d6cd1 --- /dev/null +++ b/chromium/chromecast/public/cast_media_shlib.h @@ -0,0 +1,40 @@ +// Copyright 2015 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_PUBLIC_CAST_MEDIA_SHLIB_H_ +#define CHROMECAST_PUBLIC_CAST_MEDIA_SHLIB_H_ + +#include <string> +#include <vector> + +#include "chromecast_export.h" + +namespace chromecast { +namespace media { + +// Provides access to platform-specific media systems and hardware resources. +// In cast_shell, all usage is from the browser process. An implementation is +// assumed to be in an uninitialized state initially. When uninitialized, no +// API calls will be made except for Initialize, which brings the implementation +// into an initialized state. A call to Finalize returns the implementation to +// its uninitialized state. The implementation must support multiple +// transitions between these states, to support resource grant/revoke events and +// also to allow multiple unit tests to bring up the media systems in isolation +// from other tests. +class CHROMECAST_EXPORT CastMediaShlib { + public: + // Initializes platform-specific media systems. Only called when in an + // uninitialized state. + static void Initialize(const std::vector<std::string>& argv); + + // Tears down platform-specific media systems and returns to the uninitialized + // state. The implementation must release all media-related hardware + // resources. + static void Finalize(); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_CAST_MEDIA_SHLIB_H_ diff --git a/chromium/chromecast/public/cast_sys_info.h b/chromium/chromecast/public/cast_sys_info.h new file mode 100644 index 00000000000..6297f5b3e2e --- /dev/null +++ b/chromium/chromecast/public/cast_sys_info.h @@ -0,0 +1,75 @@ +// Copyright 2015 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_PUBLIC_CAST_SYS_INFO_H_ +#define CHROMECAST_PUBLIC_CAST_SYS_INFO_H_ + +#include <string> +#include <vector> + +namespace chromecast { + +// Pure abstract interface for system information which is accessed by other +// processes as well as cast_shell browser process. All information should be +// immutable. +// It should be possible to instantiate multiple instances of CastSysInfo and +// should be able to be instantiated at any point in the startup process. Other +// processes must be able to create an instance of CastSysInfo. +class CastSysInfo { + public: + enum BuildType { + BUILD_ENG, + BUILD_BETA, + BUILD_PRODUCTION, + }; + + virtual ~CastSysInfo() {} + + // Returns the system build type. + virtual BuildType GetBuildType() = 0; + // Returns release channel of system. + virtual std::string GetSystemReleaseChannel() = 0; + // Returns serial number of the device. + virtual std::string GetSerialNumber() = 0; + // Returns product code name of the device. + virtual std::string GetProductName() = 0; + // Returns model name of device (eg: Chromecast, Nexus Player, ...). + virtual std::string GetDeviceModel() = 0; + // Returns the board's name. + virtual std::string GetBoardName() = 0; + // Returns the revision of board (eg: 514, ...). + virtual std::string GetBoardRevision() = 0; + // Returns device manufacturer (eg: Google, ...). + virtual std::string GetManufacturer() = 0; + // Returns the system's build number (eg: 100, 20000 ...). + // This describes system version which may be different with + // CAST_BUILD_NUMBER. + virtual std::string GetSystemBuildNumber() = 0; + + // Returns default country and locale baked from the factory. + virtual std::string GetFactoryCountry() = 0; + virtual std::string GetFactoryLocale(std::string* second_locale) = 0; + + // Returns the name of the wifi interface used to connect to the internet. + virtual std::string GetWifiInterface() = 0; + // Returns the name of the software AP interface. + virtual std::string GetApInterface() = 0; + + // Returns the GL_VENDOR string of GPU driver. Must match the value that would + // be obtained from a GL context, but implementations must *not* create a GL + // context. + virtual std::string GetGlVendor() = 0; + // Returns the GL_RENDERER string of GPU driver. Must match the value that + // would be obtained from a GL context, but implementations must *not* create + // a GL context. + virtual std::string GetGlRenderer() = 0; + // Returns the GL_VERSION string of GPU driver. Must match the value that + // would be obtained from a GL context, but implementations must *not* create + // a GL context. + virtual std::string GetGlVersion() = 0; +}; + +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_CAST_SYS_INFO_H_ diff --git a/chromium/chromecast/public/chromecast_export.h b/chromium/chromecast/public/chromecast_export.h new file mode 100644 index 00000000000..022ffc7843a --- /dev/null +++ b/chromium/chromecast/public/chromecast_export.h @@ -0,0 +1,12 @@ +// Copyright 2015 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_PUBLIC_CHROMECAST_EXPORT_H_ +#define CHROMECAST_PUBLIC_CHROMECAST_EXPORT_H_ + +// Export attribute for classes that are exposed in shared libraries, +// allowing OEM partners to replace with their own implementations. +#define CHROMECAST_EXPORT __attribute__((visibility("default"))) + +#endif // CHROMECAST_PUBLIC_CHROMECAST_EXPORT_H_ diff --git a/chromium/chromecast/public/graphics_properties_shlib.h b/chromium/chromecast/public/graphics_properties_shlib.h new file mode 100644 index 00000000000..693a7c1c52d --- /dev/null +++ b/chromium/chromecast/public/graphics_properties_shlib.h @@ -0,0 +1,32 @@ +// Copyright 2015 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_PUBLIC_GRAPHICS_PROPERTIES_SHLIB_H_ +#define CHROMECAST_PUBLIC_GRAPHICS_PROPERTIES_SHLIB_H_ + +#include <string> +#include <vector> + +#include "chromecast_export.h" + +namespace chromecast { + +class CHROMECAST_EXPORT GraphicsPropertiesShlib { + public: + // Optional resolutions that cast_shell queries for. 720p (1280x720) is + // assumed to be supported. + enum Resolution { + k1080p, // 1920x1080 + kUHDTV // 3840x2160 + }; + + // Returns whether or not the given display resolution is supported. + // Called in the browser process; command line args are provided. + static bool IsSupported(Resolution resolution, + const std::vector<std::string>& argv); +}; + +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_GRAPHICS_PROPERTIES_SHLIB_H_ diff --git a/chromium/chromecast/public/graphics_types.h b/chromium/chromecast/public/graphics_types.h new file mode 100644 index 00000000000..09ac12ba29b --- /dev/null +++ b/chromium/chromecast/public/graphics_types.h @@ -0,0 +1,30 @@ +// Copyright (c) 2015 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_PUBLIC_GRAPHICS_TYPES_H_ +#define CHROMECAST_PUBLIC_GRAPHICS_TYPES_H_ + +namespace chromecast { + +struct Rect { + Rect(int w, int h) : x(0), y(0), width(w), height(h) {} + Rect(int arg_x, int arg_y, int w, int h) + : x(arg_x), y(arg_y), width(w), height(h) {} + + int x; + int y; + int width; + int height; +}; + +struct Size { + Size(int w, int h) : width(w), height(h) {} + + int width; + int height; +}; + +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_GRAPHICS_TYPES_H_ diff --git a/chromium/chromecast/public/media/decoder_config.h b/chromium/chromecast/public/media/decoder_config.h new file mode 100644 index 00000000000..b1077af5763 --- /dev/null +++ b/chromium/chromecast/public/media/decoder_config.h @@ -0,0 +1,157 @@ +// Copyright 2015 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_PUBLIC_MEDIA_DECODER_CONFIG_H_ +#define CHROMECAST_PUBLIC_MEDIA_DECODER_CONFIG_H_ + +#include <stdint.h> +#include <vector> + +namespace chromecast { +namespace media { + +namespace { + +// Maximum audio bytes per sample. +static const int kMaxBytesPerSample = 4; + +// Maximum audio sampling rate. +static const int kMaxSampleRate = 192000; + +} // namespace + +enum AudioCodec { + kAudioCodecUnknown = -1, + + kAudioCodecMin = 0, + kCodecAAC = kAudioCodecMin, + kCodecMP3, + kCodecPCM, + kCodecPCM_S16BE, + kCodecVorbis, + kCodecOpus, + kCodecEAC3, + kCodecAC3, + kCodecDTS, + kAudioCodecMax = kCodecDTS, +}; + +enum VideoCodec { + kVideoCodecUnknown = -1, + + kVideoCodecMin = 0, + kCodecH264 = kVideoCodecMin, + kCodecVC1, + kCodecMPEG2, + kCodecMPEG4, + kCodecTheora, + kCodecVP8, + kCodecVP9, + kCodecHEVC, + kVideoCodecMax = kCodecHEVC, +}; + +// Profile for Video codec. +enum VideoProfile { + kVideoProfileUnknown = -1, + + kVideoProfileMin = 0, + kH264Baseline = kVideoProfileMin, + kH264Main, + kH264Extended, + kH264High, + kH264High10, + kH264High422, + kH264High444Predictive, + kH264ScalableBaseline, + kH264ScalableHigh, + kH264Stereohigh, + kH264MultiviewHigh, + kVP8ProfileAny, + kVP9ProfileAny, + kVideoProfileMax = kVP9ProfileAny, +}; + +// TODO(erickung): Remove constructor once CMA backend implementation does't +// create a new object to reset the configuration and use IsValidConfig() to +// determine if the configuration is still valid or not. +struct AudioConfig { + AudioConfig() + : codec(kAudioCodecUnknown), + bytes_per_channel(0), + channel_number(0), + samples_per_second(0), + extra_data(nullptr), + extra_data_size(0), + is_encrypted(false) {} + + // Audio codec. + AudioCodec codec; + // Number of bytes in each channel. + int bytes_per_channel; + // Number of channels in this audio stream. + int channel_number; + // Number of audio samples per second. + int samples_per_second; + // Pointer to extra data buffer for certain codec initialization. The memory + // is allocated outside this structure. Consumers of the structure should make + // a copy if it is expected to be used beyond the function ends. + const uint8_t* extra_data; + // Size of extra data in bytes. + int extra_data_size; + // content is encrypted or not. + bool is_encrypted; +}; + +// TODO(erickung): Remove constructor once CMA backend implementation does't +// create a new object to reset the configuration and use IsValidConfig() to +// determine if the configuration is still valid or not. +struct VideoConfig { + VideoConfig() + : codec(kVideoCodecUnknown), + profile(kVideoProfileUnknown), + additional_config(nullptr), + extra_data(nullptr), + extra_data_size(0), + is_encrypted(false) {} + + // Video codec. + VideoCodec codec; + // Video codec profile. + VideoProfile profile; + // Both |additional_config| and |extra_data| are the pointers to the object + // memory that are allocated outside this structure. Consumers of the + // structure should make a copy if it is expected to be used beyond the + // function ends. + // Additional video config for the video stream if available. + VideoConfig* additional_config; + // Pointer to extra data buffer for certain codec initialization. + const uint8_t* extra_data; + // Size of extra data in bytes. + int extra_data_size; + // content is encrypted or not. + bool is_encrypted; +}; + +// TODO(erickung): Remove following two inline IsValidConfig() functions. These +// are to keep existing CMA backend implementation consistent until the clean up +// is done. These SHOULD NOT be used in New CMA backend implementation. +inline bool IsValidConfig(const AudioConfig& config) { + return config.codec >= kAudioCodecMin && + config.codec <= kAudioCodecMax && + config.channel_number > 0 && + config.bytes_per_channel > 0 && + config.bytes_per_channel <= kMaxBytesPerSample && + config.samples_per_second > 0 && + config.samples_per_second <= kMaxSampleRate; +} + +inline bool IsValidConfig(const VideoConfig& config) { + return config.codec >= kVideoCodecMin && config.codec <= kVideoCodecMax; +} + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_MEDIA_DECODER_CONFIG_H_ diff --git a/chromium/chromecast/public/osd_plane.h b/chromium/chromecast/public/osd_plane.h new file mode 100644 index 00000000000..d689cba5440 --- /dev/null +++ b/chromium/chromecast/public/osd_plane.h @@ -0,0 +1,40 @@ +// Copyright (c) 2015 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_PUBLIC_OSD_PLANE_H_ +#define CHROMECAST_PUBLIC_OSD_PLANE_H_ + +namespace chromecast { + +class OsdSurface; +struct Rect; +struct Size; + +// Abstract graphics plane for OSD, to be implemented in platform-specific way. +// Platform must composite this plane on top of main (GL-based) graphics plane. +class OsdPlane { + public: + virtual ~OsdPlane() {} + + // Creates a surface for offscreen drawing. + virtual OsdSurface* CreateSurface(const Size& size) = 0; + + // Sets a clip rectangle, client should call before drawing to back buffer + // to specify the area they intend to draw on. Platforms may reduce memory + // usage by only allocating back buffer to cover this area. Areas outside + // clip rectangle must display as fully transparent. In particular, setting + // an empty clip rectangle and calling Flip should clear the plane. + virtual void SetClipRectangle(const Rect& rect) = 0; + + // Gets the current back buffer surface. Valid until next call to Flip or + // SetClipRectangle. + virtual OsdSurface* GetBackBuffer() = 0; + + // Presents current back buffer to screen. + virtual void Flip() = 0; +}; + +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_OSD_PLANE_H diff --git a/chromium/chromecast/public/osd_plane_shlib.h b/chromium/chromecast/public/osd_plane_shlib.h new file mode 100644 index 00000000000..0ee170b019e --- /dev/null +++ b/chromium/chromecast/public/osd_plane_shlib.h @@ -0,0 +1,25 @@ +// Copyright 2015 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_PUBLIC_OSD_PLANE_SHLIB_H_ +#define CHROMECAST_PUBLIC_OSD_PLANE_SHLIB_H_ + +#include <string> +#include <vector> + +#include "chromecast_export.h" + +namespace chromecast { + +class OsdPlane; + +// Entry point for loading OsdPlane from shared library. +class CHROMECAST_EXPORT OsdPlaneShlib { + public: + static OsdPlane* Create(const std::vector<std::string>& argv); +}; + +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_OSD_PLANE_SHLIB_H_ diff --git a/chromium/chromecast/public/osd_surface.h b/chromium/chromecast/public/osd_surface.h new file mode 100644 index 00000000000..c83c0654993 --- /dev/null +++ b/chromium/chromecast/public/osd_surface.h @@ -0,0 +1,56 @@ +// Copyright (c) 2015 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_PUBLIC_OSD_SURFACE_H_ +#define CHROMECAST_PUBLIC_OSD_SURFACE_H_ + +namespace chromecast { + +struct Rect; +struct Size; + +// Provides simple API (copy bitmap, blit, composite and fill) for drawing +// OSD graphics on OsdPlane. Hardware-specific implementation should be +// instantiated by OsdPlane. +class OsdSurface { + public: + struct Point { + Point(int arg_x, int arg_y) : x(arg_x), y(arg_y) {} + + const int x; + const int y; + }; + + virtual ~OsdSurface() {} + + // Blits(fast copy) bitmap from a surface. Copies |src_rect| area of surface + // |dst_point| of this surface. + virtual void Blit(OsdSurface* src_surface, + const Rect& src_rect, + const Point& dst_point) = 0; + + // Composites ARGB values of |src_surface| on top of this surface. It is NOT + // copy. Used when displaying OSD images on same plane. + virtual void Composite(OsdSurface* src_surface, + const Rect& src_rect, + const Point& dst_point) = 0; + + // Copies |damage_rect| area of src bitmap into |dst_point| of this surface. + // It is similar to Blit() except that it accepts arbitrary bitmap pointer + // instead of surface. + virtual void CopyBitmap(char* src_bitmap, + const Rect& src_rect, + const Rect& damage_rect, + const Point& dst_point) = 0; + + // Fills |rect| area of surface with |argb| value. + virtual void Fill(const Rect& rect, int argb) = 0; + + // Returns the dimensions of the surface. + virtual const Size& size() const = 0; +}; + +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_OSD_SURFACE_H_ diff --git a/chromium/chromecast/renderer/DEPS b/chromium/chromecast/renderer/DEPS index 790b870ae3c..1015677bcbc 100644 --- a/chromium/chromecast/renderer/DEPS +++ b/chromium/chromecast/renderer/DEPS @@ -1,8 +1,12 @@ include_rules = [ + "+chromecast/common", + "+chromecast/crash", "+chromecast/media", "+components/cdm/renderer", + "+components/network_hints/renderer", "+content/public/renderer", "+media/base", + "+media/renderers", "+third_party/WebKit/public/platform", "+third_party/WebKit/public/web", ] diff --git a/chromium/chromecast/renderer/cast_content_renderer_client.cc b/chromium/chromecast/renderer/cast_content_renderer_client.cc index 2a3c4d5bc77..cfce89371aa 100644 --- a/chromium/chromecast/renderer/cast_content_renderer_client.cc +++ b/chromium/chromecast/renderer/cast_content_renderer_client.cc @@ -8,10 +8,21 @@ #include "base/command_line.h" #include "base/memory/memory_pressure_listener.h" +#include "base/strings/string_number_conversions.h" +#include "chromecast/common/chromecast_switches.h" +#include "chromecast/crash/cast_crash_keys.h" +#include "chromecast/media/base/media_caps.h" +#include "chromecast/renderer/cast_media_load_deferrer.h" +#include "chromecast/renderer/cast_render_process_observer.h" #include "chromecast/renderer/key_systems_cast.h" +#include "chromecast/renderer/media/chromecast_media_renderer_factory.h" +#include "components/network_hints/renderer/prescient_networking_dispatcher.h" #include "content/public/common/content_switches.h" +#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_view.h" +#include "content/public/renderer/render_view_observer.h" #include "crypto/nss_util.h" +#include "ipc/message_filter.h" #include "third_party/WebKit/public/platform/WebColor.h" #include "third_party/WebKit/public/web/WebSettings.h" #include "third_party/WebKit/public/web/WebView.h" @@ -21,22 +32,108 @@ namespace shell { namespace { +#if defined(ARCH_CPU_ARM_FAMILY) && !defined(OS_ANDROID) +// This memory threshold is set for Chromecast. See the UMA histogram +// Platform.MeminfoMemFree when tuning. +// TODO(gunsch): These should be platform/product-dependent. Look into a way +// to move these to platform-specific repositories. +const int kCriticalMinFreeMemMB = 24; +const int kPollingIntervalMS = 5000; + +void PlatformPollFreemem(void) { + struct sysinfo sys; + + if (sysinfo(&sys) == -1) { + LOG(ERROR) << "platform_poll_freemem(): sysinfo failed"; + } else { + int free_mem_mb = static_cast<int64_t>(sys.freeram) * + sys.mem_unit / (1024 * 1024); + + if (free_mem_mb <= kCriticalMinFreeMemMB) { + // Memory is getting really low, we need to do whatever we can to + // prevent deadlocks and interfering with other processes. + base::MemoryPressureListener::NotifyMemoryPressure( + base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); + } + } + + // Setup next poll. + base::MessageLoopProxy::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&PlatformPollFreemem), + base::TimeDelta::FromMilliseconds(kPollingIntervalMS)); +} +#endif + // Default background color to set for WebViews. WebColor is in ARGB format // though the comment of WebColor says it is in RGBA. const blink::WebColor kColorBlack = 0xFF000000; +class CastRenderViewObserver : content::RenderViewObserver { + public: + explicit CastRenderViewObserver(content::RenderView* render_view); + ~CastRenderViewObserver() override {} + + void DidClearWindowObject(blink::WebLocalFrame* frame) override; +}; + +CastRenderViewObserver::CastRenderViewObserver(content::RenderView* render_view) + : content::RenderViewObserver(render_view) { +} + +void CastRenderViewObserver::DidClearWindowObject(blink::WebLocalFrame* frame) { + PlatformAddRendererNativeBindings(frame); +} + } // namespace +CastContentRendererClient::CastContentRendererClient() { +} + +CastContentRendererClient::~CastContentRendererClient() { +} + void CastContentRendererClient::RenderThreadStarted() { -#if defined(USE_NSS) + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); +#if defined(USE_NSS_CERTS) // Note: Copied from chrome_render_process_observer.cc to fix b/8676652. // // On platforms where the system NSS shared libraries are used, // initialize NSS now because it won't be able to load the .so's // after entering the sandbox. - if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess)) + if (!command_line->HasSwitch(switches::kSingleProcess)) crypto::InitNSSSafely(); #endif + +#if defined(ARCH_CPU_ARM_FAMILY) && !defined(OS_ANDROID) + PlatformPollFreemem(); +#endif + + // Set the initial known codecs mask. + if (command_line->HasSwitch(switches::kHdmiSinkSupportedCodecs)) { + int hdmi_codecs_mask; + if (base::StringToInt(command_line->GetSwitchValueASCII( + switches::kHdmiSinkSupportedCodecs), + &hdmi_codecs_mask)) { + ::media::SetHdmiSinkCodecs(hdmi_codecs_mask); + } + } + + cast_observer_.reset( + new CastRenderProcessObserver(PlatformGetRendererMessageFilters())); + + prescient_networking_dispatcher_.reset( + new network_hints::PrescientNetworkingDispatcher()); + + std::string last_launched_app = + command_line->GetSwitchValueNative(switches::kLastLaunchedApp); + if (!last_launched_app.empty()) + base::debug::SetCrashKeyValue(crash_keys::kLastApp, last_launched_app); + + std::string previous_app = + command_line->GetSwitchValueNative(switches::kPreviousApp); + if (!previous_app.empty()) + base::debug::SetCrashKeyValue(crash_keys::kPreviousApp, previous_app); } void CastContentRendererClient::RenderViewCreated( @@ -49,7 +146,21 @@ void CastContentRendererClient::RenderViewCreated( // embedders, though Android has enabled by default for mobile browsers. webview->settings()->setShrinksViewportContentToFit(false); webview->settings()->setMediaControlsOverlayPlayButtonEnabled(false); + + // Scale 1 ensures window.innerHeight/Width match application resolution. + // PageScaleOverride is the 'user agent' value which overrides page + // settings (from meta viewport tag) - thus preventing inconsistency + // between Android and non-Android cast_shell. + webview->setDefaultPageScaleLimits(1.f, 1.f); + webview->setInitialPageScaleOverride(1.f); + + // Disable application cache as Chromecast doesn't support off-line + // application running. + webview->settings()->setOfflineWebApplicationCacheEnabled(false); } + + // Note: RenderView will own the lifetime of its observer. + new CastRenderViewObserver(render_view); } void CastContentRendererClient::AddKeySystems( @@ -58,5 +169,37 @@ void CastContentRendererClient::AddKeySystems( AddChromecastPlatformKeySystems(key_systems); } +#if !defined(OS_ANDROID) +scoped_ptr<::media::RendererFactory> +CastContentRendererClient::CreateMediaRendererFactory( + ::content::RenderFrame* render_frame, + const scoped_refptr<::media::MediaLog>& media_log) { + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + if (!cmd_line->HasSwitch(switches::kEnableCmaMediaPipeline)) + return nullptr; + + return scoped_ptr<::media::RendererFactory>( + new chromecast::media::ChromecastMediaRendererFactory( + media_log, render_frame->GetRoutingID())); +} +#endif + +blink::WebPrescientNetworking* +CastContentRendererClient::GetPrescientNetworking() { + return prescient_networking_dispatcher_.get(); +} + +void CastContentRendererClient::DeferMediaLoad( + content::RenderFrame* render_frame, + const base::Closure& closure) { + if (!render_frame->IsHidden()) { + closure.Run(); + return; + } + + // Lifetime is tied to |render_frame| via content::RenderFrameObserver. + new CastMediaLoadDeferrer(render_frame, closure); +} + } // namespace shell } // namespace chromecast diff --git a/chromium/chromecast/renderer/cast_content_renderer_client.h b/chromium/chromecast/renderer/cast_content_renderer_client.h index e7d4654d5d0..efdc3a5a9f6 100644 --- a/chromium/chromecast/renderer/cast_content_renderer_client.h +++ b/chromium/chromecast/renderer/cast_content_renderer_client.h @@ -5,21 +5,56 @@ #ifndef CHROMECAST_RENDERER_CAST_CONTENT_RENDERER_CLIENT_H_ #define CHROMECAST_RENDERER_CAST_CONTENT_RENDERER_CLIENT_H_ +#include <vector> + +#include "base/macros.h" #include "content/public/renderer/content_renderer_client.h" +namespace IPC { +class MessageFilter; +} + +namespace network_hints { +class PrescientNetworkingDispatcher; +} // namespace network_hints + namespace chromecast { namespace shell { +class CastRenderProcessObserver; + +// Adds any platform-specific bindings to the current frame. +void PlatformAddRendererNativeBindings(blink::WebLocalFrame* frame); class CastContentRendererClient : public content::ContentRendererClient { public: - CastContentRendererClient() {} - virtual ~CastContentRendererClient() {} + CastContentRendererClient(); + ~CastContentRendererClient() override; + + // Returns any MessageFilters from the platform implementation that should + // be added to the render process. + std::vector<scoped_refptr<IPC::MessageFilter>> + PlatformGetRendererMessageFilters(); // ContentRendererClient implementation: - virtual void RenderThreadStarted() override; - virtual void RenderViewCreated(content::RenderView* render_view) override; - virtual void AddKeySystems( + void RenderThreadStarted() override; + void RenderViewCreated(content::RenderView* render_view) override; + void AddKeySystems( std::vector< ::media::KeySystemInfo>* key_systems) override; +#if !defined(OS_ANDROID) + scoped_ptr<media::RendererFactory> CreateMediaRendererFactory( + content::RenderFrame* render_frame, + const scoped_refptr<media::MediaLog>& media_log) override; +#endif + blink::WebPrescientNetworking* GetPrescientNetworking() override; + void DeferMediaLoad(content::RenderFrame* render_frame, + const base::Closure& closure) override; + + private: + scoped_ptr<network_hints::PrescientNetworkingDispatcher> + prescient_networking_dispatcher_; + scoped_ptr<CastRenderProcessObserver> cast_observer_; + + DISALLOW_COPY_AND_ASSIGN(CastContentRendererClient); }; } // namespace shell diff --git a/chromium/chromecast/renderer/cast_content_renderer_client_simple.cc b/chromium/chromecast/renderer/cast_content_renderer_client_simple.cc new file mode 100644 index 00000000000..5ad4fccb261 --- /dev/null +++ b/chromium/chromecast/renderer/cast_content_renderer_client_simple.cc @@ -0,0 +1,21 @@ +// Copyright 2015 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/renderer/cast_content_renderer_client.h" + +#include "ipc/message_filter.h" + +namespace chromecast { +namespace shell { + +void PlatformAddRendererNativeBindings(blink::WebLocalFrame* frame) { +} + +std::vector<scoped_refptr<IPC::MessageFilter>> +CastContentRendererClient::PlatformGetRendererMessageFilters() { + return std::vector<scoped_refptr<IPC::MessageFilter>>(); +} + +} // namespace shell +} // namespace chromecast diff --git a/chromium/chromecast/renderer/cast_media_load_deferrer.cc b/chromium/chromecast/renderer/cast_media_load_deferrer.cc new file mode 100644 index 00000000000..0f84657ce70 --- /dev/null +++ b/chromium/chromecast/renderer/cast_media_load_deferrer.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/renderer/cast_media_load_deferrer.h" + +#include "base/callback_helpers.h" +#include "base/logging.h" + +namespace chromecast { + +CastMediaLoadDeferrer::CastMediaLoadDeferrer( + content::RenderFrame* render_frame, + const base::Closure& continue_loading_cb) + : content::RenderFrameObserver(render_frame), + continue_loading_cb_(continue_loading_cb) { + DCHECK(!continue_loading_cb_.is_null()); +} + +CastMediaLoadDeferrer::~CastMediaLoadDeferrer() {} + +void CastMediaLoadDeferrer::WasShown() { + continue_loading_cb_.Run(); + delete this; +} + +} // namespace chromecast diff --git a/chromium/chromecast/renderer/cast_media_load_deferrer.h b/chromium/chromecast/renderer/cast_media_load_deferrer.h new file mode 100644 index 00000000000..72a57ca9bec --- /dev/null +++ b/chromium/chromecast/renderer/cast_media_load_deferrer.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_RENDERER_CAST_MEDIA_LOAD_DEFERRER_H_ +#define CHROMECAST_RENDERER_CAST_MEDIA_LOAD_DEFERRER_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "content/public/renderer/render_frame_observer.h" + +namespace chromecast { + +// Defers media player loading in background web apps until brought into +// foreground. +class CastMediaLoadDeferrer : public content::RenderFrameObserver { + public: + // Will run |closure| to continue loading the media resource once the page is + // swapped in. + CastMediaLoadDeferrer(content::RenderFrame* render_frame, + const base::Closure& continue_loading_cb); + ~CastMediaLoadDeferrer() override; + + private: + // content::RenderFrameObserver implementation: + void WasShown() override; + + base::Closure continue_loading_cb_; + + DISALLOW_COPY_AND_ASSIGN(CastMediaLoadDeferrer); +}; + +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_CAST_MEDIA_LOAD_DEFERRER_H_ diff --git a/chromium/chromecast/renderer/cast_render_process_observer.cc b/chromium/chromecast/renderer/cast_render_process_observer.cc new file mode 100644 index 00000000000..caba2442a0c --- /dev/null +++ b/chromium/chromecast/renderer/cast_render_process_observer.cc @@ -0,0 +1,64 @@ +// 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/renderer/cast_render_process_observer.h" + +#include "chromecast/renderer/media/capabilities_message_filter.h" +#include "chromecast/renderer/media/cma_message_filter_proxy.h" +#include "content/public/renderer/render_thread.h" + +namespace chromecast { +namespace shell { + +CastRenderProcessObserver::CastRenderProcessObserver( + const std::vector<scoped_refptr<IPC::MessageFilter>>& + platform_message_filters) + : platform_message_filters_(platform_message_filters) { + content::RenderThread* thread = content::RenderThread::Get(); + thread->AddObserver(this); + CreateCustomFilters(); +} + +CastRenderProcessObserver::~CastRenderProcessObserver() { + // CastRenderProcessObserver outlives content::RenderThread. + // No need to explicitly call RemoveObserver in teardown. +} + +void CastRenderProcessObserver::CreateCustomFilters() { + content::RenderThread* thread = content::RenderThread::Get(); +#if !defined(OS_ANDROID) + cma_message_filter_proxy_ = + new media::CmaMessageFilterProxy(thread->GetIOMessageLoopProxy()); + thread->AddFilter(cma_message_filter_proxy_.get()); +#endif // !defined(OS_ANDROID) + capabilities_message_filter_ = new CapabilitiesMessageFilter; + thread->AddFilter(capabilities_message_filter_.get()); + for (const auto& filter : platform_message_filters_) { + thread->AddFilter(filter.get()); + } +} + +void CastRenderProcessObserver::OnRenderProcessShutdown() { + content::RenderThread* thread = content::RenderThread::Get(); +#if !defined(OS_ANDROID) + if (cma_message_filter_proxy_.get()) { + thread->RemoveFilter(cma_message_filter_proxy_.get()); + cma_message_filter_proxy_ = nullptr; + } +#endif // !defined(OS_ANDROID) + if (capabilities_message_filter_.get()) { + thread->RemoveFilter(capabilities_message_filter_.get()); + capabilities_message_filter_ = nullptr; + } + for (auto& filter : platform_message_filters_) { + if (filter.get()) { + thread->RemoveFilter(filter.get()); + filter = nullptr; + } + } + platform_message_filters_.clear(); +} + +} // namespace shell +} // namespace chromecast diff --git a/chromium/chromecast/renderer/cast_render_process_observer.h b/chromium/chromecast/renderer/cast_render_process_observer.h new file mode 100644 index 00000000000..479026c5717 --- /dev/null +++ b/chromium/chromecast/renderer/cast_render_process_observer.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_RENDERER_CAST_RENDER_PROCESS_OBSERVER_H_ +#define CHROMECAST_RENDERER_CAST_RENDER_PROCESS_OBSERVER_H_ + +#include <vector> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "content/public/renderer/render_process_observer.h" + +namespace IPC { +class MessageFilter; +} + +namespace chromecast { +class CapabilitiesMessageFilter; +namespace media { +class CmaMessageFilterProxy; +} + +namespace shell { + +class CastRenderProcessObserver : public content::RenderProcessObserver { + public: + CastRenderProcessObserver( + const std::vector<scoped_refptr<IPC::MessageFilter>>& + platform_message_filters); + ~CastRenderProcessObserver() override; + + private: + // content::RenderProcessObserver implementation: + void OnRenderProcessShutdown() override; + + void CreateCustomFilters(); + +#if !defined(OS_ANDROID) + scoped_refptr<media::CmaMessageFilterProxy> cma_message_filter_proxy_; +#endif // !defined(OS_ANDROID) + scoped_refptr<CapabilitiesMessageFilter> capabilities_message_filter_; + std::vector<scoped_refptr<IPC::MessageFilter>> platform_message_filters_; + + DISALLOW_COPY_AND_ASSIGN(CastRenderProcessObserver); +}; + +} // namespace shell +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_CAST_RENDER_PROCESS_OBSERVER_H_ diff --git a/chromium/chromecast/renderer/key_systems_cast.cc b/chromium/chromecast/renderer/key_systems_cast.cc index 919ebbe8028..bede01890c5 100644 --- a/chromium/chromecast/renderer/key_systems_cast.cc +++ b/chromium/chromecast/renderer/key_systems_cast.cc @@ -14,29 +14,58 @@ #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. +using ::media::EmeFeatureSupport; +using ::media::EmeRobustness; +using ::media::EmeSessionTypeSupport; + namespace chromecast { namespace shell { void AddKeySystemWithCodecs( const std::string& key_system_name, - std::vector< ::media::KeySystemInfo>* concrete_key_systems) { - ::media::KeySystemInfo info(key_system_name); - info.supported_codecs = ::media::EME_CODEC_MP4_ALL; - concrete_key_systems->push_back(info); + std::vector<::media::KeySystemInfo>* key_systems_info) { + ::media::KeySystemInfo info; + info.key_system = key_system_name; + info.supported_init_data_types = ::media::kInitDataTypeMaskCenc; + info.supported_codecs = + ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1; + info.max_audio_robustness = ::media::EmeRobustness::EMPTY; + info.max_video_robustness = ::media::EmeRobustness::EMPTY; + info.persistent_license_support = + ::media::EmeSessionTypeSupport::NOT_SUPPORTED; + info.persistent_release_message_support = + ::media::EmeSessionTypeSupport::NOT_SUPPORTED; + info.persistent_state_support = ::media::EmeFeatureSupport::ALWAYS_ENABLED; + info.distinctive_identifier_support = + ::media::EmeFeatureSupport::ALWAYS_ENABLED; + key_systems_info->push_back(info); } void AddChromecastKeySystems( - std::vector< ::media::KeySystemInfo>* key_systems_info) { + std::vector<::media::KeySystemInfo>* key_systems_info) { #if defined(WIDEVINE_CDM_AVAILABLE) - AddWidevineWithCodecs(cdm::WIDEVINE, - ::media::EME_CODEC_MP4_ALL, - key_systems_info); -#endif + ::media::SupportedCodecs codecs = + ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1; + AddWidevineWithCodecs( + cdm::WIDEVINE, + codecs, // Regular codecs. +#if defined(OS_ANDROID) + codecs, // Hardware-secure codecs. +#endif // defined(OS_ANDROID) + EmeRobustness::HW_SECURE_ALL, // Max audio robustness. + EmeRobustness::HW_SECURE_ALL, // Max video robustness. + EmeSessionTypeSupport::NOT_SUPPORTED, // persistent-license. + EmeSessionTypeSupport::NOT_SUPPORTED, // persistent-release-message. + // Note: On Chromecast, all CDMs may have persistent state. + EmeFeatureSupport::ALWAYS_ENABLED, // Persistent state. + EmeFeatureSupport::ALWAYS_ENABLED, // Distinctive identifier. + key_systems_info); +#endif // defined(WIDEVINE_CDM_AVAILABLE) #if defined(PLAYREADY_CDM_AVAILABLE) AddKeySystemWithCodecs(media::kChromecastPlayreadyKeySystem, key_systems_info); -#endif +#endif // defined(PLAYREADY_CDM_AVAILABLE) } } // namespace shell diff --git a/chromium/chromecast/renderer/key_systems_cast.h b/chromium/chromecast/renderer/key_systems_cast.h index 36b21d5d045..c64eb1db708 100644 --- a/chromium/chromecast/renderer/key_systems_cast.h +++ b/chromium/chromecast/renderer/key_systems_cast.h @@ -13,16 +13,18 @@ namespace chromecast { namespace shell { // Adds a single key system by name. +// TODO(gunsch): modify this API to accept specifying different supported +// features, and/or APIs per key system type. void AddKeySystemWithCodecs( const std::string& key_system_name, - std::vector< ::media::KeySystemInfo>* concrete_key_systems); + std::vector<::media::KeySystemInfo>* concrete_key_systems); void AddChromecastKeySystems( - std::vector< ::media::KeySystemInfo>* key_systems_info); + std::vector<::media::KeySystemInfo>* key_systems_info); // TODO(gunsch): Remove when prefixed EME is removed. void AddChromecastPlatformKeySystems( - std::vector< ::media::KeySystemInfo>* key_systems_info); + std::vector<::media::KeySystemInfo>* key_systems_info); } // namespace shell } // namespace chromecast diff --git a/chromium/chromecast/renderer/media/audio_pipeline_proxy.cc b/chromium/chromecast/renderer/media/audio_pipeline_proxy.cc new file mode 100644 index 00000000000..30297a8a549 --- /dev/null +++ b/chromium/chromecast/renderer/media/audio_pipeline_proxy.cc @@ -0,0 +1,311 @@ +// 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/renderer/media/audio_pipeline_proxy.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/memory/shared_memory.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/thread_checker.h" +#include "chromecast/common/media/cma_messages.h" +#include "chromecast/common/media/shared_memory_chunk.h" +#include "chromecast/media/cma/base/buffering_defs.h" +#include "chromecast/media/cma/base/cma_logging.h" +#include "chromecast/media/cma/base/coded_frame_provider.h" +#include "chromecast/media/cma/ipc/media_message_fifo.h" +#include "chromecast/media/cma/ipc_streamer/av_streamer_proxy.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" +#include "chromecast/renderer/media/cma_message_filter_proxy.h" +#include "chromecast/renderer/media/media_channel_proxy.h" +#include "media/base/bind_to_current_loop.h" +#include "media/base/pipeline_status.h" + +namespace chromecast { +namespace media { + +namespace { + +void IgnoreResult() { +} + +} // namespace + +// AudioPipelineProxyInternal - +// This class is not thread safe and should run on the same thread +// as the media channel proxy. +class AudioPipelineProxyInternal { + public: + typedef base::Callback<void(scoped_ptr<base::SharedMemory>)> SharedMemCB; + + static void Release(scoped_ptr<AudioPipelineProxyInternal> proxy); + + explicit AudioPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy); + virtual ~AudioPipelineProxyInternal(); + + // Notify the other side (browser process) of some activity on the audio pipe. + // TODO(erickung): either send an IPC message or write a byte on the + // SyncSocket. + void NotifyPipeWrite(); + + // These functions are almost a one to one correspondence with AudioPipeline + // but this is an internal class and there is no reason to derive from + // AudioPipeline. + void SetClient(const base::Closure& pipe_read_cb, + const AvPipelineClient& client); + void CreateAvPipe(const SharedMemCB& shared_mem_cb); + void Initialize(const ::media::AudioDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb); + void SetVolume(float volume); + + private: + void Shutdown(); + + // Callbacks for CmaMessageFilterHost::AudioDelegate. + void OnAvPipeCreated(bool status, + base::SharedMemoryHandle shared_mem_handle, + base::FileDescriptor socket); + void OnStateChanged(::media::PipelineStatus status); + + base::ThreadChecker thread_checker_; + + scoped_refptr<MediaChannelProxy> media_channel_proxy_; + + // Store the callback for a pending state transition. + ::media::PipelineStatusCB status_cb_; + + SharedMemCB shared_mem_cb_; + + DISALLOW_COPY_AND_ASSIGN(AudioPipelineProxyInternal); +}; + +// static +void AudioPipelineProxyInternal::Release( + scoped_ptr<AudioPipelineProxyInternal> proxy) { + proxy->Shutdown(); +} + +AudioPipelineProxyInternal::AudioPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy) + : media_channel_proxy_(media_channel_proxy) { + DCHECK(media_channel_proxy.get()); + + // Creation can be done on a different thread. + thread_checker_.DetachFromThread(); +} + +AudioPipelineProxyInternal::~AudioPipelineProxyInternal() { +} + +void AudioPipelineProxyInternal::Shutdown() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Remove any callback on AudioPipelineProxyInternal. + media_channel_proxy_->SetAudioDelegate( + CmaMessageFilterProxy::AudioDelegate()); +} + +void AudioPipelineProxyInternal::NotifyPipeWrite() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // TODO(erickung): An alternative way would be to use a dedicated socket for + // this event. + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_NotifyPipeWrite( + media_channel_proxy_->GetId(), kAudioTrackId))); + VLOG_IF(4, !success) << "Sending msg failed"; +} + +void AudioPipelineProxyInternal::SetClient( + const base::Closure& pipe_read_cb, + const AvPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + + CmaMessageFilterProxy::AudioDelegate delegate; + delegate.av_pipe_cb = + base::Bind(&AudioPipelineProxyInternal::OnAvPipeCreated, + base::Unretained(this)); + delegate.state_changed_cb = + base::Bind(&AudioPipelineProxyInternal::OnStateChanged, + base::Unretained(this)); + delegate.pipe_read_cb = pipe_read_cb; + delegate.client = client; + bool success = media_channel_proxy_->SetAudioDelegate(delegate); + CHECK(success); +} + +void AudioPipelineProxyInternal::CreateAvPipe( + const SharedMemCB& shared_mem_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(shared_mem_cb_.is_null()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_CreateAvPipe( + media_channel_proxy_->GetId(), kAudioTrackId, kAppAudioBufferSize))); + if (!success) { + shared_mem_cb.Run(scoped_ptr<base::SharedMemory>()); + return; + } + shared_mem_cb_ = shared_mem_cb; +} + +void AudioPipelineProxyInternal::OnAvPipeCreated( + bool success, + base::SharedMemoryHandle shared_mem_handle, + base::FileDescriptor socket) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!shared_mem_cb_.is_null()); + if (!success) { + shared_mem_cb_.Run(scoped_ptr<base::SharedMemory>()); + return; + } + + CHECK(base::SharedMemory::IsHandleValid(shared_mem_handle)); + shared_mem_cb_.Run(scoped_ptr<base::SharedMemory>( + new base::SharedMemory(shared_mem_handle, false))); +} + +void AudioPipelineProxyInternal::Initialize( + const ::media::AudioDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_AudioInitialize( + media_channel_proxy_->GetId(), kAudioTrackId, config))); + if (!success) { + status_cb.Run( ::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + DCHECK(status_cb_.is_null()); + status_cb_ = status_cb; +} + +void AudioPipelineProxyInternal::SetVolume(float volume) { + DCHECK(thread_checker_.CalledOnValidThread()); + media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_SetVolume(media_channel_proxy_->GetId(), + kAudioTrackId, volume))); +} + +void AudioPipelineProxyInternal::OnStateChanged( + ::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!status_cb_.is_null()); + base::ResetAndReturn(&status_cb_).Run(status); +} + + +// A macro runs current member function on |io_message_loop_proxy_| thread. +#define FORWARD_ON_IO_THREAD(param_fn, ...) \ + io_message_loop_proxy_->PostTask( \ + FROM_HERE, \ + base::Bind(&AudioPipelineProxyInternal::param_fn, \ + base::Unretained(proxy_.get()), ##__VA_ARGS__)) + +AudioPipelineProxy::AudioPipelineProxy( + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, + scoped_refptr<MediaChannelProxy> media_channel_proxy) + : io_message_loop_proxy_(io_message_loop_proxy), + proxy_(new AudioPipelineProxyInternal(media_channel_proxy)), + audio_streamer_(new AvStreamerProxy()), + weak_factory_(this) { + DCHECK(io_message_loop_proxy_.get()); + weak_this_ = weak_factory_.GetWeakPtr(); + thread_checker_.DetachFromThread(); +} + +AudioPipelineProxy::~AudioPipelineProxy() { + DCHECK(thread_checker_.CalledOnValidThread()); + // Release the underlying object on the right thread. + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&AudioPipelineProxyInternal::Release, base::Passed(&proxy_))); +} + +void AudioPipelineProxy::SetClient( + const AvPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + base::Closure pipe_read_cb = ::media::BindToCurrentLoop( + base::Bind(&AudioPipelineProxy::OnPipeRead, weak_this_)); + FORWARD_ON_IO_THREAD(SetClient, pipe_read_cb, client); +} + +void AudioPipelineProxy::Initialize( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << "AudioPipelineProxy::Initialize"; + DCHECK(thread_checker_.CalledOnValidThread()); + audio_streamer_->SetCodedFrameProvider(frame_provider.Pass()); + + AudioPipelineProxyInternal::SharedMemCB shared_mem_cb = + ::media::BindToCurrentLoop(base::Bind( + &AudioPipelineProxy::OnAvPipeCreated, weak_this_, + config, status_cb)); + FORWARD_ON_IO_THREAD(CreateAvPipe, shared_mem_cb); +} + +void AudioPipelineProxy::OnAvPipeCreated( + const ::media::AudioDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb, + scoped_ptr<base::SharedMemory> shared_memory) { + CMALOG(kLogControl) << "AudioPipelineProxy::OnAvPipeCreated"; + DCHECK(thread_checker_.CalledOnValidThread()); + if (!shared_memory || + !shared_memory->Map(kAppAudioBufferSize)) { + status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + CHECK(shared_memory->memory()); + + scoped_ptr<MediaMemoryChunk> shared_memory_chunk( + new SharedMemoryChunk(shared_memory.Pass(), kAppAudioBufferSize)); + scoped_ptr<MediaMessageFifo> audio_pipe( + new MediaMessageFifo(shared_memory_chunk.Pass(), false)); + audio_pipe->ObserveWriteActivity( + base::Bind(&AudioPipelineProxy::OnPipeWrite, weak_this_)); + + audio_streamer_->SetMediaMessageFifo(audio_pipe.Pass()); + + // Now proceed to the decoder/renderer initialization. + FORWARD_ON_IO_THREAD(Initialize, config, status_cb); +} + +void AudioPipelineProxy::StartFeeding() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(audio_streamer_); + audio_streamer_->Start(); +} + +void AudioPipelineProxy::Flush(const base::Closure& done_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(audio_streamer_); + audio_streamer_->StopAndFlush(done_cb); +} + +void AudioPipelineProxy::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!audio_streamer_) + return; + audio_streamer_->StopAndFlush(base::Bind(&IgnoreResult)); +} + +void AudioPipelineProxy::SetVolume(float volume) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(SetVolume, volume); +} + +void AudioPipelineProxy::OnPipeWrite() { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(NotifyPipeWrite); +} + +void AudioPipelineProxy::OnPipeRead() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (audio_streamer_) + audio_streamer_->OnFifoReadEvent(); +} + +} // namespace cma +} // namespace chromecast
\ No newline at end of file diff --git a/chromium/chromecast/renderer/media/audio_pipeline_proxy.h b/chromium/chromecast/renderer/media/audio_pipeline_proxy.h new file mode 100644 index 00000000000..b319ee1b8a7 --- /dev/null +++ b/chromium/chromecast/renderer/media/audio_pipeline_proxy.h @@ -0,0 +1,79 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_RENDERER_MEDIA_AUDIO_PIPELINE_PROXY_H_ +#define CHROMECAST_RENDERER_MEDIA_AUDIO_PIPELINE_PROXY_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/media/cma/pipeline/audio_pipeline.h" +#include "media/base/pipeline_status.h" + +namespace base { +class MessageLoopProxy; +class SharedMemory; +} + +namespace media { +class AudioDecoderConfig; +} + +namespace chromecast { +namespace media { +class AudioPipelineProxyInternal; +struct AvPipelineClient; +class AvStreamerProxy; +class CodedFrameProvider; +class MediaChannelProxy; + +class AudioPipelineProxy : public AudioPipeline { + public: + AudioPipelineProxy( + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, + scoped_refptr<MediaChannelProxy> media_channel_proxy); + ~AudioPipelineProxy() override; + + void Initialize( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb); + void StartFeeding(); + void Flush(const base::Closure& done_cb); + void Stop(); + + // AudioPipeline implementation. + void SetClient(const AvPipelineClient& client) override; + void SetVolume(float volume) override; + + private: + base::ThreadChecker thread_checker_; + + void OnAvPipeCreated( + const ::media::AudioDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb, + scoped_ptr<base::SharedMemory> shared_memory); + void OnPipeWrite(); + void OnPipeRead(); + + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + + // |proxy_| main goal is to convert function calls to IPC messages. + scoped_ptr<AudioPipelineProxyInternal> proxy_; + + scoped_ptr<AvStreamerProxy> audio_streamer_; + + base::WeakPtr<AudioPipelineProxy> weak_this_; + base::WeakPtrFactory<AudioPipelineProxy> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(AudioPipelineProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_AUDIO_PIPELINE_PROXY_H_
\ No newline at end of file diff --git a/chromium/chromecast/renderer/media/capabilities_message_filter.cc b/chromium/chromecast/renderer/media/capabilities_message_filter.cc new file mode 100644 index 00000000000..fb10972192d --- /dev/null +++ b/chromium/chromecast/renderer/media/capabilities_message_filter.cc @@ -0,0 +1,32 @@ +// Copyright 2015 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/renderer/media/capabilities_message_filter.h" + +#include "chromecast/common/media/cast_messages.h" +#include "chromecast/media/base/media_caps.h" + +namespace chromecast { + +CapabilitiesMessageFilter::CapabilitiesMessageFilter() { +} + +CapabilitiesMessageFilter::~CapabilitiesMessageFilter() { +} + +bool CapabilitiesMessageFilter::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(CapabilitiesMessageFilter, message) + IPC_MESSAGE_HANDLER(CmaMsg_UpdateSupportedHdmiSinkCodecs, + OnUpdateSupportedHdmiSinkCodecs) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void CapabilitiesMessageFilter::OnUpdateSupportedHdmiSinkCodecs(int codecs) { + ::media::SetHdmiSinkCodecs(codecs); +} + +} // namespace chromecast diff --git a/chromium/chromecast/renderer/media/capabilities_message_filter.h b/chromium/chromecast/renderer/media/capabilities_message_filter.h new file mode 100644 index 00000000000..6c9c1deecd4 --- /dev/null +++ b/chromium/chromecast/renderer/media/capabilities_message_filter.h @@ -0,0 +1,29 @@ +// Copyright 2015 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_RENDERER_MEDIA_CAPABILITIES_MESSAGE_FILTER_H_ +#define CHROMECAST_RENDERER_MEDIA_CAPABILITIES_MESSAGE_FILTER_H_ + +#include "ipc/message_filter.h" + +namespace chromecast { + +class CapabilitiesMessageFilter : public IPC::MessageFilter { + public: + CapabilitiesMessageFilter(); + + // IPC::ChannelProxy::MessageFilter implementation: + bool OnMessageReceived(const IPC::Message& message) override; + + private: + ~CapabilitiesMessageFilter() override; + + void OnUpdateSupportedHdmiSinkCodecs(int codecs); + + DISALLOW_COPY_AND_ASSIGN(CapabilitiesMessageFilter); +}; + +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_CAPABILITIES_MESSAGE_FILTER_H_ diff --git a/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.cc b/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.cc new file mode 100644 index 00000000000..62fe7716a3d --- /dev/null +++ b/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.cc @@ -0,0 +1,76 @@ +// Copyright 2015 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/renderer/media/chromecast_media_renderer_factory.h" + +#include "base/command_line.h" +#include "chromecast/media/base/switching_media_renderer.h" +#include "chromecast/media/cma/filters/cma_renderer.h" +#include "chromecast/renderer/media/media_pipeline_proxy.h" +#include "content/public/renderer/render_thread.h" +#include "media/base/audio_hardware_config.h" +#include "media/base/media_log.h" +#include "media/renderers/default_renderer_factory.h" +#include "media/renderers/gpu_video_accelerator_factories.h" + +namespace chromecast { +namespace media { + +ChromecastMediaRendererFactory::ChromecastMediaRendererFactory( + const scoped_refptr<::media::MediaLog>& media_log, + int render_frame_id) + : render_frame_id_(render_frame_id), + media_log_(media_log) { +} + +ChromecastMediaRendererFactory::~ChromecastMediaRendererFactory() { +} + +scoped_ptr<::media::Renderer> ChromecastMediaRendererFactory::CreateRenderer( + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, + ::media::AudioRendererSink* audio_renderer_sink, + ::media::VideoRendererSink* video_renderer_sink) { + if (!default_render_factory_) { + // Chromecast doesn't have input audio devices, so leave this uninitialized + ::media::AudioParameters input_audio_params; + // TODO(servolk): Audio parameters are hardcoded for now, but in the future + // either we need to obtain AudioHardwareConfig from RenderThreadImpl, + // or media renderer needs to figure out optimal audio parameters itself. + const int kDefaultSamplingRate = 48000; + const int kDefaultBitsPerSample = 16; + // About 20ms of stereo (2 channels) 16bit (2 byte) audio + int buffer_size = kDefaultSamplingRate * 20 * 2 * 2 / 1000; + ::media::AudioParameters output_audio_params( + ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY, + ::media::CHANNEL_LAYOUT_STEREO, + kDefaultSamplingRate, kDefaultBitsPerSample, + buffer_size, ::media::AudioParameters::NO_EFFECTS); + ::media::AudioHardwareConfig audio_config(input_audio_params, + output_audio_params); + + default_render_factory_.reset(new ::media::DefaultRendererFactory( + media_log_, /*gpu_factories*/ nullptr, audio_config)); + } + + DCHECK(default_render_factory_); + // TODO(erickung): crbug.com/443956. Need to provide right LoadType. + LoadType cma_load_type = kLoadTypeMediaSource; + scoped_ptr<MediaPipeline> cma_media_pipeline( + new MediaPipelineProxy( + render_frame_id_, + content::RenderThread::Get()->GetIOMessageLoopProxy(), + cma_load_type)); + scoped_ptr<CmaRenderer> cma_renderer( + new CmaRenderer(cma_media_pipeline.Pass(), video_renderer_sink)); + scoped_ptr<::media::Renderer> default_media_render( + default_render_factory_->CreateRenderer(media_task_runner, + audio_renderer_sink, + video_renderer_sink)); + scoped_ptr<SwitchingMediaRenderer> media_renderer(new SwitchingMediaRenderer( + default_media_render.Pass(), cma_renderer.Pass())); + return media_renderer.Pass(); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.h b/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.h new file mode 100644 index 00000000000..820609478fa --- /dev/null +++ b/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.h @@ -0,0 +1,44 @@ +// Copyright 2015 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_RENDERER_MEDIA_CHROMECAST_MEDIA_RENDERER_FACTORY_H_ +#define CHROMECAST_RENDERER_MEDIA_CHROMECAST_MEDIA_RENDERER_FACTORY_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "media/base/renderer_factory.h" + +namespace media { +class MediaLog; +class DefaultRendererFactory; +} + +namespace chromecast { +namespace media { + +class ChromecastMediaRendererFactory : public ::media::RendererFactory { + public: + ChromecastMediaRendererFactory( + const scoped_refptr<::media::MediaLog>& media_log, + int render_frame_id); + ~ChromecastMediaRendererFactory() final; + + // ::media::RendererFactory implementation. + scoped_ptr<::media::Renderer> CreateRenderer( + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, + ::media::AudioRendererSink* audio_renderer_sink, + ::media::VideoRendererSink* video_renderer_sink) final; + + private: + int render_frame_id_; + scoped_refptr<::media::MediaLog> media_log_; + scoped_ptr<::media::DefaultRendererFactory> default_render_factory_; + + DISALLOW_COPY_AND_ASSIGN(ChromecastMediaRendererFactory); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_CHROMECAST_MEDIA_RENDERER_FACTORY_H_ diff --git a/chromium/chromecast/renderer/media/cma_message_filter_proxy.cc b/chromium/chromecast/renderer/media/cma_message_filter_proxy.cc new file mode 100644 index 00000000000..f2701a98725 --- /dev/null +++ b/chromium/chromecast/renderer/media/cma_message_filter_proxy.cc @@ -0,0 +1,272 @@ +// 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/renderer/media/cma_message_filter_proxy.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/time/time.h" +#include "chromecast/common/media/cma_messages.h" +#include "ipc/ipc_logging.h" +#include "ipc/ipc_sender.h" + +namespace chromecast { +namespace media { + +CmaMessageFilterProxy::MediaDelegate::MediaDelegate() { +} + +CmaMessageFilterProxy::MediaDelegate::~MediaDelegate() { +} + +CmaMessageFilterProxy::AudioDelegate::AudioDelegate() { +} + +CmaMessageFilterProxy::AudioDelegate::~AudioDelegate() { +} + +CmaMessageFilterProxy::VideoDelegate::VideoDelegate() { +} + +CmaMessageFilterProxy::VideoDelegate::~VideoDelegate() { +} + +CmaMessageFilterProxy::DelegateEntry::DelegateEntry() { +} + +CmaMessageFilterProxy::DelegateEntry::~DelegateEntry() { +} + +CmaMessageFilterProxy* CmaMessageFilterProxy::filter_ = NULL; + +// static +CmaMessageFilterProxy* CmaMessageFilterProxy::Get() { + return filter_; +} + +CmaMessageFilterProxy::CmaMessageFilterProxy( + const scoped_refptr<base::MessageLoopProxy>& io_message_loop) + : sender_(NULL), + io_message_loop_(io_message_loop) { + DCHECK(!filter_); + filter_ = this; +} + +int CmaMessageFilterProxy::CreateChannel() { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DelegateEntry* entry = new DelegateEntry(); + int id = delegates_.Add(entry); + return id; +} + +void CmaMessageFilterProxy::DestroyChannel(int id) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + delegates_.Remove(id); + delete entry; +} + +bool CmaMessageFilterProxy::SetMediaDelegate( + int id, const MediaDelegate& media_delegate) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return false; + entry->media_delegate = media_delegate; + return true; +} + +bool CmaMessageFilterProxy::SetAudioDelegate( + int id, const AudioDelegate& audio_delegate) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return false; + entry->audio_delegate = audio_delegate; + return true; +} + +bool CmaMessageFilterProxy::SetVideoDelegate( + int id, const VideoDelegate& video_delegate) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return false; + entry->video_delegate = video_delegate; + return true; +} + +bool CmaMessageFilterProxy::Send(scoped_ptr<IPC::Message> message) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + if (!sender_) + return false; + bool status = sender_->Send(message.release()); + return status; +} + +bool CmaMessageFilterProxy::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(CmaMessageFilterProxy, message) + IPC_MESSAGE_HANDLER(CmaMsg_AvPipeCreated, OnAvPipeCreated) + IPC_MESSAGE_HANDLER(CmaMsg_NotifyPipeRead, OnPipeRead) + IPC_MESSAGE_HANDLER(CmaMsg_MediaStateChanged, OnMediaStateChanged) + IPC_MESSAGE_HANDLER(CmaMsg_TrackStateChanged, OnTrackStateChanged) + IPC_MESSAGE_HANDLER(CmaMsg_Eos, OnEos) + IPC_MESSAGE_HANDLER(CmaMsg_TimeUpdate, OnTimeUpdate) + IPC_MESSAGE_HANDLER(CmaMsg_BufferingNotification, OnBufferingNotification) + IPC_MESSAGE_HANDLER(CmaMsg_PlaybackError, OnPlaybackError) + IPC_MESSAGE_HANDLER(CmaMsg_PlaybackStatistics, OnStatisticsUpdated) + IPC_MESSAGE_HANDLER(CmaMsg_NaturalSizeChanged, OnNaturalSizeChanged) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void CmaMessageFilterProxy::OnFilterAdded(IPC::Sender* sender) { + sender_ = sender; +} + +void CmaMessageFilterProxy::OnFilterRemoved() { + sender_ = NULL; +} + +void CmaMessageFilterProxy::OnChannelClosing() { + sender_ = NULL; +} + +CmaMessageFilterProxy::~CmaMessageFilterProxy() { + DCHECK(filter_); + filter_ = NULL; +} + +void CmaMessageFilterProxy::OnAvPipeCreated( + int id, + TrackId track_id, + bool status, + base::SharedMemoryHandle shared_memory_handle, + base::FileDescriptor socket_handle) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const AvPipeCB& cb = (track_id == kAudioTrackId) ? + entry->audio_delegate.av_pipe_cb : + entry->video_delegate.av_pipe_cb; + if (!cb.is_null()) + cb.Run(status, shared_memory_handle, socket_handle); +} + +void CmaMessageFilterProxy::OnPipeRead( + int id, TrackId track_id) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const base::Closure& cb = (track_id == kAudioTrackId) ? + entry->audio_delegate.pipe_read_cb : + entry->video_delegate.pipe_read_cb; + if (!cb.is_null()) + cb.Run(); +} + +void CmaMessageFilterProxy::OnMediaStateChanged( + int id, ::media::PipelineStatus status) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const ::media::PipelineStatusCB& cb = + entry->media_delegate.state_changed_cb; + if (!cb.is_null()) + cb.Run(status); +} + +void CmaMessageFilterProxy::OnTrackStateChanged( + int id, TrackId track_id, ::media::PipelineStatus status) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const ::media::PipelineStatusCB& cb = (track_id == kAudioTrackId) ? + entry->audio_delegate.state_changed_cb : + entry->video_delegate.state_changed_cb; + if (!cb.is_null()) + cb.Run(status); +} + +void CmaMessageFilterProxy::OnEos(int id, TrackId track_id) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const base::Closure& cb = (track_id == kAudioTrackId) ? + entry->audio_delegate.client.eos_cb : + entry->video_delegate.client.av_pipeline_client.eos_cb; + if (!cb.is_null()) + cb.Run(); +} + +void CmaMessageFilterProxy::OnTimeUpdate( + int id, + base::TimeDelta time, + base::TimeDelta max_time, + base::TimeTicks stc) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const MediaPipelineClient::TimeUpdateCB& cb = + entry->media_delegate.client.time_update_cb; + if (!cb.is_null()) + cb.Run(time, max_time, stc); +} + +void CmaMessageFilterProxy::OnBufferingNotification( + int id, ::media::BufferingState buffering_state) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const ::media::BufferingStateCB& cb = + entry->media_delegate.client.buffering_state_cb; + if (!cb.is_null()) + cb.Run(buffering_state); +} + +void CmaMessageFilterProxy::OnPlaybackError( + int id, TrackId track_id, ::media::PipelineStatus status) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const ::media::PipelineStatusCB& cb = + (track_id == kNoTrackId) ? entry->media_delegate.client.error_cb : + (track_id == kAudioTrackId) ? + entry->audio_delegate.client.playback_error_cb : + entry->video_delegate.client.av_pipeline_client.playback_error_cb; + if (!cb.is_null()) + cb.Run(status); +} + +void CmaMessageFilterProxy::OnStatisticsUpdated( + int id, TrackId track_id, const ::media::PipelineStatistics& stats) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const ::media::StatisticsCB& cb = (track_id == kAudioTrackId) ? + entry->audio_delegate.client.statistics_cb : + entry->video_delegate.client.av_pipeline_client.statistics_cb; + if (!cb.is_null()) + cb.Run(stats); +} + +void CmaMessageFilterProxy::OnNaturalSizeChanged( + int id, TrackId track_id, const gfx::Size& size) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + if (track_id == kAudioTrackId) + return; + const VideoPipelineClient::NaturalSizeChangedCB& cb = + entry->video_delegate.client.natural_size_changed_cb; + if (!cb.is_null()) + cb.Run(size); +} + +} // namespace media +} // namespace chromecast
\ No newline at end of file diff --git a/chromium/chromecast/renderer/media/cma_message_filter_proxy.h b/chromium/chromecast/renderer/media/cma_message_filter_proxy.h new file mode 100644 index 00000000000..0930f19938e --- /dev/null +++ b/chromium/chromecast/renderer/media/cma_message_filter_proxy.h @@ -0,0 +1,135 @@ +// 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_RENDERER_MEDIA_CMA_MESSAGE_FILTER_PROXY_H_ +#define CHROMECAST_RENDERER_MEDIA_CMA_MESSAGE_FILTER_PROXY_H_ + +#include "base/id_map.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/sync_socket.h" +#include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" +#include "chromecast/media/cma/pipeline/media_pipeline_client.h" +#include "chromecast/media/cma/pipeline/video_pipeline_client.h" +#include "ipc/message_filter.h" +#include "media/base/buffering_state.h" +#include "media/base/pipeline_status.h" + +namespace base { +class MessageLoopProxy; +} + +namespace chromecast { +namespace media { + +typedef base::Callback<void( + bool, base::SharedMemoryHandle, base::FileDescriptor)> AvPipeCB; + +class CmaMessageFilterProxy : public IPC::MessageFilter { + public: + struct MediaDelegate { + MediaDelegate(); + ~MediaDelegate(); + + ::media::PipelineStatusCB state_changed_cb; + MediaPipelineClient client; + }; + + struct AudioDelegate { + AudioDelegate(); + ~AudioDelegate(); + + AvPipeCB av_pipe_cb; + base::Closure pipe_read_cb; + ::media::PipelineStatusCB state_changed_cb; + AvPipelineClient client; + }; + + struct VideoDelegate { + VideoDelegate(); + ~VideoDelegate(); + + AvPipeCB av_pipe_cb; + base::Closure pipe_read_cb; + ::media::PipelineStatusCB state_changed_cb; + VideoPipelineClient client; + }; + + explicit CmaMessageFilterProxy( + const scoped_refptr<base::MessageLoopProxy>& io_message_loop); + + // Getter for the one CmaMessageFilterHost object. + static CmaMessageFilterProxy* Get(); + + int CreateChannel(); + void DestroyChannel(int id); + + // For adding/removing delegates. + bool SetMediaDelegate(int id, const MediaDelegate& media_delegate); + bool SetAudioDelegate(int id, const AudioDelegate& audio_delegate); + bool SetVideoDelegate(int id, const VideoDelegate& video_delegate); + + // Sends an IPC message using |channel_|. + bool Send(scoped_ptr<IPC::Message> message); + + // IPC::ChannelProxy::MessageFilter implementation. Called on IO thread. + bool OnMessageReceived(const IPC::Message& message) override; + void OnFilterAdded(IPC::Sender* sender) override; + void OnFilterRemoved() override; + void OnChannelClosing() override; + + protected: + ~CmaMessageFilterProxy() override; + + private: + struct DelegateEntry { + DelegateEntry(); + ~DelegateEntry(); + + MediaDelegate media_delegate; + AudioDelegate audio_delegate; + VideoDelegate video_delegate; + }; + + void OnAvPipeCreated(int id, + TrackId track_id, + bool status, + base::SharedMemoryHandle shared_memory_handle, + base::FileDescriptor socket_handle); + void OnPipeRead(int id, TrackId track_id); + void OnMediaStateChanged(int id, ::media::PipelineStatus status); + void OnTrackStateChanged(int id, TrackId track_id, + ::media::PipelineStatus status); + void OnEos(int id, TrackId track_id); + void OnTimeUpdate(int id, + base::TimeDelta time, + base::TimeDelta max_time, + base::TimeTicks stc); + void OnBufferingNotification(int id, ::media::BufferingState state); + void OnPlaybackError(int id, TrackId track_id, + ::media::PipelineStatus status); + void OnStatisticsUpdated(int id, TrackId track_id, + const ::media::PipelineStatistics& stats); + void OnNaturalSizeChanged(int id, TrackId track_id, + const gfx::Size& natural_size); + + // The singleton instance for this filter. + static CmaMessageFilterProxy* filter_; + + // A map of media ids to delegates. + IDMap<DelegateEntry> delegates_; + + IPC::Sender* sender_; + + scoped_refptr<base::MessageLoopProxy> const io_message_loop_; + + DISALLOW_COPY_AND_ASSIGN(CmaMessageFilterProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_CMA_MESSAGE_FILTER_PROXY_H_ diff --git a/chromium/chromecast/renderer/media/cma_renderer_unittest.cc b/chromium/chromecast/renderer/media/cma_renderer_unittest.cc new file mode 100644 index 00000000000..19ff6ecec62 --- /dev/null +++ b/chromium/chromecast/renderer/media/cma_renderer_unittest.cc @@ -0,0 +1,125 @@ +// Copyright 2015 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/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/message_loop/message_loop.h" +#include "base/time/time.h" +#include "chromecast/common/media/cma_messages.h" +#include "chromecast/common/media/shared_memory_chunk.h" +#include "chromecast/media/cma/base/buffering_defs.h" +#include "chromecast/media/cma/filters/cma_renderer.h" +#include "chromecast/media/cma/ipc/media_memory_chunk.h" +#include "chromecast/media/cma/ipc/media_message_fifo.h" +#include "chromecast/media/cma/pipeline/media_pipeline.h" +#include "chromecast/renderer/media/cma_message_filter_proxy.h" +#include "chromecast/renderer/media/media_pipeline_proxy.h" +#include "ipc/ipc_test_sink.h" +#include "media/base/demuxer_stream_provider.h" +#include "media/base/fake_demuxer_stream.h" +#include "media/base/null_video_sink.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { +namespace media { + +namespace { + +class CmaRendererTest : public testing::Test { + public: + CmaRendererTest() { + demuxer_stream_provider_.reset( + new ::media::FakeDemuxerStreamProvider(1, 1, false)); + null_sink_.reset(new ::media::NullVideoSink( + false, + base::TimeDelta::FromSecondsD(1.0 / 60), + base::Bind(&MockCB::OnFrameReceived, base::Unretained(&mock_)), + message_loop_.task_runner())); + + cma_proxy_ = new CmaMessageFilterProxy(message_loop_.message_loop_proxy()); + cma_proxy_->OnFilterAdded(&ipc_sink_); + + renderer_.reset(new CmaRenderer( + scoped_ptr<MediaPipelineProxy>( + new MediaPipelineProxy(0, message_loop_.message_loop_proxy(), + LoadType::kLoadTypeMediaSource)), + null_sink_.get())); + } + + ~CmaRendererTest() override { + cma_proxy_->OnFilterRemoved(); + } + + protected: + base::MessageLoop message_loop_; + scoped_ptr<::media::FakeDemuxerStreamProvider> demuxer_stream_provider_; + IPC::TestSink ipc_sink_; + scoped_refptr<CmaMessageFilterProxy> cma_proxy_; + scoped_ptr<CmaRenderer> renderer_; + + class MockCB { + public: + MOCK_METHOD1(OnInitialized, void(::media::PipelineStatus)); + MOCK_METHOD1(OnFrameReceived, + void(const scoped_refptr<::media::VideoFrame>&)); + MOCK_METHOD1(OnStatistics, void(const ::media::PipelineStatistics&)); + MOCK_METHOD1(OnBufferingState, void(::media::BufferingState)); + MOCK_METHOD0(OnEnded, void()); + MOCK_METHOD1(OnError, void(::media::PipelineStatus)); + MOCK_METHOD0(OnWaitingForDecryptionKey, void()); + }; + MockCB mock_; + + private: + scoped_ptr<::media::NullVideoSink> null_sink_; + + DISALLOW_COPY_AND_ASSIGN(CmaRendererTest); +}; + +} // namespace + +TEST_F(CmaRendererTest, TestInitialization) { + renderer_->Initialize( + demuxer_stream_provider_.get(), + base::Bind(&MockCB::OnInitialized, base::Unretained(&mock_)), + base::Bind(&MockCB::OnStatistics, base::Unretained(&mock_)), + base::Bind(&MockCB::OnBufferingState, base::Unretained(&mock_)), + base::Bind(&MockCB::OnEnded, base::Unretained(&mock_)), + base::Bind(&MockCB::OnError, base::Unretained(&mock_)), + base::Bind(&MockCB::OnWaitingForDecryptionKey, base::Unretained(&mock_))); + message_loop_.RunUntilIdle(); + + EXPECT_TRUE(ipc_sink_.GetUniqueMessageMatching(CmaHostMsg_CreateAvPipe::ID)); + base::SharedMemory* shared_memory = new base::SharedMemory(); + EXPECT_TRUE(shared_memory->CreateAndMapAnonymous(kAppVideoBufferSize)); + + // Renderer MediaMessageFifo instance expects the first bytes of the shared + // memory blob to describe how much space is available for writing. + *(static_cast<size_t*>(shared_memory->memory())) = + shared_memory->requested_size() - MediaMessageFifo::kDescriptorSize; + + base::FileDescriptor foreign_socket_handle; + cma_proxy_->OnMessageReceived(CmaMsg_AvPipeCreated( + 1, kVideoTrackId, true, + shared_memory->handle(), foreign_socket_handle)); + message_loop_.RunUntilIdle(); + + EXPECT_FALSE( + ipc_sink_.GetUniqueMessageMatching(CmaHostMsg_AudioInitialize::ID)); + EXPECT_TRUE( + ipc_sink_.GetUniqueMessageMatching(CmaHostMsg_VideoInitialize::ID)); + + EXPECT_CALL(mock_, OnInitialized(::media::PIPELINE_OK)); + cma_proxy_->OnMessageReceived( + CmaMsg_TrackStateChanged(1, kVideoTrackId, ::media::PIPELINE_OK)); + message_loop_.RunUntilIdle(); +} + + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/renderer/media/media_channel_proxy.cc b/chromium/chromecast/renderer/media/media_channel_proxy.cc new file mode 100644 index 00000000000..2ef1d251d92 --- /dev/null +++ b/chromium/chromecast/renderer/media/media_channel_proxy.cc @@ -0,0 +1,79 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/renderer/media/media_channel_proxy.h" + +#include "base/logging.h" +#include "chromecast/common/media/cma_messages.h" + +namespace chromecast { +namespace media { + +MediaChannelProxy::MediaChannelProxy() + : is_open_(false), + id_(0) { + filter_ = CmaMessageFilterProxy::Get(); + DCHECK(filter_.get()); +} + +MediaChannelProxy::~MediaChannelProxy() { +} + +void MediaChannelProxy::Open(LoadType load_type) { + CHECK(!is_open_); + // Renderer side. + id_ = filter_->CreateChannel(); + is_open_ = true; + + // Browser side. + bool success = Send( + scoped_ptr<IPC::Message>(new CmaHostMsg_CreateMedia(id_, load_type))); + if (!success) { + is_open_ = false; + id_ = 0; + } +} + +void MediaChannelProxy::Close() { + if (!is_open_) + return; + + // Browser side. + Send(scoped_ptr<IPC::Message>(new CmaHostMsg_DestroyMedia(id_))); + + // Renderer side. + is_open_ = false; + filter_->DestroyChannel(id_); + id_ = 0; +} + +bool MediaChannelProxy::SetMediaDelegate( + const CmaMessageFilterProxy::MediaDelegate& media_delegate) { + if (!is_open_) + return false; + return filter_->SetMediaDelegate(id_, media_delegate); +} + +bool MediaChannelProxy::SetAudioDelegate( + const CmaMessageFilterProxy::AudioDelegate& audio_delegate) { + if (!is_open_) + return false; + return filter_->SetAudioDelegate(id_, audio_delegate); +} + +bool MediaChannelProxy::SetVideoDelegate( + const CmaMessageFilterProxy::VideoDelegate& video_delegate) { + if (!is_open_) + return false; + return filter_->SetVideoDelegate(id_, video_delegate); +} + +bool MediaChannelProxy::Send(scoped_ptr<IPC::Message> message) { + if (!is_open_) + return false; + return filter_->Send(message.Pass()); +} + +} // namespace media +} // namespace chromecast
\ No newline at end of file diff --git a/chromium/chromecast/renderer/media/media_channel_proxy.h b/chromium/chromecast/renderer/media/media_channel_proxy.h new file mode 100644 index 00000000000..c7c46965bf6 --- /dev/null +++ b/chromium/chromecast/renderer/media/media_channel_proxy.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_RENDERER_MEDIA_MEDIA_CHANNEL_PROXY_H_ +#define CHROMECAST_RENDERER_MEDIA_MEDIA_CHANNEL_PROXY_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "chromecast/media/cma/pipeline/load_type.h" +#include "chromecast/renderer/media/cma_message_filter_proxy.h" + +namespace IPC { +class Message; +} + +namespace chromecast { +namespace media { + +// MediaChannelProxy - Manage the lifetime of a CMA ipc channel. +// Must be invoked from the IO thread of the renderer process. +class MediaChannelProxy + : public base::RefCountedThreadSafe<MediaChannelProxy> { + public: + MediaChannelProxy(); + + // Opens a CMA ipc channel. + void Open(LoadType load_type); + + // Closes the ipc channel. + void Close(); + + // Returns the ID of the CMA ipc channel. + // Returns 0 or negative ID if no channel has been opened. + int GetId() { return id_; } + + // Manages delegates. + bool SetMediaDelegate( + const CmaMessageFilterProxy::MediaDelegate& media_delegate); + bool SetAudioDelegate( + const CmaMessageFilterProxy::AudioDelegate& audio_delegate); + bool SetVideoDelegate( + const CmaMessageFilterProxy::VideoDelegate& video_delegate); + + // Sends an IPC message over this CMA ipc channel. + bool Send(scoped_ptr<IPC::Message> message); + + private: + friend class base::RefCountedThreadSafe<MediaChannelProxy>; + virtual ~MediaChannelProxy(); + + // Message filter running on the renderer side. + scoped_refptr<CmaMessageFilterProxy> filter_; + + // Indicates whether the CMA channel is open. + bool is_open_; + + // Unique identifier per media pipeline. + int id_; + + DISALLOW_COPY_AND_ASSIGN(MediaChannelProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_MEDIA_CHANNEL_PROXY_H_
\ No newline at end of file diff --git a/chromium/chromecast/renderer/media/media_pipeline_proxy.cc b/chromium/chromecast/renderer/media/media_pipeline_proxy.cc new file mode 100644 index 00000000000..830ac0fe134 --- /dev/null +++ b/chromium/chromecast/renderer/media/media_pipeline_proxy.cc @@ -0,0 +1,283 @@ +// 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/renderer/media/media_pipeline_proxy.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/message_loop/message_loop_proxy.h" +#include "chromecast/common/media/cma_messages.h" +#include "chromecast/media/cma/base/coded_frame_provider.h" +#include "chromecast/renderer/media/audio_pipeline_proxy.h" +#include "chromecast/renderer/media/media_channel_proxy.h" +#include "chromecast/renderer/media/video_pipeline_proxy.h" + +namespace chromecast { +namespace media { + +// MediaPipelineProxyInternal - +// This class is not thread safe and should run on the same thread +// as the media channel proxy. +class MediaPipelineProxyInternal { + public: + static void Release(scoped_ptr<MediaPipelineProxyInternal> proxy); + + explicit MediaPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy); + virtual ~MediaPipelineProxyInternal(); + + void SetClient(const MediaPipelineClient& client); + void SetCdm(int render_frame_id, int cdm_id); + void StartPlayingFrom(const base::TimeDelta& time); + void Flush(const ::media::PipelineStatusCB& status_cb); + void Stop(); + void SetPlaybackRate(double playback_rate); + + private: + void Shutdown(); + + // Callbacks for CmaMessageFilterHost::MediaDelegate. + void OnStateChanged(::media::PipelineStatus status); + + base::ThreadChecker thread_checker_; + + scoped_refptr<MediaChannelProxy> media_channel_proxy_; + + MediaPipelineClient client_; + + // Store the callback for a pending state transition. + ::media::PipelineStatusCB status_cb_; + + DISALLOW_COPY_AND_ASSIGN(MediaPipelineProxyInternal); +}; + +// static +void MediaPipelineProxyInternal::Release( + scoped_ptr<MediaPipelineProxyInternal> proxy) { + proxy->Shutdown(); +} + +MediaPipelineProxyInternal::MediaPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy) + : media_channel_proxy_(media_channel_proxy) { + DCHECK(media_channel_proxy.get()); + + // Creation can be done on a different thread. + thread_checker_.DetachFromThread(); +} + +MediaPipelineProxyInternal::~MediaPipelineProxyInternal() { +} + +void MediaPipelineProxyInternal::Shutdown() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Remove any callback on VideoPipelineProxyInternal. + media_channel_proxy_->SetMediaDelegate( + CmaMessageFilterProxy::MediaDelegate()); +} + +void MediaPipelineProxyInternal::SetClient( + const MediaPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!client.error_cb.is_null()); + DCHECK(!client.buffering_state_cb.is_null()); + client_ = client; + + CmaMessageFilterProxy::MediaDelegate delegate; + delegate.state_changed_cb = + base::Bind(&MediaPipelineProxyInternal::OnStateChanged, + base::Unretained(this)); + delegate.client = client; + bool success = media_channel_proxy_->SetMediaDelegate(delegate); + CHECK(success); +} + +void MediaPipelineProxyInternal::SetCdm(int render_frame_id, int cdm_id) { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_SetCdm(media_channel_proxy_->GetId(), + render_frame_id, + cdm_id))); + LOG_IF(ERROR, !success) << "Failed to send SetCdm=" << cdm_id; +} + +void MediaPipelineProxyInternal::Flush( + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_Flush(media_channel_proxy_->GetId()))); + if (!success) { + status_cb.Run(::media::PIPELINE_ERROR_ABORT); + return; + } + DCHECK(status_cb_.is_null()); + status_cb_ = status_cb; +} + +void MediaPipelineProxyInternal::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_Stop(media_channel_proxy_->GetId()))); + if (!success) + client_.error_cb.Run(::media::PIPELINE_ERROR_ABORT); +} + +void MediaPipelineProxyInternal::StartPlayingFrom(const base::TimeDelta& time) { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_StartPlayingFrom( + media_channel_proxy_->GetId(), time))); + if (!success) + client_.error_cb.Run(::media::PIPELINE_ERROR_ABORT); +} + +void MediaPipelineProxyInternal::SetPlaybackRate(double playback_rate) { + DCHECK(thread_checker_.CalledOnValidThread()); + media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_SetPlaybackRate( + media_channel_proxy_->GetId(), playback_rate))); +} + +void MediaPipelineProxyInternal::OnStateChanged( + ::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!status_cb_.is_null()); + base::ResetAndReturn(&status_cb_).Run(status); +} + + +// A macro runs current member function on |io_message_loop_proxy_| thread. +#define FORWARD_ON_IO_THREAD(param_fn, ...) \ + io_message_loop_proxy_->PostTask( \ + FROM_HERE, \ + base::Bind(&MediaPipelineProxyInternal::param_fn, \ + base::Unretained(proxy_.get()), ##__VA_ARGS__)) + +MediaPipelineProxy::MediaPipelineProxy( + int render_frame_id, + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, + LoadType load_type) + : io_message_loop_proxy_(io_message_loop_proxy), + render_frame_id_(render_frame_id), + media_channel_proxy_(new MediaChannelProxy), + proxy_(new MediaPipelineProxyInternal(media_channel_proxy_)), + has_audio_(false), + has_video_(false), + audio_pipeline_(new AudioPipelineProxy( + io_message_loop_proxy, media_channel_proxy_)), + video_pipeline_(new VideoPipelineProxy( + io_message_loop_proxy, media_channel_proxy_)), + weak_factory_(this) { + weak_this_ = weak_factory_.GetWeakPtr(); + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&MediaChannelProxy::Open, media_channel_proxy_, + load_type)); + thread_checker_.DetachFromThread(); +} + +MediaPipelineProxy::~MediaPipelineProxy() { + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&MediaPipelineProxyInternal::Release, base::Passed(&proxy_))); + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&MediaChannelProxy::Close, media_channel_proxy_)); +} + +void MediaPipelineProxy::SetClient( + const MediaPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(SetClient, client); +} + +void MediaPipelineProxy::SetCdm(int cdm_id) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(SetCdm, render_frame_id_, cdm_id); +} + +AudioPipeline* MediaPipelineProxy::GetAudioPipeline() const { + return audio_pipeline_.get(); +} + +VideoPipeline* MediaPipelineProxy::GetVideoPipeline() const { + return video_pipeline_.get(); +} + +void MediaPipelineProxy::InitializeAudio( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + has_audio_ = true; + audio_pipeline_->Initialize(config, frame_provider.Pass(), status_cb); +} + +void MediaPipelineProxy::InitializeVideo( + const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + has_video_ = true; + video_pipeline_->Initialize(config, frame_provider.Pass(), status_cb); +} + +void MediaPipelineProxy::StartPlayingFrom(base::TimeDelta time) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (has_audio_) + audio_pipeline_->StartFeeding(); + if (has_video_) + video_pipeline_->StartFeeding(); + FORWARD_ON_IO_THREAD(StartPlayingFrom, time); +} + +void MediaPipelineProxy::Flush(const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(has_audio_ || has_video_); + + ::media::SerialRunner::Queue bound_fns; + if (has_audio_) { + bound_fns.Push(base::Bind(&AudioPipelineProxy::Flush, + base::Unretained(audio_pipeline_.get()))); + } + if (has_video_) { + bound_fns.Push(base::Bind(&VideoPipelineProxy::Flush, + base::Unretained(video_pipeline_.get()))); + } + ::media::PipelineStatusCB cb = + base::Bind(&MediaPipelineProxy::OnProxyFlushDone, weak_this_, status_cb); + pending_callbacks_ = ::media::SerialRunner::Run(bound_fns, cb); +} + +void MediaPipelineProxy::OnProxyFlushDone( + const ::media::PipelineStatusCB& status_cb, + ::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(status, ::media::PIPELINE_OK); + pending_callbacks_.reset(); + FORWARD_ON_IO_THREAD(Flush, status_cb); +} + +void MediaPipelineProxy::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(has_audio_ || has_video_); + + if (has_audio_) + audio_pipeline_->Stop(); + if (has_video_) + video_pipeline_->Stop(); + + FORWARD_ON_IO_THREAD(Stop); +} + +void MediaPipelineProxy::SetPlaybackRate(double playback_rate) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(SetPlaybackRate, playback_rate); +} + +} // namespace cma +} // namespace chromecast
\ No newline at end of file diff --git a/chromium/chromecast/renderer/media/media_pipeline_proxy.h b/chromium/chromecast/renderer/media/media_pipeline_proxy.h new file mode 100644 index 00000000000..decec653b86 --- /dev/null +++ b/chromium/chromecast/renderer/media/media_pipeline_proxy.h @@ -0,0 +1,85 @@ +// 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_RENDERER_MEDIA_MEDIA_PIPELINE_PROXY_H_ +#define CHROMECAST_RENDERER_MEDIA_MEDIA_PIPELINE_PROXY_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "chromecast/media/cma/pipeline/load_type.h" +#include "chromecast/media/cma/pipeline/media_pipeline.h" +#include "chromecast/media/cma/pipeline/media_pipeline_client.h" +#include "media/base/serial_runner.h" + +namespace base { +class MessageLoopProxy; +} + +namespace chromecast { +namespace media { +class AudioPipelineProxy; +class MediaChannelProxy; +class MediaPipelineProxyInternal; +class VideoPipelineProxy; + +class MediaPipelineProxy : public MediaPipeline { + public: + MediaPipelineProxy( + int render_frame_id, + scoped_refptr<base::MessageLoopProxy> message_loop_proxy, + LoadType load_type); + ~MediaPipelineProxy() override; + + // MediaPipeline implementation. + void SetClient(const MediaPipelineClient& client) override; + void SetCdm(int cdm_id) override; + AudioPipeline* GetAudioPipeline() const override; + VideoPipeline* GetVideoPipeline() const override; + void InitializeAudio( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) override; + void InitializeVideo( + const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) override; + void StartPlayingFrom(base::TimeDelta time) override; + void Flush(const ::media::PipelineStatusCB& status_cb) override; + void Stop() override; + void SetPlaybackRate(double playback_rate) override; + + private: + void OnProxyFlushDone(const ::media::PipelineStatusCB& status_cb, + ::media::PipelineStatus status); + + base::ThreadChecker thread_checker_; + + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + + const int render_frame_id_; + + // CMA channel to convey IPC messages. + scoped_refptr<MediaChannelProxy> const media_channel_proxy_; + + scoped_ptr<MediaPipelineProxyInternal> proxy_; + + bool has_audio_; + bool has_video_; + scoped_ptr<AudioPipelineProxy> audio_pipeline_; + scoped_ptr<VideoPipelineProxy> video_pipeline_; + scoped_ptr< ::media::SerialRunner> pending_callbacks_; + + base::WeakPtr<MediaPipelineProxy> weak_this_; + base::WeakPtrFactory<MediaPipelineProxy> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(MediaPipelineProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_MEDIA_PIPELINE_PROXY_H_
\ No newline at end of file diff --git a/chromium/chromecast/renderer/media/video_pipeline_proxy.cc b/chromium/chromecast/renderer/media/video_pipeline_proxy.cc new file mode 100644 index 00000000000..6c924cd4e2e --- /dev/null +++ b/chromium/chromecast/renderer/media/video_pipeline_proxy.cc @@ -0,0 +1,299 @@ +// 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/renderer/media/video_pipeline_proxy.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/memory/shared_memory.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/thread_checker.h" +#include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/common/media/cma_messages.h" +#include "chromecast/common/media/shared_memory_chunk.h" +#include "chromecast/media/cma/base/buffering_defs.h" +#include "chromecast/media/cma/base/cma_logging.h" +#include "chromecast/media/cma/base/coded_frame_provider.h" +#include "chromecast/media/cma/ipc/media_message_fifo.h" +#include "chromecast/media/cma/ipc_streamer/av_streamer_proxy.h" +#include "chromecast/renderer/media/cma_message_filter_proxy.h" +#include "chromecast/renderer/media/media_channel_proxy.h" +#include "media/base/bind_to_current_loop.h" +#include "media/base/pipeline_status.h" + +namespace chromecast { +namespace media { + +namespace { + +void IgnoreResult() { +} + +} // namespace + +// VideoPipelineProxyInternal - +// This class is not thread safe and should run on the same thread +// as the media channel proxy. +class VideoPipelineProxyInternal { + public: + typedef base::Callback<void(scoped_ptr<base::SharedMemory>)> SharedMemCB; + + static void Release(scoped_ptr<VideoPipelineProxyInternal> proxy); + + explicit VideoPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy); + virtual ~VideoPipelineProxyInternal(); + + // Notify the other side (browser process) of some activity on the video pipe. + // TODO(erickung): either send an IPC message or write a byte on the + // SyncSocket. + void NotifyPipeWrite(); + + // These functions are almost a one to one correspondence with VideoPipeline + // but this is an internal class and there is no reason to derive from + // VideoPipeline. + void SetClient(const base::Closure& pipe_read_cb, + const VideoPipelineClient& client); + void CreateAvPipe(const SharedMemCB& shared_mem_cb); + void Initialize(const ::media::VideoDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb); + + private: + void Shutdown(); + + // Callbacks for CmaMessageFilterHost::VideoDelegate. + void OnAvPipeCreated(bool status, + base::SharedMemoryHandle shared_mem_handle, + base::FileDescriptor socket); + void OnStateChanged(::media::PipelineStatus status); + + base::ThreadChecker thread_checker_; + + scoped_refptr<MediaChannelProxy> media_channel_proxy_; + + // Store the callback for a pending state transition. + ::media::PipelineStatusCB status_cb_; + + SharedMemCB shared_mem_cb_; + + DISALLOW_COPY_AND_ASSIGN(VideoPipelineProxyInternal); +}; + +// static +void VideoPipelineProxyInternal::Release( + scoped_ptr<VideoPipelineProxyInternal> proxy) { + proxy->Shutdown(); +} + +VideoPipelineProxyInternal::VideoPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy) + : media_channel_proxy_(media_channel_proxy) { + DCHECK(media_channel_proxy.get()); + + // Creation can be done on a different thread. + thread_checker_.DetachFromThread(); +} + +VideoPipelineProxyInternal::~VideoPipelineProxyInternal() { +} + +void VideoPipelineProxyInternal::Shutdown() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Remove any callback on VideoPipelineProxyInternal. + media_channel_proxy_->SetVideoDelegate( + CmaMessageFilterProxy::VideoDelegate()); +} + +void VideoPipelineProxyInternal::NotifyPipeWrite() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // TODO(damienv): An alternative way would be to use a dedicated socket for + // this event. + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_NotifyPipeWrite(media_channel_proxy_->GetId(), + kVideoTrackId))); + VLOG_IF(4, !success) << "Sending msg failed"; +} + +void VideoPipelineProxyInternal::SetClient( + const base::Closure& pipe_read_cb, + const VideoPipelineClient& video_client) { + DCHECK(thread_checker_.CalledOnValidThread()); + + CmaMessageFilterProxy::VideoDelegate delegate; + delegate.av_pipe_cb = + base::Bind(&VideoPipelineProxyInternal::OnAvPipeCreated, + base::Unretained(this)); + delegate.state_changed_cb = + base::Bind(&VideoPipelineProxyInternal::OnStateChanged, + base::Unretained(this)); + delegate.pipe_read_cb = pipe_read_cb; + delegate.client = video_client; + bool success = media_channel_proxy_->SetVideoDelegate(delegate); + CHECK(success); +} + +void VideoPipelineProxyInternal::CreateAvPipe( + const SharedMemCB& shared_mem_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(shared_mem_cb_.is_null()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_CreateAvPipe( + media_channel_proxy_->GetId(), kVideoTrackId, kAppVideoBufferSize))); + if (!success) { + shared_mem_cb.Run(scoped_ptr<base::SharedMemory>()); + return; + } + shared_mem_cb_ = shared_mem_cb; +} + +void VideoPipelineProxyInternal::OnAvPipeCreated( + bool success, + base::SharedMemoryHandle shared_mem_handle, + base::FileDescriptor socket) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!shared_mem_cb_.is_null()); + if (!success) { + shared_mem_cb_.Run(scoped_ptr<base::SharedMemory>()); + return; + } + + CHECK(base::SharedMemory::IsHandleValid(shared_mem_handle)); + shared_mem_cb_.Run(scoped_ptr<base::SharedMemory>( + new base::SharedMemory(shared_mem_handle, false))); +} + +void VideoPipelineProxyInternal::Initialize( + const ::media::VideoDecoderConfig& arg1, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_VideoInitialize(media_channel_proxy_->GetId(), + kVideoTrackId, arg1))); + if (!success) { + status_cb.Run( ::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + DCHECK(status_cb_.is_null()); + status_cb_ = status_cb; +} + +void VideoPipelineProxyInternal::OnStateChanged( + ::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!status_cb_.is_null()); + base::ResetAndReturn(&status_cb_).Run(status); +} + + +// A macro runs current member function on |io_message_loop_proxy_| thread. +#define FORWARD_ON_IO_THREAD(param_fn, ...) \ + io_message_loop_proxy_->PostTask( \ + FROM_HERE, \ + base::Bind(&VideoPipelineProxyInternal::param_fn, \ + base::Unretained(proxy_.get()), ##__VA_ARGS__)) + +VideoPipelineProxy::VideoPipelineProxy( + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, + scoped_refptr<MediaChannelProxy> media_channel_proxy) + : io_message_loop_proxy_(io_message_loop_proxy), + proxy_(new VideoPipelineProxyInternal(media_channel_proxy)), + video_streamer_(new AvStreamerProxy()), + weak_factory_(this) { + DCHECK(io_message_loop_proxy_.get()); + weak_this_ = weak_factory_.GetWeakPtr(); + thread_checker_.DetachFromThread(); +} + +VideoPipelineProxy::~VideoPipelineProxy() { + DCHECK(thread_checker_.CalledOnValidThread()); + // Release the underlying object on the right thread. + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&VideoPipelineProxyInternal::Release, base::Passed(&proxy_))); +} + +void VideoPipelineProxy::SetClient( + const VideoPipelineClient& video_client) { + DCHECK(thread_checker_.CalledOnValidThread()); + base::Closure pipe_read_cb = + ::media::BindToCurrentLoop( + base::Bind(&VideoPipelineProxy::OnPipeRead, weak_this_)); + FORWARD_ON_IO_THREAD(SetClient, pipe_read_cb, video_client); +} + +void VideoPipelineProxy::Initialize( + const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << "VideoPipelineProxy::Initialize"; + DCHECK(thread_checker_.CalledOnValidThread()); + video_streamer_->SetCodedFrameProvider(frame_provider.Pass()); + + VideoPipelineProxyInternal::SharedMemCB shared_mem_cb = + ::media::BindToCurrentLoop(base::Bind( + &VideoPipelineProxy::OnAvPipeCreated, weak_this_, + config, status_cb)); + FORWARD_ON_IO_THREAD(CreateAvPipe, shared_mem_cb); +} + +void VideoPipelineProxy::OnAvPipeCreated( + const ::media::VideoDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb, + scoped_ptr<base::SharedMemory> shared_memory) { + CMALOG(kLogControl) << "VideoPipelineProxy::OnAvPipeCreated"; + DCHECK(thread_checker_.CalledOnValidThread()); + if (!shared_memory || + !shared_memory->Map(kAppVideoBufferSize)) { + status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + CHECK(shared_memory->memory()); + + scoped_ptr<MediaMemoryChunk> shared_memory_chunk( + new SharedMemoryChunk(shared_memory.Pass(), kAppVideoBufferSize)); + scoped_ptr<MediaMessageFifo> video_pipe( + new MediaMessageFifo(shared_memory_chunk.Pass(), false)); + video_pipe->ObserveWriteActivity( + base::Bind(&VideoPipelineProxy::OnPipeWrite, weak_this_)); + + video_streamer_->SetMediaMessageFifo(video_pipe.Pass()); + + // Now proceed to the decoder/renderer initialization. + FORWARD_ON_IO_THREAD(Initialize, config, status_cb); +} + +void VideoPipelineProxy::StartFeeding() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(video_streamer_); + video_streamer_->Start(); +} + +void VideoPipelineProxy::Flush(const base::Closure& done_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(video_streamer_); + video_streamer_->StopAndFlush(done_cb); +} + +void VideoPipelineProxy::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!video_streamer_) + return; + video_streamer_->StopAndFlush(base::Bind(&IgnoreResult)); +} + +void VideoPipelineProxy::OnPipeWrite() { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(NotifyPipeWrite); +} + +void VideoPipelineProxy::OnPipeRead() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (video_streamer_) + video_streamer_->OnFifoReadEvent(); +} + +} // namespace media +} // namespace chromecast
\ No newline at end of file diff --git a/chromium/chromecast/renderer/media/video_pipeline_proxy.h b/chromium/chromecast/renderer/media/video_pipeline_proxy.h new file mode 100644 index 00000000000..88745525091 --- /dev/null +++ b/chromium/chromecast/renderer/media/video_pipeline_proxy.h @@ -0,0 +1,76 @@ +// 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_RENDERER_MEDIA_VIDEO_PIPELINE_PROXY_H_ +#define CHROMECAST_RENDERER_MEDIA_VIDEO_PIPELINE_PROXY_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "chromecast/media/cma/pipeline/video_pipeline.h" +#include "media/base/pipeline_status.h" + +namespace base { +class MessageLoopProxy; +class SharedMemory; +} + +namespace media { +class VideoDecoderConfig; +} + +namespace chromecast { +namespace media { +struct AvPipelineClient; +class AvStreamerProxy; +class CodedFrameProvider; +class VideoPipelineProxyInternal; +class MediaChannelProxy; + +class VideoPipelineProxy : public VideoPipeline { + public: + VideoPipelineProxy( + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, + scoped_refptr<MediaChannelProxy> media_channel_proxy); + ~VideoPipelineProxy() override; + + void Initialize(const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb); + void StartFeeding(); + void Flush(const base::Closure& done_cb); + void Stop(); + + // VideoPipeline implementation. + void SetClient(const VideoPipelineClient& video_client) override; + + private: + base::ThreadChecker thread_checker_; + + void OnAvPipeCreated( + const ::media::VideoDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb, + scoped_ptr<base::SharedMemory> shared_memory); + void OnPipeWrite(); + void OnPipeRead(); + + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + + // |proxy_| main goal is to convert function calls to IPC messages. + scoped_ptr<VideoPipelineProxyInternal> proxy_; + + scoped_ptr<AvStreamerProxy> video_streamer_; + + base::WeakPtr<VideoPipelineProxy> weak_this_; + base::WeakPtrFactory<VideoPipelineProxy> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(VideoPipelineProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_VIDEO_PIPELINE_PROXY_H_
\ No newline at end of file diff --git a/chromium/chromecast/tools/build/generate_test_lists.py b/chromium/chromecast/tools/build/generate_test_lists.py new file mode 100755 index 00000000000..46e8b3d293d --- /dev/null +++ b/chromium/chromecast/tools/build/generate_test_lists.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# 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. + +"""Helper script to generate unit test lists for the Chromecast build scripts. +""" + +import glob +import optparse +import sys + + +def CombineList(test_files_dir, list_output_file, include_filters, + additional_runtime_options): + """Writes a unit test file in a format compatible for Chromecast scripts. + + If include_filters is True, uses filters to create a test runner list + and also include additional options, if any. + Otherwise, creates a list only of the tests to build. + + Args: + test_files_dir: Path to the intermediate directory containing tests/filters. + list_output_file: Path to write the unit test file out to. + include_filters: Whether or not to include the filters when generating + the test list. + """ + + # GYP targets may provide a numbered priority for the filename. Sort to + # use that priority. + test_files = sorted(glob.glob(test_files_dir + "/*.tests")) + filter_files = sorted(glob.glob(test_files_dir + "/*.filters")) + + test_bin_set = set() + for test_filename in test_files: + with open(test_filename, "r") as test_file: + for test_file_line in test_file: + # Binary name may be a simple test target (cast_net_unittests) or be a + # qualified gyp path (../base.gyp:base_unittests). + test_binary_name = test_file_line.split(":")[-1].strip() + test_bin_set.add(test_binary_name) + + test_filters = {} + if include_filters: + for filter_filename in filter_files: + with open(filter_filename, "r") as filter_file: + for filter_line in filter_file: + filter = filter_line.strip() + test_binary_name = filter.split(" ", 1)[0] + + if test_binary_name not in test_bin_set: + raise Exception("Filter found for unknown target: " + + test_binary_name) + + # Note: This may overwrite a previous rule. This is okay, since higher + # priority files are evaluated after lower priority files. + test_filters[test_binary_name] = filter + + test_binaries = ( + list(test_bin_set - set(test_filters.keys())) + + test_filters.values()) + + if additional_runtime_options: + lines = [ + binary + " " + additional_runtime_options + for binary in test_binaries + ] + else: + lines = test_binaries + with open(list_output_file, "w") as f: + f.write("\n".join(sorted(lines))) + + +def CreateList(inputs, list_output_file): + with open(list_output_file, "w") as f: + f.write("\n".join(inputs)) + + +def DoMain(argv): + """Main method. Runs helper commands for generating unit test lists.""" + parser = optparse.OptionParser( + """usage: %prog [<options>] <command> [<test names>] + + Valid commands: + create_list prints all given test names/args to a file, one line + per string + pack_build packs all test files from the given output directory + into a single test list file + pack_run packs all test and filter files from the given + output directory into a single test list file + """) + parser.add_option("-o", action="store", dest="list_output_file", + help="Output path in which to write the test list.") + parser.add_option("-t", action="store", dest="test_files_dir", + help="Intermediate test list directory.") + parser.add_option("-a", action="store", dest="additional_runtime_options", + help="Additional options applied to all tests.") + options, inputs = parser.parse_args(argv) + + list_output_file = options.list_output_file + test_files_dir = options.test_files_dir + additional_runtime_options = options.additional_runtime_options + + if len(inputs) < 1: + parser.error("No command given.\n") + command = inputs[0] + test_names = inputs[1:] + + if not list_output_file: + parser.error("Output path (-o) is required.\n") + + if command == "create_list": + return CreateList(test_names, list_output_file) + + if command == "pack_build": + if not test_files_dir: + parser.error("pack_build require a test files directory (-t).\n") + return CombineList(test_files_dir, list_output_file, False, None) + + if command == "pack_run": + if not test_files_dir: + parser.error("pack_run require a test files directory (-t).\n") + return CombineList(test_files_dir, list_output_file, True, + additional_runtime_options) + + parser.error("Invalid command specified.") + + +if __name__ == "__main__": + DoMain(sys.argv[1:]) diff --git a/chromium/chromecast/tools/trace.py b/chromium/chromecast/tools/trace.py new file mode 100755 index 00000000000..bf93611e0ba --- /dev/null +++ b/chromium/chromecast/tools/trace.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +# +# Copyright 2015 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. +# +# This script was originally written by Alok Priyadarshi (alokp@) +# with some minor local modifications. + +import contextlib +import json +import logging +import math +import optparse +import os +import sys +import websocket + + +class TracingClient(object): + def BufferUsage(self, buffer_usage): + percent = int(math.floor(buffer_usage * 100)) + logging.debug('Buffer Usage: %i', percent) + + +class TracingBackend(object): + def __init__(self, devtools_port): + self._socket = None + self._next_request_id = 0 + self._tracing_client = None + self._tracing_data = [] + + def Connect(self, device_ip, devtools_port, timeout=10): + assert not self._socket + url = 'ws://%s:%i/devtools/browser' % (device_ip, devtools_port) + print('Connect to %s ...' % url) + self._socket = websocket.create_connection(url, timeout=timeout) + self._next_request_id = 0 + + def Disconnect(self): + if self._socket: + self._socket.close() + self._socket = None + + def StartTracing(self, + tracing_client=None, + custom_categories=None, + record_continuously=False, + buffer_usage_reporting_interval=0, + timeout=10): + self._tracing_client = tracing_client + self._socket.settimeout(timeout) + req = { + 'method': 'Tracing.start', + 'params': { + 'categories': custom_categories, + 'bufferUsageReportingInterval': buffer_usage_reporting_interval, + 'options': 'record-continuously' if record_continuously else + 'record-until-full' + } + } + self._SendRequest(req) + + def StopTracing(self, timeout=30): + self._socket.settimeout(timeout) + req = {'method': 'Tracing.end'} + self._SendRequest(req) + while self._socket: + res = self._ReceiveResponse() + if 'method' in res and self._HandleResponse(res): + self._tracing_client = None + result = self._tracing_data + self._tracing_data = [] + return result + + def _SendRequest(self, req): + req['id'] = self._next_request_id + self._next_request_id += 1 + data = json.dumps(req) + self._socket.send(data) + + def _ReceiveResponse(self): + while self._socket: + data = self._socket.recv() + res = json.loads(data) + return res + + def _HandleResponse(self, res): + method = res.get('method') + value = res.get('params', {}).get('value') + if 'Tracing.dataCollected' == method: + if type(value) in [str, unicode]: + self._tracing_data.append(value) + elif type(value) is list: + self._tracing_data.extend(value) + else: + logging.warning('Unexpected type in tracing data') + elif 'Tracing.bufferUsage' == method and self._tracing_client: + self._tracing_client.BufferUsage(value) + elif 'Tracing.tracingComplete' == method: + return True + + +@contextlib.contextmanager +def Connect(device_ip, devtools_port): + backend = TracingBackend(devtools_port) + try: + backend.Connect(device_ip, devtools_port) + yield backend + finally: + backend.Disconnect() + + +def DumpTrace(trace, options): + filepath = os.path.expanduser(options.output) if options.output \ + else os.path.join(os.getcwd(), 'trace.json') + + dirname = os.path.dirname(filepath) + if dirname: + if not os.path.exists(dirname): + os.makedirs(dirname) + else: + filepath = os.path.join(os.getcwd(), filepath) + + with open(filepath, "w") as f: + json.dump(trace, f) + return filepath + + +def _CreateOptionParser(): + parser = optparse.OptionParser(description='Record about://tracing profiles ' + 'from any running instance of Chrome.') + parser.add_option( + '-v', '--verbose', help='Verbose logging.', action='store_true') + parser.add_option( + '-p', '--port', help='Remote debugging port.', type="int", default=9222) + parser.add_option( + '-d', '--device', help='Device ip address.', type='string', + default='127.0.0.1') + + tracing_opts = optparse.OptionGroup(parser, 'Tracing options') + tracing_opts.add_option( + '-c', '--category-filter', + help='Apply filter to control what category groups should be traced.', + type='string') + tracing_opts.add_option( + '--record-continuously', + help='Keep recording until stopped. The trace buffer is of fixed size ' + 'and used as a ring buffer. If this option is omitted then ' + 'recording stops when the trace buffer is full.', + action='store_true') + parser.add_option_group(tracing_opts) + + output_options = optparse.OptionGroup(parser, 'Output options') + output_options.add_option( + '-o', '--output', + help='Save trace output to file.') + parser.add_option_group(output_options) + + return parser + + +def _ProcessOptions(options): + websocket.enableTrace(options.verbose) + + +def main(): + parser = _CreateOptionParser() + options, _args = parser.parse_args() + _ProcessOptions(options) + + with Connect(options.device, options.port) as tracing_backend: + tracing_backend.StartTracing(TracingClient(), + options.category_filter, + options.record_continuously) + raw_input('Capturing trace. Press Enter to stop...') + trace = tracing_backend.StopTracing() + + filepath = DumpTrace(trace, options) + print('Done') + print('Trace written to file://%s' % filepath) + + +if __name__ == '__main__': + sys.exit(main()) |