diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-08-14 11:38:45 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-08-14 17:16:47 +0000 |
commit | 3a97ca8dd9b96b599ae2d33e40df0dd2f7ea5859 (patch) | |
tree | 43cc572ba067417c7341db81f71ae7cc6e0fcc3e /chromium/chromecast | |
parent | f61ab1ac7f855cd281809255c0aedbb1895e1823 (diff) | |
download | qtwebengine-chromium-3a97ca8dd9b96b599ae2d33e40df0dd2f7ea5859.tar.gz |
BASELINE: Update chromium to 45.0.2454.40
Change-Id: Id2121d9f11a8fc633677236c65a3e41feef589e4
Reviewed-by: Andras Becsi <andras.becsi@theqtcompany.com>
Diffstat (limited to 'chromium/chromecast')
220 files changed, 7772 insertions, 2619 deletions
diff --git a/chromium/chromecast/BUILD.gn b/chromium/chromecast/BUILD.gn index 6169cf86808..e5f3b89ee80 100644 --- a/chromium/chromecast/BUILD.gn +++ b/chromium/chromecast/BUILD.gn @@ -16,6 +16,18 @@ component("chromecast") { deps = [ "//chromecast/base", "//chromecast/base/metrics", + "//chromecast/crash", "//chromecast/media", ] } + +group("chromecast_unittests") { + testonly = true + + deps = [ + "//chromecast/app:cast_shell_unittests", + "//chromecast/base:cast_base_unittests", + "//chromecast/crash:cast_crash_unittests", + "//chromecast/media:cast_media_unittests", + ] +} diff --git a/chromium/chromecast/android/AndroidManifest.xml b/chromium/chromecast/android/AndroidManifest.xml new file mode 100644 index 00000000000..bd6061880c7 --- /dev/null +++ b/chromium/chromecast/android/AndroidManifest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-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. +--> + +<!-- + This is a dummy manifest which is required by lint for determining min/target + SDK version. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="dummy.package"> + + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="22" /> + +</manifest> diff --git a/chromium/chromecast/android/DEPS b/chromium/chromecast/android/DEPS index df718c8e423..941b823e6ed 100644 --- a/chromium/chromecast/android/DEPS +++ b/chromium/chromecast/android/DEPS @@ -1,7 +1,7 @@ include_rules = [ # Includes for JNI. "+chromecast/android", + "+chromecast/app/android", "+chromecast/browser/android", - "+chromecast/crash/android", "+components/external_video_surface", ] diff --git a/chromium/chromecast/android/cast_metrics_helper_android.cc b/chromium/chromecast/android/cast_metrics_helper_android.cc new file mode 100644 index 00000000000..62530bd0224 --- /dev/null +++ b/chromium/chromecast/android/cast_metrics_helper_android.cc @@ -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. + +#include "chromecast/android/cast_metrics_helper_android.h" + +#include "chromecast/base/metrics/cast_metrics_helper.h" +#include "jni/CastMetricsHelper_jni.h" + +namespace chromecast { + +// static +bool CastMetricsHelperAndroid::RegisterJni(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +void LogMediaPlay(JNIEnv* env, jclass clazz) { + metrics::CastMetricsHelper::GetInstance()->LogMediaPlay(); +} + +void LogMediaPause(JNIEnv* env, jclass clazz) { + metrics::CastMetricsHelper::GetInstance()->LogMediaPause(); +} + +} // namespace chromecast diff --git a/chromium/chromecast/android/cast_metrics_helper_android.h b/chromium/chromecast/android/cast_metrics_helper_android.h new file mode 100644 index 00000000000..23e0f470d70 --- /dev/null +++ b/chromium/chromecast/android/cast_metrics_helper_android.h @@ -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. + +#ifndef CHROMECAST_ANDROID_CAST_METRICS_HELPER_ANDROID_H_ +#define CHROMECAST_ANDROID_CAST_METRICS_HELPER_ANDROID_H_ + +#include <jni.h> +#include <vector> + +#include "base/macros.h" + +namespace chromecast { + +class CastMetricsHelperAndroid { + public: + // Registers the JNI methods for CastMetricsHelperAndroid. + static bool RegisterJni(JNIEnv* env); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(CastMetricsHelperAndroid); +}; + +} // namespace chromecast + +#endif // CHROMECAST_ANDROID_CAST_METRICS_HELPER_ANDROID_H_ diff --git a/chromium/chromecast/android/chromecast_config_android.cc b/chromium/chromecast/android/chromecast_config_android.cc deleted file mode 100644 index d698b70a18b..00000000000 --- a/chromium/chromecast/android/chromecast_config_android.cc +++ /dev/null @@ -1,33 +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/android/chromecast_config_android.h" - -namespace chromecast { -namespace android { - -namespace { -base::LazyInstance<ChromecastConfigAndroid> g_instance = - LAZY_INSTANCE_INITIALIZER; -} // namespace - -// static -ChromecastConfigAndroid* ChromecastConfigAndroid::GetInstance() { - return g_instance.Pointer(); -} - -ChromecastConfigAndroid::ChromecastConfigAndroid() { -} - -ChromecastConfigAndroid::~ChromecastConfigAndroid() { -} - -// Registers a handler to be notified when SendUsageStats is changed. -void ChromecastConfigAndroid::SetSendUsageStatsChangedCallback( - const base::Callback<void(bool)>& callback) { - send_usage_stats_changed_callback_ = callback; -} - -} // namespace android -} // namespace chromecast diff --git a/chromium/chromecast/android/chromecast_config_android_stub.cc b/chromium/chromecast/android/chromecast_config_android_stub.cc deleted file mode 100644 index dc8bdca2d6a..00000000000 --- a/chromium/chromecast/android/chromecast_config_android_stub.cc +++ /dev/null @@ -1,15 +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/android/chromecast_config_android.h" - -namespace chromecast { -namespace android { - -bool ChromecastConfigAndroid::CanSendUsageStats() { - return false; -} - -} // namespace android -} // namespace chromecast diff --git a/chromium/chromecast/app/BUILD.gn b/chromium/chromecast/app/BUILD.gn new file mode 100644 index 00000000000..51cf158402f --- /dev/null +++ b/chromium/chromecast/app/BUILD.gn @@ -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. + +import("//testing/test.gni") + +source_set("cast_crash_client") { + sources = [ + "android/cast_crash_reporter_client_android.cc", + "android/cast_crash_reporter_client_android.h", + "linux/cast_crash_reporter_client.cc", + "linux/cast_crash_reporter_client.h", + ] + + configs += [ "//chromecast:config" ] + + deps = [ + "//base", + "//chromecast/base", + "//chromecast/crash", + "//components/crash/app", + "//components/crash/app:lib", + "//content/public/common", + ] +} + +# TODO(kmackay) Consider renaming this. +test("cast_shell_unittests") { + sources = [ + "linux/cast_crash_reporter_client_unittest.cc", + ] + + deps = [ + ":cast_crash_client", + "//base", + "//base/test:run_all_unittests", + "//base/test:test_support", + "//chromecast/crash", + "//testing/gtest", + ] +} diff --git a/chromium/chromecast/app/android/DEPS b/chromium/chromecast/app/android/DEPS index 5021862892e..c31bf9fbce2 100644 --- a/chromium/chromecast/app/android/DEPS +++ b/chromium/chromecast/app/android/DEPS @@ -1,3 +1,4 @@ include_rules = [ + "+breakpad", "+chromecast/android", ] diff --git a/chromium/chromecast/crash/android/cast_crash_reporter_client_android.cc b/chromium/chromecast/app/android/cast_crash_reporter_client_android.cc index ae4063745e5..5bc66ab9e3c 100644 --- a/chromium/chromecast/crash/android/cast_crash_reporter_client_android.cc +++ b/chromium/chromecast/app/android/cast_crash_reporter_client_android.cc @@ -2,21 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromecast/crash/android/cast_crash_reporter_client_android.h" +#include "chromecast/app/android/cast_crash_reporter_client_android.h" #include "base/base_paths.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/path_service.h" -#include "chromecast/android/chromecast_config_android.h" +#include "chromecast/base/chromecast_config_android.h" +#include "chromecast/base/version.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 { -CastCrashReporterClientAndroid::CastCrashReporterClientAndroid() { +CastCrashReporterClientAndroid::CastCrashReporterClientAndroid( + const std::string& process_type) + : process_type_(process_type) { } CastCrashReporterClientAndroid::~CastCrashReporterClientAndroid() { @@ -37,7 +39,9 @@ base::FilePath CastCrashReporterClientAndroid::GetReporterLogFilename() { return base::FilePath(FILE_PATH_LITERAL("uploads.log")); } +// static bool CastCrashReporterClientAndroid::GetCrashDumpLocation( + const std::string& process_type, base::FilePath* crash_dir) { base::FilePath crash_dir_local; if (!PathService::Get(base::DIR_ANDROID_APP_DATA, &crash_dir_local)) { @@ -45,9 +49,12 @@ bool CastCrashReporterClientAndroid::GetCrashDumpLocation( } crash_dir_local = crash_dir_local.Append("crashes"); - if (!base::DirectoryExists(crash_dir_local)) { - if (!base::CreateDirectory(crash_dir_local)) { - return false; + // Only try to create the directory in the browser process (empty value). + if (process_type.empty()) { + if (!base::DirectoryExists(crash_dir_local)) { + if (!base::CreateDirectory(crash_dir_local)) { + return false; + } } } @@ -56,6 +63,12 @@ bool CastCrashReporterClientAndroid::GetCrashDumpLocation( return true; } +bool CastCrashReporterClientAndroid::GetCrashDumpLocation( + base::FilePath* crash_dir) { + return CastCrashReporterClientAndroid::GetCrashDumpLocation(process_type_, + crash_dir); +} + size_t CastCrashReporterClientAndroid::RegisterCrashKeys() { return crash_keys::RegisterCastCrashKeys(); } diff --git a/chromium/chromecast/crash/android/cast_crash_reporter_client_android.h b/chromium/chromecast/app/android/cast_crash_reporter_client_android.h index e98eef72605..a87d1670079 100644 --- a/chromium/chromecast/crash/android/cast_crash_reporter_client_android.h +++ b/chromium/chromecast/app/android/cast_crash_reporter_client_android.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_CRASH_ANDROID_CAST_CRASH_REPORTER_CLIENT_ANDROID_H_ -#define CHROMECAST_CRASH_ANDROID_CAST_CRASH_REPORTER_CLIENT_ANDROID_H_ +#ifndef CHROMECAST_APP_ANDROID_CAST_CRASH_REPORTER_CLIENT_ANDROID_H_ +#define CHROMECAST_APP_ANDROID_CAST_CRASH_REPORTER_CLIENT_ANDROID_H_ #include "base/compiler_specific.h" #include "components/crash/app/crash_reporter_client.h" @@ -13,9 +13,12 @@ namespace chromecast { class CastCrashReporterClientAndroid : public crash_reporter::CrashReporterClient { public: - CastCrashReporterClientAndroid(); + explicit CastCrashReporterClientAndroid(const std::string& process_type); ~CastCrashReporterClientAndroid() override; + static bool GetCrashDumpLocation(const std::string& process_type, + base::FilePath* crash_dir); + // crash_reporter::CrashReporterClient implementation: void GetProductNameAndVersion(const char** product_name, const char** version) override; @@ -23,14 +26,15 @@ class CastCrashReporterClientAndroid bool GetCrashDumpLocation(base::FilePath* crash_dir) override; int GetAndroidMinidumpDescriptor() override; bool GetCollectStatsConsent() override; - bool EnableBreakpadForProcess( - const std::string& process_type) override; + bool EnableBreakpadForProcess(const std::string& process_type) override; size_t RegisterCrashKeys() override; private: + std::string process_type_; + DISALLOW_COPY_AND_ASSIGN(CastCrashReporterClientAndroid); }; } // namespace chromecast -#endif // CHROMECAST_CRASH_ANDROID_CAST_CRASH_REPORTER_CLIENT_ANDROID_H_ +#endif // CHROMECAST_APP_ANDROID_CAST_CRASH_REPORTER_CLIENT_ANDROID_H_ diff --git a/chromium/chromecast/app/android/crash_handler.cc b/chromium/chromecast/app/android/crash_handler.cc new file mode 100644 index 00000000000..8a2dc7ac657 --- /dev/null +++ b/chromium/chromecast/app/android/crash_handler.cc @@ -0,0 +1,105 @@ +// 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/app/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/app/android/cast_crash_reporter_client_android.h" +#include "chromecast/base/version.h" +#include "components/crash/app/breakpad_linux.h" +#include "components/crash/app/crash_reporter_client.h" +#include "content/public/common/content_switches.h" +#include "jni/CastCrashHandler_jni.h" + +namespace { + +chromecast::CrashHandler* g_crash_handler = NULL; + +// 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 { + +// static +void CrashHandler::Initialize(const std::string& process_type, + const base::FilePath& log_file_path) { + DCHECK(!g_crash_handler); + g_crash_handler = new CrashHandler(process_type, log_file_path); + g_crash_handler->Initialize(); +} + +// static +bool CrashHandler::GetCrashDumpLocation(base::FilePath* crash_dir) { + DCHECK(g_crash_handler); + return g_crash_handler->crash_reporter_client_->GetCrashDumpLocation( + crash_dir); +} + +// static +bool CrashHandler::RegisterCastCrashJni(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +CrashHandler::CrashHandler(const std::string& process_type, + const base::FilePath& log_file_path) + : log_file_path_(log_file_path), + process_type_(process_type), + crash_reporter_client_(new CastCrashReporterClientAndroid(process_type)) { + if (!crash_reporter_client_->GetCrashDumpLocation(&crash_dump_path_)) { + LOG(ERROR) << "Could not get crash dump location"; + } + SetCrashReporterClient(crash_reporter_client_.get()); +} + +CrashHandler::~CrashHandler() { + DCHECK(g_crash_handler); + g_crash_handler = NULL; +} + +void CrashHandler::Initialize() { + if (process_type_.empty()) { + InitializeUploader(); + breakpad::InitCrashReporter(process_type_); + return; + } + + if (process_type_ != switches::kZygoteProcess) { + breakpad::InitNonBrowserCrashReporterForAndroid(process_type_); + } +} + +void CrashHandler::InitializeUploader() { + JNIEnv* env = base::android::AttachCurrentThread(); + base::android::ScopedJavaLocalRef<jstring> crash_dump_path_java = + base::android::ConvertUTF8ToJavaString(env, crash_dump_path_.value()); + Java_CastCrashHandler_initializeUploader( + env, + base::android::GetApplicationContext(), + crash_dump_path_java.obj(), + UploadCrashToStaging()); +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/android/crash_handler.h b/chromium/chromecast/app/android/crash_handler.h index 853e7f50973..d4a79754d07 100644 --- a/chromium/chromecast/crash/android/crash_handler.h +++ b/chromium/chromecast/app/android/crash_handler.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_CRASH_ANDROID_CRASH_HANDLER_H_ -#define CHROMECAST_CRASH_ANDROID_CRASH_HANDLER_H_ +#ifndef CHROMECAST_APP_ANDROID_CRASH_HANDLER_H_ +#define CHROMECAST_APP_ANDROID_CRASH_HANDLER_H_ #include <jni.h> #include <string> @@ -12,10 +12,6 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" -namespace google_breakpad { -class ExceptionHandler; -} - namespace chromecast { class CastCrashReporterClientAndroid; @@ -33,16 +29,12 @@ class CrashHandler { // Registers JNI methods for this module. static bool RegisterCastCrashJni(JNIEnv* env); - // Returns whether or not the user has allowed for uploading crash dumps. - bool CanUploadCrashDump(); - - void UploadCrashDumps(); - private: - CrashHandler(const base::FilePath& log_file_path); + CrashHandler(const std::string& process_type, + const base::FilePath& log_file_path); ~CrashHandler(); - void Initialize(const std::string& process_type); + void Initialize(); // Starts a thread to periodically check for uploads void InitializeUploader(); @@ -53,12 +45,13 @@ class CrashHandler { // Location to which crash dumps should be written. base::FilePath crash_dump_path_; + std::string process_type_; + scoped_ptr<CastCrashReporterClientAndroid> crash_reporter_client_; - scoped_ptr<google_breakpad::ExceptionHandler> crash_uploader_; DISALLOW_COPY_AND_ASSIGN(CrashHandler); }; } // namespace chromecast -#endif // CHROMECAST_CRASH_ANDROID_CRASH_HANDLER_H_ +#endif // CHROMECAST_APP_ANDROID_CRASH_HANDLER_H_ diff --git a/chromium/chromecast/app/cast_main_delegate.cc b/chromium/chromecast/app/cast_main_delegate.cc index 58c7507eaf3..88764598541 100644 --- a/chromium/chromecast/app/cast_main_delegate.cc +++ b/chromium/chromecast/app/cast_main_delegate.cc @@ -4,10 +4,15 @@ #include "chromecast/app/cast_main_delegate.h" +#include <algorithm> #include <string> +#include <vector> #include "base/command_line.h" #include "base/cpu.h" +#include "base/files/file.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/path_service.h" @@ -16,7 +21,6 @@ #include "chromecast/browser/cast_content_browser_client.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" @@ -24,7 +28,11 @@ #include "ui/base/resource/resource_bundle.h" #if defined(OS_ANDROID) -#include "chromecast/crash/android/crash_handler.h" +#include "base/android/apk_assets.h" +#include "chromecast/app/android/cast_crash_reporter_client_android.h" +#include "chromecast/app/android/crash_handler.h" +#else +#include "chromecast/app/linux/cast_crash_reporter_client.h" #endif // defined(OS_ANDROID) namespace { @@ -34,6 +42,10 @@ base::LazyInstance<chromecast::CastCrashReporterClient>::Leaky g_crash_reporter_client = LAZY_INSTANCE_INITIALIZER; #endif // !defined(OS_ANDROID) +#if defined(OS_ANDROID) +const int kMaxCrashFiles = 10; +#endif // defined(OS_ANDROID) + } // namespace namespace chromecast { @@ -54,7 +66,13 @@ bool CastMainDelegate::BasicStartupComplete(int* exit_code) { PathService::Get(FILE_CAST_ANDROID_LOG, &log_file); settings.logging_dest = logging::LOG_TO_ALL; settings.log_file = log_file.value().c_str(); - settings.delete_old = logging::DELETE_OLD_LOG_FILE; + const base::CommandLine* command_line(base::CommandLine::ForCurrentProcess()); + std::string process_type = + command_line->GetSwitchValueASCII(switches::kProcessType); + // Only delete the old logs if the current process is the browser process. + // Empty process_type signifies browser process. + settings.delete_old = process_type.empty() ? logging::DELETE_OLD_LOG_FILE + : logging::APPEND_TO_OLD_LOG_FILE; #else settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; #endif // defined(OS_ANDROID) @@ -62,6 +80,42 @@ bool CastMainDelegate::BasicStartupComplete(int* exit_code) { // Time, process, and thread ID are available through logcat. logging::SetLogItems(true, true, false, false); +#if defined(OS_ANDROID) + // Only delete the old crash dumps if the current process is the browser + // process. Empty |process_type| signifies browser process. + if (process_type.empty()) { + // Get a listing of all of the crash dump files. + base::FilePath crash_directory; + if (CastCrashReporterClientAndroid::GetCrashDumpLocation( + process_type, &crash_directory)) { + base::FileEnumerator crash_directory_list(crash_directory, false, + base::FileEnumerator::FILES); + std::vector<base::FilePath> crash_files; + for (base::FilePath file = crash_directory_list.Next(); !file.empty(); + file = crash_directory_list.Next()) { + crash_files.push_back(file); + } + // Delete crash dumps except for the |kMaxCrashFiles| most recent ones. + if (crash_files.size() > kMaxCrashFiles) { + auto newest_first = + [](const base::FilePath& l, const base::FilePath& r) -> bool { + base::File::Info l_info, r_info; + base::GetFileInfo(l, &l_info); + base::GetFileInfo(r, &r_info); + return l_info.last_modified > r_info.last_modified; + }; + std::partial_sort(crash_files.begin(), + crash_files.begin() + kMaxCrashFiles, + crash_files.end(), newest_first); + for (auto file = crash_files.begin() + kMaxCrashFiles; + file != crash_files.end(); ++file) { + base::DeleteFile(*file, false); + } + } + } + } +#endif // defined(OS_ANDROID) + content::SetContentClient(&content_client_); return false; } @@ -120,17 +174,31 @@ void CastMainDelegate::ZygoteForked() { #endif // !defined(OS_ANDROID) void CastMainDelegate::InitializeResourceBundle() { + base::FilePath pak_file; + CHECK(PathService::Get(FILE_CAST_PAK, &pak_file)); #if defined(OS_ANDROID) // On Android, the renderer runs with a different UID and can never access // the file system. Use the file descriptor passed in at launch time. - int pak_fd = - base::GlobalDescriptors::GetInstance()->MaybeGet(kAndroidPakDescriptor); + auto global_descriptors = base::GlobalDescriptors::GetInstance(); + int pak_fd = global_descriptors->MaybeGet(kAndroidPakDescriptor); + base::MemoryMappedFile::Region pak_region; if (pak_fd >= 0) { - ui::ResourceBundle::InitSharedInstanceWithPakFileRegion( - base::File(pak_fd), base::MemoryMappedFile::Region::kWholeFile); - ui::ResourceBundle::GetSharedInstance().AddDataPackFromFile( - base::File(pak_fd), ui::SCALE_FACTOR_100P); + pak_region = global_descriptors->GetRegion(kAndroidPakDescriptor); + ui::ResourceBundle::InitSharedInstanceWithPakFileRegion(base::File(pak_fd), + pak_region); + ui::ResourceBundle::GetSharedInstance().AddDataPackFromFileRegion( + base::File(pak_fd), pak_region, ui::SCALE_FACTOR_100P); return; + } else { + pak_fd = base::android::OpenApkAsset("assets/cast_shell.pak", &pak_region); + // Loaded from disk for browsertests. + if (pak_fd < 0) { + int flags = base::File::FLAG_OPEN | base::File::FLAG_READ; + pak_fd = base::File(pak_file, flags).TakePlatformFile(); + pak_region = base::MemoryMappedFile::Region::kWholeFile; + } + DCHECK_GE(pak_fd, 0); + global_descriptors->Set(kAndroidPakDescriptor, pak_fd, pak_region); } #endif // defined(OS_ANDROID) @@ -138,25 +206,26 @@ void CastMainDelegate::InitializeResourceBundle() { // TODO(gunsch): Use LOAD_COMMON_RESOURCES once ResourceBundle no longer // hardcodes resource file names. ui::ResourceBundle::InitSharedInstanceWithLocale( - "en-US", - resource_delegate_.get(), + "en-US", resource_delegate_.get(), ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES); - base::FilePath pak_file; - CHECK(PathService::Get(FILE_CAST_PAK, &pak_file)); +#if defined(OS_ANDROID) + ui::ResourceBundle::GetSharedInstance().AddDataPackFromFileRegion( + base::File(pak_fd), pak_region, ui::SCALE_FACTOR_NONE); +#else ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( - pak_file, - ui::SCALE_FACTOR_NONE); + pak_file, ui::SCALE_FACTOR_NONE); +#endif // defined(OS_ANDROID) } content::ContentBrowserClient* CastMainDelegate::CreateContentBrowserClient() { - browser_client_.reset(new CastContentBrowserClient); + browser_client_ = CastContentBrowserClient::Create(); return browser_client_.get(); } content::ContentRendererClient* CastMainDelegate::CreateContentRendererClient() { - renderer_client_.reset(new CastContentRendererClient); + renderer_client_ = CastContentRendererClient::Create(); return renderer_client_.get(); } diff --git a/chromium/chromecast/app/linux/cast_crash_reporter_client.cc b/chromium/chromecast/app/linux/cast_crash_reporter_client.cc new file mode 100644 index 00000000000..0bf2877057e --- /dev/null +++ b/chromium/chromecast/app/linux/cast_crash_reporter_client.cc @@ -0,0 +1,77 @@ +// 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/app/linux/cast_crash_reporter_client.h" + +#include "base/time/time.h" +#include "chromecast/base/error_codes.h" +#include "chromecast/crash/linux/crash_util.h" +#include "components/crash/app/breakpad_linux.h" +#include "content/public/common/content_switches.h" + +namespace chromecast { + +namespace { + +std::string* g_process_type = nullptr; +uint64_t g_process_start_time_ms = 0u; + +} // namespace + +// static +void CastCrashReporterClient::InitCrashReporter( + const std::string& process_type) { + DCHECK(!g_process_type); + g_process_start_time_ms = + (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds(); + + // Save the process type (leaked). + g_process_type = new std::string(process_type); + + // Start up breakpad for this process, if applicable. + breakpad::InitCrashReporter(process_type); +} + +// static +const char* CastCrashReporterClient::GetProcessType() { + return g_process_type ? g_process_type->c_str() : nullptr; +} + +// static +uint64_t CastCrashReporterClient::GetProcessStartTime() { + return g_process_start_time_ms; +} + +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; +} + +bool CastCrashReporterClient::HandleCrashDump(const char* crashdump_filename) { + // Set the initial error code to ERROR_WEB_CONTENT_RENDER_VIEW_GONE to show + // an error message on next cast_shell run. Though the error code is for + // renderer process crash, the actual messages can be used for browser process + // as well. + if (!GetProcessType() || !strcmp(GetProcessType(), "")) + SetInitialErrorCode(ERROR_WEB_CONTENT_RENDER_VIEW_GONE); + + // Upload crash dump. If user didn't opt-in crash report, minidump writer + // instantiated within CrashUtil::RequestUploadCrashDump() does nothing. + CrashUtil::RequestUploadCrashDump(crashdump_filename, + GetProcessType() ? GetProcessType() : "", + GetProcessStartTime()); + + // Always return true to indicate that this crash dump has been processed, + // so that it won't fallback to use chrome's default uploader. + return true; +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/cast_crash_reporter_client.h b/chromium/chromecast/app/linux/cast_crash_reporter_client.h index a0916a041ea..07712af233d 100644 --- a/chromium/chromecast/crash/cast_crash_reporter_client.h +++ b/chromium/chromecast/app/linux/cast_crash_reporter_client.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_CRASH_CAST_CRASH_REPORTER_CLIENT_H_ -#define CHROMECAST_CRASH_CAST_CRASH_REPORTER_CLIENT_H_ +#ifndef CHROMECAST_APP_LINUX_CAST_CRASH_REPORTER_CLIENT_H_ +#define CHROMECAST_APP_LINUX_CAST_CRASH_REPORTER_CLIENT_H_ #include <string> @@ -20,12 +20,11 @@ class CastCrashReporterClient : public crash_reporter::CrashReporterClient { ~CastCrashReporterClient() override; // crash_reporter::CrashReporterClient implementation: - bool EnableBreakpadForProcess( - const std::string& process_type) override; + bool EnableBreakpadForProcess(const std::string& process_type) override; bool HandleCrashDump(const char* crashdump_filename) override; private: - static char* GetProcessType(); + static const char* GetProcessType(); static uint64_t GetProcessStartTime(); DISALLOW_COPY_AND_ASSIGN(CastCrashReporterClient); @@ -33,4 +32,4 @@ class CastCrashReporterClient : public crash_reporter::CrashReporterClient { } // namespace chromecast -#endif // CHROMECAST_CRASH_CAST_CRASH_REPORTER_CLIENT_H_ +#endif // CHROMECAST_APP_LINUX_CAST_CRASH_REPORTER_CLIENT_H_ diff --git a/chromium/chromecast/app/linux/cast_crash_reporter_client_unittest.cc b/chromium/chromecast/app/linux/cast_crash_reporter_client_unittest.cc new file mode 100644 index 00000000000..88cbad9c50c --- /dev/null +++ b/chromium/chromecast/app/linux/cast_crash_reporter_client_unittest.cc @@ -0,0 +1,116 @@ +// 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 <fstream> + +#include "base/base_paths.h" +#include "base/bind.h" +#include "base/files/file.h" +#include "base/files/file_util.h" +#include "base/memory/scoped_vector.h" +#include "base/test/scoped_path_override.h" +#include "chromecast/app/linux/cast_crash_reporter_client.h" +#include "chromecast/crash/app_state_tracker.h" +#include "chromecast/crash/linux/crash_util.h" +#include "chromecast/crash/linux/dump_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { +namespace { + +const char kFakeDumpstateContents[] = "Dumpstate Contents\nDumpdumpdumpdump\n"; +const char kFakeMinidumpContents[] = "Minidump Contents\nLine1\nLine2\n"; + +int WriteFakeDumpStateFile(const std::string& path) { + // Append the correct extension and write the data to file. + base::File dumpstate(base::FilePath(path).AddExtension(".txt.gz"), + base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + dumpstate.Write( + 0, kFakeDumpstateContents, sizeof(kFakeDumpstateContents) - 1); + return 0; +} + +ScopedVector<DumpInfo> GetCurrentDumps(const std::string& logfile_path) { + ScopedVector<DumpInfo> dumps; + std::string entry; + + std::ifstream in(logfile_path); + DCHECK(in.is_open()); + while (std::getline(in, entry)) { + scoped_ptr<DumpInfo> info(new DumpInfo(entry)); + dumps.push_back(info.Pass()); + } + return dumps.Pass(); +} + +} // namespace + +TEST(CastCrashReporterClientTest, EndToEnd) { + // Set up a temporary directory which will be used as our fake home dir. + base::FilePath fake_home_dir; + ASSERT_TRUE(base::CreateNewTempDirectory("", &fake_home_dir)); + base::ScopedPathOverride home(base::DIR_HOME, fake_home_dir); + + // Set a callback to be used in place of the |dumpstate| executable. + CrashUtil::SetDumpStateCbForTest(base::Bind(&WriteFakeDumpStateFile)); + + // "Launch" YouTube. + AppStateTracker::SetLastLaunchedApp("youtube"); + AppStateTracker::SetCurrentApp("youtube"); + + // "Launch" and switch to Pandora. + AppStateTracker::SetLastLaunchedApp("pandora"); + AppStateTracker::SetCurrentApp("pandora"); + + // "Launch" Netflix. + AppStateTracker::SetLastLaunchedApp("netflix"); + // Netflix crashed. + + // A minidump file is created. + base::FilePath minidump_path; + base::CreateTemporaryFile(&minidump_path); + base::File minidump(minidump_path, + base::File::FLAG_OPEN | base::File::FLAG_APPEND); + minidump.Write(0, kFakeMinidumpContents, sizeof(kFakeMinidumpContents) - 1); + minidump.Close(); + + // Handle the crash. + CastCrashReporterClient client; + ASSERT_TRUE(client.HandleCrashDump(minidump_path.value().c_str())); + + // Assert that the original file has been moved. + ASSERT_FALSE(base::PathExists(minidump_path)); + + // Assert that the file has been moved to "minidumps", with the expected + // contents. + std::string contents; + base::FilePath new_minidump = + fake_home_dir.Append("minidumps").Append(minidump_path.BaseName()); + ASSERT_TRUE(base::PathExists(new_minidump)); + ASSERT_TRUE(base::ReadFileToString(new_minidump, &contents)); + ASSERT_EQ(kFakeMinidumpContents, contents); + + // Assert that the dumpstate file has been written with the expected contents. + base::FilePath dumpstate = new_minidump.AddExtension(".txt.gz"); + ASSERT_TRUE(base::PathExists(dumpstate)); + ASSERT_TRUE(base::ReadFileToString(dumpstate, &contents)); + ASSERT_EQ(kFakeDumpstateContents, contents); + + // Assert that the lockfile has logged the correct information. + base::FilePath lockfile = + fake_home_dir.Append("minidumps").Append("lockfile"); + ASSERT_TRUE(base::PathExists(lockfile)); + ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile.value()); + ASSERT_EQ(1u, dumps.size()); + + const DumpInfo& dump_info = *(dumps[0]); + ASSERT_TRUE(dump_info.valid()); + EXPECT_EQ(new_minidump.value(), dump_info.crashed_process_dump()); + EXPECT_EQ(dumpstate.value(), dump_info.logfile()); + EXPECT_EQ("youtube", dump_info.params().previous_app_name); + EXPECT_EQ("pandora", dump_info.params().current_app_name); + EXPECT_EQ("netflix", dump_info.params().last_app_name); +} + +} // namespace chromecast
\ No newline at end of file diff --git a/chromium/chromecast/base/BUILD.gn b/chromium/chromecast/base/BUILD.gn index b105a71926a..c27a4c5eccf 100644 --- a/chromium/chromecast/base/BUILD.gn +++ b/chromium/chromecast/base/BUILD.gn @@ -2,11 +2,88 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//chrome/version.gni") # TODO layering violation! +import("//chromecast/chromecast.gni") +import("//testing/test.gni") + source_set("base") { sources = [ "cast_paths.cc", "cast_paths.h", + "chromecast_switches.cc", + "chromecast_switches.h", + "error_codes.cc", + "error_codes.h", + "path_utils.cc", + "path_utils.h", + "process_utils.cc", + "process_utils.h", + "serializers.cc", + "serializers.h", ] configs += [ "//chromecast:config" ] + + public_deps = [ + "//chromecast/base/metrics", + ] + + deps = [ + "//base", + ] +} + +test("cast_base_unittests") { + sources = [ + "error_codes_unittest.cc", + "path_utils_unittest.cc", + "process_utils_unittest.cc", + "serializers_unittest.cc", + ] + + deps = [ + ":base", + "//base", + "//base/test:run_all_unittests", + "//base/test:test_support", + "//testing/gtest", + ] +} + +source_set("cast_sys_info") { + sources = [ + "cast_sys_info_dummy.cc", + "cast_sys_info_dummy.h", + "cast_sys_info_util.h", + ] + + if (!is_chromecast_chrome_branded && !is_android) { + sources += [ "cast_sys_info_util_simple.cc" ] + } + + # TODO(mbjorge): put cast_sys_info_android in here + + deps = [ + "//base", + "//chromecast/public", + ] +} + +process_version("cast_version") { + template_file = "version.h.in" + output = "$target_gen_dir/version.h" + extra_args = [ + "-e", + "VERSION_FULL=\"%s.%s.%s.%s\"%(MAJOR,MINOR,BUILD,PATCH)", + + # TODO(slan): Populate the fields below with real values + "-e", + "CAST_BUILD_INCREMENTAL=20150608.181153", + "-e", + "CAST_BUILD_RELEASE=1.15", + "-e", + "CAST_IS_DEBUG_BUILD=1", + "-e", + "CAST_PRODUCT_TYPE=0", + ] } diff --git a/chromium/chromecast/base/cast_sys_info_android.cc b/chromium/chromecast/base/cast_sys_info_android.cc new file mode 100644 index 00000000000..0d85a06ad82 --- /dev/null +++ b/chromium/chromecast/base/cast_sys_info_android.cc @@ -0,0 +1,127 @@ +// 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_android.h" + +#include "base/android/build_info.h" +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/strings/string_number_conversions.h" +#include "base/sys_info.h" +#include "chromecast/base/cast_sys_info_util.h" +#include "chromecast/base/version.h" +#include "jni/CastSysInfoAndroid_jni.h" + +namespace chromecast { + +namespace { +const char kBuildTypeUser[] = "user"; +} // namespace + +// static +bool CastSysInfoAndroid::RegisterJni(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +// static +scoped_ptr<CastSysInfo> CreateSysInfo() { + return make_scoped_ptr(new CastSysInfoAndroid()); +} + +CastSysInfoAndroid::CastSysInfoAndroid() + : build_info_(base::android::BuildInfo::GetInstance()) { +} + +CastSysInfoAndroid::~CastSysInfoAndroid() { +} + +CastSysInfo::BuildType CastSysInfoAndroid::GetBuildType() { + if (CAST_IS_DEBUG_BUILD()) + return BUILD_ENG; + + int build_number; + if (!base::StringToInt(CAST_BUILD_INCREMENTAL, &build_number)) + build_number = 0; + + // Note: no way to determine which channel was used on play store. + if (strcmp(build_info_->build_type(), kBuildTypeUser) == 0 && + build_number > 0) { + return BUILD_PRODUCTION; + } + + // Dogfooders without a user system build should all still have non-Debug + // builds of the cast receiver APK, but with valid build numbers. + if (build_number > 0) + return BUILD_BETA; + + // Default to ENG build. + return BUILD_ENG; +} + +std::string CastSysInfoAndroid::GetSerialNumber() { + JNIEnv* env = base::android::AttachCurrentThread(); + return base::android::ConvertJavaStringToUTF8( + Java_CastSysInfoAndroid_getSerialNumber(env)); +} + +std::string CastSysInfoAndroid::GetProductName() { + return build_info_->device(); +} + +std::string CastSysInfoAndroid::GetDeviceModel() { + return build_info_->model(); +} + +std::string CastSysInfoAndroid::GetManufacturer() { + return build_info_->manufacturer(); +} + +std::string CastSysInfoAndroid::GetSystemBuildNumber() { + return base::SysInfo::GetAndroidBuildID(); +} + +std::string CastSysInfoAndroid::GetSystemReleaseChannel() { + return ""; +} + +std::string CastSysInfoAndroid::GetBoardName() { + return ""; +} + +std::string CastSysInfoAndroid::GetBoardRevision() { + return ""; +} + +std::string CastSysInfoAndroid::GetFactoryCountry() { + return ""; +} + +std::string CastSysInfoAndroid::GetFactoryLocale(std::string* second_locale) { + return ""; +} + +std::string CastSysInfoAndroid::GetWifiInterface() { + return ""; +} + +std::string CastSysInfoAndroid::GetApInterface() { + return ""; +} + +std::string CastSysInfoAndroid::GetGlVendor() { + NOTREACHED() << "GL information shouldn't be requested on Android."; + return ""; +} + +std::string CastSysInfoAndroid::GetGlRenderer() { + NOTREACHED() << "GL information shouldn't be requested on Android."; + return ""; +} + +std::string CastSysInfoAndroid::GetGlVersion() { + NOTREACHED() << "GL information shouldn't be requested on Android."; + return ""; +} + +} // namespace chromecast diff --git a/chromium/chromecast/base/cast_sys_info_android.h b/chromium/chromecast/base/cast_sys_info_android.h new file mode 100644 index 00000000000..0cc4b55ea04 --- /dev/null +++ b/chromium/chromecast/base/cast_sys_info_android.h @@ -0,0 +1,57 @@ +// 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_ANDROID_H_ +#define CHROMECAST_BASE_CAST_SYS_INFO_ANDROID_H_ + +#include <jni.h> + +#include "base/macros.h" +#include "chromecast/public/cast_sys_info.h" + +namespace base { +namespace android { +class BuildInfo; +} +} + +namespace chromecast { + +class CastSysInfoAndroid : public CastSysInfo { + public: + static bool RegisterJni(JNIEnv* env); + + CastSysInfoAndroid(); + ~CastSysInfoAndroid() override; + + // CastSysInfo implementation: + BuildType GetBuildType() override; + std::string GetSerialNumber() override; + std::string GetProductName() override; + std::string GetDeviceModel() override; + std::string GetManufacturer() override; + std::string GetSystemBuildNumber() override; + std::string GetSystemReleaseChannel() override; + std::string GetBoardName() override; + std::string GetBoardRevision() 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; + + // Native implementation of Java methods. + void DeviceNameChanged(JNIEnv* env, jobject obj, jstring device_name); + + private: + const base::android::BuildInfo* const build_info_; + + DISALLOW_COPY_AND_ASSIGN(CastSysInfoAndroid); +}; + +} // namespace chromecast + +#endif // CHROMECAST_BASE_CAST_SYS_INFO_ANDROID_H_ diff --git a/chromium/chromecast/base/chromecast_config_android.cc b/chromium/chromecast/base/chromecast_config_android.cc new file mode 100644 index 00000000000..f16b4733069 --- /dev/null +++ b/chromium/chromecast/base/chromecast_config_android.cc @@ -0,0 +1,56 @@ +// 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/chromecast_config_android.h" + +#include "base/android/jni_android.h" +#include "base/lazy_instance.h" +#include "jni/ChromecastConfigAndroid_jni.h" + +namespace chromecast { +namespace android { + +namespace { +base::LazyInstance<ChromecastConfigAndroid> g_instance = + LAZY_INSTANCE_INITIALIZER; +} // namespace + +// static +ChromecastConfigAndroid* ChromecastConfigAndroid::GetInstance() { + return g_instance.Pointer(); +} + +// static +bool ChromecastConfigAndroid::RegisterJni(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +ChromecastConfigAndroid::ChromecastConfigAndroid() { +} + +ChromecastConfigAndroid::~ChromecastConfigAndroid() { +} + +bool ChromecastConfigAndroid::CanSendUsageStats() { + // TODO(gunsch): make opt-in.stats pref the source of truth for this data, + // instead of Android prefs, then delete ChromecastConfigAndroid. + JNIEnv* env = base::android::AttachCurrentThread(); + return Java_ChromecastConfigAndroid_canSendUsageStats( + env, base::android::GetApplicationContext()); +} + +// Registers a handler to be notified when SendUsageStats is changed. +void ChromecastConfigAndroid::SetSendUsageStatsChangedCallback( + const base::Callback<void(bool)>& callback) { + send_usage_stats_changed_callback_ = callback; +} + +// Called from Java. +void SetSendUsageStatsEnabled(JNIEnv* env, jclass caller, jboolean enabled) { + ChromecastConfigAndroid::GetInstance()-> + send_usage_stats_changed_callback().Run(enabled); +} + +} // namespace android +} // namespace chromecast diff --git a/chromium/chromecast/android/chromecast_config_android.h b/chromium/chromecast/base/chromecast_config_android.h index e09d657acc9..1d9082f51c2 100644 --- a/chromium/chromecast/android/chromecast_config_android.h +++ b/chromium/chromecast/base/chromecast_config_android.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_ANDROID_CHROMECAST_CONFIG_ANDROID_H_ -#define CHROMECAST_ANDROID_CHROMECAST_CONFIG_ANDROID_H_ +#ifndef CHROMECAST_BASE_CHROMECAST_CONFIG_ANDROID_H_ +#define CHROMECAST_BASE_CHROMECAST_CONFIG_ANDROID_H_ #include <jni.h> @@ -17,6 +17,7 @@ namespace android { class ChromecastConfigAndroid { public: static ChromecastConfigAndroid* GetInstance(); + static bool RegisterJni(JNIEnv* env); // Returns whether or not the user has allowed sending usage stats and // crash reports. @@ -44,4 +45,4 @@ class ChromecastConfigAndroid { } // namespace android } // namespace chromecast -#endif // CHROMECAST_ANDROID_CHROMECAST_CONFIG_ANDROID_H_ +#endif // CHROMECAST_BASE_CHROMECAST_CONFIG_ANDROID_H_ diff --git a/chromium/chromecast/common/chromecast_switches.cc b/chromium/chromecast/base/chromecast_switches.cc index 435a35abdf9..5f97eba504d 100644 --- a/chromium/chromecast/common/chromecast_switches.cc +++ b/chromium/chromecast/base/chromecast_switches.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/chromecast_switches.h" +#include "chromecast/base/chromecast_switches.h" namespace switches { @@ -12,6 +12,11 @@ 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"; +// Enables old (VIDEO_HOLE) hole punching codepath for video plane. +// TODO(halliwell): remove switch and old codepath once overlays +// are well established. +const char kEnableLegacyHolePunching[] = "enable-legacy-hole-punching"; + // Enable file accesses. It should not be enabled for most Cast devices. const char kEnableLocalFileAccesses[] = "enable-local-file-accesses"; @@ -29,4 +34,9 @@ const char kLastLaunchedApp[] = "last-launched-app"; // started. const char kPreviousApp[] = "previous-app"; +// Enables applications to output transparent pixels to final framebuffer +// (e.g. using transparent body background), to be composited with other +// externally-populated hardware planes. +const char kEnableTransparentBackground[] = "enable-transparent-background"; + } // namespace switches diff --git a/chromium/chromecast/common/chromecast_switches.h b/chromium/chromecast/base/chromecast_switches.h index 697a8a15763..c7ac0143534 100644 --- a/chromium/chromecast/common/chromecast_switches.h +++ b/chromium/chromecast/base/chromecast_switches.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_CHROMECAST_SWITCHES_H_ -#define CHROMECAST_COMMON_CHROMECAST_SWITCHES_H_ +#ifndef CHROMECAST_BASE_CHROMECAST_SWITCHES_H_ +#define CHROMECAST_BASE_CHROMECAST_SWITCHES_H_ #include "build/build_config.h" @@ -12,6 +12,7 @@ namespace switches { // Media switches extern const char kEnableCmaMediaPipeline[]; extern const char kHdmiSinkSupportedCodecs[]; +extern const char kEnableLegacyHolePunching[]; // Content-implementation switches extern const char kEnableLocalFileAccesses[]; @@ -26,6 +27,9 @@ extern const char kNoWifi[]; extern const char kLastLaunchedApp[]; extern const char kPreviousApp[]; +// Graphics switches +extern const char kEnableTransparentBackground[]; + } // namespace switches -#endif // CHROMECAST_COMMON_CHROMECAST_SWITCHES_H_ +#endif // CHROMECAST_BASE_CHROMECAST_SWITCHES_H_ diff --git a/chromium/chromecast/base/error_codes.cc b/chromium/chromecast/base/error_codes.cc new file mode 100644 index 00000000000..e26a82009cc --- /dev/null +++ b/chromium/chromecast/base/error_codes.cc @@ -0,0 +1,83 @@ +// 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/error_codes.h" + +#include <fcntl.h> + +#include <string> + +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "chromecast/base/path_utils.h" + +namespace chromecast { + +namespace { + +const char kInitialErrorFile[] = "initial_error"; + +base::FilePath GetInitialErrorFilePath() { + return GetHomePathASCII(kInitialErrorFile); +} + +} // namespace + +ErrorCode GetInitialErrorCode() { + std::string initial_error_code_str; + if (!base::ReadFileToString(GetInitialErrorFilePath(), + &initial_error_code_str)) { + return NO_ERROR; + } + + int initial_error_code = 0; + if (base::StringToInt(initial_error_code_str, &initial_error_code) && + initial_error_code >= NO_ERROR && initial_error_code <= ERROR_UNKNOWN) { + VLOG(1) << "Initial error from " << GetInitialErrorFilePath().value() + << ": " << initial_error_code; + return static_cast<ErrorCode>(initial_error_code); + } + + LOG(ERROR) << "Unknown initial error code: " << initial_error_code_str; + return NO_ERROR; +} + +bool SetInitialErrorCode(ErrorCode initial_error_code) { + // Note: Do not use Chromium IO methods in this function. When cast_shell + // crashes, this function can be called by any thread. + const std::string error_file_path = GetInitialErrorFilePath().value(); + + if (initial_error_code > NO_ERROR && initial_error_code <= ERROR_UNKNOWN) { + const std::string initial_error_code_str( + base::IntToString(initial_error_code)); + int fd = creat(error_file_path.c_str(), 0640); + if (fd < 0) { + PLOG(ERROR) << "Could not open error code file"; + return false; + } + + int written = + write(fd, initial_error_code_str.data(), initial_error_code_str.size()); + + if (written != static_cast<int>(initial_error_code_str.size())) { + PLOG(ERROR) << "Could not write error code to file: written=" << written + << ", expected=" << initial_error_code_str.size(); + close(fd); + return false; + } + + close(fd); + return true; + } + + // Remove initial error file if no error. + if (unlink(error_file_path.c_str()) == 0 || errno == ENOENT) + return true; + + PLOG(ERROR) << "Failed to remove error file"; + return false; +} + +} // namespace chromecast diff --git a/chromium/chromecast/base/error_codes.h b/chromium/chromecast/base/error_codes.h new file mode 100644 index 00000000000..80d430f2046 --- /dev/null +++ b/chromium/chromecast/base/error_codes.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_BASE_ERROR_CODES_H_ +#define CHROMECAST_BASE_ERROR_CODES_H_ + +namespace chromecast { + +enum ErrorCode { + NO_ERROR = 0, + + // web_content [1, 9999] + ERROR_WEB_CONTENT_RENDER_VIEW_GONE = 1, + ERROR_WEB_CONTENT_NAME_NOT_RESOLVED, + ERROR_WEB_CONTENT_INTERNET_DISCONNECTED, + + // reboot [10000, 19999] + // The following error codes do not reset the volume when the page is + // launched. Add codes that do not reset the volume before ERROR_REBOOT_NOW. + ERROR_REBOOT_NOW = 10000, + ERROR_REBOOT_FDR, + ERROR_REBOOT_OTA, + ERROR_REBOOT_IDLE, + // Chromecast WebUI uses 19999 for END_OF_REBOOT_SECTION, so reserve it here. + END_OF_REBOOT_SECTION = 19999, + + // misc [20000, 29999] + ERROR_ABORTED = 20000, + ERROR_LOST_PEER_CONNECTION = 20001, + + ERROR_UNKNOWN = 30000, +}; + +// Gets the error code for the first idle screen. +ErrorCode GetInitialErrorCode(); + +// Sets the error code for the first idle screen. Returns true if set +// successfully. +bool SetInitialErrorCode(ErrorCode initial_error_code); + +} // namespace chromecast + +#endif // CHROMECAST_BASE_ERROR_CODES_H_ diff --git a/chromium/chromecast/base/error_codes_unittest.cc b/chromium/chromecast/base/error_codes_unittest.cc new file mode 100644 index 00000000000..293d8952f77 --- /dev/null +++ b/chromium/chromecast/base/error_codes_unittest.cc @@ -0,0 +1,65 @@ +// 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/base_paths.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/test/scoped_path_override.h" +#include "chromecast/base/error_codes.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { + +class ErrorCodesTest : public testing::Test { + protected: + ErrorCodesTest() {} + ~ErrorCodesTest() override {} + + void SetUp() override { + // Set up a temporary directory which will be used as our fake home dir. + ASSERT_TRUE(base::CreateNewTempDirectory("", &fake_home_dir_)); + path_override_.reset( + new base::ScopedPathOverride(base::DIR_HOME, fake_home_dir_)); + } + + base::FilePath fake_home_dir_; + scoped_ptr<base::ScopedPathOverride> path_override_; +}; + +TEST_F(ErrorCodesTest, GetInitialErrorCodeReturnsNoErrorIfMissingFile) { + EXPECT_EQ(NO_ERROR, GetInitialErrorCode()); +} + +TEST_F(ErrorCodesTest, SetInitialErrorCodeSucceedsWithNoError) { + ASSERT_TRUE(SetInitialErrorCode(NO_ERROR)); + + // File should not be written. + ASSERT_FALSE(base::PathExists(fake_home_dir_.Append("initial_error"))); + EXPECT_EQ(NO_ERROR, GetInitialErrorCode()); +} + +TEST_F(ErrorCodesTest, SetInitialErrorCodeSucceedsWithValidErrors) { + // Write initial error and read it from the file. + EXPECT_TRUE(SetInitialErrorCode(ERROR_WEB_CONTENT_RENDER_VIEW_GONE)); + EXPECT_TRUE(base::PathExists(fake_home_dir_.Append("initial_error"))); + EXPECT_EQ(ERROR_WEB_CONTENT_RENDER_VIEW_GONE, GetInitialErrorCode()); + + // File should be updated with most recent error. + EXPECT_TRUE(SetInitialErrorCode(ERROR_UNKNOWN)); + EXPECT_TRUE(base::PathExists(fake_home_dir_.Append("initial_error"))); + EXPECT_EQ(ERROR_UNKNOWN, GetInitialErrorCode()); + + // File should be updated with most recent error. + EXPECT_TRUE(SetInitialErrorCode(ERROR_WEB_CONTENT_NAME_NOT_RESOLVED)); + EXPECT_TRUE(base::PathExists(fake_home_dir_.Append("initial_error"))); + EXPECT_EQ(ERROR_WEB_CONTENT_NAME_NOT_RESOLVED, GetInitialErrorCode()); + + // File should be removed after writing NO_ERROR. + EXPECT_TRUE(SetInitialErrorCode(NO_ERROR)); + EXPECT_FALSE(base::PathExists(fake_home_dir_.Append("initial_error"))); + EXPECT_EQ(NO_ERROR, GetInitialErrorCode()); +} + +} // namespace chromecast diff --git a/chromium/chromecast/base/metrics/BUILD.gn b/chromium/chromecast/base/metrics/BUILD.gn index 7ed81a562fe..191b30413c1 100644 --- a/chromium/chromecast/base/metrics/BUILD.gn +++ b/chromium/chromecast/base/metrics/BUILD.gn @@ -11,11 +11,11 @@ source_set("metrics") { "grouped_histogram.h", ] + configs += [ "//chromecast:config" ] + deps = [ - "//chromecast/base", + "//base", ] - - configs += [ "//chromecast:config" ] } source_set("test_support") { @@ -26,9 +26,12 @@ source_set("test_support") { "cast_metrics_test_helper.h", ] - deps = [ + public_deps = [ ":metrics", - "//chromecast/base", + ] + + deps = [ + "//base", ] configs += [ "//chromecast:config" ] diff --git a/chromium/chromecast/base/metrics/cast_metrics_helper.cc b/chromium/chromecast/base/metrics/cast_metrics_helper.cc index 85cdfe8931e..6d07b4a9341 100644 --- a/chromium/chromecast/base/metrics/cast_metrics_helper.cc +++ b/chromium/chromecast/base/metrics/cast_metrics_helper.cc @@ -7,9 +7,9 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/metrics/histogram.h" #include "base/metrics/user_metrics.h" +#include "base/single_thread_task_runner.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "chromecast/base/metrics/cast_histograms.h" @@ -19,12 +19,12 @@ namespace chromecast { namespace metrics { // A useful macro to make sure current member function runs on the valid thread. -#define MAKE_SURE_THREAD(callback, ...) \ - if (!message_loop_proxy_->BelongsToCurrentThread()) { \ - message_loop_proxy_->PostTask(FROM_HERE, base::Bind( \ - &CastMetricsHelper::callback, \ - base::Unretained(this), ##__VA_ARGS__)); \ - return; \ +#define MAKE_SURE_THREAD(callback, ...) \ + if (!task_runner_->BelongsToCurrentThread()) { \ + task_runner_->PostTask(FROM_HERE, \ + base::Bind(&CastMetricsHelper::callback, \ + base::Unretained(this), ##__VA_ARGS__)); \ + return; \ } namespace { @@ -93,11 +93,11 @@ CastMetricsHelper* CastMetricsHelper::GetInstance() { } CastMetricsHelper::CastMetricsHelper( - scoped_refptr<base::MessageLoopProxy> message_loop_proxy) - : message_loop_proxy_(message_loop_proxy), + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : task_runner_(task_runner), metrics_sink_(NULL), record_action_callback_(base::Bind(&base::RecordComputedAction)) { - DCHECK(message_loop_proxy_.get()); + DCHECK(task_runner_.get()); DCHECK(!g_instance); g_instance = this; } @@ -119,7 +119,6 @@ void CastMetricsHelper::UpdateCurrentAppInfo(const std::string& app_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(); } @@ -158,19 +157,6 @@ void CastMetricsHelper::LogTimeToFirstPaint() { LOG(INFO) << uma_name << " is " << launch_time.InSecondsF() << " seconds."; } -void CastMetricsHelper::LogTimeToDisplayVideo() { - if (!new_startup_time_) { // For faster check. - return; - } - MAKE_SURE_THREAD(LogTimeToDisplayVideo); - new_startup_time_ = false; - base::TimeDelta launch_time = base::TimeTicks::Now() - app_start_time_; - const std::string uma_name(GetMetricsNameWithAppName("Startup", - "TimeToDisplayVideo")); - LogMediumTimeHistogramEvent(uma_name, launch_time); - LOG(INFO) << uma_name << " is " << launch_time.InSecondsF() << " seconds."; -} - void CastMetricsHelper::LogTimeToBufferAv(BufferingType buffering_type, base::TimeDelta time) { MAKE_SURE_THREAD(LogTimeToBufferAv, buffering_type, time); @@ -251,7 +237,7 @@ void CastMetricsHelper::LogFramesPer5Seconds(int displayed_frames, std::string CastMetricsHelper::GetMetricsNameWithAppName( const std::string& prefix, const std::string& suffix) const { - DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + DCHECK(task_runner_->BelongsToCurrentThread()); std::string metrics_name(prefix); if (!app_id_.empty()) { if (!metrics_name.empty()) @@ -273,7 +259,7 @@ void CastMetricsHelper::SetMetricsSink(MetricsSink* delegate) { void CastMetricsHelper::SetRecordActionCallback( const RecordActionCallback& callback) { - DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + DCHECK(task_runner_->BelongsToCurrentThread()); record_action_callback_ = callback; } diff --git a/chromium/chromecast/base/metrics/cast_metrics_helper.h b/chromium/chromecast/base/metrics/cast_metrics_helper.h index 144d9dc3b32..6f5c3425df0 100644 --- a/chromium/chromecast/base/metrics/cast_metrics_helper.h +++ b/chromium/chromecast/base/metrics/cast_metrics_helper.h @@ -12,7 +12,7 @@ #include "base/time/time.h" namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; } namespace chromecast { @@ -58,7 +58,7 @@ class CastMetricsHelper { static CastMetricsHelper* GetInstance(); explicit CastMetricsHelper( - scoped_refptr<base::MessageLoopProxy> message_loop_proxy); + scoped_refptr<base::SingleThreadTaskRunner> task_runner); virtual ~CastMetricsHelper(); // This function updates the info and stores the startup time of the current @@ -79,10 +79,6 @@ class CastMetricsHelper { // 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(); - // Logs UMA record of the time needed to re-buffer A/V. virtual void LogTimeToBufferAv(BufferingType buffering_type, base::TimeDelta time); @@ -112,8 +108,8 @@ class CastMetricsHelper { 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 + // Creates a CastMetricsHelper instance with no task runner. This should only + // be used by tests, since invoking any non-overridden methods on this // instance will cause a failure. CastMetricsHelper(); @@ -134,7 +130,7 @@ class CastMetricsHelper { void LogMediumTimeHistogramEvent(const std::string& name, const base::TimeDelta& value); - scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; // Start time of the most recent app. base::TimeTicks app_start_time_; @@ -144,11 +140,6 @@ class CastMetricsHelper { 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, - // this is set to false. - bool new_startup_time_; - base::TimeTicks previous_video_stat_sample_time_; MetricsSink* metrics_sink_; diff --git a/chromium/chromecast/base/metrics/cast_metrics_test_helper.cc b/chromium/chromecast/base/metrics/cast_metrics_test_helper.cc index c78abe10409..2e4f0749d3f 100644 --- a/chromium/chromecast/base/metrics/cast_metrics_test_helper.cc +++ b/chromium/chromecast/base/metrics/cast_metrics_test_helper.cc @@ -23,7 +23,6 @@ class CastMetricsHelperStub : public CastMetricsHelper { 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; @@ -65,9 +64,6 @@ void CastMetricsHelperStub::LogMediaPlay() { void CastMetricsHelperStub::LogMediaPause() { } -void CastMetricsHelperStub::LogTimeToDisplayVideo() { -} - void CastMetricsHelperStub::LogTimeToBufferAv(BufferingType buffering_type, base::TimeDelta time) { } diff --git a/chromium/chromecast/base/path_utils.cc b/chromium/chromecast/base/path_utils.cc new file mode 100644 index 00000000000..4f5b27b7e02 --- /dev/null +++ b/chromium/chromecast/base/path_utils.cc @@ -0,0 +1,55 @@ +// 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/path_utils.h" + +#include "base/logging.h" +#include "base/path_service.h" + +namespace chromecast { + +namespace { + +base::FilePath GetPath(base::BasePathKey default_dir_key, + const base::FilePath& path) { + if (path.IsAbsolute()) + return path; + + base::FilePath default_dir; + if (!PathService::Get(default_dir_key, &default_dir)) + LOG(DFATAL) << "Cannot get default dir: " << default_dir_key; + + base::FilePath adjusted_path(default_dir.Append(path)); + VLOG(1) << "Path adjusted from " << path.value() << " to " + << adjusted_path.value(); + return adjusted_path; +} + +} // namespace + +base::FilePath GetHomePath(const base::FilePath& path) { + return GetPath(base::DIR_HOME, path); +} + +base::FilePath GetHomePathASCII(const std::string& path) { + return GetHomePath(base::FilePath(path)); +} + +base::FilePath GetBinPath(const base::FilePath& path) { + return GetPath(base::DIR_EXE, path); +} + +base::FilePath GetBinPathASCII(const std::string& path) { + return GetBinPath(base::FilePath(path)); +} + +base::FilePath GetTmpPath(const base::FilePath& path) { + return GetPath(base::DIR_TEMP, path); +} + +base::FilePath GetTmpPathASCII(const std::string& path) { + return GetTmpPath(base::FilePath(path)); +} + +} // namespace chromecast diff --git a/chromium/chromecast/base/path_utils.h b/chromium/chromecast/base/path_utils.h new file mode 100644 index 00000000000..5c1e205c2c3 --- /dev/null +++ b/chromium/chromecast/base/path_utils.h @@ -0,0 +1,31 @@ +// 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_PATH_UTILS_H_ +#define CHROMECAST_BASE_PATH_UTILS_H_ + +#include <string> + +#include "base/files/file_path.h" + +namespace chromecast { + +// If |path| is relative, returns the path created by prepending HOME directory +// to |path| (e.g. {HOME}/|path|). If |path| is absolute, returns |path|. +base::FilePath GetHomePath(const base::FilePath& path); +base::FilePath GetHomePathASCII(const std::string& path); + +// If |path| is relative, returns the path created by prepending BIN directory +// to |path| (e.g. {BIN}/|path|). If |path| is absolute, returns |path|. +base::FilePath GetBinPath(const base::FilePath& path); +base::FilePath GetBinPathASCII(const std::string& path); + +// If |path| is relative, returns the path created by prepending TMP directory +// to |path| (e.g. {TMP}/|path|). If |path| is absolute, returns |path|. +base::FilePath GetTmpPath(const base::FilePath& path); +base::FilePath GetTmpPathASCII(const std::string& path); + +} // namespace chromecast + +#endif // CHROMECAST_BASE_PATH_UTILS_H_ diff --git a/chromium/chromecast/base/path_utils_unittest.cc b/chromium/chromecast/base/path_utils_unittest.cc new file mode 100644 index 00000000000..618bb0f3e4b --- /dev/null +++ b/chromium/chromecast/base/path_utils_unittest.cc @@ -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. + +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "chromecast/base/path_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { +namespace { +const char kTestRelPath[] = "rel/path"; +const char kTestAbsPath[] = "/abs/path/to/dir"; + +std::string GetTestString(int base_dir_key) { + base::FilePath basedir; + EXPECT_TRUE(PathService::Get(base_dir_key, &basedir)); + return basedir.value() + "/" + kTestRelPath; +} + +} // namespace + +TEST(PathUtilsTest, GetHomePath) { + // Test with relative path. + std::string expected = GetTestString(base::DIR_HOME); + base::FilePath actual = GetHomePath(base::FilePath(kTestRelPath)); + EXPECT_EQ(expected, actual.value()); + + // Test with absolute path. + actual = GetHomePath(base::FilePath(kTestAbsPath)); + EXPECT_EQ(kTestAbsPath, actual.value()); +} + +TEST(PathUtilsTest, GetBinPath) { + // Test with relative path. + std::string expected = GetTestString(base::DIR_EXE); + base::FilePath actual = GetBinPath(base::FilePath(kTestRelPath)); + EXPECT_EQ(expected, actual.value()); + + // Test with absolute path. + actual = GetBinPath(base::FilePath(kTestAbsPath)); + EXPECT_EQ(kTestAbsPath, actual.value()); +} + +TEST(PathUtilsTest, GetTmpPath) { + // Test with relative path. + std::string expected = GetTestString(base::DIR_TEMP); + base::FilePath actual = GetTmpPath(base::FilePath(kTestRelPath)); + EXPECT_EQ(expected, actual.value()); + + // Test with absolute path. + actual = GetTmpPath(base::FilePath(kTestAbsPath)); + EXPECT_EQ(kTestAbsPath, actual.value()); +} + +} // chromecast
\ No newline at end of file diff --git a/chromium/chromecast/base/process_utils.cc b/chromium/chromecast/base/process_utils.cc new file mode 100644 index 00000000000..523fb1c697f --- /dev/null +++ b/chromium/chromecast/base/process_utils.cc @@ -0,0 +1,45 @@ +// 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/process_utils.h" + +#include <errno.h> +#include <stdio.h> + +#include "base/logging.h" +#include "base/posix/safe_strerror.h" +#include "base/strings/string_util.h" + +namespace chromecast { + +bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) { + DCHECK(output); + + // Join the args into one string, creating the command. + std::string command = JoinString(argv, ' '); + + // Open the process. + FILE* fp = popen(command.c_str(), "r"); + if (!fp) { + LOG(ERROR) << "popen (" << command << ") failed: " + << base::safe_strerror(errno); + return false; + } + + // Fill |output| with the stdout from the process. + output->clear(); + while (!feof(fp)) { + char buffer[256]; + size_t bytes_read = fread(buffer, 1, sizeof(buffer), fp); + if (bytes_read <= 0) + break; + output->append(buffer, bytes_read); + } + + // pclose() function waits for the associated process to terminate and returns + // the exit status. + return (pclose(fp) == 0); +} + +} // namespace chromecast diff --git a/chromium/chromecast/base/process_utils.h b/chromium/chromecast/base/process_utils.h new file mode 100644 index 00000000000..c79da17c037 --- /dev/null +++ b/chromium/chromecast/base/process_utils.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_BASE_PROCESS_UTILS_H_ +#define CHROMECAST_BASE_PROCESS_UTILS_H_ + +#include <string> +#include <vector> + +namespace chromecast { + +// Executes application, for which arguments are specified by |argv| and wait +// for it to exit. Stores the output (stdout) in |output|. Returns true on +// success. +// TODO(slan): Replace uses of this with base::GetAppOutput when crbug/493711 is +// resolved. +bool GetAppOutput(const std::vector<std::string>& argv, std::string* output); + +} // namespace chromecast + +#endif // CHROMECAST_BASE_PROCESS_UTILS_H_ diff --git a/chromium/chromecast/base/process_utils_unittest.cc b/chromium/chromecast/base/process_utils_unittest.cc new file mode 100644 index 00000000000..f9c2a0d7d8c --- /dev/null +++ b/chromium/chromecast/base/process_utils_unittest.cc @@ -0,0 +1,52 @@ +// 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/process_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { + +// Verify that a simple process works as expected. +TEST(ProcessUtilsTest, SimpleProcess) { + // Create a simple command. + std::vector<std::string> args; + args.push_back("echo"); + args.push_back("Hello World"); + + // Execute the command and collect the output. + std::string stdout_result; + ASSERT_TRUE(GetAppOutput(args, &stdout_result)); + + // Echo will append a newline to the stdout. + EXPECT_EQ("Hello World\n", stdout_result); +} + +// Verify that false is returned for an invalid command. +TEST(ProcessUtilsTest, InvalidCommand) { + // Create a command which is not valid. + std::vector<std::string> args; + args.push_back("invalid_command"); + + // The command should not run. + std::string stdout_result; + ASSERT_FALSE(GetAppOutput(args, &stdout_result)); + ASSERT_TRUE(stdout_result.empty()); +} + +// Verify that false is returned when a command an error code. +TEST(ProcessUtilsTest, ProcessReturnsError) { + // Create a simple command. + std::vector<std::string> args; + args.push_back("cd"); + args.push_back("path/to/invalid/directory"); + args.push_back("2>&1"); // Pipe the stderr into stdout. + + // Execute the command and collect the output. Verify that the output of the + // process is collected, even when the process returns an error code. + std::string stderr_result; + ASSERT_FALSE(GetAppOutput(args, &stderr_result)); + ASSERT_FALSE(stderr_result.empty()); +} + +} // namespace chromecast diff --git a/chromium/chromecast/common/version.h.in b/chromium/chromecast/base/version.h.in index 918401f42ae..62b01d31929 100644 --- a/chromium/chromecast/common/version.h.in +++ b/chromium/chromecast/base/version.h.in @@ -4,13 +4,14 @@ // version.h is generated from version.h.in. Edit the source! -#ifndef CHROMECAST_COMMON_VERSION_INFO_H_ -#define CHROMECAST_COMMON_VERSION_INFO_H_ +#ifndef CHROMECAST_BASE_VERSION_INFO_H_ +#define CHROMECAST_BASE_VERSION_INFO_H_ #define PRODUCT_VERSION "@VERSION_FULL@" #define CAST_BUILD_INCREMENTAL "@CAST_BUILD_INCREMENTAL@" +#define CAST_BUILD_RELEASE "@CAST_BUILD_RELEASE@" #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_ +#endif // CHROMECAST_BASE_VERSION_INFO_H_ diff --git a/chromium/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 b/chromium/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 index 7b02275ffcf..b977a9ddaeb 100644 --- a/chromium/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 +++ b/chromium/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 @@ -8,6 +8,13 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.chromium.chromecast.shell"> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="22" /> + + <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"/> + <application android:name="org.chromium.chromecast.shell.CastApplication" android:icon="@mipmap/app_icon" android:label="@string/app_name"> @@ -48,10 +55,4 @@ {% 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 7c2e3547b30..9e8b1d52cd0 100644 --- a/chromium/chromecast/browser/android/cast_window_android.cc +++ b/chromium/chromecast/browser/android/cast_window_android.cc @@ -4,7 +4,8 @@ #include "chromecast/browser/android/cast_window_android.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "chromecast/browser/android/cast_window_manager.h" #include "chromecast/browser/cast_content_window.h" #include "content/public/browser/devtools_agent_host.h" @@ -125,7 +126,7 @@ 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). - base::MessageLoopProxy::current()->PostDelayedTask( + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&CastWindowAndroid::Destroy, weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(kWebContentsDestructionDelayInMs)); diff --git a/chromium/chromecast/browser/android/cast_window_manager.cc b/chromium/chromecast/browser/android/cast_window_manager.cc index 01fe689b3d5..05950d257ff 100644 --- a/chromium/chromecast/browser/android/cast_window_manager.cc +++ b/chromium/chromecast/browser/android/cast_window_manager.cc @@ -78,10 +78,8 @@ void StopCastWindow(JNIEnv* env, jclass clazz, 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. - CastBrowserProcess::GetInstance()->pref_service()-> - SetInteger(prefs::kRemoteDebuggingPort, enable ? 1 : 0); + CastBrowserProcess::GetInstance()->pref_service()->SetBoolean( + prefs::kEnableRemoteDebugging, enable); } } // namespace shell diff --git a/chromium/chromecast/browser/cast_browser_main_parts.cc b/chromium/chromecast/browser/cast_browser_main_parts.cc index 91b13cef9f7..81701cb2d98 100644 --- a/chromium/chromecast/browser/cast_browser_main_parts.cc +++ b/chromium/chromecast/browser/cast_browser_main_parts.cc @@ -16,22 +16,25 @@ #include "base/path_service.h" #include "base/prefs/pref_registry_simple.h" #include "base/run_loop.h" +#include "base/thread_task_runner_handle.h" #include "cc/base/switches.h" #include "chromecast/base/cast_paths.h" #include "chromecast/base/cast_sys_info_util.h" +#include "chromecast/base/chromecast_switches.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/cast_net_log.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/common/chromecast_switches.h" #include "chromecast/common/platform_client_auth.h" #include "chromecast/media/base/key_systems_common.h" +#include "chromecast/media/base/media_message_loop.h" #include "chromecast/net/connectivity_checker.h" #include "chromecast/public/cast_media_shlib.h" #include "chromecast/public/cast_sys_info.h" @@ -41,11 +44,12 @@ #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" +#include "media/base/media.h" +#include "ui/compositor/compositor_switches.h" #if defined(OS_ANDROID) +#include "chromecast/app/android/crash_handler.h" #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" @@ -55,8 +59,8 @@ #endif #if defined(USE_AURA) +#include "chromecast/graphics/cast_screen.h" #include "ui/aura/env.h" -#include "ui/aura/test/test_screen.h" #include "ui/gfx/screen.h" #endif @@ -163,7 +167,6 @@ DefaultCommandLineSwitch g_default_switches[] = { // Disables Chromecast-specific WiFi-related features on ATV for now. { switches::kNoWifi, "" }, { switches::kEnableOverlayFullscreenVideo, ""}, - { switches::kDisableInfobarForProtectedMediaIdentifier, ""}, { switches::kDisableGestureRequirementForMediaPlayback, ""}, #endif // Always enable HTMLMediaElement logs. @@ -176,9 +179,12 @@ DefaultCommandLineSwitch g_default_switches[] = { // This is needed for now to enable the egltest Ozone platform to work with // current Linux/NVidia OpenGL drivers. { switches::kIgnoreGpuBlacklist, ""}, -#elif defined(ARCH_CPU_ARM_FAMILY) && !defined(DISABLE_DISPLAY) +#elif defined(ARCH_CPU_ARM_FAMILY) // On Linux arm, enable CMA pipeline by default. { switches::kEnableCmaMediaPipeline, "" }, +#if !defined(DISABLE_DISPLAY) + { switches::kEnableHardwareOverlays, "" }, +#endif #endif #endif // defined(OS_LINUX) // Needed to fix a bug where the raster thread doesn't get scheduled for a @@ -207,7 +213,8 @@ CastBrowserMainParts::CastBrowserMainParts( cast_browser_process_(new CastBrowserProcess()), parameters_(parameters), url_request_context_factory_(url_request_context_factory), - audio_manager_factory_(audio_manager_factory.Pass()) { + audio_manager_factory_(audio_manager_factory.Pass()), + net_log_(new CastNetLog()) { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); AddDefaultCommandLineSwitches(command_line); } @@ -238,7 +245,7 @@ void CastBrowserMainParts::PreMainMessageLoopStart() { void CastBrowserMainParts::PostMainMessageLoopStart() { cast_browser_process_->SetMetricsHelper(make_scoped_ptr( - new metrics::CastMetricsHelper(base::MessageLoopProxy::current()))); + new metrics::CastMetricsHelper(base::ThreadTaskRunnerHandle::Get()))); #if defined(OS_ANDROID) base::MessageLoopForUI::current()->Start(); @@ -267,14 +274,13 @@ int CastBrowserMainParts::PreCreateThreads() { // is assumed as an interface to access display information, e.g. from metrics // code. See CastContentWindow::CreateWindowTree for update when resolution // is available. + cast_browser_process_->SetCastScreen(make_scoped_ptr(new CastScreen)); DCHECK(!gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE)); gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, - aura::TestScreen::Create(gfx::Size(0, 0))); + cast_browser_process_->cast_screen()); #endif - return 0; -} -void CastBrowserMainParts::PreMainMessageLoopRun() { +#if !defined(OS_ANDROID) // Set GL strings so GPU config code can make correct feature blacklisting/ // whitelisting decisions. // Note: SetGLStrings MUST be called after GpuDataManager::Initialize. @@ -282,7 +288,12 @@ void CastBrowserMainParts::PreMainMessageLoopRun() { content::GpuDataManager::GetInstance()->SetGLStrings( sys_info->GetGlVendor(), sys_info->GetGlRenderer(), sys_info->GetGlVersion()); +#endif // !defined(OS_ANDROID) + return 0; +} + +void CastBrowserMainParts::PreMainMessageLoopRun() { scoped_refptr<PrefRegistrySimple> pref_registry(new PrefRegistrySimple()); metrics::RegisterPrefs(pref_registry.get()); cast_browser_process_->SetPrefService( @@ -297,11 +308,13 @@ void CastBrowserMainParts::PreMainMessageLoopRun() { #endif // defined(OS_ANDROID) cast_browser_process_->SetConnectivityChecker( - make_scoped_refptr(new ConnectivityChecker( + ConnectivityChecker::Create( content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::IO)))); + content::BrowserThread::IO))); + + cast_browser_process_->SetNetLog(net_log_.get()); - url_request_context_factory_->InitializeOnUIThread(); + url_request_context_factory_->InitializeOnUIThread(net_log_.get()); cast_browser_process_->SetBrowserContext( make_scoped_ptr(new CastBrowserContext(url_request_context_factory_))); @@ -317,7 +330,10 @@ void CastBrowserMainParts::PreMainMessageLoopRun() { cast_browser_process_->SetRemoteDebuggingServer( make_scoped_ptr(new RemoteDebuggingServer())); - media::CastMediaShlib::Initialize(cmd_line->argv()); + media::MediaMessageLoop::GetTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&media::CastMediaShlib::Initialize, cmd_line->argv())); + ::media::InitializeMediaLibrary(); cast_browser_process_->SetCastService(CastService::Create( cast_browser_process_->browser_context(), @@ -382,7 +398,10 @@ void CastBrowserMainParts::PostMainMessageLoopRun() { DeregisterKillOnAlarm(); #endif - media::CastMediaShlib::Finalize(); + // Finalize CastMediaShlib on media thread to ensure it's not accessed + // after Finalize. + media::MediaMessageLoop::GetTaskRunner()->PostTask( + FROM_HERE, base::Bind(&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 49954cef55d..4b5b5d8dfb9 100644 --- a/chromium/chromecast/browser/cast_browser_main_parts.h +++ b/chromium/chromecast/browser/cast_browser_main_parts.h @@ -15,6 +15,10 @@ namespace media { class AudioManagerFactory; } +namespace net { +class NetLog; +} + namespace chromecast { namespace shell { class CastBrowserProcess; @@ -42,6 +46,7 @@ class CastBrowserMainParts : public content::BrowserMainParts { const content::MainFunctionParams parameters_; // For running browser tests. URLRequestContextFactory* const url_request_context_factory_; scoped_ptr<::media::AudioManagerFactory> audio_manager_factory_; + scoped_ptr<net::NetLog> net_log_; DISALLOW_COPY_AND_ASSIGN(CastBrowserMainParts); }; diff --git a/chromium/chromecast/browser/cast_browser_process.cc b/chromium/chromecast/browser/cast_browser_process.cc index 25fcd44ff4a..d3cfbfa9ead 100644 --- a/chromium/chromecast/browser/cast_browser_process.cc +++ b/chromium/chromecast/browser/cast_browser_process.cc @@ -18,6 +18,10 @@ #include "components/crash/browser/crash_dump_manager_android.h" #endif // defined(OS_ANDROID) +#if defined(USE_AURA) +#include "chromecast/graphics/cast_screen.h" +#endif // defined(USE_AURA) + namespace chromecast { namespace shell { @@ -31,7 +35,8 @@ CastBrowserProcess* CastBrowserProcess::GetInstance() { return g_instance; } -CastBrowserProcess::CastBrowserProcess() { +CastBrowserProcess::CastBrowserProcess() + : net_log_(nullptr) { DCHECK(!g_instance); g_instance = this; } @@ -54,6 +59,13 @@ void CastBrowserProcess::SetCastService(scoped_ptr<CastService> cast_service) { cast_service_.swap(cast_service); } +#if defined(USE_AURA) +void CastBrowserProcess::SetCastScreen(scoped_ptr<CastScreen> cast_screen) { + DCHECK(!cast_screen_); + cast_screen_ = cast_screen.Pass(); +} +#endif // defined(USE_AURA) + void CastBrowserProcess::SetMetricsHelper( scoped_ptr<metrics::CastMetricsHelper> metrics_helper) { DCHECK(!metrics_helper_); @@ -97,5 +109,10 @@ void CastBrowserProcess::SetConnectivityChecker( connectivity_checker_.swap(connectivity_checker); } +void CastBrowserProcess::SetNetLog(net::NetLog* net_log) { + DCHECK(!net_log_); + net_log_ = net_log; +} + } // namespace shell } // namespace chromecast diff --git a/chromium/chromecast/browser/cast_browser_process.h b/chromium/chromecast/browser/cast_browser_process.h index f4bec89e7a1..2af917a2493 100644 --- a/chromium/chromecast/browser/cast_browser_process.h +++ b/chromium/chromecast/browser/cast_browser_process.h @@ -15,8 +15,13 @@ namespace breakpad { class CrashDumpManager; } // namespace breakpad +namespace net { +class NetLog; +} // namespace net + namespace chromecast { class CastService; +class CastScreen; class ConnectivityChecker; namespace metrics { @@ -40,6 +45,9 @@ class CastBrowserProcess { void SetBrowserContext(scoped_ptr<CastBrowserContext> browser_context); void SetCastService(scoped_ptr<CastService> cast_service); +#if defined(USE_AURA) + void SetCastScreen(scoped_ptr<CastScreen> cast_screen); +#endif // defined(USE_AURA) void SetMetricsHelper(scoped_ptr<metrics::CastMetricsHelper> metrics_helper); void SetMetricsServiceClient( scoped_ptr<metrics::CastMetricsServiceClient> metrics_service_client); @@ -54,9 +62,13 @@ class CastBrowserProcess { #endif // defined(OS_ANDROID) void SetConnectivityChecker( scoped_refptr<ConnectivityChecker> connectivity_checker); + void SetNetLog(net::NetLog* net_log); CastBrowserContext* browser_context() const { return browser_context_.get(); } CastService* cast_service() const { return cast_service_.get(); } +#if defined(USE_AURA) + CastScreen* cast_screen() const { return cast_screen_.get(); } +#endif // defined(USE_AURA) metrics::CastMetricsServiceClient* metrics_service_client() const { return metrics_service_client_.get(); } @@ -68,11 +80,15 @@ class CastBrowserProcess { ConnectivityChecker* connectivity_checker() const { return connectivity_checker_.get(); } + net::NetLog* net_log() const { return net_log_; } private: // Note: The following order should match the order they are set in // CastBrowserMainParts. scoped_ptr<metrics::CastMetricsHelper> metrics_helper_; +#if defined(USE_AURA) + scoped_ptr<CastScreen> cast_screen_; +#endif // defined(USE_AURA) scoped_ptr<PrefService> pref_service_; scoped_refptr<ConnectivityChecker> connectivity_checker_; scoped_ptr<CastBrowserContext> browser_context_; @@ -84,6 +100,8 @@ class CastBrowserProcess { #endif // defined(OS_ANDROID) scoped_ptr<RemoteDebuggingServer> remote_debugging_server_; + net::NetLog* net_log_; + // 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 3e075faadf7..5b2c0540649 100644 --- a/chromium/chromecast/browser/cast_content_browser_client.cc +++ b/chromium/chromecast/browser/cast_content_browser_client.cc @@ -9,10 +9,10 @@ #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/base/chromecast_switches.h" #include "chromecast/browser/cast_browser_context.h" #include "chromecast/browser/cast_browser_main_parts.h" #include "chromecast/browser/cast_browser_process.h" @@ -22,8 +22,9 @@ #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/chromecast_switches.h" #include "chromecast/common/global_descriptors.h" +#include "chromecast/media/cma/backend/media_pipeline_device.h" +#include "chromecast/media/cma/backend/media_pipeline_device_factory.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" @@ -37,7 +38,6 @@ #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" #include "net/url_request/url_request_context_getter.h" @@ -52,9 +52,7 @@ namespace chromecast { namespace shell { CastContentBrowserClient::CastContentBrowserClient() - : v8_natives_fd_(-1), - v8_snapshot_fd_(-1), - url_request_context_factory_(new URLRequestContextFactory()) { + : url_request_context_factory_(new URLRequestContextFactory()) { } CastContentBrowserClient::~CastContentBrowserClient() { @@ -64,18 +62,48 @@ CastContentBrowserClient::~CastContentBrowserClient() { url_request_context_factory_.release()); } +void CastContentBrowserClient::AppendExtraCommandLineSwitches( + base::CommandLine* command_line) { +} + +std::vector<scoped_refptr<content::BrowserMessageFilter>> +CastContentBrowserClient::GetBrowserMessageFilters() { + return std::vector<scoped_refptr<content::BrowserMessageFilter>>(); +} + +scoped_ptr<::media::AudioManagerFactory> +CastContentBrowserClient::CreateAudioManagerFactory() { + // Return nullptr. The factory will not be set, and the statically linked + // implementation of AudioManager will be used. + return scoped_ptr<::media::AudioManagerFactory>(); +} + +#if !defined(OS_ANDROID) +scoped_ptr<media::MediaPipelineDevice> +CastContentBrowserClient::CreateMediaPipelineDevice( + const media::MediaPipelineDeviceParams& params) { + scoped_ptr<media::MediaPipelineDeviceFactory> factory = + GetMediaPipelineDeviceFactory(params); + return make_scoped_ptr(new media::MediaPipelineDevice(factory.Pass())); +} +#endif + content::BrowserMainParts* CastContentBrowserClient::CreateBrowserMainParts( const content::MainFunctionParams& parameters) { return new CastBrowserMainParts(parameters, url_request_context_factory_.get(), - PlatformCreateAudioManagerFactory()); + CreateAudioManagerFactory()); } void CastContentBrowserClient::RenderProcessWillLaunch( content::RenderProcessHost* host) { #if !defined(OS_ANDROID) scoped_refptr<media::CmaMessageFilterHost> cma_message_filter( - new media::CmaMessageFilterHost(host->GetID())); + new media::CmaMessageFilterHost( + host->GetID(), + base::Bind( + &CastContentBrowserClient::CreateMediaPipelineDevice, + base::Unretained(this)))); host->AddFilter(cma_message_filter.get()); #endif // !defined(OS_ANDROID) @@ -89,7 +117,7 @@ void CastContentBrowserClient::RenderProcessWillLaunch( base::Bind(&CastContentBrowserClient::AddNetworkHintsMessageFilter, base::Unretained(this), host->GetID())); - auto extra_filters = PlatformGetBrowserMessageFilters(); + auto extra_filters = GetBrowserMessageFilters(); for (auto const& filter : extra_filters) { host->AddFilter(filter.get()); } @@ -149,19 +177,11 @@ bool CastContentBrowserClient::IsHandledURL(const GURL& url) { void CastContentBrowserClient::AppendExtraCommandLineSwitches( base::CommandLine* command_line, int child_process_id) { - 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. @@ -176,6 +196,8 @@ void CastContentBrowserClient::AppendExtraCommandLineSwitches( if (browser_command_line->HasSwitch(switches::kEnableCmaMediaPipeline)) command_line->AppendSwitch(switches::kEnableCmaMediaPipeline); + if (browser_command_line->HasSwitch(switches::kEnableLegacyHolePunching)) + command_line->AppendSwitch(switches::kEnableLegacyHolePunching); } #if defined(OS_LINUX) @@ -187,7 +209,7 @@ void CastContentBrowserClient::AppendExtraCommandLineSwitches( } #endif - PlatformAppendExtraCommandLineSwitches(command_line); + AppendExtraCommandLineSwitches(command_line); } content::AccessTokenStore* CastContentBrowserClient::CreateAccessTokenStore() { @@ -305,41 +327,25 @@ bool CastContentBrowserClient::CanCreateWindow( bool opener_suppressed, content::ResourceContext* context, int render_process_id, - int opener_id, + int opener_render_view_id, + int opener_render_frame_id, bool* no_javascript_access) { *no_javascript_access = true; return false; } +#if defined(OS_ANDROID) 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) - 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_file.TakePlatformFile())); + content::FileDescriptorInfo* mappings, + std::map<int, base::MemoryMappedFile::Region>* regions) { + mappings->Share( + kAndroidPakDescriptor, + base::GlobalDescriptors::GetInstance()->Get(kAndroidPakDescriptor)); + regions->insert(std::make_pair( + kAndroidPakDescriptor, base::GlobalDescriptors::GetInstance()->GetRegion( + kAndroidPakDescriptor))); if (breakpad::IsCrashReporterEnabled()) { base::File minidump_file( @@ -353,23 +359,18 @@ 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 +void CastContentBrowserClient::GetAdditionalMappedFilesForChildProcess( + const base::CommandLine& command_line, + int child_process_id, + content::FileDescriptorInfo* mappings) { int crash_signal_fd = GetCrashSignalFD(command_line); if (crash_signal_fd >= 0) { mappings->Share(kCrashDumpSignal, crash_signal_fd); } -#endif // defined(OS_ANDROID) } +#endif // defined(OS_ANDROID) #if defined(OS_ANDROID) && defined(VIDEO_HOLE) content::ExternalVideoSurfaceContainer* diff --git a/chromium/chromecast/browser/cast_content_browser_client.h b/chromium/chromecast/browser/cast_content_browser_client.h index 5e5c262f92a..1db6ba38ae0 100644 --- a/chromium/chromecast/browser/cast_content_browser_client.h +++ b/chromium/chromecast/browser/cast_content_browser_client.h @@ -30,6 +30,11 @@ class HostResolver; } namespace chromecast { +namespace media { +class MediaPipelineDevice; +class MediaPipelineDeviceParams; +} + namespace shell { class CastBrowserMainParts; @@ -37,16 +42,29 @@ class URLRequestContextFactory; class CastContentBrowserClient: public content::ContentBrowserClient { public: - CastContentBrowserClient(); + // Creates an implementation of CastContentBrowserClient. Platform should + // link in an implementation as needed. + static scoped_ptr<CastContentBrowserClient> Create(); + ~CastContentBrowserClient() override; // Appends extra command line arguments before launching a new process. - void PlatformAppendExtraCommandLineSwitches(base::CommandLine* command_line); + virtual void AppendExtraCommandLineSwitches(base::CommandLine* command_line); + + // Returns any BrowserMessageFilters that should be added when launching a + // new render process. + virtual std::vector<scoped_refptr<content::BrowserMessageFilter>> + GetBrowserMessageFilters(); - // Returns any BrowserMessageFilters from the platform implementation that - // should be added when launching a new render process. - std::vector<scoped_refptr<content::BrowserMessageFilter>> - PlatformGetBrowserMessageFilters(); + // Provide an AudioManagerFactory instance for WebAudio playback. + virtual scoped_ptr<::media::AudioManagerFactory> CreateAudioManagerFactory(); + +#if !defined(OS_ANDROID) + // Creates a MediaPipelineDevice (CMA backend) for media playback, called + // once per media player instance. + virtual scoped_ptr<media::MediaPipelineDevice> CreateMediaPipelineDevice( + const media::MediaPipelineDeviceParams& params); +#endif // content::ContentBrowserClient implementation: content::BrowserMainParts* CreateBrowserMainParts( @@ -95,18 +113,30 @@ class CastContentBrowserClient: public content::ContentBrowserClient { bool opener_suppressed, content::ResourceContext* context, int render_process_id, - int opener_id, + int opener_render_view_id, + int opener_render_frame_id, bool* no_javascript_access) override; +#if defined(OS_ANDROID) + void GetAdditionalMappedFilesForChildProcess( + const base::CommandLine& command_line, + int child_process_id, + content::FileDescriptorInfo* mappings, + std::map<int, base::MemoryMappedFile::Region>* regions) override; +#else void GetAdditionalMappedFilesForChildProcess( const base::CommandLine& command_line, int child_process_id, content::FileDescriptorInfo* mappings) override; +#endif // defined(OS_ANDROID) #if defined(OS_ANDROID) && defined(VIDEO_HOLE) content::ExternalVideoSurfaceContainer* OverrideCreateExternalVideoSurfaceContainer( content::WebContents* web_contents) override; #endif // defined(OS_ANDROID) && defined(VIDEO_HOLE) + protected: + CastContentBrowserClient(); + private: void AddNetworkHintsMessageFilter(int render_process_id, net::URLRequestContext* context); @@ -115,8 +145,6 @@ class CastContentBrowserClient: public content::ContentBrowserClient { 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); @@ -129,9 +157,6 @@ class CastContentBrowserClient: public content::ContentBrowserClient { 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_; DISALLOW_COPY_AND_ASSIGN(CastContentBrowserClient); diff --git a/chromium/chromecast/browser/cast_content_browser_client_simple.cc b/chromium/chromecast/browser/cast_content_browser_client_simple.cc index bde5a71d028..2b27e60db0d 100644 --- a/chromium/chromecast/browser/cast_content_browser_client_simple.cc +++ b/chromium/chromecast/browser/cast_content_browser_client_simple.cc @@ -4,26 +4,14 @@ #include "chromecast/browser/cast_content_browser_client.h" -#include "content/public/browser/browser_message_filter.h" -#include "media/audio/audio_manager_factory.h" +#include "base/memory/scoped_ptr.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>(); +// static +scoped_ptr<CastContentBrowserClient> CastContentBrowserClient::Create() { + return make_scoped_ptr(new CastContentBrowserClient()); } } // namespace shell diff --git a/chromium/chromecast/browser/cast_content_window.cc b/chromium/chromecast/browser/cast_content_window.cc index 27c91b74cb4..8ca03391915 100644 --- a/chromium/chromecast/browser/cast_content_window.cc +++ b/chromium/chromecast/browser/cast_content_window.cc @@ -4,16 +4,21 @@ #include "chromecast/browser/cast_content_window.h" +#include "base/command_line.h" #include "base/threading/thread_restrictions.h" +#include "chromecast/base/chromecast_switches.h" #include "chromecast/base/metrics/cast_metrics_helper.h" +#include "chromecast/browser/cast_browser_process.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" #include "ipc/ipc_message.h" #if defined(USE_AURA) +#include "chromecast/graphics/cast_screen.h" #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" #endif @@ -66,26 +71,27 @@ void CastContentWindow::CreateWindowTree( content::WebContents* web_contents) { #if defined(USE_AURA) // Aura initialization - // TODO(lcwu): We only need a minimal implementation of gfx::Screen - // and aura's TestScreen will do for us now. We should change to use - // ozone's screen implementation when it is ready. - gfx::Screen* old_screen = - gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE); - if (!old_screen || old_screen->GetPrimaryDisplay().size() != initial_size) { - gfx::Screen* new_screen = aura::TestScreen::Create(initial_size); - DCHECK(new_screen) << "New screen not created."; - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, new_screen); - if (old_screen) { - delete old_screen; - } - } + CastScreen* cast_screen = + shell::CastBrowserProcess::GetInstance()->cast_screen(); + if (!gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE)) + gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, cast_screen); + if (cast_screen->GetPrimaryDisplay().size() != initial_size) + cast_screen->UpdateDisplaySize(initial_size); + CHECK(aura::Env::GetInstance()); window_tree_host_.reset( aura::WindowTreeHost::Create(gfx::Rect(initial_size))); window_tree_host_->InitHost(); window_tree_host_->window()->SetLayoutManager( new CastFillLayout(window_tree_host_->window())); - window_tree_host_->compositor()->SetBackgroundColor(SK_ColorBLACK); + + const base::CommandLine* command_line(base::CommandLine::ForCurrentProcess()); + if (command_line->HasSwitch(switches::kEnableTransparentBackground)) { + window_tree_host_->compositor()->SetBackgroundColor(SK_ColorTRANSPARENT); + window_tree_host_->compositor()->SetHostHasTransparentBackground(true); + } else { + window_tree_host_->compositor()->SetBackgroundColor(SK_ColorBLACK); + } focus_client_.reset(new aura::test::TestFocusClient()); aura::client::SetFocusClient( @@ -127,4 +133,14 @@ void CastContentWindow::MediaStartedPlaying() { metrics::CastMetricsHelper::GetInstance()->LogMediaPlay(); } +void CastContentWindow::RenderViewCreated( + content::RenderViewHost* render_view_host) { + const base::CommandLine* command_line(base::CommandLine::ForCurrentProcess()); + if (command_line->HasSwitch(switches::kEnableTransparentBackground)) { + content::RenderWidgetHostView* view = render_view_host->GetView(); + if (view) + view->SetBackgroundColor(SK_ColorTRANSPARENT); + } +} + } // namespace chromecast diff --git a/chromium/chromecast/browser/cast_content_window.h b/chromium/chromecast/browser/cast_content_window.h index 1360a5e0ca4..9d76426e667 100644 --- a/chromium/chromecast/browser/cast_content_window.h +++ b/chromium/chromecast/browser/cast_content_window.h @@ -46,6 +46,7 @@ class CastContentWindow : public content::WebContentsObserver { void DidFirstVisuallyNonEmptyPaint() override; void MediaPaused() override; void MediaStartedPlaying() override; + void RenderViewCreated(content::RenderViewHost* render_view_host) override; private: #if defined(USE_AURA) diff --git a/chromium/chromecast/browser/cast_net_log.cc b/chromium/chromecast/browser/cast_net_log.cc new file mode 100644 index 00000000000..d1c62d4b5f8 --- /dev/null +++ b/chromium/chromecast/browser/cast_net_log.cc @@ -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. + +#include "chromecast/browser/cast_net_log.h" + +#include <stdio.h> + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/values.h" +#include "content/public/common/content_switches.h" +#include "net/log/net_log_util.h" +#include "net/log/write_to_file_net_log_observer.h" + +namespace chromecast { + +namespace { + +base::DictionaryValue* GetShellConstants() { + scoped_ptr<base::DictionaryValue> constants_dict = net::GetNetConstants(); + + // Add a dictionary with client information + base::DictionaryValue* dict = new base::DictionaryValue(); + + dict->SetString("name", "cast_shell"); + dict->SetString( + "command_line", + base::CommandLine::ForCurrentProcess()->GetCommandLineString()); + + constants_dict->Set("clientInfo", dict); + + return constants_dict.release(); +} + +} // namespace + +CastNetLog::CastNetLog() { + // TODO(derekjchow): This code is virtually identical to ShellNetLog which is + // nearly identical to code in ChromeNetLog. Consider merging the code. + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + + if (command_line->HasSwitch(switches::kLogNetLog)) { + base::FilePath log_path = + command_line->GetSwitchValuePath(switches::kLogNetLog); + // Much like logging.h, bypass threading restrictions by using fopen + // directly. Have to write on a thread that's shutdown to handle events on + // shutdown properly, and posting events to another thread as they occur + // would result in an unbounded buffer size, so not much can be gained by + // doing this on another thread. It's only used when debugging, so + // performance is not a big concern. + base::ScopedFILE file; + file.reset(fopen(log_path.value().c_str(), "w")); + + if (!file) { + LOG(ERROR) << "Could not open file " << log_path.value() + << " for net logging"; + } else { + scoped_ptr<base::Value> constants(GetShellConstants()); + write_to_file_observer_.reset(new net::WriteToFileNetLogObserver()); + write_to_file_observer_->StartObserving(this, file.Pass(), + constants.get(), nullptr); + } + } +} + +CastNetLog::~CastNetLog() { + // Remove the observer we own before we're destroyed. + if (write_to_file_observer_) + write_to_file_observer_->StopObserving(nullptr); +} + +} // namespace chromecast diff --git a/chromium/chromecast/browser/cast_net_log.h b/chromium/chromecast/browser/cast_net_log.h new file mode 100644 index 00000000000..6be2b5b34c6 --- /dev/null +++ b/chromium/chromecast/browser/cast_net_log.h @@ -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. + +#ifndef CHROMECAST_BROWSER_CAST_NET_LOG_H_ +#define CHROMECAST_BROWSER_CAST_NET_LOG_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "net/log/write_to_file_net_log_observer.h" + +namespace chromecast { + +class CastNetLog : public net::NetLog { + public: + CastNetLog(); + ~CastNetLog() override; + + private: + scoped_ptr<net::WriteToFileNetLogObserver> write_to_file_observer_; + + DISALLOW_COPY_AND_ASSIGN(CastNetLog); +}; + +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_CAST_NET_LOG_H_ diff --git a/chromium/chromecast/browser/cast_network_delegate.cc b/chromium/chromecast/browser/cast_network_delegate.cc index 51d9fd4aa5b..3e004a0a539 100644 --- a/chromium/chromecast/browser/cast_network_delegate.cc +++ b/chromium/chromecast/browser/cast_network_delegate.cc @@ -6,7 +6,7 @@ #include "base/command_line.h" #include "base/files/file_path.h" -#include "chromecast/common/chromecast_switches.h" +#include "chromecast/base/chromecast_switches.h" namespace chromecast { namespace shell { diff --git a/chromium/chromecast/browser/cast_permission_manager.cc b/chromium/chromecast/browser/cast_permission_manager.cc index 54352c0db79..047a6ff5a90 100644 --- a/chromium/chromecast/browser/cast_permission_manager.cc +++ b/chromium/chromecast/browser/cast_permission_manager.cc @@ -20,7 +20,7 @@ CastPermissionManager::~CastPermissionManager() { void CastPermissionManager::RequestPermission( content::PermissionType permission, - content::WebContents* web_contents, + content::RenderFrameHost* render_frame_host, int request_id, const GURL& origin, bool user_gesture, @@ -31,7 +31,7 @@ void CastPermissionManager::RequestPermission( void CastPermissionManager::CancelPermissionRequest( content::PermissionType permission, - content::WebContents* web_contents, + content::RenderFrameHost* render_frame_host, int request_id, const GURL& origin) { } diff --git a/chromium/chromecast/browser/cast_permission_manager.h b/chromium/chromecast/browser/cast_permission_manager.h index c82f08a4be2..237eff99306 100644 --- a/chromium/chromecast/browser/cast_permission_manager.h +++ b/chromium/chromecast/browser/cast_permission_manager.h @@ -20,13 +20,13 @@ class CastPermissionManager : public content::PermissionManager { // content::PermissionManager implementation: void RequestPermission( content::PermissionType permission, - content::WebContents* web_contents, + content::RenderFrameHost* render_frame_host, 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, + content::RenderFrameHost* render_frame_host, int request_id, const GURL& requesting_origin) override; void ResetPermission(content::PermissionType permission, diff --git a/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.h b/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.h index 190abec1244..1726af230cf 100644 --- a/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.h +++ b/chromium/chromecast/browser/devtools/cast_dev_tools_delegate.h @@ -6,7 +6,6 @@ #define CHROMECAST_BROWSER_DEVTOOLS_CAST_DEV_TOOLS_DELEGATE_H_ #include "components/devtools_http_handler/devtools_http_handler_delegate.h" -#include "net/socket/stream_listen_socket.h" namespace chromecast { namespace shell { diff --git a/chromium/chromecast/browser/devtools/remote_debugging_server.cc b/chromium/chromecast/browser/devtools/remote_debugging_server.cc index 6391b6d6303..dda2123e1ea 100644 --- a/chromium/chromecast/browser/devtools/remote_debugging_server.cc +++ b/chromium/chromecast/browser/devtools/remote_debugging_server.cc @@ -8,6 +8,7 @@ #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/files/file_path.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "chromecast/browser/cast_browser_process.h" #include "chromecast/browser/devtools/cast_dev_tools_delegate.h" @@ -112,42 +113,40 @@ std::string GetFrontendUrl() { } // namespace -RemoteDebuggingServer::RemoteDebuggingServer() : port_(0) { +RemoteDebuggingServer::RemoteDebuggingServer() + : port_(kDefaultRemoteDebuggingPort) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - pref_port_.Init(prefs::kRemoteDebuggingPort, - CastBrowserProcess::GetInstance()->pref_service(), - base::Bind(&RemoteDebuggingServer::OnPortChanged, - base::Unretained(this))); + pref_enabled_.Init(prefs::kEnableRemoteDebugging, + CastBrowserProcess::GetInstance()->pref_service(), + base::Bind(&RemoteDebuggingServer::OnEnabledChanged, + base::Unretained(this))); + + std::string port_str = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kRemoteDebuggingPort); + if (!port_str.empty()) { + int port = kDefaultRemoteDebuggingPort; + if (base::StringToInt(port_str, &port)) { + port_ = static_cast<uint16>(port); + } else { + port_ = kDefaultRemoteDebuggingPort; + } + } // Starts new dev tools, clearing port number saved in config. // Remote debugging in production must be triggered only by config server. - pref_port_.SetValue(ShouldStartImmediately() ? - kDefaultRemoteDebuggingPort : 0); - OnPortChanged(); + pref_enabled_.SetValue(ShouldStartImmediately() && port_ != 0); + OnEnabledChanged(); } RemoteDebuggingServer::~RemoteDebuggingServer() { - pref_port_.SetValue(0); - OnPortChanged(); + pref_enabled_.SetValue(false); + OnEnabledChanged(); } -void RemoteDebuggingServer::OnPortChanged() { - uint16 new_port = static_cast<uint16>(std::max(*pref_port_, 0)); - VLOG(1) << "OnPortChanged called: old_port=" << port_ - << ", new_port=" << new_port; - - if (new_port == port_) { - VLOG(1) << "Port has not been changed. Ignore silently."; - return; - } - - if (devtools_http_handler_) { - LOG(INFO) << "Stop old devtools: port=" << port_; - devtools_http_handler_.reset(); - } - - port_ = new_port; - if (port_ > 0) { +void RemoteDebuggingServer::OnEnabledChanged() { + bool enabled = *pref_enabled_ && port_ != 0; + if (enabled && !devtools_http_handler_) { devtools_http_handler_.reset(new DevToolsHttpHandler( CreateSocketFactory(port_), GetFrontendUrl(), @@ -157,6 +156,9 @@ void RemoteDebuggingServer::OnPortChanged() { std::string(), GetUserAgent())); LOG(INFO) << "Devtools started: port=" << port_; + } else if (!enabled && devtools_http_handler_) { + LOG(INFO) << "Stop devtools: port=" << port_; + devtools_http_handler_.reset(); } } diff --git a/chromium/chromecast/browser/devtools/remote_debugging_server.h b/chromium/chromecast/browser/devtools/remote_debugging_server.h index 399b34449f9..146f137fc2a 100644 --- a/chromium/chromecast/browser/devtools/remote_debugging_server.h +++ b/chromium/chromecast/browser/devtools/remote_debugging_server.h @@ -23,8 +23,8 @@ class RemoteDebuggingServer { ~RemoteDebuggingServer(); private: - // Called on port number changed. - void OnPortChanged(); + // Called when pref_enabled_ is changed. + void OnEnabledChanged(); // Returns whether or not the remote debugging server should be available // on device startup. @@ -32,7 +32,7 @@ class RemoteDebuggingServer { scoped_ptr<devtools_http_handler::DevToolsHttpHandler> devtools_http_handler_; - IntegerPrefMember pref_port_; + BooleanPrefMember pref_enabled_; uint16 port_; DISALLOW_COPY_AND_ASSIGN(RemoteDebuggingServer); diff --git a/chromium/chromecast/browser/media/cast_browser_cdm_factory.cc b/chromium/chromecast/browser/media/cast_browser_cdm_factory.cc index 61d80019090..67a0891070f 100644 --- a/chromium/chromecast/browser/media/cast_browser_cdm_factory.cc +++ b/chromium/chromecast/browser/media/cast_browser_cdm_factory.cc @@ -6,8 +6,8 @@ #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 "base/single_thread_task_runner.h" +#include "chromecast/media/base/media_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" @@ -36,7 +36,7 @@ scoped_ptr<::media::BrowserCdm> CastBrowserCdmFactory::CreateBrowserCdm( } if (browser_cdm) { - CmaMessageLoop::GetMessageLoopProxy()->PostTask( + MediaMessageLoop::GetTaskRunner()->PostTask( FROM_HERE, base::Bind(&BrowserCdmCast::Initialize, base::Unretained(browser_cdm.get()), @@ -45,9 +45,8 @@ scoped_ptr<::media::BrowserCdm> CastBrowserCdmFactory::CreateBrowserCdm( ::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())); + return make_scoped_ptr(new BrowserCdmCastUi( + browser_cdm.Pass(), MediaMessageLoop::GetTaskRunner())); } LOG(INFO) << "No matching key system found."; diff --git a/chromium/chromecast/browser/media/cma_message_filter_host.cc b/chromium/chromecast/browser/media/cma_message_filter_host.cc index d3664c915a6..226d5744b39 100644 --- a/chromium/chromecast/browser/media/cma_message_filter_host.cc +++ b/chromium/chromecast/browser/media/cma_message_filter_host.cc @@ -10,14 +10,16 @@ #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/base/media_message_loop.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 "chromecast/public/cast_media_shlib.h" +#include "chromecast/public/graphics_types.h" +#include "chromecast/public/video_plane.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "media/base/bind_to_current_loop.h" @@ -52,7 +54,7 @@ uint64_t GetPipelineCmaId(int process_id, int media_id) { } MediaPipelineHost* GetMediaPipeline(int process_id, int media_id) { - DCHECK(CmaMessageLoop::GetTaskRunner()->BelongsToCurrentThread()); + DCHECK(MediaMessageLoop::GetTaskRunner()->BelongsToCurrentThread()); MediaPipelineCmaMap::iterator it = g_pipeline_map_cma.Get().find(GetPipelineCmaId(process_id, media_id)); if (it == g_pipeline_map_cma.Get().end()) @@ -61,7 +63,7 @@ MediaPipelineHost* GetMediaPipeline(int process_id, int media_id) { } void SetMediaPipeline(int process_id, int media_id, MediaPipelineHost* host) { - DCHECK(CmaMessageLoop::GetTaskRunner()->BelongsToCurrentThread()); + DCHECK(MediaMessageLoop::GetTaskRunner()->BelongsToCurrentThread()); std::pair<MediaPipelineCmaMap::iterator, bool> ret = g_pipeline_map_cma.Get().insert( std::make_pair(GetPipelineCmaId(process_id, media_id), host)); @@ -73,7 +75,7 @@ void SetMediaPipeline(int process_id, int media_id, MediaPipelineHost* host) { void DestroyMediaPipeline(int process_id, int media_id, scoped_ptr<MediaPipelineHost> media_pipeline) { - DCHECK(CmaMessageLoop::GetTaskRunner()->BelongsToCurrentThread()); + DCHECK(MediaMessageLoop::GetTaskRunner()->BelongsToCurrentThread()); MediaPipelineCmaMap::iterator it = g_pipeline_map_cma.Get().find(GetPipelineCmaId(process_id, media_id)); if (it != g_pipeline_map_cma.Get().end()) @@ -117,7 +119,7 @@ void SetCdmOnUiThread( BrowserCdmCast* browser_cdm_cast = static_cast<BrowserCdmCastUi*>(cdm)->browser_cdm_cast(); - CmaMessageLoop::GetTaskRunner()->PostTask( + MediaMessageLoop::GetTaskRunner()->PostTask( FROM_HERE, base::Bind(&SetCdmOnCmaThread, render_process_id, @@ -125,22 +127,183 @@ void SetCdmOnUiThread( browser_cdm_cast)); } +// TODO(halliwell): remove this and the NotifyExternalSurface path once +// VIDEO_HOLE is no longer required. +void EstimateVideoPlaneRect(const gfx::QuadF& quad, + VideoPlane::Transform* transform, + RectF* rect) { + const gfx::PointF& p0 = quad.p1(); + const gfx::PointF& p1 = quad.p2(); + const gfx::PointF& p2 = quad.p3(); + const gfx::PointF& p3 = quad.p4(); + + float det0 = p0.x() * p1.y() - p1.x() * p0.y(); + float det1 = p1.x() * p2.y() - p2.x() * p1.y(); + float det2 = p2.x() * p3.y() - p3.x() * p2.y(); + float det3 = p3.x() * p0.y() - p0.x() * p3.y(); + + // Calculate the area of the quad (i.e. moment 0 of the polygon). + // Note: the area can here be either positive or negative + // depending on whether the quad is clock wise or counter clock wise. + float area = 0.5 * (det0 + det1 + det2 + det3); + if (area == 0.0) { + // Empty rectangle in that case. + *transform = VideoPlane::TRANSFORM_NONE; + *rect = RectF(p0.x(), p0.y(), 0.0, 0.0); + return; + } + + // Calculate the center of gravity of the polygon + // (i.e. moment 1 of the polygon). + float cx = (1.0 / (6.0 * area)) * + ((p0.x() + p1.x()) * det0 + + (p1.x() + p2.x()) * det1 + + (p2.x() + p3.x()) * det2 + + (p3.x() + p0.x()) * det3); + float cy = (1.0 / (6.0 * area)) * + ((p0.y() + p1.y()) * det0 + + (p1.y() + p2.y()) * det1 + + (p2.y() + p3.y()) * det2 + + (p3.y() + p0.y()) * det3); + + // For numerical stability, subtract the center of gravity now instead of + // later doing: + // mxx -= cx * cx; + // myy -= cy * cy; + // mxy -= cx * cy; + // to get the centered 2nd moments. + gfx::PointF p0c(p0.x() - cx, p0.y() - cy); + gfx::PointF p1c(p1.x() - cx, p1.y() - cy); + gfx::PointF p2c(p2.x() - cx, p2.y() - cy); + gfx::PointF p3c(p3.x() - cx, p3.y() - cy); + + // Calculate the second moments of the polygon. + float det0c = p0c.x() * p1c.y() - p1c.x() * p0c.y(); + float det1c = p1c.x() * p2c.y() - p2c.x() * p1c.y(); + float det2c = p2c.x() * p3c.y() - p3c.x() * p2c.y(); + float det3c = p3c.x() * p0c.y() - p0c.x() * p3c.y(); + float mxx = (1.0 / (12.0 * area)) * + ((p0c.x() * p0c.x() + p0c.x() * p1c.x() + p1c.x() * p1c.x()) * det0c + + (p1c.x() * p1c.x() + p1c.x() * p2c.x() + p2c.x() * p2c.x()) * det1c + + (p2c.x() * p2c.x() + p2c.x() * p3c.x() + p3c.x() * p3c.x()) * det2c + + (p3c.x() * p3c.x() + p3c.x() * p0c.x() + p0c.x() * p0c.x()) * det3c); + float myy = (1.0 / (12.0 * area)) * + ((p0c.y() * p0c.y() + p0c.y() * p1c.y() + p1c.y() * p1c.y()) * det0c + + (p1c.y() * p1c.y() + p1c.y() * p2c.y() + p2c.y() * p2c.y()) * det1c + + (p2c.y() * p2c.y() + p2c.y() * p3c.y() + p3c.y() * p3c.y()) * det2c + + (p3c.y() * p3c.y() + p3c.y() * p0c.y() + p0c.y() * p0c.y()) * det3c); + + // Remark: the 2nd moments of a rotated & centered rectangle are given by: + // mxx = (1/12) * (h^2 * s^2 + w^2 * c^2) + // myy = (1/12) * (h^2 * c^2 + w^2 * s^2) + // mxy = (1/12) * (w^2 - h^2) * c * s + // where w = width of the original rectangle, + // h = height of the original rectangle, + // c = cos(teta) with teta the angle of the rotation, + // s = sin(teta) + // + // For reference, mxy can be calculated from the quad using: + // float mxy = (1.0 / (24.0 * area)) * + // ((2.0 * p0c.x() * p0c.y() + p0c.x() * p1c.y() + p1c.x() * p0c.y() + + // 2.0 * p1c.x() * p1c.y()) * det0c + + // (2.0 * p1c.x() * p1c.y() + p1c.x() * p2c.y() + p2c.x() * p1c.y() + + // 2.0 * p2c.x() * p2c.y()) * det1c + + // (2.0 * p2c.x() * p2c.y() + p2c.x() * p3c.y() + p3c.x() * p2c.y() + + // 2.0 * p3c.x() * p3c.y()) * det2c + + // (2.0 * p3c.x() * p3c.y() + p3c.x() * p0c.y() + p0c.x() * p3c.y() + + // 2.0 * p0c.x() * p0c.y()) * det3c); + + // The rotation is assumed to be 0, 90, 180 or 270 degrees, so mxy = 0.0 + // Using this assumption: mxx = (1/12) h^2 or (1/12) w^2 depending on the + // rotation. Same for myy. + if (mxx < 0 || myy < 0) { + // mxx and myy should be positive, only numerical errors can lead to a + // negative value. + LOG(WARNING) << "Numerical errors: " << mxx << " " << myy; + *transform = VideoPlane::TRANSFORM_NONE; + *rect = RectF(p0.x(), p0.y(), 0.0, 0.0); + return; + } + float size_x = sqrt(12.0 * mxx); + float size_y = sqrt(12.0 * myy); + + // Estimate the parameters of the rotation since teta can only be known + // modulo 90 degrees. In previous equations, you can always swap w and h + // and subtract 90 degrees to teta to get the exact same second moment. + int idx_best = -1; + float err_best = 0.0; + + // First, estimate the rotation angle assuming + // dist(p0,p1) is equal to |size_x|, + // dist(p0,p3) is equal to |size_y|. + gfx::PointF r1(size_x, 0); + gfx::PointF r2(size_x, size_y); + gfx::PointF r3(0, size_y); + for (int k = 0; k < 4; k++) { + float cur_err = + fabs((p1.x() - p0.x()) - r1.x()) + fabs((p1.y() - p0.y()) - r1.y()) + + fabs((p2.x() - p0.x()) - r2.x()) + fabs((p2.y() - p0.y()) - r2.y()) + + fabs((p3.x() - p0.x()) - r3.x()) + fabs((p3.y() - p0.y()) - r3.y()); + if (idx_best < 0 || cur_err < err_best) { + idx_best = k; + err_best = cur_err; + } + // 90 degree rotation. + r1 = gfx::PointF(-r1.y(), r1.x()); + r2 = gfx::PointF(-r2.y(), r2.x()); + r3 = gfx::PointF(-r3.y(), r3.x()); + } + + // Then, estimate the rotation angle assuming: + // dist(p0,p1) is equal to |size_y|, + // dist(p0,p3) is equal to |size_x|. + r1 = gfx::PointF(size_y, 0); + r2 = gfx::PointF(size_y, size_x); + r3 = gfx::PointF(0, size_x); + for (int k = 0; k < 4; k++) { + float cur_err = + fabs((p1.x() - p0.x()) - r1.x()) + fabs((p1.y() - p0.y()) - r1.y()) + + fabs((p2.x() - p0.x()) - r2.x()) + fabs((p2.y() - p0.y()) - r2.y()) + + fabs((p3.x() - p0.x()) - r3.x()) + fabs((p3.y() - p0.y()) - r3.y()); + if (idx_best < 0 || cur_err < err_best) { + idx_best = k; + err_best = cur_err; + } + // 90 degree rotation. + r1 = gfx::PointF(-r1.y(), r1.x()); + r2 = gfx::PointF(-r2.y(), r2.x()); + r3 = gfx::PointF(-r3.y(), r3.x()); + } + + *transform = static_cast<VideoPlane::Transform>(idx_best); + *rect = RectF(cx - size_x / 2.0, cy - size_y / 2.0, size_x, size_y); +} + void UpdateVideoSurfaceHost(int surface_id, const gfx::QuadF& quad) { // Currently supports only one video plane. CHECK_EQ(surface_id, 0); - VideoPlane* video_plane = GetVideoPlane(); + // Convert quad into rect + transform + VideoPlane::Transform transform = VideoPlane::TRANSFORM_NONE; + RectF rect(0, 0, 0, 0); + EstimateVideoPlaneRect(quad, &transform, &rect); + + VideoPlane* video_plane = CastMediaShlib::GetVideoPlane(); video_plane->SetGeometry( - quad, - VideoPlane::COORDINATE_TYPE_GRAPHICS_PLANE); + rect, + VideoPlane::COORDINATE_TYPE_GRAPHICS_PLANE, + transform); } } // namespace -CmaMessageFilterHost::CmaMessageFilterHost(int render_process_id) +CmaMessageFilterHost::CmaMessageFilterHost( + int render_process_id, + const media::CreatePipelineDeviceCB& create_pipeline_device_cb) : content::BrowserMessageFilter(CastMediaMsgStart), process_id_(render_process_id), - task_runner_(CmaMessageLoop::GetMessageLoopProxy()), + create_pipeline_device_cb_(create_pipeline_device_cb), + task_runner_(MediaMessageLoop::GetTaskRunner()), weak_factory_(this) { weak_this_ = weak_factory_.GetWeakPtr(); } @@ -222,10 +385,9 @@ void CmaMessageFilterHost::CreateMedia(int media_id, LoadType load_type) { 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)); + FROM_HERE, base::Bind(&MediaPipelineHost::Initialize, + base::Unretained(media_pipeline_host.get()), + load_type, client, create_pipeline_device_cb_)); std::pair<MediaPipelineMap::iterator, bool> ret = media_pipelines_.insert( std::make_pair(media_id, media_pipeline_host.release())); @@ -374,7 +536,8 @@ void CmaMessageFilterHost::AudioInitialize( } void CmaMessageFilterHost::VideoInitialize( - int media_id, TrackId track_id, const ::media::VideoDecoderConfig& config) { + int media_id, TrackId track_id, + const std::vector<::media::VideoDecoderConfig>& configs) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); MediaPipelineHost* media_pipeline = LookupById(media_id); if (!media_pipeline) { @@ -404,7 +567,7 @@ void CmaMessageFilterHost::VideoInitialize( FROM_HERE, base::Bind(&MediaPipelineHost::VideoInitialize, base::Unretained(media_pipeline), - track_id, client, config, pipeline_status_cb)); + track_id, client, configs, pipeline_status_cb)); } void CmaMessageFilterHost::StartPlayingFrom( diff --git a/chromium/chromecast/browser/media/cma_message_filter_host.h b/chromium/chromecast/browser/media/cma_message_filter_host.h index e00057466f5..9b1620516ff 100644 --- a/chromium/chromecast/browser/media/cma_message_filter_host.h +++ b/chromium/chromecast/browser/media/cma_message_filter_host.h @@ -12,6 +12,7 @@ #include "base/memory/shared_memory.h" #include "base/memory/weak_ptr.h" #include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/media/cma/backend/media_pipeline_device.h" #include "chromecast/media/cma/pipeline/load_type.h" #include "content/public/browser/browser_message_filter.h" #include "content/public/browser/browser_thread.h" @@ -42,7 +43,9 @@ class MediaPipelineHost; class CmaMessageFilterHost : public content::BrowserMessageFilter { public: - explicit CmaMessageFilterHost(int render_process_id); + CmaMessageFilterHost( + int render_process_id, + const media::CreatePipelineDeviceCB& create_pipeline_device_cb); // content::BrowserMessageFilter implementation. void OnChannelClosing() override; @@ -71,9 +74,10 @@ class CmaMessageFilterHost 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 VideoInitialize( + int media_id, + TrackId track_id, + const std::vector<::media::VideoDecoderConfig>& configs); void StartPlayingFrom(int media_id, base::TimeDelta time); void Flush(int media_id); void Stop(int media_id); @@ -109,6 +113,9 @@ class CmaMessageFilterHost // Render process ID correponding to this message filter. const int process_id_; + // Factory function for device-specific part of media pipeline creation + media::CreatePipelineDeviceCB create_pipeline_device_cb_; + // List of media pipeline and message loop media pipelines are running on. MediaPipelineMap media_pipelines_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; @@ -123,4 +130,3 @@ class CmaMessageFilterHost } // namespace chromecast #endif // CHROMECAST_BROWSER_MEDIA_CMA_MESSAGE_FILTER_HOST_H_ - diff --git a/chromium/chromecast/browser/media/cma_message_loop.h b/chromium/chromecast/browser/media/cma_message_loop.h deleted file mode 100644 index 5ce2cd40c83..00000000000 --- a/chromium/chromecast/browser/media/cma_message_loop.h +++ /dev/null @@ -1,44 +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_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 index ff3eb6926f9..087077d044b 100644 --- a/chromium/chromecast/browser/media/media_pipeline_host.cc +++ b/chromium/chromecast/browser/media/media_pipeline_host.cc @@ -52,15 +52,15 @@ MediaPipelineHost::~MediaPipelineHost() { void MediaPipelineHost::Initialize( LoadType load_type, - const MediaPipelineClient& client) { + const MediaPipelineClient& client, + const media::CreatePipelineDeviceCB& create_pipeline_device_cb) { 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()); + load_type, create_pipeline_device_cb.Run(default_parameters).Pass()); media_pipeline_->SetClient(client); } @@ -118,13 +118,13 @@ void MediaPipelineHost::AudioInitialize( void MediaPipelineHost::VideoInitialize( TrackId track_id, const VideoPipelineClient& client, - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, 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); + configs, scoped_ptr<CodedFrameProvider>(), status_cb); } void MediaPipelineHost::StartPlayingFrom(base::TimeDelta time) { @@ -171,4 +171,3 @@ void MediaPipelineHost::NotifyPipeWrite(TrackId track_id) { } // namespace media } // namespace chromecast - diff --git a/chromium/chromecast/browser/media/media_pipeline_host.h b/chromium/chromecast/browser/media/media_pipeline_host.h index dc15db94240..d53ba3f9c69 100644 --- a/chromium/chromecast/browser/media/media_pipeline_host.h +++ b/chromium/chromecast/browser/media/media_pipeline_host.h @@ -14,6 +14,7 @@ #include "base/threading/thread_checker.h" #include "base/time/time.h" #include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/media/cma/backend/media_pipeline_device.h" #include "chromecast/media/cma/pipeline/load_type.h" #include "media/base/pipeline_status.h" @@ -40,8 +41,10 @@ class MediaPipelineHost { MediaPipelineHost(); ~MediaPipelineHost(); - void Initialize(LoadType load_type, - const MediaPipelineClient& client); + void Initialize( + LoadType load_type, + const MediaPipelineClient& client, + const media::CreatePipelineDeviceCB& create_pipeline_device_cb); void SetAvPipe(TrackId track_id, scoped_ptr<base::SharedMemory> shared_mem, @@ -53,7 +56,7 @@ class MediaPipelineHost { const ::media::PipelineStatusCB& status_cb); void VideoInitialize(TrackId track_id, const VideoPipelineClient& client, - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, const ::media::PipelineStatusCB& status_cb); void StartPlayingFrom(base::TimeDelta time); void Flush(const ::media::PipelineStatusCB& status_cb); @@ -83,4 +86,3 @@ class MediaPipelineHost { } // namespace chromecast #endif // CHROMECAST_BROWSER_MEDIA_MEDIA_PIPELINE_HOST_H_ - diff --git a/chromium/chromecast/browser/metrics/BUILD.gn b/chromium/chromecast/browser/metrics/BUILD.gn new file mode 100644 index 00000000000..a07fbdfc5ee --- /dev/null +++ b/chromium/chromecast/browser/metrics/BUILD.gn @@ -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. + +source_set("metrics") { + sources = [ + "cast_metrics_prefs.cc", + "cast_metrics_prefs.h", + "cast_metrics_service_client.cc", + "cast_metrics_service_client.h", + "cast_stability_metrics_provider.cc", + "cast_stability_metrics_provider.h", + "platform_metrics_providers.h", + ] + + deps = [ + "//base", + "//base:i18n", + "//base:prefs", + "//chromecast/base", + "//chromecast/common", + "//components/metrics", + "//components/metrics:gpu", + "//components/metrics:net", + "//components/metrics:profiler", + "//content/public/common", + "//content/public/browser", + ] + + if (is_linux) { + sources += [ + "external_metrics.cc", + "external_metrics.h", + ] + + deps += [ "//components/metrics:serialization" ] + } + + # TODO(kmackay) add source "platform_metrics_providers_simple.cc" + # according to condition (chromecast_branding != "Chrome") +} diff --git a/chromium/chromecast/browser/metrics/cast_metrics_prefs.cc b/chromium/chromecast/browser/metrics/cast_metrics_prefs.cc index b53ed807243..2755ccaca42 100644 --- a/chromium/chromecast/browser/metrics/cast_metrics_prefs.cc +++ b/chromium/chromecast/browser/metrics/cast_metrics_prefs.cc @@ -4,6 +4,7 @@ #include "chromecast/browser/metrics/cast_metrics_prefs.h" +#include "chromecast/browser/metrics/cast_metrics_service_client.h" #include "chromecast/browser/metrics/cast_stability_metrics_provider.h" #include "components/metrics/metrics_service.h" @@ -12,6 +13,7 @@ namespace metrics { void RegisterPrefs(PrefRegistrySimple* registry) { ::metrics::MetricsService::RegisterPrefs(registry); + CastMetricsServiceClient::RegisterPrefs(registry); CastStabilityMetricsProvider::RegisterPrefs(registry); } diff --git a/chromium/chromecast/browser/metrics/cast_metrics_service_client.cc b/chromium/chromecast/browser/metrics/cast_metrics_service_client.cc index a367322da4e..daa5216c64b 100644 --- a/chromium/chromecast/browser/metrics/cast_metrics_service_client.cc +++ b/chromium/chromecast/browser/metrics/cast_metrics_service_client.cc @@ -7,14 +7,15 @@ #include "base/command_line.h" #include "base/guid.h" #include "base/i18n/rtl.h" +#include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" +#include "base/thread_task_runner_handle.h" +#include "chromecast/base/chromecast_switches.h" #include "chromecast/browser/metrics/cast_stability_metrics_provider.h" #include "chromecast/browser/metrics/platform_metrics_providers.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" @@ -32,7 +33,11 @@ namespace chromecast { namespace metrics { namespace { + const int kStandardUploadIntervalMinutes = 5; + +const char kMetricsOldClientID[] = "user_experience_metrics.client_id"; + } // namespace // static @@ -45,6 +50,10 @@ scoped_ptr<CastMetricsServiceClient> CastMetricsServiceClient::Create( request_context)); } +void CastMetricsServiceClient::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterStringPref(kMetricsOldClientID, std::string()); +} + void CastMetricsServiceClient::SetMetricsClientId( const std::string& client_id) { client_id_ = client_id; @@ -71,8 +80,7 @@ scoped_ptr< ::metrics::ClientInfo> CastMetricsServiceClient::LoadClientInfo() { 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()) { + if (!pref_service_->GetString(kMetricsOldClientID).empty()) { // Force old client id to be regenerated. See b/9487011. client_info->client_id = base::GenerateGUID(); pref_service_->SetBoolean(prefs::kMetricsIsNewClientID, true); @@ -162,12 +170,10 @@ base::TimeDelta CastMetricsServiceClient::GetStandardUploadInterval() { } void CastMetricsServiceClient::EnableMetricsService(bool enabled) { - if (!metrics_service_loop_->BelongsToCurrentThread()) { - metrics_service_loop_->PostTask( - FROM_HERE, - base::Bind(&CastMetricsServiceClient::EnableMetricsService, - base::Unretained(this), - enabled)); + if (!task_runner_->BelongsToCurrentThread()) { + task_runner_->PostTask( + FROM_HERE, base::Bind(&CastMetricsServiceClient::EnableMetricsService, + base::Unretained(this), enabled)); return; } @@ -188,7 +194,7 @@ CastMetricsServiceClient::CastMetricsServiceClient( #if !defined(OS_ANDROID) external_metrics_(NULL), #endif // !defined(OS_ANDROID) - metrics_service_loop_(base::MessageLoopProxy::current()), + task_runner_(base::ThreadTaskRunnerHandle::Get()), request_context_(request_context) { } diff --git a/chromium/chromecast/browser/metrics/cast_metrics_service_client.h b/chromium/chromecast/browser/metrics/cast_metrics_service_client.h index abc2b57311c..ec021a2bf6c 100644 --- a/chromium/chromecast/browser/metrics/cast_metrics_service_client.h +++ b/chromium/chromecast/browser/metrics/cast_metrics_service_client.h @@ -12,10 +12,11 @@ #include "base/memory/scoped_ptr.h" #include "components/metrics/metrics_service_client.h" +class PrefRegistrySimple; class PrefService; namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; class TaskRunner; } @@ -45,6 +46,7 @@ class CastMetricsServiceClient : public ::metrics::MetricsServiceClient { base::TaskRunner* io_task_runner, PrefService* pref_service, net::URLRequestContextGetter* request_context); + static void RegisterPrefs(PrefRegistrySimple* registry); void Initialize(CastService* cast_service); void Finalize(); @@ -92,7 +94,7 @@ class CastMetricsServiceClient : public ::metrics::MetricsServiceClient { #if defined(OS_LINUX) ExternalMetrics* external_metrics_; #endif // defined(OS_LINUX) - const scoped_refptr<base::MessageLoopProxy> metrics_service_loop_; + const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_ptr< ::metrics::MetricsStateManager> metrics_state_manager_; scoped_ptr< ::metrics::MetricsService> metrics_service_; net::URLRequestContextGetter* const request_context_; diff --git a/chromium/chromecast/browser/pref_service_helper.cc b/chromium/chromecast/browser/pref_service_helper.cc index 3f6f5c607c3..36ab18dcbcf 100644 --- a/chromium/chromecast/browser/pref_service_helper.cc +++ b/chromium/chromecast/browser/pref_service_helper.cc @@ -43,8 +43,8 @@ scoped_ptr<PrefService> PrefServiceHelper::CreatePrefService( const base::FilePath config_path(GetConfigPath()); VLOG(1) << "Loading config from " << config_path.value(); + registry->RegisterBooleanPref(prefs::kEnableRemoteDebugging, false); registry->RegisterBooleanPref(prefs::kMetricsIsNewClientID, false); - registry->RegisterIntegerPref(prefs::kRemoteDebuggingPort, 0); RegisterPlatformPrefs(registry); diff --git a/chromium/chromecast/browser/service/cast_service_android.cc b/chromium/chromecast/browser/service/cast_service_android.cc index 8822c83e7b8..507716c380f 100644 --- a/chromium/chromecast/browser/service/cast_service_android.cc +++ b/chromium/chromecast/browser/service/cast_service_android.cc @@ -5,7 +5,7 @@ #include "chromecast/browser/service/cast_service_android.h" #include "base/bind.h" -#include "chromecast/android/chromecast_config_android.h" +#include "chromecast/base/chromecast_config_android.h" #include "chromecast/browser/metrics/cast_metrics_service_client.h" namespace chromecast { diff --git a/chromium/chromecast/browser/url_request_context_factory.cc b/chromium/chromecast/browser/url_request_context_factory.cc index 9143071fb8e..693a220f3bc 100644 --- a/chromium/chromecast/browser/url_request_context_factory.cc +++ b/chromium/chromecast/browser/url_request_context_factory.cc @@ -8,9 +8,9 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/threading/worker_pool.h" +#include "chromecast/base/chromecast_switches.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" @@ -143,7 +143,7 @@ URLRequestContextFactory::URLRequestContextFactory() URLRequestContextFactory::~URLRequestContextFactory() { } -void URLRequestContextFactory::InitializeOnUIThread() { +void URLRequestContextFactory::InitializeOnUIThread(net::NetLog* net_log) { 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 @@ -157,6 +157,8 @@ void URLRequestContextFactory::InitializeOnUIThread() { content::BrowserThread::IO), content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::FILE))); + + net_log_ = net_log; } net::URLRequestContextGetter* URLRequestContextFactory::CreateMainGetter( @@ -328,6 +330,7 @@ net::URLRequestContext* URLRequestContextFactory::CreateSystemRequestContext() { system_context->set_cookie_store( content::CreateCookieStore(content::CookieStoreConfig())); system_context->set_network_delegate(system_network_delegate_.get()); + system_context->set_net_log(net_log_); return system_context; } @@ -347,6 +350,7 @@ net::URLRequestContext* URLRequestContextFactory::CreateMediaRequestContext() { media_context->CopyFrom(main_context); media_context->set_http_transaction_factory( media_transaction_factory_.get()); + media_context->set_net_log(net_log_); return media_context; } @@ -399,6 +403,7 @@ net::URLRequestContext* URLRequestContextFactory::CreateMainRequestContext( main_transaction_factory_.get()); main_context->set_job_factory(main_job_factory_.get()); main_context->set_network_delegate(app_network_delegate_.get()); + main_context->set_net_log(net_log_); return main_context; } diff --git a/chromium/chromecast/browser/url_request_context_factory.h b/chromium/chromecast/browser/url_request_context_factory.h index 5ae43cb7a4a..c0d881d83f9 100644 --- a/chromium/chromecast/browser/url_request_context_factory.h +++ b/chromium/chromecast/browser/url_request_context_factory.h @@ -11,6 +11,7 @@ namespace net { class HttpTransactionFactory; class HttpUserAgentSettings; +class NetLog; class ProxyConfigService; class URLRequestJobFactory; } // namespace net @@ -25,7 +26,7 @@ class URLRequestContextFactory { ~URLRequestContextFactory(); // Some members must be initialized on UI thread. - void InitializeOnUIThread(); + void InitializeOnUIThread(net::NetLog* net_log); // Since main context requires a bunch of input params, if these get called // multiple times, either multiple main contexts should be supported/managed @@ -113,6 +114,8 @@ class URLRequestContextFactory { bool media_dependencies_initialized_; scoped_ptr<net::HttpTransactionFactory> media_transaction_factory_; + + net::NetLog* net_log_; }; } // namespace shell diff --git a/chromium/chromecast/chromecast.gyp b/chromium/chromecast/chromecast.gyp index ed8261946da..15372f4dbe6 100644 --- a/chromium/chromecast/chromecast.gyp +++ b/chromium/chromecast/chromecast.gyp @@ -44,15 +44,13 @@ 'public/graphics_properties_shlib.h', 'public/graphics_types.h', 'public/media/decoder_config.h', + 'public/media/stream_id.h', 'public/osd_plane.h', 'public/osd_plane_shlib.h', 'public/osd_surface.h', + 'public/video_plane.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)', @@ -62,39 +60,72 @@ 'sources': [ 'base/cast_paths.cc', 'base/cast_paths.h', + 'base/chromecast_switches.cc', + 'base/chromecast_switches.h', + 'base/error_codes.cc', + 'base/error_codes.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/path_utils.cc', + 'base/path_utils.h', + 'base/process_utils.cc', + 'base/process_utils.h', 'base/serializers.cc', 'base/serializers.h' ], }, # end of target 'cast_base' { - 'target_name': 'cast_crash_client', + 'target_name': 'cast_crash', 'type': '<(component)', + 'include_dirs': [ + # TODO(gfhuang): we should not need to include this directly, but + # somehow depending on component.gyp:breakpad_component is not + # working as expected. + '../breakpad/src', + ], 'dependencies': [ + 'cast_base', + 'cast_version_header', '../breakpad/breakpad.gyp:breakpad_client', - '../components/components.gyp:crash_component', ], 'sources': [ + 'crash/app_state_tracker.cc', + 'crash/app_state_tracker.h', 'crash/cast_crash_keys.cc', 'crash/cast_crash_keys.h', - 'crash/cast_crash_reporter_client.cc', - 'crash/cast_crash_reporter_client.h', + 'crash/cast_crashdump_uploader.cc', + 'crash/cast_crashdump_uploader.h', + 'crash/linux/crash_util.cc', + 'crash/linux/crash_util.h', + 'crash/linux/dummy_minidump_generator.cc', + 'crash/linux/dummy_minidump_generator.h', + 'crash/linux/dump_info.cc', + 'crash/linux/dump_info.h', + 'crash/linux/minidump_generator.h', + 'crash/linux/synchronized_minidump_manager.cc', + 'crash/linux/synchronized_minidump_manager.h', + 'crash/linux/minidump_params.cc', + 'crash/linux/minidump_params.h', + 'crash/linux/minidump_writer.cc', + 'crash/linux/minidump_writer.h', + ], + }, # end of target 'cast_crash' + { + 'target_name': 'cast_crash_client', + 'type': '<(component)', + 'dependencies': [ + 'cast_crash', + '../components/components.gyp:crash_component', + '../content/content.gyp:content_common', + ], + 'sources' : [ + # TODO(slan): Move android crash_client here as well. + 'app/linux/cast_crash_reporter_client.cc', + 'app/linux/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', @@ -102,6 +133,10 @@ 'sources': [ 'net/connectivity_checker.cc', 'net/connectivity_checker.h', + 'net/connectivity_checker_impl.cc', + 'net/connectivity_checker_impl.h', + 'net/fake_connectivity_checker.cc', + 'net/fake_connectivity_checker.h', 'net/net_switches.cc', 'net/net_switches.h', 'net/net_util_cast.cc', @@ -238,6 +273,8 @@ 'browser/cast_download_manager_delegate.h', 'browser/cast_http_user_agent_settings.cc', 'browser/cast_http_user_agent_settings.h', + 'browser/cast_net_log.cc', + 'browser/cast_net_log.h', 'browser/cast_network_delegate.cc', 'browser/cast_network_delegate.h', 'browser/cast_permission_manager.cc', @@ -271,8 +308,6 @@ 'common/cast_content_client.h', 'common/cast_resource_delegate.cc', 'common/cast_resource_delegate.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', @@ -304,7 +339,6 @@ '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': [ ['OS=="android"', { @@ -328,6 +362,8 @@ 'sources': [ 'browser/metrics/external_metrics.cc', 'browser/metrics/external_metrics.h', + 'graphics/cast_screen.cc', + 'graphics/cast_screen.h', ], 'dependencies': [ '../components/components.gyp:metrics_serialization', @@ -354,7 +390,7 @@ 'base/cast_sys_info_dummy.h', ], 'conditions': [ - ['chromecast_branding!="Chrome"', { + ['chromecast_branding!="Chrome" and OS!="android"', { 'sources': [ 'base/cast_sys_info_util_simple.cc', ], @@ -375,10 +411,10 @@ 'message': 'Generating version header file: <@(_outputs)', 'inputs': [ '<(version_path)', - 'common/version.h.in', + 'base/version.h.in', ], 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/chromecast/common/version.h', + '<(SHARED_INTERMEDIATE_DIR)/chromecast/base/version.h', ], 'action': [ 'python', @@ -392,7 +428,7 @@ '-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', + 'base/version.h.in', '<@(_outputs)', ], 'includes': [ @@ -448,18 +484,22 @@ 'sources': [ 'android/cast_jni_registrar.cc', 'android/cast_jni_registrar.h', - 'android/chromecast_config_android.cc', - 'android/chromecast_config_android.h', + 'android/cast_metrics_helper_android.cc', + 'android/cast_metrics_helper_android.h', 'android/platform_jni_loader.h', + 'app/android/cast_crash_reporter_client_android.cc', + 'app/android/cast_crash_reporter_client_android.h', 'app/android/cast_jni_loader.cc', + 'app/android/crash_handler.cc', + 'app/android/crash_handler.h', + 'base/cast_sys_info_android.cc', + 'base/cast_sys_info_android.h', + 'base/chromecast_config_android.cc', + 'base/chromecast_config_android.h', 'browser/android/cast_window_android.cc', 'browser/android/cast_window_android.h', 'browser/android/cast_window_manager.cc', 'browser/android/cast_window_manager.h', - 'crash/android/cast_crash_reporter_client_android.cc', - 'crash/android/cast_crash_reporter_client_android.h', - 'crash/android/crash_handler.cc', - 'crash/android/crash_handler.h', ], 'conditions': [ ['chromecast_branding=="Chrome"', { @@ -468,24 +508,39 @@ ], }, { 'sources': [ - 'android/chromecast_config_android_stub.cc', 'android/platform_jni_loader_stub.cc', ], }] ], }, # end of target 'libcast_shell_android' { + 'target_name': 'cast_base_java', + 'type': 'none', + 'dependencies': [ + '../base/base.gyp:base_java', + ], + 'variables': { + 'android_manifest_path': 'android/AndroidManifest.xml', + 'java_in_dir': 'base/java', + }, + 'includes': ['../build/java.gypi'], + }, # end of target 'cast_base_java' + { 'target_name': 'cast_shell_java', 'type': 'none', 'dependencies': [ '<(android_support_v13_target)', + 'cast_base_java', + 'cast_shell_manifest', '../base/base.gyp:base_java', + '../components/components.gyp:external_video_surface_java', '../content/content.gyp:content_java', '../media/media.gyp:media_java', '../net/net.gyp:net_java', '../ui/android/ui_android.gyp:ui_java', ], 'variables': { + 'android_manifest_path': '<(SHARED_INTERMEDIATE_DIR)/cast_shell_manifest/AndroidManifest.xml', 'has_java_resources': 1, 'java_in_dir': 'browser/android/apk', 'resource_dir': 'browser/android/apk/res', @@ -531,7 +586,10 @@ 'target_name': 'cast_jni_headers', 'type': 'none', 'sources': [ + 'base/java/src/org/chromium/chromecast/base/ChromecastConfigAndroid.java', 'browser/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java', + 'browser/android/apk/src/org/chromium/chromecast/shell/CastMetricsHelper.java', + 'browser/android/apk/src/org/chromium/chromecast/shell/CastSysInfoAndroid.java', 'browser/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java', 'browser/android/apk/src/org/chromium/chromecast/shell/CastWindowManager.java', ], @@ -562,8 +620,6 @@ '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', diff --git a/chromium/chromecast/chromecast_tests.gypi b/chromium/chromecast/chromecast_tests.gypi index a5a7204fcc4..2c6317ba6ee 100644 --- a/chromium/chromecast/chromecast_tests.gypi +++ b/chromium/chromecast/chromecast_tests.gypi @@ -16,9 +16,32 @@ '../testing/gtest.gyp:gtest', ], 'sources': [ + 'base/error_codes_unittest.cc', + 'base/path_utils_unittest.cc', + 'base/process_utils_unittest.cc', 'base/serializers_unittest.cc', ], - }, + }, # end of cast_base_unittests + { + 'target_name': 'cast_crash_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'chromecast.gyp:cast_crash', + '../base/base.gyp:run_all_unittests', + '../testing/gmock.gyp:gmock', + '../testing/gtest.gyp:gtest', + ], + 'include_dirs': [ + '../breakpad/src', + ], + 'sources': [ + 'crash/cast_crashdump_uploader_unittest.cc', + 'crash/linux/dummy_minidump_generator_unittest.cc', + 'crash/linux/dump_info_unittest.cc', + 'crash/linux/synchronized_minidump_manager_unittest.cc', + 'crash/linux/minidump_writer_unittest.cc', + ], + }, # end of cast_crash_unittests { 'target_name': 'cast_tests', 'type': 'none', @@ -39,6 +62,7 @@ 'type': 'none', 'dependencies': [ 'cast_base_unittests', + 'cast_crash_unittests', '../base/base.gyp:base_unittests', '../content/content_shell_and_tests.gyp:content_unittests', '../crypto/crypto.gyp:crypto_unittests', @@ -111,8 +135,8 @@ }], ['OS!="android"', { 'dependencies': [ + 'cast_shell_unittests', 'cast_shell_browser_test', - 'cast_shell_media_unittests', 'media/media.gyp:cast_media_unittests', ], 'variables': { @@ -121,6 +145,19 @@ ], }, }], + ['disable_display==1', { + 'variables': { + 'filters': [ + # These are not supported by the backend right now. b/21737919 + 'cast_media_unittests --gtest_filter=-AudioVideoPipelineDeviceTest.VorbisPlayback:AudioVideoPipelineDeviceTest.WebmPlayback', + ], + } + }], + ['enable_plugins==1', { + 'dependencies': [ + '../ppapi/ppapi_internal.gyp:ppapi_unittests', + ], + }], ], 'includes': ['build/tests/test_list.gypi'], }, @@ -229,26 +266,6 @@ }, { # 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': [ @@ -280,6 +297,18 @@ 'browser/test/chromecast_shell_browser_test.cc', ], }, + { + 'target_name': 'cast_shell_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'chromecast.gyp:cast_crash_client', + '../base/base.gyp:run_all_unittests', + '../testing/gtest.gyp:gtest', + ], + 'sources': [ + 'app/linux/cast_crash_reporter_client_unittest.cc', + ], + }, # end of cast_shell_unittests ], # end of targets }], ], # end of conditions diff --git a/chromium/chromecast/common/BUILD.gn b/chromium/chromecast/common/BUILD.gn new file mode 100644 index 00000000000..127823b3c0b --- /dev/null +++ b/chromium/chromecast/common/BUILD.gn @@ -0,0 +1,27 @@ +# 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("common") { + sources = [ + "cast_content_client.cc", + "cast_content_client.h", + "cast_resource_delegate.cc", + "cast_resource_delegate.h", + "global_descriptors.h", + "platform_client_auth.h", + "pref_names.cc", + "pref_names.h", + ] + + # TODO(kmackay) Add "platform_client_auth._simple.cc" to sources + # if chromecast_branding != "Chrome" + + deps = [ + "//base", + "//chromecast/base:cast_version", + "//content/public/common", + "//ui/base", + "//ui/gfx", + ] +} diff --git a/chromium/chromecast/common/cast_content_client.cc b/chromium/chromecast/common/cast_content_client.cc index 4772fdd5c06..ca3a2639a66 100644 --- a/chromium/chromecast/common/cast_content_client.cc +++ b/chromium/chromecast/common/cast_content_client.cc @@ -6,7 +6,7 @@ #include "base/strings/stringprintf.h" #include "base/sys_info.h" -#include "chromecast/common/version.h" +#include "chromecast/base/version.h" #include "content/public/common/user_agent.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" diff --git a/chromium/chromecast/common/media/cma_messages.h b/chromium/chromecast/common/media/cma_messages.h index 3fbed3dbad7..1edb9827794 100644 --- a/chromium/chromecast/common/media/cma_messages.h +++ b/chromium/chromecast/common/media/cma_messages.h @@ -50,11 +50,12 @@ IPC_MESSAGE_CONTROL3(CmaHostMsg_CreateAvPipe, IPC_MESSAGE_CONTROL3(CmaHostMsg_AudioInitialize, int /* Media pipeline ID */, chromecast::media::TrackId /* Track ID */, - media::AudioDecoderConfig /* Audio config */) + ::media::AudioDecoderConfig /* Audio config */) IPC_MESSAGE_CONTROL3(CmaHostMsg_VideoInitialize, int /* Media pipeline ID */, chromecast::media::TrackId /* Track ID */, - media::VideoDecoderConfig /* Video config */) + /* Video Config */ + std::vector<::media::VideoDecoderConfig>) IPC_MESSAGE_CONTROL3(CmaHostMsg_SetVolume, int /* Media pipeline ID */, chromecast::media::TrackId /* Track ID */, @@ -112,4 +113,4 @@ IPC_MESSAGE_CONTROL3(CmaMsg_PlaybackStatistics, IPC_MESSAGE_CONTROL3(CmaMsg_NaturalSizeChanged, int /* Media pipeline ID */, chromecast::media::TrackId /* Track ID */, - gfx::Size /* Size */)
\ No newline at end of file + gfx::Size /* Size */) diff --git a/chromium/chromecast/common/media/cma_param_traits.cc b/chromium/chromecast/common/media/cma_param_traits.cc index 33accb04672..8849be19f42 100644 --- a/chromium/chromecast/common/media/cma_param_traits.cc +++ b/chromium/chromecast/common/media/cma_param_traits.cc @@ -30,40 +30,37 @@ 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()); + WriteParam(m, p.codec()); + WriteParam(m, p.sample_format()); + WriteParam(m, p.channel_layout()); + WriteParam(m, p.samples_per_second()); + WriteParam(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); + WriteParam(m, extra_data); } bool ParamTraits<media::AudioDecoderConfig>::Read( - const Message* m, PickleIterator* iter, + const Message* m, + base::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)) + if (!ReadParam(m, iter, &codec) || !ReadParam(m, iter, &sample_format) || + !ReadParam(m, iter, &channel_layout) || + !ReadParam(m, iter, &samples_per_second) || + !ReadParam(m, iter, &is_encrypted) || !ReadParam(m, iter, &extra_data)) return false; - const uint8* extra_data_ptr = NULL; - if (extra_data.size() > 0) + const uint8* extra_data_ptr = nullptr; + if (!extra_data.empty()) extra_data_ptr = &extra_data[0]; *r = media::AudioDecoderConfig(codec, sample_format, channel_layout, samples_per_second, @@ -79,24 +76,25 @@ void ParamTraits<media::AudioDecoderConfig>::Log( 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()); + WriteParam(m, p.codec()); + WriteParam(m, p.profile()); + WriteParam(m, p.format()); + WriteParam(m, p.coded_size()); + WriteParam(m, p.visible_rect()); + WriteParam(m, p.natural_size()); + WriteParam(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); + WriteParam(m, extra_data); } bool ParamTraits<media::VideoDecoderConfig>::Read( - const Message* m, PickleIterator* iter, + const Message* m, + base::PickleIterator* iter, media::VideoDecoderConfig* r) { media::VideoCodec codec; media::VideoCodecProfile profile; @@ -105,20 +103,15 @@ bool ParamTraits<media::VideoDecoderConfig>::Read( 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)) + if (!ReadParam(m, iter, &codec) || !ReadParam(m, iter, &profile) || + !ReadParam(m, iter, &format) || !ReadParam(m, iter, &coded_size) || + !ReadParam(m, iter, &visible_rect) || + !ReadParam(m, iter, &natural_size) || + !ReadParam(m, iter, &is_encrypted) || !ReadParam(m, iter, &extra_data)) return false; - const uint8* extra_data_ptr = NULL; - if (extra_data.size() > 0) + const uint8* extra_data_ptr = nullptr; + if (!extra_data.empty()) extra_data_ptr = &extra_data[0]; *r = media::VideoDecoderConfig(codec, profile, format, coded_size, visible_rect, natural_size, diff --git a/chromium/chromecast/common/media/cma_param_traits.h b/chromium/chromecast/common/media/cma_param_traits.h index 0bebac35c2a..106012bed8b 100644 --- a/chromium/chromecast/common/media/cma_param_traits.h +++ b/chromium/chromecast/common/media/cma_param_traits.h @@ -18,7 +18,7 @@ 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 bool Read(const Message* m, base::PickleIterator* iter, param_type* r); static void Log(const param_type& p, std::string* l); }; @@ -26,7 +26,7 @@ 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 bool Read(const Message* m, base::PickleIterator* iter, param_type* r); static void Log(const param_type& p, std::string* l); }; diff --git a/chromium/chromecast/common/pref_names.cc b/chromium/chromecast/common/pref_names.cc index 0b885f50080..7a03c26184d 100644 --- a/chromium/chromecast/common/pref_names.cc +++ b/chromium/chromecast/common/pref_names.cc @@ -6,14 +6,13 @@ namespace prefs { +// Boolean which specifies if remote debugging is enabled +const char kEnableRemoteDebugging[] = "enable_remote_debugging"; + // 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"; - // Total number of child process crashes (other than renderer / extension // renderer ones, and plugin children, which are counted separately) since the // last report. diff --git a/chromium/chromecast/common/pref_names.h b/chromium/chromecast/common/pref_names.h index 6caa2cc6eea..26820dc2475 100644 --- a/chromium/chromecast/common/pref_names.h +++ b/chromium/chromecast/common/pref_names.h @@ -7,8 +7,8 @@ namespace prefs { +extern const char kEnableRemoteDebugging[]; extern const char kMetricsIsNewClientID[]; -extern const char kRemoteDebuggingPort[]; extern const char kStabilityChildProcessCrashCount[]; extern const char kStabilityKernelCrashCount[]; extern const char kStabilityOtherUserCrashCount[]; diff --git a/chromium/chromecast/crash/BUILD.gn b/chromium/chromecast/crash/BUILD.gn new file mode 100644 index 00000000000..d085b353730 --- /dev/null +++ b/chromium/chromecast/crash/BUILD.gn @@ -0,0 +1,58 @@ +# 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") + +source_set("crash") { + sources = [ + "app_state_tracker.cc", + "app_state_tracker.h", + "cast_crash_keys.cc", + "cast_crash_keys.h", + "cast_crashdump_uploader.cc", + "cast_crashdump_uploader.h", + "linux/crash_util.cc", + "linux/crash_util.h", + "linux/dummy_minidump_generator.cc", + "linux/dummy_minidump_generator.h", + "linux/dump_info.cc", + "linux/dump_info.h", + "linux/minidump_generator.h", + "linux/minidump_params.cc", + "linux/minidump_params.h", + "linux/minidump_writer.cc", + "linux/minidump_writer.h", + "linux/synchronized_minidump_manager.cc", + "linux/synchronized_minidump_manager.h", + ] + + configs += [ "//chromecast:config" ] + + deps = [ + "//base", + "//breakpad:client", + "//chromecast/base", + "//chromecast/base:cast_version", + ] +} + +test("cast_crash_unittests") { + sources = [ + "cast_crashdump_uploader_unittest.cc", + "linux/dummy_minidump_generator_unittest.cc", + "linux/dump_info_unittest.cc", + "linux/minidump_writer_unittest.cc", + "linux/synchronized_minidump_manager_unittest.cc", + ] + + deps = [ + ":crash", + "//base", + "//base/test:run_all_unittests", + "//base/test:test_support", + "//breakpad:client", + "//testing/gmock", + "//testing/gtest", + ] +} diff --git a/chromium/chromecast/crash/DEPS b/chromium/chromecast/crash/DEPS index 4389c51b6d2..9ebc4972913 100644 --- a/chromium/chromecast/crash/DEPS +++ b/chromium/chromecast/crash/DEPS @@ -1,4 +1,3 @@ include_rules = [ "+breakpad", - "+components/crash", ] diff --git a/chromium/chromecast/crash/android/DEPS b/chromium/chromecast/crash/android/DEPS deleted file mode 100644 index 2443d792c84..00000000000 --- a/chromium/chromecast/crash/android/DEPS +++ /dev/null @@ -1,4 +0,0 @@ -include_rules = [ - "+chromecast/android", - "+chromecast/common", -] diff --git a/chromium/chromecast/crash/android/crash_handler.cc b/chromium/chromecast/crash/android/crash_handler.cc deleted file mode 100644 index 1c42dfa931a..00000000000 --- a/chromium/chromecast/crash/android/crash_handler.cc +++ /dev/null @@ -1,143 +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/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" -#include "chromecast/crash/android/cast_crash_reporter_client_android.h" -#include "components/crash/app/breakpad_linux.h" -#include "components/crash/app/crash_reporter_client.h" -#include "content/public/common/content_switches.h" -#include "jni/CastCrashHandler_jni.h" - -namespace { - -chromecast::CrashHandler* g_crash_handler = NULL; - -bool HandleCrash(void* /* crash_context */) { - DCHECK(g_crash_handler); - g_crash_handler->UploadCrashDumps(); - - // 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 { - -// static -void CrashHandler::Initialize(const std::string& process_type, - const base::FilePath& log_file_path) { - DCHECK(!g_crash_handler); - g_crash_handler = new CrashHandler(log_file_path); - g_crash_handler->Initialize(process_type); -} - -// static -bool CrashHandler::GetCrashDumpLocation(base::FilePath* crash_dir) { - DCHECK(g_crash_handler); - return g_crash_handler->crash_reporter_client_-> - GetCrashDumpLocation(crash_dir); -} - -// static -bool CrashHandler::RegisterCastCrashJni(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -CrashHandler::CrashHandler(const base::FilePath& log_file_path) - : log_file_path_(log_file_path), - crash_reporter_client_(new CastCrashReporterClientAndroid) { - if (!crash_reporter_client_->GetCrashDumpLocation(&crash_dump_path_)) { - LOG(ERROR) << "Could not get crash dump location"; - } - SetCrashReporterClient(crash_reporter_client_.get()); -} - -CrashHandler::~CrashHandler() { - DCHECK(g_crash_handler); - g_crash_handler = NULL; -} - -void CrashHandler::Initialize(const std::string& process_type) { - if (process_type.empty()) { - InitializeUploader(); - - // 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, &HandleCrash, NULL, NULL, true, -1)); - - breakpad::InitCrashReporter(process_type); - - return; - } - - if (process_type != switches::kZygoteProcess) { - breakpad::InitNonBrowserCrashReporterForAndroid(process_type); - } -} - -void CrashHandler::InitializeUploader() { - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::ScopedJavaLocalRef<jstring> crash_dump_path_java = - base::android::ConvertUTF8ToJavaString(env, - crash_dump_path_.value()); - Java_CastCrashHandler_initializeUploader( - env, crash_dump_path_java.obj(), UploadCrashToStaging()); -} - -bool CrashHandler::CanUploadCrashDump() { - DCHECK(crash_reporter_client_); - return crash_reporter_client_->GetCollectStatsConsent(); -} - -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/app_state_tracker.cc b/chromium/chromecast/crash/app_state_tracker.cc new file mode 100644 index 00000000000..b563ece66cd --- /dev/null +++ b/chromium/chromecast/crash/app_state_tracker.cc @@ -0,0 +1,63 @@ +// 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/app_state_tracker.h" + +#include "base/lazy_instance.h" +#include "chromecast/crash/cast_crash_keys.h" + +namespace { + +struct CurrentAppState { + std::string previous_app; + std::string current_app; + std::string last_launched_app; +}; + +base::LazyInstance<CurrentAppState> g_app_state = LAZY_INSTANCE_INITIALIZER; + +CurrentAppState* GetAppState() { + return g_app_state.Pointer(); +} + +} // namespace + +namespace chromecast { + +// static +std::string AppStateTracker::GetLastLaunchedApp() { + return GetAppState()->last_launched_app; +} + +// static +std::string AppStateTracker::GetCurrentApp() { + return GetAppState()->current_app; +} + +// static +std::string AppStateTracker::GetPreviousApp() { + return GetAppState()->previous_app; +} + +// static +void AppStateTracker::SetLastLaunchedApp(const std::string& app_id) { + GetAppState()->last_launched_app = app_id; + + // TODO(slan): Currently SetCrashKeyValue is a no-op on chromecast until + // we add call to InitCrashKeys + base::debug::SetCrashKeyValue(crash_keys::kLastApp, app_id); +} + +// static +void AppStateTracker::SetCurrentApp(const std::string& app_id) { + CurrentAppState* app_state = GetAppState(); + app_state->previous_app = app_state->current_app; + app_state->current_app = app_id; + + base::debug::SetCrashKeyValue(crash_keys::kCurrentApp, app_id); + base::debug::SetCrashKeyValue(crash_keys::kPreviousApp, + app_state->previous_app); +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/app_state_tracker.h b/chromium/chromecast/crash/app_state_tracker.h new file mode 100644 index 00000000000..3df8686c951 --- /dev/null +++ b/chromium/chromecast/crash/app_state_tracker.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_CRASH_APP_STATE_TRACKER_H_ +#define CHROMECAST_CRASH_APP_STATE_TRACKER_H_ + +#include <string> + +namespace chromecast { + +class AppStateTracker { + public: + // Record |app_id| as the last app that attempted to launch. + static void SetLastLaunchedApp(const std::string& app_id); + + // The current app becomes the previous app, |app_id| becomes the current app. + static void SetCurrentApp(const std::string& app_id); + + // Returns the id of the app that was last attempted to launch. + static std::string GetLastLaunchedApp(); + + // Returns the id of the active app. + static std::string GetCurrentApp(); + + // Returns the id of the app which was previously active. + static std::string GetPreviousApp(); +}; + +} // namespace chromecast + +#endif // CHROMECAST_CRASH_APP_STATE_TRACKER_H_ diff --git a/chromium/chromecast/crash/cast_crash_reporter_client.cc b/chromium/chromecast/crash/cast_crash_reporter_client.cc deleted file mode 100644 index 8edbfa6b7b2..00000000000 --- a/chromium/chromecast/crash/cast_crash_reporter_client.cc +++ /dev/null @@ -1,58 +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/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_simple.cc b/chromium/chromecast/crash/cast_crash_reporter_client_simple.cc deleted file mode 100644 index aef4add13c0..00000000000 --- a/chromium/chromecast/crash/cast_crash_reporter_client_simple.cc +++ /dev/null @@ -1,17 +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/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/crash/cast_crashdump_uploader.cc b/chromium/chromecast/crash/cast_crashdump_uploader.cc new file mode 100644 index 00000000000..1c15ac6a864 --- /dev/null +++ b/chromium/chromecast/crash/cast_crashdump_uploader.cc @@ -0,0 +1,123 @@ +// 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_crashdump_uploader.h" + +#include <sys/stat.h> + +#include "base/logging.h" +// TODO(slan): Find a replacement for LibcurlWrapper in Chromium to remove the +// breakpad dependency. +#include "breakpad/src/common/linux/libcurl_wrapper.h" + +namespace chromecast { +namespace { + +// Keep these in sync with "//breakpad/src/client/mac/sender/uploader.mm" +const char kProdKey[] = "prod"; +const char kVerKey[] = "ver"; +const char kGuidKey[] = "guid"; +const char kPtimeKey[] = "ptime"; +const char kCtimeKey[] = "ctime"; +const char kEmailKey[] = "email"; +const char kCommentsKey[] = "comments"; + +} // namespace + +CastCrashdumpData::CastCrashdumpData() { +} + +CastCrashdumpData::~CastCrashdumpData() { +} + +CastCrashdumpUploader::CastCrashdumpUploader(const CastCrashdumpData& data) + : CastCrashdumpUploader(data, new google_breakpad::LibcurlWrapper()) { + // This instance of libcurlwrapper will leak. +} + +CastCrashdumpUploader::CastCrashdumpUploader( + const CastCrashdumpData& data, + google_breakpad::LibcurlWrapper* http_layer) + : http_layer_(http_layer), data_(data) { + DCHECK(http_layer_); +} + +CastCrashdumpUploader::~CastCrashdumpUploader() { +} + +bool CastCrashdumpUploader::AddAttachment(const std::string& label, + const std::string& filename) { + attachments_[label] = filename; + return true; +} + +bool CastCrashdumpUploader::CheckRequiredParametersArePresent() { + return !(data_.product.empty() || data_.version.empty() || + data_.guid.empty() || data_.minidump_pathname.empty()); +} + +bool CastCrashdumpUploader::Upload(std::string* response) { + if (!http_layer_->Init()) { + LOG(ERROR) << "http layer Init failed"; + return false; + } + + if (!CheckRequiredParametersArePresent()) { + LOG(ERROR) << "Missing required parameters"; + return false; + } + + struct stat st; + if (0 != stat(data_.minidump_pathname.c_str(), &st)) { + LOG(ERROR) << data_.minidump_pathname << " does not exist."; + return false; + } + + if (!http_layer_->AddFile(data_.minidump_pathname, "upload_file_minidump")) { + LOG(ERROR) << "Failed to add file: " << data_.minidump_pathname; + return false; + } + + // Populate |parameters_|. + parameters_[kProdKey] = data_.product; + parameters_[kVerKey] = data_.version; + parameters_[kGuidKey] = data_.guid; + parameters_[kPtimeKey] = data_.ptime; + parameters_[kCtimeKey] = data_.ctime; + parameters_[kEmailKey] = data_.email; + parameters_[kCommentsKey] = data_.comments; + + // Add each attachement in |attachments_|. + for (auto iter = attachments_.begin(); iter != attachments_.end(); ++iter) { + // Search for the attachment. + if (0 != stat(iter->second.c_str(), &st)) { + LOG(ERROR) << iter->second << " could not be found"; + return false; + } + + // Add the attachment + if (!http_layer_->AddFile(iter->second, iter->first)) { + LOG(ERROR) << "Failed to add file: " << iter->second + << " with label: " << iter->first; + return false; + } + } + + LOG(INFO) << "Sending request to " << data_.crash_server; + + int http_status_code; + std::string http_header_data; + return http_layer_->SendRequest(data_.crash_server, + parameters_, + &http_status_code, + &http_header_data, + response); +} + +void CastCrashdumpUploader::SetParameter(const std::string& key, + const std::string& value) { + parameters_[key] = value; +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/cast_crashdump_uploader.h b/chromium/chromecast/crash/cast_crashdump_uploader.h new file mode 100644 index 00000000000..acdfd9ad3b3 --- /dev/null +++ b/chromium/chromecast/crash/cast_crashdump_uploader.h @@ -0,0 +1,65 @@ +// 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_CRASHDUMP_UPLOADER_H_ +#define CHROMECAST_CRASH_CAST_CRASHDUMP_UPLOADER_H_ + +#include <map> +#include <string> + +#include "base/macros.h" + +namespace google_breakpad { +class LibcurlWrapper; +} + +namespace chromecast { + +struct CastCrashdumpData { + CastCrashdumpData(); + ~CastCrashdumpData(); + + std::string product; + std::string version; + std::string guid; + std::string ptime; + std::string ctime; + std::string email; + std::string comments; + std::string minidump_pathname; + std::string crash_server; + std::string proxy_host; + std::string proxy_userpassword; +}; + +class CastCrashdumpUploader { + public: + // Does not take ownership of |http_layer|. + CastCrashdumpUploader(const CastCrashdumpData& data, + google_breakpad::LibcurlWrapper* http_layer); + CastCrashdumpUploader(const CastCrashdumpData& data); + ~CastCrashdumpUploader(); + + bool AddAttachment(const std::string& label, const std::string& filename); + void SetParameter(const std::string& key, const std::string& value); + bool Upload(std::string* response); + + private: + bool CheckRequiredParametersArePresent(); + + google_breakpad::LibcurlWrapper* http_layer_; + CastCrashdumpData data_; + + // Holds the following mapping for attachments: <label, filepath> + std::map<std::string, std::string> attachments_; + + // Holds the following mapping for HTTP request params: <key, value> + std::map<std::string, std::string> parameters_; + + DISALLOW_COPY_AND_ASSIGN(CastCrashdumpUploader); +}; + +} // namespace chromecast + +#endif // CHROMECAST_CRASH_CAST_CRASHDUMP_UPLOADER_H_ diff --git a/chromium/chromecast/crash/cast_crashdump_uploader_unittest.cc b/chromium/chromecast/crash/cast_crashdump_uploader_unittest.cc new file mode 100644 index 00000000000..f88cce8488e --- /dev/null +++ b/chromium/chromecast/crash/cast_crashdump_uploader_unittest.cc @@ -0,0 +1,190 @@ +// 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 <string> + +#include "base/files/file_util.h" +#include "breakpad/src/common/linux/libcurl_wrapper.h" +#include "chromecast/crash/cast_crashdump_uploader.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { + +class MockLibcurlWrapper : public google_breakpad::LibcurlWrapper { + public: + MOCK_METHOD0(Init, bool()); + MOCK_METHOD2(SetProxy, + bool(const std::string& proxy_host, + const std::string& proxy_userpwd)); + MOCK_METHOD2(AddFile, + bool(const std::string& upload_file_path, + const std::string& basename)); + MOCK_METHOD5(SendRequest, + bool(const std::string& url, + const std::map<std::string, std::string>& parameters, + int* http_status_code, + std::string* http_header_data, + std::string* http_response_data)); +}; + +// Declared for the scope of this file to increase readability. +using testing::_; +using testing::Return; + +TEST(CastCrashdumpUploaderTest, UploadFailsWhenInitFails) { + testing::StrictMock<MockLibcurlWrapper> m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(false)); + + CastCrashdumpData data; + data.product = "foobar"; + data.version = "1.0"; + data.guid = "AAA-BBB"; + data.email = "test@test.com"; + data.comments = "none"; + data.minidump_pathname = "/tmp/foo.dmp"; + data.crash_server = "http://foo.com"; + CastCrashdumpUploader uploader(data, &m); + + ASSERT_FALSE(uploader.Upload(nullptr)); +} + +TEST(CastCrashdumpUploaderTest, UploadSucceedsWithValidParameters) { + testing::StrictMock<MockLibcurlWrapper> m; + + // Create a temporary file. + base::FilePath temp; + ASSERT_TRUE(base::CreateTemporaryFile(&temp)); + + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, AddFile(temp.value(), _)).WillOnce(Return(true)); + EXPECT_CALL(m, SendRequest("http://foo.com", _, _, _, _)).Times(1).WillOnce( + Return(true)); + + CastCrashdumpData data; + data.product = "foobar"; + data.version = "1.0"; + data.guid = "AAA-BBB"; + data.email = "test@test.com"; + data.comments = "none"; + data.minidump_pathname = temp.value(); + data.crash_server = "http://foo.com"; + CastCrashdumpUploader uploader(data, &m); + + ASSERT_TRUE(uploader.Upload(nullptr)); +} + +TEST(CastCrashdumpUploaderTest, UploadFailsWithInvalidPathname) { + testing::StrictMock<MockLibcurlWrapper> m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, SendRequest(_, _, _, _, _)).Times(0); + + CastCrashdumpData data; + data.product = "foobar"; + data.version = "1.0"; + data.guid = "AAA-BBB"; + data.email = "test@test.com"; + data.comments = "none"; + data.minidump_pathname = "/invalid/file/path"; + data.crash_server = "http://foo.com"; + CastCrashdumpUploader uploader(data, &m); + + ASSERT_FALSE(uploader.Upload(nullptr)); +} + +TEST(CastCrashdumpUploaderTest, UploadFailsWithoutAllRequiredParameters) { + testing::StrictMock<MockLibcurlWrapper> m; + + // Create a temporary file. + base::FilePath temp; + ASSERT_TRUE(base::CreateTemporaryFile(&temp)); + + // Has all the require fields for a crashdump. + CastCrashdumpData data; + data.product = "foobar"; + data.version = "1.0"; + data.guid = "AAA-BBB"; + data.email = "test@test.com"; + data.comments = "none"; + data.minidump_pathname = temp.value(); + data.crash_server = "http://foo.com"; + + // Test with empty product name. + data.product = ""; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + CastCrashdumpUploader uploader_no_product(data, &m); + ASSERT_FALSE(uploader_no_product.Upload(nullptr)); + data.product = "foobar"; + + // Test with empty product version. + data.version = ""; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + CastCrashdumpUploader uploader_no_version(data, &m); + ASSERT_FALSE(uploader_no_version.Upload(nullptr)); + data.version = "1.0"; + + // Test with empty client GUID. + data.guid = ""; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + CastCrashdumpUploader uploader_no_guid(data, &m); + ASSERT_FALSE(uploader_no_guid.Upload(nullptr)); +} + +TEST(CastCrashdumpUploaderTest, UploadFailsWithInvalidAttachment) { + testing::StrictMock<MockLibcurlWrapper> m; + + // Create a temporary file. + base::FilePath minidump; + ASSERT_TRUE(base::CreateTemporaryFile(&minidump)); + + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, AddFile(minidump.value(), _)).WillOnce(Return(true)); + + CastCrashdumpData data; + data.product = "foobar"; + data.version = "1.0"; + data.guid = "AAA-BBB"; + data.email = "test@test.com"; + data.comments = "none"; + data.minidump_pathname = minidump.value(); + data.crash_server = "http://foo.com"; + CastCrashdumpUploader uploader(data, &m); + + // Add a file that does not exist as an attachment. + uploader.AddAttachment("label", "/path/does/not/exist"); + ASSERT_FALSE(uploader.Upload(nullptr)); +} + +TEST(CastCrashdumpUploaderTest, UploadSucceedsWithValidAttachment) { + testing::StrictMock<MockLibcurlWrapper> m; + + // Create a temporary file. + base::FilePath minidump; + ASSERT_TRUE(base::CreateTemporaryFile(&minidump)); + + // Create a valid attachment. + base::FilePath attachment; + ASSERT_TRUE(base::CreateTemporaryFile(&attachment)); + + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, AddFile(minidump.value(), _)).WillOnce(Return(true)); + EXPECT_CALL(m, AddFile(attachment.value(), _)).WillOnce(Return(true)); + EXPECT_CALL(m, SendRequest(_, _, _, _, _)).Times(1).WillOnce(Return(true)); + + CastCrashdumpData data; + data.product = "foobar"; + data.version = "1.0"; + data.guid = "AAA-BBB"; + data.email = "test@test.com"; + data.comments = "none"; + data.minidump_pathname = minidump.value(); + data.crash_server = "http://foo.com"; + CastCrashdumpUploader uploader(data, &m); + + // Add a file that does not exist as an attachment. + uploader.AddAttachment("label", attachment.value()); + ASSERT_TRUE(uploader.Upload(nullptr)); +} + +} // namespace chromeceast diff --git a/chromium/chromecast/crash/linux/crash_util.cc b/chromium/chromecast/crash/linux/crash_util.cc new file mode 100644 index 00000000000..e80b179c16e --- /dev/null +++ b/chromium/chromecast/crash/linux/crash_util.cc @@ -0,0 +1,91 @@ +// 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/linux/crash_util.h" + +#include <stdlib.h> + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/time/time.h" +#include "chromecast/base/path_utils.h" +#include "chromecast/base/version.h" +#include "chromecast/crash/app_state_tracker.h" +#include "chromecast/crash/linux/dummy_minidump_generator.h" +#include "chromecast/crash/linux/minidump_writer.h" + +namespace chromecast { + +namespace { + +// This can be set to a callback for testing. This allows us to inject a fake +// dumpstate routine to avoid calling an executable during an automated test. +// This value should not be mutated through any other function except +// CrashUtil::SetDumpStateCbForTest(). +static base::Callback<int(const std::string&)>* g_dumpstate_cb = nullptr; + +} // namespace + +// static +uint64_t CrashUtil::GetCurrentTimeMs() { + return (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds(); +} + +// static +bool CrashUtil::RequestUploadCrashDump( + const std::string& existing_minidump_path, + const std::string& crashed_process_name, + uint64_t crashed_process_start_time_ms) { + LOG(INFO) << "Request to upload crash dump " << existing_minidump_path + << " for process " << crashed_process_name; + + // Note: Do not use Chromium IO methods in this function. When cast_shell + // crashes, this function can be called by any thread, which may not allow IO. + // Use stdlib system calls for IO instead. + uint64_t uptime_ms = GetCurrentTimeMs() - crashed_process_start_time_ms; + MinidumpParams params(crashed_process_name, + uptime_ms, + "", // suffix + AppStateTracker::GetPreviousApp(), + AppStateTracker::GetCurrentApp(), + AppStateTracker::GetLastLaunchedApp(), + CAST_BUILD_RELEASE, + CAST_BUILD_INCREMENTAL); + DummyMinidumpGenerator minidump_generator(existing_minidump_path); + + base::FilePath filename = base::FilePath(existing_minidump_path).BaseName(); + + scoped_ptr<MinidumpWriter> writer; + if (g_dumpstate_cb) { + writer.reset(new MinidumpWriter( + &minidump_generator, filename.value(), params, *g_dumpstate_cb)); + } else { + writer.reset( + new MinidumpWriter(&minidump_generator, filename.value(), params)); + } + bool success = false; + writer->set_non_blocking(false); + success = (0 == writer->Write()); // error already logged. + + // In case the file is still in $TEMP, remove it. + if (remove(existing_minidump_path.c_str()) < 0 && errno != ENOENT) { + LOG(ERROR) << "Unable to delete temp minidump file " + << existing_minidump_path << ": " << strerror(errno); + success = false; + } + + // Use std::endl to flush the log stream in case this process exits. + LOG(INFO) << "Request to upload crash dump finished. " + << "Exit now if it is main process that crashed." << std::endl; + + return success; +} + +void CrashUtil::SetDumpStateCbForTest( + const base::Callback<int(const std::string&)>& cb) { + DCHECK(!g_dumpstate_cb); + g_dumpstate_cb = new base::Callback<int(const std::string&)>(cb); +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/linux/crash_util.h b/chromium/chromecast/crash/linux/crash_util.h new file mode 100644 index 00000000000..04de5651676 --- /dev/null +++ b/chromium/chromecast/crash/linux/crash_util.h @@ -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. + +#ifndef CHROMECAST_CRASH_LINUX_CRASH_UTIL_H_ +#define CHROMECAST_CRASH_LINUX_CRASH_UTIL_H_ + +#include <stdint.h> + +#include <string> + +#include "base/callback.h" + +namespace chromecast { + +class CrashUtil { + public: + // Helper function to request upload an existing minidump file. Returns true + // on success, false otherwise. + static bool RequestUploadCrashDump(const std::string& existing_minidump_path, + const std::string& crashed_process_name, + uint64_t crashed_process_start_time_ms); + + // Util function to get current time in ms. This is used to record + // crashed_process_start_time_ms in client side. + static uint64_t GetCurrentTimeMs(); + + // Call this to set a callback to be used instead of invoking an executable + // in a seperate process. See MinidumpWriter::SetDumpStateCbForTest() for more + // details on this callback's signature. + static void SetDumpStateCbForTest( + const base::Callback<int(const std::string&)>& cb); +}; + +} // namespace chromecast + +#endif // CHROMECAST_CRASH_LINUX_CRASH_UTIL_H_ diff --git a/chromium/chromecast/crash/linux/dummy_minidump_generator.cc b/chromium/chromecast/crash/linux/dummy_minidump_generator.cc new file mode 100644 index 00000000000..019febfc497 --- /dev/null +++ b/chromium/chromecast/crash/linux/dummy_minidump_generator.cc @@ -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. + +#include "chromecast/crash/linux/dummy_minidump_generator.h" + +#include <stdio.h> +#include <sys/stat.h> + +#include <vector> + +#include "base/logging.h" + +namespace chromecast { + +namespace { + +const int kBufferSize = 32768; + +} // namespace + +DummyMinidumpGenerator::DummyMinidumpGenerator( + const std::string& existing_minidump_path) + : existing_minidump_path_(existing_minidump_path) { +} + +bool DummyMinidumpGenerator::Generate(const std::string& minidump_path) { + // Use stdlib calls here to avoid potential IO restrictions on this thread. + + // Return false if the file does not exist. + struct stat st; + if (stat(existing_minidump_path_.c_str(), &st) != 0) { + PLOG(ERROR) << existing_minidump_path_.c_str() << " does not exist: "; + return false; + } + + LOG(INFO) << "Moving minidump from " << existing_minidump_path_ << " to " + << minidump_path << " for further uploading."; + + // Attempt to rename(). If this operation fails, the files are on different + // volumes. Fall back to a copy and delete. + if (rename(existing_minidump_path_.c_str(), minidump_path.c_str()) < 0) { + // Any errors will be logged within CopyAndDelete(). + return CopyAndDelete(minidump_path); + } + + return true; +} + +bool DummyMinidumpGenerator::CopyAndDelete(const std::string& dest_path) { + FILE* src = fopen(existing_minidump_path_.c_str(), "r"); + if (!src) { + PLOG(ERROR) << existing_minidump_path_ << " failed to open: "; + return false; + } + + FILE* dest = fopen(dest_path.c_str(), "w"); + if (!dest) { + PLOG(ERROR) << dest_path << " failed to open: "; + return false; + } + + // Copy all bytes from |src| into |dest|. + std::vector<char> buffer(kBufferSize); + bool success = false; + while (!success) { + size_t bytes_read = fread(&buffer[0], 1, buffer.size(), src); + if (bytes_read < buffer.size()) { + if (feof(src)) { + success = true; + } else { + // An error occurred. + PLOG(ERROR) << "Error reading " << existing_minidump_path_ << ": "; + break; + } + } + + size_t bytes_written = fwrite(&buffer[0], 1, bytes_read, dest); + if (bytes_written < bytes_read) { + // An error occurred. + PLOG(ERROR) << "Error writing to " << dest_path << ": "; + success = false; + break; + } + } + + // Close both files. + fclose(src); + fclose(dest); + + // Attempt to delete file at |existing_minidump_path_|. We should log this + // error, but the function should not fail if the file is not removed. + if (remove(existing_minidump_path_.c_str()) < 0) + PLOG(ERROR) << "Could not remove " << existing_minidump_path_ << ": "; + + return success; +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/linux/dummy_minidump_generator.h b/chromium/chromecast/crash/linux/dummy_minidump_generator.h new file mode 100644 index 00000000000..eb031250035 --- /dev/null +++ b/chromium/chromecast/crash/linux/dummy_minidump_generator.h @@ -0,0 +1,47 @@ +// 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_LINUX_DUMMY_MINIDUMP_GENERATOR_H_ +#define CHROMECAST_CRASH_LINUX_DUMMY_MINIDUMP_GENERATOR_H_ + +#include <string> + +#include "base/macros.h" +#include "chromecast/crash/linux/minidump_generator.h" + +namespace chromecast { + +class DummyMinidumpGenerator : public MinidumpGenerator { + public: + // A dummy minidump generator to move an existing minidump into + // crash_uploader's monitoring path ($HOME/minidumps). The path is monitored + // with file lock-control, so that third process should not write to it + // directly. + explicit DummyMinidumpGenerator(const std::string& existing_minidump_path); + + // MinidumpGenerator implementation: + bool Generate(const std::string& minidump_path) override; + + // Provide access to internal utility for testing. + bool CopyAndDeleteForTest(const std::string& dest_path) { + return CopyAndDelete(dest_path); + } + + private: + // Copies the contents of the file at |existing_minidump_path_| to the file at + // |dest_path|. If the copy operation succeeds, delete the file at + // |existing_minidump_path_|. Returns false if |existing_minidump_path_| can't + // be opened, or if the copy operation fails. Ideally, we would use Chrome + // utilities for a task like this, but we must ensure that this operation can + // occur on any thread (regardless of IO restrictions). + bool CopyAndDelete(const std::string& dest_path); + + const std::string existing_minidump_path_; + + DISALLOW_COPY_AND_ASSIGN(DummyMinidumpGenerator); +}; + +} // namespace chromecast + +#endif // CHROMECAST_CRASH_LINUX_DUMMY_MINIDUMP_GENERATOR_H_ diff --git a/chromium/chromecast/crash/linux/dummy_minidump_generator_unittest.cc b/chromium/chromecast/crash/linux/dummy_minidump_generator_unittest.cc new file mode 100644 index 00000000000..c38304f270b --- /dev/null +++ b/chromium/chromecast/crash/linux/dummy_minidump_generator_unittest.cc @@ -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. + +#include <string> + +#include "base/files/file_util.h" +#include "base/rand_util.h" +#include "chromecast/crash/linux/dummy_minidump_generator.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { + +namespace { +// This value should stay in sync with the internal buffer size used in +// CopyAndDelete(). +const int kInternalBufferSize = 32768; +} // namespace + +TEST(DummyMinidumpGeneratorTest, GenerateFailsWithInvalidPath) { + // Create directory in which to put minidump. + base::FilePath minidump_dir; + ASSERT_TRUE(base::CreateNewTempDirectory("", &minidump_dir)); + + // Attempt to generate a minidump from an invalid path. + DummyMinidumpGenerator generator("/path/does/not/exist/minidump.dmp"); + ASSERT_FALSE(generator.Generate(minidump_dir.Append("minidump.dmp").value())); +} + +TEST(DummyMinidumpGeneratorTest, GenerateSucceedsWithValidPath) { + // Create directory in which to put minidump. + base::FilePath minidump_dir; + ASSERT_TRUE(base::CreateNewTempDirectory("", &minidump_dir)); + + // Create a fake minidump file. + base::FilePath fake_minidump; + ASSERT_TRUE(base::CreateTemporaryFile(&fake_minidump)); + const std::string data("Test contents of the minidump file.\n"); + ASSERT_EQ(static_cast<int>(data.size()), + base::WriteFile(fake_minidump, data.c_str(), data.size())); + + DummyMinidumpGenerator generator(fake_minidump.value()); + base::FilePath new_minidump = minidump_dir.Append("minidump.dmp"); + EXPECT_TRUE(generator.Generate(new_minidump.value())); + + // Original file should not exist, and new file should contain original + // contents. + std::string copied_data; + EXPECT_FALSE(base::PathExists(fake_minidump)); + ASSERT_TRUE(base::PathExists(new_minidump)); + EXPECT_TRUE(base::ReadFileToString(new_minidump, &copied_data)); + EXPECT_EQ(data, copied_data); +} + +TEST(DummyMinidumpGeneratorTest, CopyAndDeleteFailsWithInvalidSource) { + // Create directory in which to put minidump. + base::FilePath minidump_dir; + ASSERT_TRUE(base::CreateNewTempDirectory("", &minidump_dir)); + + // Attempt to copy from an invalid path. + DummyMinidumpGenerator generator("/path/does/not/exist/minidump.dmp"); + ASSERT_FALSE(generator.CopyAndDeleteForTest( + minidump_dir.Append("minidump.dmp").value())); +} + +TEST(DummyMinidumpGeneratorTest, CopyAndDeleteSucceedsWithSmallSource) { + // Create directory in which to put minidump. + base::FilePath minidump_dir; + ASSERT_TRUE(base::CreateNewTempDirectory("", &minidump_dir)); + + // Create a fake minidump file. + base::FilePath fake_minidump; + ASSERT_TRUE(base::CreateTemporaryFile(&fake_minidump)); + const std::string data("Test contents of the minidump file.\n"); + ASSERT_EQ(static_cast<int>(data.size()), + base::WriteFile(fake_minidump, data.c_str(), data.size())); + + base::FilePath new_minidump = minidump_dir.Append("minidump.dmp"); + DummyMinidumpGenerator generator(fake_minidump.value()); + ASSERT_TRUE(generator.CopyAndDeleteForTest(new_minidump.value())); + + // Original file should not exist, and new file should contain original + // contents. + std::string copied_data; + EXPECT_FALSE(base::PathExists(fake_minidump)); + ASSERT_TRUE(base::PathExists(new_minidump)); + EXPECT_TRUE(base::ReadFileToString(new_minidump, &copied_data)); + EXPECT_EQ(data, copied_data); +} + +TEST(DummyMinidumpGeneratorTest, CopyAndDeleteSucceedsWithLargeSource) { + // Create directory in which to put minidump. + base::FilePath minidump_dir; + ASSERT_TRUE(base::CreateNewTempDirectory("", &minidump_dir)); + + // Create a large fake minidump file. + // Note: The file must be greater than the size of the buffer used to copy the + // file in CopyAndDelete(). Create a big string in memory. + base::FilePath fake_minidump; + ASSERT_TRUE(base::CreateTemporaryFile(&fake_minidump)); + size_t str_len = kInternalBufferSize * 10 + 1; + const std::string data = base::RandBytesAsString(str_len); + + // Write the string to the file and verify that the file is big enough. + ASSERT_EQ(static_cast<int>(data.size()), + base::WriteFile(fake_minidump, data.c_str(), data.size())); + int64_t filesize = 0; + ASSERT_TRUE(base::GetFileSize(fake_minidump, &filesize)); + ASSERT_GT(filesize, kInternalBufferSize); + + base::FilePath new_minidump = minidump_dir.Append("minidump.dmp"); + DummyMinidumpGenerator generator(fake_minidump.value()); + ASSERT_TRUE(generator.CopyAndDeleteForTest(new_minidump.value())); + + // Original file should not exist, and new file should contain original + // contents. + std::string copied_data; + EXPECT_FALSE(base::PathExists(fake_minidump)); + ASSERT_TRUE(base::PathExists(new_minidump)); + EXPECT_TRUE(base::ReadFileToString(new_minidump, &copied_data)); + EXPECT_EQ(data, copied_data); +} + +TEST(DummyMinidumpGeneratorTest, CopyAndDeleteSucceedsWhenEOFAlignsWithBuffer) { + // Create directory in which to put minidump. + base::FilePath minidump_dir; + ASSERT_TRUE(base::CreateNewTempDirectory("", &minidump_dir)); + + // Create a large fake minidump file. + // Note: The file must be greater than the size of the buffer used to copy the + // file in CopyAndDelete(). Create a big string in memory. + base::FilePath fake_minidump; + ASSERT_TRUE(base::CreateTemporaryFile(&fake_minidump)); + size_t str_len = kInternalBufferSize; + const std::string data = base::RandBytesAsString(str_len); + + // Write the string to the file and verify that the file is big enough. + ASSERT_EQ(static_cast<int>(data.size()), + base::WriteFile(fake_minidump, data.c_str(), data.size())); + int64_t filesize = 0; + ASSERT_TRUE(base::GetFileSize(fake_minidump, &filesize)); + ASSERT_EQ(kInternalBufferSize, filesize); + + base::FilePath new_minidump = minidump_dir.Append("minidump.dmp"); + DummyMinidumpGenerator generator(fake_minidump.value()); + ASSERT_TRUE(generator.CopyAndDeleteForTest(new_minidump.value())); + + // Original file should not exist, and new file should contain original + // contents. + std::string copied_data; + EXPECT_FALSE(base::PathExists(fake_minidump)); + ASSERT_TRUE(base::PathExists(new_minidump)); + EXPECT_TRUE(base::ReadFileToString(new_minidump, &copied_data)); + EXPECT_EQ(data, copied_data); +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/linux/dump_info.cc b/chromium/chromecast/crash/linux/dump_info.cc new file mode 100644 index 00000000000..c98ddf948dd --- /dev/null +++ b/chromium/chromecast/crash/linux/dump_info.cc @@ -0,0 +1,138 @@ +// 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/linux/dump_info.h" + +#include <stdlib.h> + +#include <sstream> + +#include "base/logging.h" +#include "base/strings/string_split.h" + +namespace chromecast { + +namespace { + +const char kDumpTimeFormat[] = "%Y-%m-%d %H:%M:%S"; +const unsigned kDumpTimeMaxLen = 255; + +const int kNumRequiredParams = 5; +} // namespace + +DumpInfo::DumpInfo(const std::string& entry) : valid_(false) { + // TODO(slan): This ctor is doing non-trivial work. Change this. + if ((valid_ = ParseEntry(entry)) == true) { + entry_ = GetEntryAsString(); + } +} + +DumpInfo::DumpInfo(const std::string& crashed_process_dump, + const std::string& logfile, + const time_t& dump_time, + const MinidumpParams& params) + : crashed_process_dump_(crashed_process_dump), + logfile_(logfile), + dump_time_(dump_time), + params_(params), + valid_(false) { + // The format is + // <name>|<dump time>|<dump>|<uptime>|<logfile>|<suffix>|<prev_app_name>| + // <curent_app name>|<last_app_name> + // <dump time> is in the format of kDumpTimeFormat + + // Validate the time passed in. + struct tm* tm = gmtime(&dump_time); + char buf[kDumpTimeMaxLen]; + int n = strftime(buf, kDumpTimeMaxLen, kDumpTimeFormat, tm); + if (n <= 0) { + LOG(INFO) << "strftime failed"; + return; + } + entry_ = GetEntryAsString(); + valid_ = true; +} + +DumpInfo::~DumpInfo() { +} + +std::string DumpInfo::GetEntryAsString() { + struct tm* tm = gmtime(&dump_time_); + char buf[kDumpTimeMaxLen]; + int n = strftime(buf, kDumpTimeMaxLen, kDumpTimeFormat, tm); + DCHECK_GT(n, 0); + + std::stringstream entrystream; + entrystream << params_.process_name << "|" << buf << "|" + << crashed_process_dump_ << "|" << params_.process_uptime << "|" + << logfile_ << "|" << params_.suffix << "|" + << params_.previous_app_name << "|" << params_.current_app_name + << "|" << params_.last_app_name << "|" + << params_.cast_release_version << "|" + << params_.cast_build_number << std::endl; + return entrystream.str(); +} + +bool DumpInfo::ParseEntry(const std::string& entry) { + // The format is + // <name>|<dump time>|<dump>|<uptime>|<logfile>{|<suffix>{|<prev_app_name>{ + // |<current_app name>{|last_launched_app_name}}}} + // <dump time> is in the format |kDumpTimeFormat| + std::vector<std::string> fields; + base::SplitString(entry, '|', &fields); + if (fields.size() < kNumRequiredParams) { + LOG(INFO) << "Invalid entry: Too few fields."; + return false; + } + + // Extract required fields. + params_.process_name = fields[0]; + if (!SetDumpTimeFromString(fields[1])) + return false; + crashed_process_dump_ = fields[2]; + params_.process_uptime = atoll(fields[3].c_str()); + logfile_ = fields[4]; + + // Extract all other optional fields. + for (size_t i = 5; i < fields.size(); ++i) { + const std::string& temp = fields[i]; + switch (i) { + case 5: // Optional field: suffix + params_.suffix = temp; + break; + case 6: // Optional field: prev_app_name + params_.previous_app_name = temp; + break; + case 7: // Optional field: current_app_name + params_.current_app_name = temp; + break; + case 8: // Optional field: last_launched_app_name + params_.last_app_name = temp; + break; + case 9: // extract an optional cast release version + params_.cast_release_version = temp; + break; + case 10: // extract an optional cast build number + params_.cast_build_number = temp; + break; + default: + LOG(INFO) << "Entry has too many fields invalid"; + return false; + } + } + valid_ = true; + return true; +} + +bool DumpInfo::SetDumpTimeFromString(const std::string& timestr) { + struct tm tm = {0}; + char* text = strptime(timestr.c_str(), kDumpTimeFormat, &tm); + dump_time_ = mktime(&tm); + if (!text || dump_time_ < 0) { + LOG(INFO) << "Failed to convert dump time invalid"; + return false; + } + return true; +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/linux/dump_info.h b/chromium/chromecast/crash/linux/dump_info.h new file mode 100644 index 00000000000..7425f5d982d --- /dev/null +++ b/chromium/chromecast/crash/linux/dump_info.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_CRASH_LINUX_DUMP_INFO_H_ +#define CHROMECAST_CRASH_LINUX_DUMP_INFO_H_ + +#include <string> + +#include "base/macros.h" +#include "chromecast/crash/linux/minidump_params.h" + +namespace chromecast { + +// Class that encapsulates the construction and parsing of dump entries +// in the log file. +class DumpInfo { + public: + // Attempt to construct a DumpInfo object by parsing the given entry string + // and extracting the contained information and populate the relevant + // fields. + explicit DumpInfo(const std::string& entry); + + // Attempt to construct a DumpInfo object that has the following info: + // + // -crashed_process_dump: the full path of the dump written + // -crashed_process_logfile: the full path of the logfile written + // -dump_time: the time of the dump written + // -params: a structure containing other useful crash information + // + // As a result of construction, the |entry_| will be filled with the + // appropriate string to add to the log file. + DumpInfo(const std::string& crashed_process_dump, + const std::string& crashed_process_logfile, + const time_t& dump_time, + const MinidumpParams& params); + + ~DumpInfo(); + + const std::string& crashed_process_dump() const { + return crashed_process_dump_; + } + const std::string& logfile() const { return logfile_; } + const time_t& dump_time() const { return dump_time_; } + const std::string& entry() const { return entry_; } + const MinidumpParams& params() const { return params_; } + const bool valid() const { return valid_; } + + private: + bool ParseEntry(const std::string& entry); + bool SetDumpTimeFromString(const std::string& timestr); + std::string GetEntryAsString(); + + std::string crashed_process_dump_; + std::string logfile_; + time_t dump_time_; + std::string entry_; + MinidumpParams params_; + bool valid_; + + DISALLOW_COPY_AND_ASSIGN(DumpInfo); +}; + +} // namespace chromecast + +#endif // CHROMECAST_CRASH_LINUX_DUMP_INFO_H_ diff --git a/chromium/chromecast/crash/linux/dump_info_unittest.cc b/chromium/chromecast/crash/linux/dump_info_unittest.cc new file mode 100644 index 00000000000..e99daa47831 --- /dev/null +++ b/chromium/chromecast/crash/linux/dump_info_unittest.cc @@ -0,0 +1,131 @@ +// 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 <time.h> + +#include "chromecast/crash/linux/dump_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { + +TEST(DumpInfoTest, EmptyStringIsNotValid) { + DumpInfo dump_info(""); + ASSERT_FALSE(dump_info.valid()); +} + +TEST(DumpInfoTest, TooFewFieldsIsNotValid) { + DumpInfo dump_info("name|2001-11-12 18:31:01|dump_string"); + ASSERT_FALSE(dump_info.valid()); +} + +TEST(DumpInfoTest, BadTimeStringIsNotValid) { + DumpInfo info("name|Mar 23 2014 01:23:45|dump_string|123456789|logfile.log"); + ASSERT_FALSE(info.valid()); +} + +TEST(DumpInfoTest, AllRequiredFieldsIsValid) { + DumpInfo info("name|2001-11-12 18:31:01|dump_string|123456789|logfile.log"); + struct tm tm = {0}; + tm.tm_isdst = 0; + tm.tm_sec = 1; + tm.tm_min = 31; + tm.tm_hour = 18; + tm.tm_mday = 12; + tm.tm_mon = 10; + tm.tm_year = 101; + time_t dump_time = mktime(&tm); + + ASSERT_TRUE(info.valid()); + ASSERT_EQ("name", info.params().process_name); + ASSERT_EQ(dump_time, info.dump_time()); + ASSERT_EQ("dump_string", info.crashed_process_dump()); + ASSERT_EQ(123456789u, info.params().process_uptime); + ASSERT_EQ("logfile.log", info.logfile()); +} + +TEST(DumpInfoTest, EmptyProcessNameIsValid) { + DumpInfo dump_info("|2001-11-12 18:31:01|dump_string|123456789|logfile.log"); + ASSERT_TRUE(dump_info.valid()); +} + +TEST(DumpInfoTest, SomeRequiredFieldsEmptyIsValid) { + DumpInfo info("name|2001-11-12 18:31:01|||"); + struct tm tm = {0}; + tm.tm_isdst = 0; + tm.tm_sec = 1; + tm.tm_min = 31; + tm.tm_hour = 18; + tm.tm_mday = 12; + tm.tm_mon = 10; + tm.tm_year = 101; + time_t dump_time = mktime(&tm); + + ASSERT_TRUE(info.valid()); + ASSERT_EQ("name", info.params().process_name); + ASSERT_EQ(dump_time, info.dump_time()); + ASSERT_EQ("", info.crashed_process_dump()); + ASSERT_EQ(0u, info.params().process_uptime); + ASSERT_EQ("", info.logfile()); +} + +TEST(DumpInfoTest, AllOptionalFieldsIsValid) { + DumpInfo info( + "name|2001-11-12 18:31:01|dump_string|123456789|logfile.log|" + "suffix|previous_app|current_app|last_app|RELEASE|BUILD_NUMBER"); + struct tm tm = {0}; + tm.tm_isdst = 0; + tm.tm_sec = 1; + tm.tm_min = 31; + tm.tm_hour = 18; + tm.tm_mday = 12; + tm.tm_mon = 10; + tm.tm_year = 101; + time_t dump_time = mktime(&tm); + + ASSERT_TRUE(info.valid()); + ASSERT_EQ("name", info.params().process_name); + ASSERT_EQ(dump_time, info.dump_time()); + ASSERT_EQ("dump_string", info.crashed_process_dump()); + ASSERT_EQ(123456789u, info.params().process_uptime); + ASSERT_EQ("logfile.log", info.logfile()); + + ASSERT_EQ("suffix", info.params().suffix); + ASSERT_EQ("previous_app", info.params().previous_app_name); + ASSERT_EQ("current_app", info.params().current_app_name); + ASSERT_EQ("last_app", info.params().last_app_name); +} + +TEST(DumpInfoTest, SomeOptionalFieldsIsValid) { + DumpInfo info( + "name|2001-11-12 18:31:01|dump_string|123456789|logfile.log|" + "suffix|previous_app"); + struct tm tm = {0}; + tm.tm_isdst = 0; + tm.tm_sec = 1; + tm.tm_min = 31; + tm.tm_hour = 18; + tm.tm_mday = 12; + tm.tm_mon = 10; + tm.tm_year = 101; + time_t dump_time = mktime(&tm); + + ASSERT_TRUE(info.valid()); + ASSERT_EQ("name", info.params().process_name); + ASSERT_EQ(dump_time, info.dump_time()); + ASSERT_EQ("dump_string", info.crashed_process_dump()); + ASSERT_EQ(123456789u, info.params().process_uptime); + ASSERT_EQ("logfile.log", info.logfile()); + + ASSERT_EQ("suffix", info.params().suffix); + ASSERT_EQ("previous_app", info.params().previous_app_name); +} + +TEST(DumpInfoTest, TooManyFieldsIsNotValid) { + DumpInfo info( + "name|2001-11-12 18:31:01|dump_string|123456789|logfile.log|" + "suffix|previous_app|current_app|last_app|VERSION|BUILD_NUM|extra_field"); + ASSERT_FALSE(info.valid()); +} + +} // chromecast
\ No newline at end of file diff --git a/chromium/chromecast/crash/linux/minidump_generator.h b/chromium/chromecast/crash/linux/minidump_generator.h new file mode 100644 index 00000000000..9780e430ec7 --- /dev/null +++ b/chromium/chromecast/crash/linux/minidump_generator.h @@ -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. + +#ifndef CHROMECAST_CRASH_LINUX_MINIDUMP_GENERATOR_H_ +#define CHROMECAST_CRASH_LINUX_MINIDUMP_GENERATOR_H_ + +#include <string> + +namespace chromecast { + +class MinidumpGenerator { + public: + virtual ~MinidumpGenerator() {} + + // Interface to generate a minidump file in given path. + // This is called inside MinidumpWriter::DoWorkLocked(). + virtual bool Generate(const std::string& minidump_path) = 0; +}; + +} // namespace chromecast + +#endif // CHROMECAST_CRASH_LINUX_MINIDUMP_GENERATOR_H_ diff --git a/chromium/chromecast/crash/linux/minidump_params.cc b/chromium/chromecast/crash/linux/minidump_params.cc new file mode 100644 index 00000000000..3d532830c48 --- /dev/null +++ b/chromium/chromecast/crash/linux/minidump_params.cc @@ -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. + +#include "chromecast/crash/linux/minidump_params.h" + +namespace chromecast { + +MinidumpParams::MinidumpParams(const std::string& p_process_name, + const uint64_t p_process_uptime, + const std::string& p_suffix, + const std::string& p_previous_app_name, + const std::string& p_current_app_name, + const std::string& p_last_app_name, + const std::string& p_cast_release_version, + const std::string& p_cast_build_number) + : process_name(p_process_name), + process_uptime(p_process_uptime), + suffix(p_suffix), + previous_app_name(p_previous_app_name), + current_app_name(p_current_app_name), + last_app_name(p_last_app_name), + cast_release_version(p_cast_release_version), + cast_build_number(p_cast_build_number) { +} + +MinidumpParams::MinidumpParams() : process_uptime(0) { +} + +MinidumpParams::MinidumpParams(const MinidumpParams& params) = default; + +MinidumpParams::~MinidumpParams() { +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/linux/minidump_params.h b/chromium/chromecast/crash/linux/minidump_params.h new file mode 100644 index 00000000000..1a1c15518ad --- /dev/null +++ b/chromium/chromecast/crash/linux/minidump_params.h @@ -0,0 +1,39 @@ +// 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_LINUX_MINIDUMP_PARAMS_H_ +#define CHROMECAST_CRASH_LINUX_MINIDUMP_PARAMS_H_ + +#include <string> + +namespace chromecast { + +struct MinidumpParams { + MinidumpParams(); + MinidumpParams(const std::string& p_process_name, + const uint64_t p_process_uptime, + const std::string& p_suffix, + const std::string& p_previous_app_name, + const std::string& p_current_app_name, + const std::string& p_last_app_name, + const std::string& p_cast_release_version, + const std::string& p_cast_build_number); + MinidumpParams(const MinidumpParams& params); + ~MinidumpParams(); + + std::string process_name; + uint64_t process_uptime; + std::string suffix; + std::string previous_app_name; + std::string current_app_name; + std::string last_app_name; + // Release version is in the format of "major.minor", such as "1.15". + std::string cast_release_version; + // Build number is numerical string such as "20000". + std::string cast_build_number; +}; + +} // namespace chromecast + +#endif // CHROMECAST_CRASH_LINUX_MINIDUMP_PARAMS_H_ diff --git a/chromium/chromecast/crash/linux/minidump_writer.cc b/chromium/chromecast/crash/linux/minidump_writer.cc new file mode 100644 index 00000000000..5f4016e1c4e --- /dev/null +++ b/chromium/chromecast/crash/linux/minidump_writer.cc @@ -0,0 +1,135 @@ +// 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/linux/minidump_writer.h" + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "chromecast/base/path_utils.h" +#include "chromecast/base/process_utils.h" +#include "chromecast/crash/linux/dump_info.h" +#include "chromecast/crash/linux/minidump_generator.h" + +namespace chromecast { + +namespace { + +const char kDumpStateSuffix[] = ".txt.gz"; + +const int kDefaultDumpIntervalHours = 24; +const int kDefaultMaxDumps = 5; +const int kDefaultMaxRecentDumps = 5; + +// Fork and run dumpstate, saving results to minidump_name + ".txt.gz". +int DumpState(const std::string& minidump_name) { + std::vector<std::string> argv; + argv.push_back(GetBinPathASCII("dumpstate").value()); + argv.push_back("-w"); + argv.push_back("crash-request"); + argv.push_back("-z"); + argv.push_back("-o"); + argv.push_back( + minidump_name); // dumpstate appends ".txt.gz" to the filename. + + std::string log; + if (!chromecast::GetAppOutput(argv, &log)) { + LOG(ERROR) << "failed to execute dumpstate"; + return -1; + } + return 0; +} + +} // namespace + +MinidumpWriter::MinidumpWriter(MinidumpGenerator* minidump_generator, + const std::string& minidump_filename, + const MinidumpParams& params, + const DumpStateCallback& dump_state_cb) + : minidump_generator_(minidump_generator), + minidump_path_(minidump_filename), + params_(params), + max_dumps_(kDefaultMaxDumps), + dump_interval_(base::TimeDelta::FromHours(kDefaultDumpIntervalHours)), + max_recent_dumps_(kDefaultMaxRecentDumps), + dump_state_cb_(dump_state_cb) { +} + +MinidumpWriter::MinidumpWriter(MinidumpGenerator* minidump_generator, + const std::string& minidump_filename, + const MinidumpParams& params) + : MinidumpWriter(minidump_generator, + minidump_filename, + params, + base::Bind(&DumpState)) { +} + +MinidumpWriter::~MinidumpWriter() { +} + +int MinidumpWriter::DoWork() { + // If path is not absolute, append it to |dump_path_|. + if (!minidump_path_.value().empty() && minidump_path_.value()[0] != '/') + minidump_path_ = dump_path_.Append(minidump_path_); + + // The path should be a file in the |dump_path_| directory. + if (dump_path_ != minidump_path_.DirName()) { + LOG(INFO) << "The absolute path: " << minidump_path_.value() << " is not" + << "in the correct directory: " << dump_path_.value(); + return -1; + } + + // Query if we are able to write another minidump. + if (!CanWriteDump()) { + LOG(INFO) << "Skipping writing of dump due to limits"; + return -1; + } + + // Generate a minidump at the specified |minidump_path_|. + if (!minidump_generator_->Generate(minidump_path_.value())) { + LOG(ERROR) << "Generate minidump failed " << minidump_path_.value(); + return -1; + } + + // Run the dumpstate callback. + DCHECK(!dump_state_cb_.is_null()); + if (dump_state_cb_.Run(minidump_path_.value()) < 0) { + LOG(ERROR) << "DumpState callback failed."; + return -1; + } + + // Add this entry to the lockfile. + const DumpInfo info(minidump_path_.value(), + minidump_path_.value() + kDumpStateSuffix, + time(NULL), + params_); + if (AddEntryToLockFile(info) < 0) { + LOG(ERROR) << "lockfile logging failed"; + return -1; + } + + return 0; +} + +bool MinidumpWriter::CanWriteDump() { + const auto& dumps = GetDumpMetadata(); + + // If no more dumps can be written, return false. + if (static_cast<int>(dumps.size()) >= max_dumps_) + return false; + + // If too many dumps have been written recently, return false. + time_t cur_time = time(0); + int recent_dumps = 0; + for (auto dump : dumps) { + if (difftime(cur_time, dump->dump_time()) <= dump_interval_.InSecondsF()) { + if (++recent_dumps >= max_recent_dumps_) + return false; + } + } + + return true; +} + +} // namespace crash_manager diff --git a/chromium/chromecast/crash/linux/minidump_writer.h b/chromium/chromecast/crash/linux/minidump_writer.h new file mode 100644 index 00000000000..69e6233133c --- /dev/null +++ b/chromium/chromecast/crash/linux/minidump_writer.h @@ -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. + +#ifndef CHROMECAST_CRASH_LINUX_MINIDUMP_WRITER_H_ +#define CHROMECAST_CRASH_LINUX_MINIDUMP_WRITER_H_ + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/time/time.h" +#include "chromecast/crash/linux/minidump_params.h" +#include "chromecast/crash/linux/synchronized_minidump_manager.h" + +namespace chromecast { + +class MinidumpGenerator; + +// Class for writing a minidump with synchronized access to the minidumps +// directory. +class MinidumpWriter : public SynchronizedMinidumpManager { + public: + typedef base::Callback<int(const std::string&)> DumpStateCallback; + + // Constructs a writer for a minidump. If |minidump_filename| is absolute, it + // must be a path to a file in the |dump_path_| directory. Otherwise, it + // should be a filename only, in which case, |minidump_generator| creates + // a minidump at $HOME/minidumps/|minidump_filename|. |params| describes the + // minidump metadata. |dump_state_cb| is Run() to generate a log dump. Please + // see the comments on |dump_state_cb_| below for details about this + // parameter. + // This does not take ownership of |minidump_generator|. + MinidumpWriter(MinidumpGenerator* minidump_generator, + const std::string& minidump_filename, + const MinidumpParams& params, + const base::Callback<int(const std::string&)>& dump_state_cb); + + // Like the constructor above, but the default implementation of + // |dump_state_cb_| is used inside DoWork(). + MinidumpWriter(MinidumpGenerator* minidump_generator, + const std::string& minidump_filename, + const MinidumpParams& params); + + ~MinidumpWriter() override; + + // Acquires exclusive access to the minidumps directory and generates a + // minidump and a log to be uploaded later. Returns 0 if successful, -1 + // otherwise. + int Write() { return AcquireLockAndDoWork(); } + + int max_dumps() const { return max_dumps_; } + int max_recent_dumps() const { return max_recent_dumps_; } + const base::TimeDelta& dump_interval() const { return dump_interval_; }; + + protected: + // MinidumpManager implementation: + int DoWork() override; + + private: + // Returns true if we can write another dump, false otherwise. We can write + // another dump if the number of minidumps is strictly less than |max_dumps_| + // and the number of minidumps which occurred within the last |dump_interval_| + // is strictly less than |max_recent_dumps_|. + bool CanWriteDump(); + + MinidumpGenerator* const minidump_generator_; + base::FilePath minidump_path_; + const MinidumpParams params_; + + const int max_dumps_; + const base::TimeDelta dump_interval_; + const int max_recent_dumps_; + + // This callback is Run() to dump a log to |minidump_path_|.txt.gz, taking + // |minidump_path_| as a parameter. It returns 0 on success, and a negative + // integer otherwise. If a callback is not passed in the constructor, the + // default implemementaion is used. + DumpStateCallback dump_state_cb_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpWriter); +}; + +} // namespace chromecast + +#endif // CHROMECAST_CRASH_LINUX_MINIDUMP_WRITER_H_ diff --git a/chromium/chromecast/crash/linux/minidump_writer_unittest.cc b/chromium/chromecast/crash/linux/minidump_writer_unittest.cc new file mode 100644 index 00000000000..c096f7ae60e --- /dev/null +++ b/chromium/chromecast/crash/linux/minidump_writer_unittest.cc @@ -0,0 +1,172 @@ +// 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 <fstream> + +#include "base/base_paths.h" +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/test/scoped_path_override.h" +#include "chromecast/crash/linux/minidump_generator.h" +#include "chromecast/crash/linux/minidump_writer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { +namespace { + +const char kDumplogFile[] = "dumplog"; +const char kLockfileName[] = "lockfile"; +const char kMinidumpSubdir[] = "minidumps"; + +std::string GetCurrentTimeASCII() { + char cur_time[20]; + time_t now = time(NULL); + struct tm* tm = gmtime(&now); + strftime(cur_time, 20, "%Y-%m-%d %H:%M:%S", tm); + return std::string(cur_time); +} + +class FakeMinidumpGenerator : public MinidumpGenerator { + public: + FakeMinidumpGenerator() {} + ~FakeMinidumpGenerator() override {} + + // MinidumpGenerator implementation: + bool Generate(const std::string& minidump_path) override { return true; } +}; + +int FakeDumpState(const std::string& minidump_path) { + return 0; +} + +} // namespace + +class MinidumpWriterTest : public testing::Test { + protected: + MinidumpWriterTest() {} + ~MinidumpWriterTest() override {} + + void SetUp() override { + // Set up a temporary directory which will be used as our fake home dir. + base::FilePath fake_home_dir; + ASSERT_TRUE(base::CreateNewTempDirectory("", &fake_home_dir)); + home_.reset(new base::ScopedPathOverride(base::DIR_HOME, fake_home_dir)); + minidump_dir_ = fake_home_dir.Append(kMinidumpSubdir); + dumplog_file_ = minidump_dir_.Append(kDumplogFile); + + // Create the minidump directory and lockfile. + ASSERT_TRUE(base::CreateDirectory(minidump_dir_)); + base::File lockfile( + minidump_dir_.Append(kLockfileName), + base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + ASSERT_TRUE(lockfile.IsValid()); + } + + FakeMinidumpGenerator fake_generator_; + base::FilePath minidump_dir_; + base::FilePath dumplog_file_; + + private: + scoped_ptr<base::ScopedPathOverride> home_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpWriterTest); +}; + +TEST_F(MinidumpWriterTest, Write_FailsWithIncorrectMinidumpPath) { + MinidumpWriter writer(&fake_generator_, + "/path/to/wrong/dir", + MinidumpParams(), + base::Bind(&FakeDumpState)); + + ASSERT_EQ(-1, writer.Write()); +} + +TEST_F(MinidumpWriterTest, Write_FailsWithMultiLevelRelativeMinidumpPath) { + MinidumpWriter writer(&fake_generator_, + "subdir/dumplog", + MinidumpParams(), + base::Bind(&FakeDumpState)); + + ASSERT_EQ(-1, writer.Write()); +} + +TEST_F(MinidumpWriterTest, Write_SucceedsWithSimpleFilename) { + MinidumpWriter writer(&fake_generator_, + "dumplog", + MinidumpParams(), + base::Bind(&FakeDumpState)); + + ASSERT_EQ(0, writer.Write()); +} + +TEST_F(MinidumpWriterTest, Write_SucceedsWithCorrectMinidumpPath) { + MinidumpWriter writer(&fake_generator_, + dumplog_file_.value(), + MinidumpParams(), + base::Bind(&FakeDumpState)); + + ASSERT_EQ(0, writer.Write()); +} + +TEST_F(MinidumpWriterTest, Write_FailsWithSubdirInCorrectPath) { + MinidumpWriter writer(&fake_generator_, + dumplog_file_.Append("subdir/logfile").value(), + MinidumpParams(), + base::Bind(&FakeDumpState)); + ASSERT_EQ(-1, writer.Write()); +} + +TEST_F(MinidumpWriterTest, Write_FailsWhenTooManyDumpsPresent) { + MinidumpWriter writer(&fake_generator_, + dumplog_file_.value(), + MinidumpParams(), + base::Bind(&FakeDumpState)); + + // Write dump logs to the lockfile. + std::ofstream lockfile(minidump_dir_.Append(kLockfileName).value()); + ASSERT_TRUE(lockfile.is_open()); + size_t too_many_dumps = writer.max_dumps() + 1; + for (size_t i = 0; i < too_many_dumps; ++i) { + lockfile << "p|2012-01-01 01:02:03|/dump/path||" << std::endl; + } + lockfile.close(); + + ASSERT_EQ(-1, writer.Write()); +} + +TEST_F(MinidumpWriterTest, Write_FailsWhenTooManyRecentDumpsPresent) { + MinidumpWriter writer(&fake_generator_, + dumplog_file_.value(), + MinidumpParams(), + base::Bind(&FakeDumpState)); + + // Write dump logs to the lockfile. + std::ofstream lockfile(minidump_dir_.Append(kLockfileName).value()); + ASSERT_TRUE(lockfile.is_open()); + size_t too_many_recent_dumps = writer.max_recent_dumps() + 1; + for (size_t i = 0; i < too_many_recent_dumps; ++i) { + lockfile << "|" << GetCurrentTimeASCII() << "|/dump/path||" << std::endl; + } + + ASSERT_EQ(-1, writer.Write()); +} + +TEST_F(MinidumpWriterTest, Write_SucceedsWhenDumpLimitsNotExceeded) { + MinidumpWriter writer(&fake_generator_, + dumplog_file_.value(), + MinidumpParams(), + base::Bind(&FakeDumpState)); + + ASSERT_GT(writer.max_dumps(), 1); + ASSERT_GT(writer.max_recent_dumps(), 0); + + // Write an old dump logs to the lockfile. + std::ofstream lockfile(minidump_dir_.Append(kLockfileName).value()); + ASSERT_TRUE(lockfile.is_open()); + lockfile << "p|2012-01-01 01:02:03|/dump/path||" << std::endl; +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/linux/synchronized_minidump_manager.cc b/chromium/chromecast/crash/linux/synchronized_minidump_manager.cc new file mode 100644 index 00000000000..358427a2dd3 --- /dev/null +++ b/chromium/chromecast/crash/linux/synchronized_minidump_manager.cc @@ -0,0 +1,219 @@ +// 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/linux/synchronized_minidump_manager.h" + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <string.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <fstream> + +#include "base/logging.h" +#include "chromecast/base/path_utils.h" +#include "chromecast/crash/linux/dump_info.h" + +namespace chromecast { + +namespace { + +const mode_t kDirMode = 0770; +const mode_t kFileMode = 0660; +const char kLockfileName[] = "lockfile"; +const char kMinidumpsDir[] = "minidumps"; + +} // namespace + +SynchronizedMinidumpManager::SynchronizedMinidumpManager() + : non_blocking_(false), lockfile_fd_(-1) { + dump_path_ = GetHomePathASCII(kMinidumpsDir); + lockfile_path_ = dump_path_.Append(kLockfileName).value(); +} + +SynchronizedMinidumpManager::~SynchronizedMinidumpManager() { + // Release the lock if held. + ReleaseLockFile(); +} + +// TODO(slan): Move some of this pruning logic to ReleaseLockFile? +int SynchronizedMinidumpManager::GetNumDumps(bool delete_all_dumps) { + DIR* dirp; + struct dirent* dptr; + int num_dumps = 0; + + // folder does not exist + dirp = opendir(dump_path_.value().c_str()); + if (dirp == NULL) { + LOG(ERROR) << "Unable to open directory " << dump_path_.value(); + return 0; + } + + while ((dptr = readdir(dirp)) != NULL) { + struct stat buf; + const std::string file_fullname = dump_path_.value() + "/" + dptr->d_name; + if (lstat(file_fullname.c_str(), &buf) == -1 || !S_ISREG(buf.st_mode)) { + // if we cannot lstat this file, it is probably bad, so skip + // if the file is not regular, skip + continue; + } + // 'lockfile' is not counted + if (lockfile_path_ != file_fullname) { + ++num_dumps; + if (delete_all_dumps) { + LOG(INFO) << "Removing " << dptr->d_name + << "which was not in the lockfile"; + if (remove(file_fullname.c_str()) < 0) { + LOG(INFO) << "remove failed. error " << strerror(errno); + } + } + } + } + + closedir(dirp); + return num_dumps; +} + +int SynchronizedMinidumpManager::AcquireLockAndDoWork() { + int success = -1; + if (AcquireLockFile() >= 0) { + success = DoWork(); + ReleaseLockFile(); + } + return success; +} + +const ScopedVector<DumpInfo>& SynchronizedMinidumpManager::GetDumpMetadata() { + DCHECK_GE(lockfile_fd_, 0); + if (!dump_metadata_) + ParseLockFile(); + return *dump_metadata_; +} + +int SynchronizedMinidumpManager::AcquireLockFile() { + DCHECK_LT(lockfile_fd_, 0); + // Make the directory for the minidumps if it does not exist. + if (mkdir(dump_path_.value().c_str(), kDirMode) < 0 && errno != EEXIST) { + LOG(ERROR) << "mkdir for " << dump_path_.value().c_str() + << " failed. error = " << strerror(errno); + return -1; + } + + // Open the lockfile. Create it if it does not exist. + lockfile_fd_ = open(lockfile_path_.c_str(), O_RDWR | O_CREAT, kFileMode); + + // If opening or creating the lockfile failed, we don't want to proceed + // with dump writing for fear of exhausting up system resources. + if (lockfile_fd_ < 0) { + LOG(ERROR) << "open lockfile failed " << lockfile_path_; + return -1; + } + + // Acquire the lock on the file. Whether or not we are in non-blocking mode, + // flock failure means that we did not acquire it and this method should fail. + int operation_mode = non_blocking_ ? (LOCK_EX | LOCK_NB) : LOCK_EX; + if (flock(lockfile_fd_, operation_mode) < 0) { + ReleaseLockFile(); + LOG(INFO) << "flock lockfile failed, error = " << strerror(errno); + return -1; + } + + // The lockfile is open and locked. Parse it to provide subclasses with a + // record of all the current dumps. + if (ParseLockFile() < 0) { + LOG(ERROR) << "Lockfile did not parse correctly. "; + return -1; + } + + // We successfully have acquired the lock. + return 0; +} + +int SynchronizedMinidumpManager::ParseLockFile() { + DCHECK_GE(lockfile_fd_, 0); + DCHECK(!dump_metadata_); + + scoped_ptr<ScopedVector<DumpInfo> > dumps(new ScopedVector<DumpInfo>()); + std::string entry; + + // Instead of using |lockfile_fd_|, use <fstream> for readability. + std::ifstream in(lockfile_path_); + if (!in.is_open()) { + NOTREACHED(); + LOG(ERROR) << lockfile_path_ << " could not be opened."; + return -1; + } + + // Grab each entry. + while (std::getline(in, entry)) { + scoped_ptr<DumpInfo> info(new DumpInfo(entry)); + if (info->valid() && info->crashed_process_dump().size() > 0) { + dumps->push_back(info.Pass()); + } else { + LOG(WARNING) << "Entry is not valid: " << entry; + return -1; + } + } + + dump_metadata_ = dumps.Pass(); + return 0; +} + +int SynchronizedMinidumpManager::AddEntryToLockFile(const DumpInfo& dump_info) { + DCHECK_LE(0, lockfile_fd_); + + // Make sure dump_info is valid. + if (!dump_info.valid()) { + LOG(ERROR) << "Entry to be added is invalid"; + return -1; + } + + // Open the file. + std::ofstream out(lockfile_path_, std::ios::app); + if (!out.is_open()) { + NOTREACHED() << "Lockfile would not open."; + return -1; + } + + // Write the string and close the file. + out << dump_info.entry(); + out.close(); + return 0; +} + +int SynchronizedMinidumpManager::RemoveEntryFromLockFile(int index) { + const auto& entries = GetDumpMetadata(); + if (index < 0 || static_cast<size_t>(index) >= entries.size()) + return -1; + + // Remove the entry and write all remaining entries to file. + dump_metadata_->erase(dump_metadata_->begin() + index); + std::ofstream out(lockfile_path_); + for (auto info : *dump_metadata_) { + out << info->entry(); + } + out.close(); + return 0; +} + +void SynchronizedMinidumpManager::ReleaseLockFile() { + // flock is associated with the fd entry in the open fd table, so closing + // all fd's will release the lock. To be safe, we explicitly unlock. + if (lockfile_fd_ >= 0) { + flock(lockfile_fd_, LOCK_UN); + close(lockfile_fd_); + + // We may use this object again, so we should reset this. + lockfile_fd_ = -1; + } + + dump_metadata_.reset(); +} + +} // namespace chromecast diff --git a/chromium/chromecast/crash/linux/synchronized_minidump_manager.h b/chromium/chromecast/crash/linux/synchronized_minidump_manager.h new file mode 100644 index 00000000000..4adaa30cfc1 --- /dev/null +++ b/chromium/chromecast/crash/linux/synchronized_minidump_manager.h @@ -0,0 +1,106 @@ +// 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_LINUX_SYNCHRONIZED_MINIDUMP_MANAGER_H_ +#define CHROMECAST_CRASH_LINUX_SYNCHRONIZED_MINIDUMP_MANAGER_H_ + +#include <string> + +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/scoped_vector.h" + +namespace chromecast { + +class DumpInfo; + +// Abstract base class for mutually-exclusive minidump handling. Ensures +// synchronized access among instances of this class to the minidumps directory +// using a file lock. The "lockfile" also holds serialized metadata about each +// of the minidumps in the directory. Derived classes should not access the +// lockfile directly. Instead, use protected methods to query and modify the +// metadata, but only within the implementation of DoWork(). +class SynchronizedMinidumpManager { + public: + virtual ~SynchronizedMinidumpManager(); + + // Returns whether this object's file locking method is nonblocking or not. + bool non_blocking() { return non_blocking_; } + + // Sets the file locking mechansim to be nonblocking or not. + void set_non_blocking(bool non_blocking) { non_blocking_ = non_blocking; } + + protected: + SynchronizedMinidumpManager(); + + // Acquires the lock, calls DoWork(), then releases the lock when DoWork() + // returns. Derived classes should expose a method which calls this. Returns + // the status of DoWork(), or -1 if the lock was not successfully acquired. + int AcquireLockAndDoWork(); + + // Derived classes must implement this method. It will be called from + // DoWorkLocked after the lock has been successfully acquired. The lockfile + // shall be accessed and mutated only through the methods below. All other + // files shall be managed as needed by the derived class. + virtual int DoWork() = 0; + + // Access the container holding all the metadata for the dumps. Note that + // the child class must only call this inside DoWork(). This is lazy. If the + // lockfile has not been parsed yet, it will be parsed when this is called. + const ScopedVector<DumpInfo>& GetDumpMetadata(); + + // Serialize |dump_info| and append it to the lockfile. Note that the child + // class must only call this inside DoWork(). This should be the only method + // used to write to the lockfile. Only call this if the minidump has been + // generated in the minidumps directory successfully. Returns 0 on success, + // -1 otherwise. + int AddEntryToLockFile(const DumpInfo& dump_info); + + // Remove the lockfile entry at |index| in the container returned by + // GetDumpMetadata(). If the index is invalid or an IO error occurred, returns + // -1. Otherwise returns 0. When this function returns, both the in-memory + // containter returned by GetDumpMetadata and the persistent lockfile will be + // current. + int RemoveEntryFromLockFile(int index); + + // Get the number of un-uploaded dumps in the dump_path directory. + // If delete_all_dumps is true, also delete all these files, this is used to + // clean lingering dump files. + int GetNumDumps(bool delete_all_dumps); + + // TODO(slan): Remove this accessor. All I/O on the lockfile in inherited + // classes should be done via GetDumpMetadata(), AddEntryToLockFile(), and + // RemoveEntryFromLockFile(). + const std::string& lockfile_path() { return lockfile_path_; } + + // If true, the flock on the lockfile will be nonblocking + bool non_blocking_; + + // Cached path for the minidumps directory. + base::FilePath dump_path_; + + private: + // Acquire the lock file. Blocks if another process holds it, or if called + // a second time by the same process. Returns the fd of the lockfile if + // successful, or -1 if failed. + int AcquireLockFile(); + + // Parse the lockfile, populating |dumps_| for descendants to use. Return -1 + // if an error occurred. Otherwise, return 0. This must not be called unless + // |this| has acquired the lock. + int ParseLockFile(); + + // Release the lock file with the associated *fd*. + void ReleaseLockFile(); + + std::string lockfile_path_; + int lockfile_fd_; + scoped_ptr<ScopedVector<DumpInfo> > dump_metadata_; + + DISALLOW_COPY_AND_ASSIGN(SynchronizedMinidumpManager); +}; + +} // namespace chromecast + +#endif // CHROMECAST_CRASH_LINUX_SYNCHRONIZED_MINIDUMP_MANAGER_H_ diff --git a/chromium/chromecast/crash/linux/synchronized_minidump_manager_unittest.cc b/chromium/chromecast/crash/linux/synchronized_minidump_manager_unittest.cc new file mode 100644 index 00000000000..ea3e908b3e9 --- /dev/null +++ b/chromium/chromecast/crash/linux/synchronized_minidump_manager_unittest.cc @@ -0,0 +1,388 @@ +// 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 <fcntl.h> +#include <stdlib.h> +#include <sys/file.h> +#include <sys/stat.h> // mkdir +#include <sys/types.h> // +#include <stdio.h> // perror +#include <time.h> + +#include <fstream> + +#include "base/base_paths.h" +#include "base/bind.h" +#include "base/files/file.h" +#include "base/files/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/process/launch.h" +#include "base/test/scoped_path_override.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread.h" +#include "chromecast/crash/linux/dump_info.h" +#include "chromecast/crash/linux/synchronized_minidump_manager.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { +namespace { + +const char kLockfileName[] = "lockfile"; +const char kMinidumpSubdir[] = "minidumps"; + +ScopedVector<DumpInfo> GetCurrentDumps(const std::string& logfile_path) { + ScopedVector<DumpInfo> dumps; + std::string entry; + + std::ifstream in(logfile_path); + DCHECK(in.is_open()); + while (std::getline(in, entry)) { + scoped_ptr<DumpInfo> info(new DumpInfo(entry)); + dumps.push_back(info.Pass()); + } + return dumps.Pass(); +} + +// A trivial implementation of SynchronizedMinidumpManager, which does no work +// to the +// minidump and exposes its protected members for testing. +class SynchronizedMinidumpManagerSimple : public SynchronizedMinidumpManager { + public: + SynchronizedMinidumpManagerSimple() + : SynchronizedMinidumpManager(), + work_done_(false), + add_entry_return_code_(-1), + lockfile_path_(dump_path_.Append(kLockfileName).value()) {} + ~SynchronizedMinidumpManagerSimple() override {} + + void SetDumpInfoToWrite(scoped_ptr<DumpInfo> dump_info) { + dump_info_ = dump_info.Pass(); + } + + int DoWorkLocked() { return AcquireLockAndDoWork(); } + + // SynchronizedMinidumpManager implementation: + int DoWork() override { + if (dump_info_) + add_entry_return_code_ = AddEntryToLockFile(*dump_info_); + work_done_ = true; + return 0; + } + + // Accessors for testing. + const std::string& dump_path() { return dump_path_.value(); } + const std::string& lockfile_path() { return lockfile_path_; } + bool work_done() { return work_done_; } + int add_entry_return_code() { return add_entry_return_code_; } + + private: + bool work_done_; + int add_entry_return_code_; + std::string lockfile_path_; + scoped_ptr<DumpInfo> dump_info_; +}; + +void DoWorkLockedTask(SynchronizedMinidumpManagerSimple* manager) { + manager->DoWorkLocked(); +} + +class SleepySynchronizedMinidumpManagerSimple + : public SynchronizedMinidumpManagerSimple { + public: + SleepySynchronizedMinidumpManagerSimple(int sleep_duration_ms) + : SynchronizedMinidumpManagerSimple(), + sleep_duration_ms_(sleep_duration_ms) {} + ~SleepySynchronizedMinidumpManagerSimple() override {} + + // SynchronizedMinidumpManager implementation: + int DoWork() override { + // The lock has been acquired. Fall asleep for |kSleepDurationMs|, then + // write the file. + base::PlatformThread::Sleep( + base::TimeDelta::FromMilliseconds(sleep_duration_ms_)); + return SynchronizedMinidumpManagerSimple::DoWork(); + } + + private: + const int sleep_duration_ms_; +}; + +class SynchronizedMinidumpManagerTest : public testing::Test { + public: + SynchronizedMinidumpManagerTest() {} + ~SynchronizedMinidumpManagerTest() override {} + + void SetUp() override { + // Set up a temporary directory which will be used as our fake home dir. + ASSERT_TRUE(base::CreateNewTempDirectory("", &fake_home_dir_)); + path_override_.reset( + new base::ScopedPathOverride(base::DIR_HOME, fake_home_dir_)); + minidump_dir_ = fake_home_dir_.Append(kMinidumpSubdir); + lockfile_ = minidump_dir_.Append(kLockfileName); + + // Create a minidump directory. + ASSERT_TRUE(base::CreateDirectory(minidump_dir_)); + ASSERT_TRUE(base::IsDirectoryEmpty(minidump_dir_)); + + // Create a lockfile in that directory. + base::File lockfile( + lockfile_, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + ASSERT_TRUE(lockfile.IsValid()); + } + + void TearDown() override { + // Remove the temp directory. + path_override_.reset(); + ASSERT_TRUE(base::DeleteFile(fake_home_dir_, true)); + } + + protected: + base::FilePath fake_home_dir_; // Path to the test home directory. + base::FilePath minidump_dir_; // Path the the minidump directory. + base::FilePath lockfile_; // Path to the lockfile in |minidump_dir_|. + + private: + scoped_ptr<base::ScopedPathOverride> path_override_; +}; + +} // namespace + +TEST_F(SynchronizedMinidumpManagerTest, FilePathsAreCorrect) { + SynchronizedMinidumpManagerSimple manager; + + // Verify file paths for directory and lock file. + ASSERT_EQ(minidump_dir_.value(), manager.dump_path()); + ASSERT_EQ(lockfile_.value(), manager.lockfile_path()); +} + +TEST_F(SynchronizedMinidumpManagerTest, AcquireLockOnNonExistentDirectory) { + // The directory was created in SetUp(). Delete it and its contents. + ASSERT_TRUE(base::DeleteFile(minidump_dir_, true)); + ASSERT_FALSE(base::PathExists(minidump_dir_)); + + SynchronizedMinidumpManagerSimple manager; + ASSERT_EQ(0, manager.DoWorkLocked()); + ASSERT_TRUE(manager.work_done()); + + // Verify the directory and the lockfile both exist. + ASSERT_TRUE(base::DirectoryExists(minidump_dir_)); + ASSERT_TRUE(base::PathExists(lockfile_)); +} + +TEST_F(SynchronizedMinidumpManagerTest, AcquireLockOnExistingEmptyDirectory) { + // The lockfile was created in SetUp(). Delete it. + ASSERT_TRUE(base::DeleteFile(lockfile_, false)); + ASSERT_FALSE(base::PathExists(lockfile_)); + + SynchronizedMinidumpManagerSimple manager; + ASSERT_EQ(0, manager.DoWorkLocked()); + ASSERT_TRUE(manager.work_done()); + + // Verify the directory and the lockfile both exist. + ASSERT_TRUE(base::DirectoryExists(minidump_dir_)); + ASSERT_TRUE(base::PathExists(lockfile_)); +} + +TEST_F(SynchronizedMinidumpManagerTest, + AcquireLockOnExistingDirectoryWithLockfile) { + SynchronizedMinidumpManagerSimple manager; + ASSERT_EQ(0, manager.DoWorkLocked()); + ASSERT_TRUE(manager.work_done()); + + // Verify the directory and the lockfile both exist. + ASSERT_TRUE(base::DirectoryExists(minidump_dir_)); + ASSERT_TRUE(base::PathExists(lockfile_)); +} + +TEST_F(SynchronizedMinidumpManagerTest, + AddEntryToLockFile_FailsWithInvalidEntry) { + // Test that the manager tried to log the entry and failed. + SynchronizedMinidumpManagerSimple manager; + manager.SetDumpInfoToWrite(make_scoped_ptr(new DumpInfo(""))); + ASSERT_EQ(0, manager.DoWorkLocked()); + ASSERT_EQ(-1, manager.add_entry_return_code()); + + // Verify the lockfile is untouched. + ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value()); + ASSERT_EQ(0u, dumps.size()); +} + +TEST_F(SynchronizedMinidumpManagerTest, + AddEntryToLockFile_SucceedsWithValidEntries) { + // Sample parameters. + time_t now = time(0); + MinidumpParams params; + params.process_name = "process"; + + // Write the first entry. + SynchronizedMinidumpManagerSimple manager; + manager.SetDumpInfoToWrite( + make_scoped_ptr(new DumpInfo("dump1", "log1", now, params))); + ASSERT_EQ(0, manager.DoWorkLocked()); + ASSERT_EQ(0, manager.add_entry_return_code()); + + // Test that the manager was successful in logging the entry. + ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value()); + ASSERT_EQ(1u, dumps.size()); + + // Write the second entry. + manager.SetDumpInfoToWrite( + make_scoped_ptr(new DumpInfo("dump2", "log2", now, params))); + ASSERT_EQ(0, manager.DoWorkLocked()); + ASSERT_EQ(0, manager.add_entry_return_code()); + + // Test that the second entry is also valid. + dumps = GetCurrentDumps(lockfile_.value()); + ASSERT_EQ(2u, dumps.size()); + + // TODO(slan): Weird time incosistencies making this fail. + // ASSERT_EQ(dumps[0]->entry(), DumpInfo("dump", "log", now, params).entry()); +} + +TEST_F(SynchronizedMinidumpManagerTest, + AcquireLockFile_FailsWhenNonBlockingAndFileLocked) { + // Lock the lockfile here. Note that the Chromium base::File tools permit + // multiple locks on the same process to succeed, so we must use POSIX system + // calls to accomplish this. + int fd = open(lockfile_.value().c_str(), O_RDWR | O_CREAT, 0660); + ASSERT_GE(fd, 0); + ASSERT_EQ(0, flock(fd, LOCK_EX)); + + SynchronizedMinidumpManagerSimple manager; + manager.set_non_blocking(true); + ASSERT_EQ(-1, manager.DoWorkLocked()); + ASSERT_FALSE(manager.work_done()); + + // Test that the manager was not able to log the crash dump. + ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value()); + ASSERT_EQ(0u, dumps.size()); +} + +TEST_F(SynchronizedMinidumpManagerTest, + AcquireLockFile_WaitsForOtherThreadWhenBlocking) { + // Create some parameters for a minidump. + time_t now = time(0); + MinidumpParams params; + params.process_name = "process"; + + // Create a manager that grabs the lock then sleeps. Post a DoWork task to + // another thread. |sleepy_manager| will grab the lock and hold it for + // |sleep_time_ms|. It will then write a dump and release the lock. + const int sleep_time_ms = 100; + SleepySynchronizedMinidumpManagerSimple sleepy_manager(sleep_time_ms); + sleepy_manager.SetDumpInfoToWrite( + make_scoped_ptr(new DumpInfo("dump", "log", now, params))); + base::Thread sleepy_thread("sleepy"); + sleepy_thread.Start(); + sleepy_thread.task_runner()->PostTask( + FROM_HERE, + base::Bind(&DoWorkLockedTask, base::Unretained(&sleepy_manager))); + + // Meanwhile, this thread should wait brielfy to allow the other thread to + // grab the lock. + const int concurrency_delay = 50; + base::PlatformThread::Sleep( + base::TimeDelta::FromMilliseconds(concurrency_delay)); + + // |sleepy_manager| has the lock by now, but has not released it. Attempt to + // grab it. DoWorkLocked() should block until |manager| has a chance to write + // the dump. + SynchronizedMinidumpManagerSimple manager; + manager.SetDumpInfoToWrite( + make_scoped_ptr(new DumpInfo("dump", "log", now, params))); + manager.set_non_blocking(false); + + EXPECT_EQ(0, manager.DoWorkLocked()); + EXPECT_EQ(0, manager.add_entry_return_code()); + EXPECT_TRUE(manager.work_done()); + + // Check that the other manager was also successful. + EXPECT_EQ(0, sleepy_manager.add_entry_return_code()); + EXPECT_TRUE(sleepy_manager.work_done()); + + // Test that both entries were logged. + ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value()); + EXPECT_EQ(2u, dumps.size()); +} + +// TODO(slan): These tests are passing but forking them is creating duplicates +// of all tests in this thread. Figure out how to lock the file more cleanly +// from another process. +TEST_F(SynchronizedMinidumpManagerTest, + DISABLED_AcquireLockFile_FailsWhenNonBlockingAndLockedFromOtherProcess) { + // Fork the process. + pid_t pid = base::ForkWithFlags(0u, nullptr, nullptr); + if (pid != 0) { + // The child process should instantiate a manager which immediately grabs + // the lock, and falls aleep for some period of time, then writes a dump, + // and finally releases the lock. + SleepySynchronizedMinidumpManagerSimple sleepy_manager(100); + ASSERT_EQ(0, sleepy_manager.DoWorkLocked()); + ASSERT_TRUE(sleepy_manager.work_done()); + return; + } + + // Meanwhile, this process should wait brielfy to allow the other thread to + // grab the lock. + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); + + SynchronizedMinidumpManagerSimple manager; + manager.set_non_blocking(true); + ASSERT_EQ(-1, manager.DoWorkLocked()); + ASSERT_FALSE(manager.work_done()); + + // Test that the manager was not able to log the crash dump. + ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value()); + ASSERT_EQ(0u, dumps.size()); +} + +// TODO(slan): These tests are passing but forking them is creating duplicates +// of all tests in this thread. Figure out how to lock the file more cleanly +// from another process. +TEST_F(SynchronizedMinidumpManagerTest, + DISABLED_AcquireLockFile_WaitsForOtherProcessWhenBlocking) { + // Create some parameters for a minidump. + time_t now = time(0); + MinidumpParams params; + params.process_name = "process"; + + // Fork the process. + pid_t pid = base::ForkWithFlags(0u, nullptr, nullptr); + if (pid != 0) { + // The child process should instantiate a manager which immediately grabs + // the lock, and falls aleep for some period of time, then writes a dump, + // and finally releases the lock. + SleepySynchronizedMinidumpManagerSimple sleepy_manager(100); + sleepy_manager.SetDumpInfoToWrite( + make_scoped_ptr(new DumpInfo("dump", "log", now, params))); + ASSERT_EQ(0, sleepy_manager.DoWorkLocked()); + ASSERT_TRUE(sleepy_manager.work_done()); + return; + } + + // Meanwhile, this process should wait brielfy to allow the other thread to + // grab the lock. + const int concurrency_delay = 50; + base::PlatformThread::Sleep( + base::TimeDelta::FromMilliseconds(concurrency_delay)); + + // |sleepy_manager| has the lock by now, but has not released it. Attempt to + // grab it. DoWorkLocked() should block until |manager| has a chance to write + // the dump. + SynchronizedMinidumpManagerSimple manager; + manager.SetDumpInfoToWrite( + make_scoped_ptr(new DumpInfo("dump", "log", now, params))); + manager.set_non_blocking(false); + + EXPECT_EQ(0, manager.DoWorkLocked()); + EXPECT_EQ(0, manager.add_entry_return_code()); + EXPECT_TRUE(manager.work_done()); + + // Test that both entries were logged. + ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value()); + EXPECT_EQ(2u, dumps.size()); +} + +} // namespace chromecast diff --git a/chromium/chromecast/graphics/cast_egl_platform_default.cc b/chromium/chromecast/graphics/cast_egl_platform_default.cc index 3cb9a1b8c32..9a59408c681 100644 --- a/chromium/chromecast/graphics/cast_egl_platform_default.cc +++ b/chromium/chromecast/graphics/cast_egl_platform_default.cc @@ -29,6 +29,7 @@ class EglPlatformDefault : public CastEglPlatform { return nullptr; } void DestroyWindow(NativeWindowType window) override {} + bool MultipleSurfaceUnsupported() override { return false; } }; } // namespace diff --git a/chromium/chromecast/graphics/cast_screen.cc b/chromium/chromecast/graphics/cast_screen.cc new file mode 100644 index 00000000000..2cf113f8ff6 --- /dev/null +++ b/chromium/chromecast/graphics/cast_screen.cc @@ -0,0 +1,88 @@ +// 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/graphics/cast_screen.h" + +#include "ui/aura/env.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/geometry/size_conversions.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/screen.h" + +namespace chromecast { + +namespace { + +const int64 kDisplayId = 1; + +const int k720pWidth = 1280; +const int k720pHeight = 720; + +// When CastScreen is first initialized, we may not have any display info +// available. These constants hold the initial size that we default to, and +// the size can be updated when we have actual display info at hand with +// UpdateDisplaySize(). +const int kInitDisplayWidth = k720pWidth; +const int kInitDisplayHeight = k720pHeight; + +} // namespace + +CastScreen::~CastScreen() { +} + +void CastScreen::UpdateDisplaySize(const gfx::Size& size) { + display_.SetScaleAndBounds(1.0f, gfx::Rect(size)); +} + +gfx::Point CastScreen::GetCursorScreenPoint() { + return aura::Env::GetInstance()->last_mouse_location(); +} + +gfx::NativeWindow CastScreen::GetWindowUnderCursor() { + NOTIMPLEMENTED(); + return gfx::NativeWindow(nullptr); +} + +gfx::NativeWindow CastScreen::GetWindowAtScreenPoint(const gfx::Point& point) { + NOTIMPLEMENTED(); + return gfx::NativeWindow(nullptr); +} + +int CastScreen::GetNumDisplays() const { + return 1; +} + +std::vector<gfx::Display> CastScreen::GetAllDisplays() const { + return std::vector<gfx::Display>(1, display_); +} + +gfx::Display CastScreen::GetDisplayNearestWindow( + gfx::NativeWindow window) const { + return display_; +} + +gfx::Display CastScreen::GetDisplayNearestPoint(const gfx::Point& point) const { + return display_; +} + +gfx::Display CastScreen::GetDisplayMatching(const gfx::Rect& match_rect) const { + return display_; +} + +gfx::Display CastScreen::GetPrimaryDisplay() const { + return display_; +} + +void CastScreen::AddObserver(gfx::DisplayObserver* observer) { +} + +void CastScreen::RemoveObserver(gfx::DisplayObserver* observer) { +} + +CastScreen::CastScreen() : display_(kDisplayId) { + display_.SetScaleAndBounds(1.0f, + gfx::Rect(kInitDisplayWidth, kInitDisplayHeight)); +} + +} // namespace chromecast diff --git a/chromium/chromecast/graphics/cast_screen.h b/chromium/chromecast/graphics/cast_screen.h new file mode 100644 index 00000000000..50fbf70931d --- /dev/null +++ b/chromium/chromecast/graphics/cast_screen.h @@ -0,0 +1,54 @@ +// 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_GRAPHICS_CAST_SCREEN_H_ +#define CHROMECAST_GRAPHICS_CAST_SCREEN_H_ + +#include "ui/gfx/display.h" +#include "ui/gfx/screen.h" + +namespace chromecast { + +namespace shell { +class CastBrowserMainParts; +} // namespace shell + +// CastScreen is Chromecast's own minimal implementation of gfx::Screen. +// Right now, it almost exactly duplicates the behavior of aura's TestScreen +// class for necessary methods. The instantiation of CastScreen occurs in +// CastBrowserMainParts, where its ownership is assigned to CastBrowserProcess. +// To then subsequently access CastScreen, see CastBrowerProcess. +class CastScreen : public gfx::Screen { + public: + ~CastScreen() override; + + // Updates the primary display size. + void UpdateDisplaySize(const gfx::Size& size); + + // gfx::Screen overrides: + gfx::Point GetCursorScreenPoint() override; + gfx::NativeWindow GetWindowUnderCursor() override; + gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override; + int GetNumDisplays() const override; + std::vector<gfx::Display> GetAllDisplays() const override; + gfx::Display GetDisplayNearestWindow(gfx::NativeView view) const override; + gfx::Display GetDisplayNearestPoint(const gfx::Point& point) const override; + gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override; + gfx::Display GetPrimaryDisplay() const override; + void AddObserver(gfx::DisplayObserver* observer) override; + void RemoveObserver(gfx::DisplayObserver* observer) override; + + private: + CastScreen(); + + gfx::Display display_; + + friend class shell::CastBrowserMainParts; + + DISALLOW_COPY_AND_ASSIGN(CastScreen); +}; + +} // namespace chromecast + +#endif // CHROMECAST_GRAPHICS_CAST_SCREEN_H_ diff --git a/chromium/chromecast/media/BUILD.gn b/chromium/chromecast/media/BUILD.gn index 6a8c6a6a3df..eb95dcd3903 100644 --- a/chromium/chromecast/media/BUILD.gn +++ b/chromium/chromecast/media/BUILD.gn @@ -12,18 +12,20 @@ group("media") { ] } -test("unittests") { +test("cast_media_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/filters/multi_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/demuxer_stream_for_test.cc", + "cma/test/demuxer_stream_for_test.h", "cma/test/frame_generator_for_test.cc", "cma/test/frame_generator_for_test.h", "cma/test/frame_segmenter_for_test.cc", @@ -37,17 +39,19 @@ test("unittests") { "cma/test/run_all_unittests.cc", ] + configs += [ "//chromecast:config" ] + deps = [ ":media", "//base", "//base:i18n", "//base/test:test_support", "//chromecast/base/metrics:test_support", + "//chromecast/public", "//media", "//media/base:test_support", "//testing/gmock", "//testing/gtest", + "//ui/gfx/geometry", ] - - configs += [ "//chromecast:config" ] } diff --git a/chromium/chromecast/media/DEPS b/chromium/chromecast/media/DEPS index f2506bb5ad6..f980e38b56d 100644 --- a/chromium/chromecast/media/DEPS +++ b/chromium/chromecast/media/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+gpu", "+media/base", "+media/cdm", "+media/renderers", diff --git a/chromium/chromecast/media/base/BUILD.gn b/chromium/chromecast/media/base/BUILD.gn index 64992f331f2..e47f8500b04 100644 --- a/chromium/chromecast/media/base/BUILD.gn +++ b/chromium/chromecast/media/base/BUILD.gn @@ -5,6 +5,10 @@ import("//build/config/crypto.gni") import("//chromecast/chromecast.gni") +declare_args() { + libcast_media_target = "" +} + source_set("base") { sources = [ "decrypt_context.cc", @@ -15,24 +19,44 @@ source_set("base") { "key_systems_common.h", "media_caps.cc", "media_caps.h", + "media_message_loop.cc", + "media_message_loop.h", "switching_media_renderer.cc", "switching_media_renderer.h", ] + configs += [ "//chromecast:config" ] + deps = [ "//base", "//crypto", "//crypto:platform", + "//media", "//third_party/widevine/cdm:version_h", ] - configs += [ "//chromecast:config" ] - if (is_chromecast_chrome_branded) { deps += [ + "${libcast_media_target}", # TODO(gyp): add dependency on internal/chromecast_internal:media_base_internal ] } else { sources += [ "key_systems_common_simple.cc" ] + + deps += [ ":libcast_media_default" ] } } + +shared_library("libcast_media_default") { + output_name = "libcast_media_1.0" + + sources = [ + "cast_media_default.cc", + ] + + configs += [ "//chromecast:config" ] + + deps = [ + "//chromecast/public", + ] +} diff --git a/chromium/chromecast/media/base/cast_media_default.cc b/chromium/chromecast/media/base/cast_media_default.cc index 0cc03d38705..0c177126ad6 100644 --- a/chromium/chromecast/media/base/cast_media_default.cc +++ b/chromium/chromecast/media/base/cast_media_default.cc @@ -3,14 +3,43 @@ // found in the LICENSE file. #include "chromecast/public/cast_media_shlib.h" +#include "chromecast/public/graphics_types.h" +#include "chromecast/public/video_plane.h" namespace chromecast { namespace media { +namespace { + +class DefaultVideoPlane : public VideoPlane { + public: + ~DefaultVideoPlane() override {} + + Size GetScreenResolution() override { + return Size(1920, 1080); + } + + void SetGeometry(const RectF& display_rect, + CoordinateType coordinate_type, + Transform transform) override {} + + void OnScreenResolutionChanged(const Size& screen_res) override {} +}; + +DefaultVideoPlane* g_video_plane = nullptr; + +} // namespace void CastMediaShlib::Initialize(const std::vector<std::string>& argv) { + g_video_plane = new DefaultVideoPlane(); } void CastMediaShlib::Finalize() { + delete g_video_plane; + g_video_plane = nullptr; +} + +VideoPlane* CastMediaShlib::GetVideoPlane() { + return g_video_plane; } } // namespace media diff --git a/chromium/chromecast/browser/media/cma_message_loop.cc b/chromium/chromecast/media/base/media_message_loop.cc index 8ba93e01c99..17e22132d73 100644 --- a/chromium/chromecast/browser/media/cma_message_loop.cc +++ b/chromium/chromecast/media/base/media_message_loop.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/browser/media/cma_message_loop.h" +#include "chromecast/media/base/media_message_loop.h" #include "base/threading/thread.h" @@ -10,26 +10,21 @@ namespace chromecast { namespace media { // static -scoped_refptr<base::MessageLoopProxy> CmaMessageLoop::GetMessageLoopProxy() { - return GetInstance()->thread_->message_loop_proxy(); -} - -// static -scoped_refptr<base::SingleThreadTaskRunner> CmaMessageLoop::GetTaskRunner() { +scoped_refptr<base::SingleThreadTaskRunner> MediaMessageLoop::GetTaskRunner() { return GetInstance()->thread_->task_runner(); } // static -CmaMessageLoop* CmaMessageLoop::GetInstance() { - return Singleton<CmaMessageLoop>::get(); +MediaMessageLoop* MediaMessageLoop::GetInstance() { + return Singleton<MediaMessageLoop>::get(); } -CmaMessageLoop::CmaMessageLoop() +MediaMessageLoop::MediaMessageLoop() : thread_(new base::Thread("CmaThread")) { thread_->Start(); } -CmaMessageLoop::~CmaMessageLoop() { +MediaMessageLoop::~MediaMessageLoop() { // This will automatically shutdown the thread. } diff --git a/chromium/chromecast/media/base/media_message_loop.h b/chromium/chromecast/media/base/media_message_loop.h new file mode 100644 index 00000000000..e9f1e09abc2 --- /dev/null +++ b/chromium/chromecast/media/base/media_message_loop.h @@ -0,0 +1,41 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_BASE_MEDIA_MESSAGE_LOOP_H_ +#define CHROMECAST_MEDIA_BASE_MEDIA_MESSAGE_LOOP_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/singleton.h" + +namespace base { +class SingleThreadTaskRunner; +class Thread; +} + +namespace chromecast { +namespace media { + +class MediaMessageLoop { + public: + static scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(); + + private: + friend struct DefaultSingletonTraits<MediaMessageLoop>; + friend class Singleton<MediaMessageLoop>; + + static MediaMessageLoop* GetInstance(); + + MediaMessageLoop(); + ~MediaMessageLoop(); + + scoped_ptr<base::Thread> thread_; + + DISALLOW_COPY_AND_ASSIGN(MediaMessageLoop); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_BASE_MEDIA_MESSAGE_LOOP_H_ diff --git a/chromium/chromecast/media/cdm/browser_cdm_cast.cc b/chromium/chromecast/media/cdm/browser_cdm_cast.cc index b62df02cb8b..607c03d9ee7 100644 --- a/chromium/chromecast/media/cdm/browser_cdm_cast.cc +++ b/chromium/chromecast/media/cdm/browser_cdm_cast.cc @@ -6,7 +6,7 @@ #include "base/bind.h" #include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" #include "media/base/cdm_key_information.h" #include "media/base/cdm_promise.h" #include "media/cdm/player_tracker_impl.h" @@ -54,28 +54,16 @@ void BrowserCdmCast::UnregisterPlayer(int registration_id) { 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; +void BrowserCdmCast::OnSessionMessage( + const std::string& session_id, + const std::vector<uint8_t>& message, + const GURL& destination_url, + ::media::MediaKeys::MessageType message_type) { session_message_cb_.Run(session_id, message_type, message, @@ -101,24 +89,22 @@ void BrowserCdmCast::OnSessionKeysChange( player_tracker_impl_->NotifyNewKey(); } -// A macro runs current member function on |cdm_loop_| thread. +// A macro runs current member function on |task_runner_| thread. #define FORWARD_ON_CDM_THREAD(param_fn, ...) \ - cdm_loop_->PostTask( \ - FROM_HERE, \ - base::Bind(&BrowserCdmCast::param_fn, \ + task_runner_->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) { + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) + : browser_cdm_cast_(browser_cdm_cast.Pass()), task_runner_(task_runner) { } BrowserCdmCastUi::~BrowserCdmCastUi() { DCHECK(thread_checker_.CalledOnValidThread()); - cdm_loop_->DeleteSoon(FROM_HERE, browser_cdm_cast_.release()); + task_runner_->DeleteSoon(FROM_HERE, browser_cdm_cast_.release()); } int BrowserCdmCastUi::RegisterPlayer(const base::Closure& new_key_cb, diff --git a/chromium/chromecast/media/cdm/browser_cdm_cast.h b/chromium/chromecast/media/cdm/browser_cdm_cast.h index 7a83a08dcc8..0e24a518975 100644 --- a/chromium/chromecast/media/cdm/browser_cdm_cast.h +++ b/chromium/chromecast/media/cdm/browser_cdm_cast.h @@ -19,7 +19,7 @@ #include "media/cdm/json_web_key.h" namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; } namespace media { @@ -55,9 +55,6 @@ class BrowserCdmCast : public ::media::BrowserCdm { 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 @@ -69,7 +66,8 @@ class BrowserCdmCast : public ::media::BrowserCdm { protected: void OnSessionMessage(const std::string& session_id, const std::vector<uint8_t>& message, - const GURL& destination_url); + const GURL& destination_url, + ::media::MediaKeys::MessageType message_type); void OnSessionClosed(const std::string& session_id); void OnSessionKeysChange(const std::string& session_id, const ::media::KeyIdAndKeyPairs& keys); @@ -101,7 +99,7 @@ class BrowserCdmCastUi : public ::media::BrowserCdm { public: BrowserCdmCastUi( scoped_ptr<BrowserCdmCast> browser_cdm_cast, - const scoped_refptr<base::MessageLoopProxy>& cdm_loop); + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); ~BrowserCdmCastUi() override; // PlayerTracker implementation: @@ -134,7 +132,7 @@ class BrowserCdmCastUi : public ::media::BrowserCdm { ::media::CdmContext* GetCdmContext() override; scoped_ptr<BrowserCdmCast> browser_cdm_cast_; - scoped_refptr<base::MessageLoopProxy> cdm_loop_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; base::ThreadChecker thread_checker_; diff --git a/chromium/chromecast/media/cdm/chromecast_init_data.cc b/chromium/chromecast/media/cdm/chromecast_init_data.cc new file mode 100644 index 00000000000..7f4d03fed00 --- /dev/null +++ b/chromium/chromecast/media/cdm/chromecast_init_data.cc @@ -0,0 +1,68 @@ +// 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/chromecast_init_data.h" + +#include "base/logging.h" +#include "base/stl_util.h" +#include "media/base/bit_reader.h" +#include "media/cdm/cenc_utils.h" + +namespace chromecast { +namespace media { + +#define RCHECK(x) \ + do { \ + if (!(x)) \ + return false; \ + } while (0) + +namespace { + +const uint8_t kChromecastPlayreadyUuid[] = { + 0x2b, 0xf8, 0x66, 0x80, 0xc6, 0xe5, 0x4e, 0x24, + 0xbe, 0x23, 0x0f, 0x81, 0x5a, 0x60, 0x6e, 0xb2}; + +} // namespace + +ChromecastInitData::ChromecastInitData() { +} + +ChromecastInitData::~ChromecastInitData() { +} + +bool FindChromecastInitData(const std::vector<uint8_t>& init_data, + InitDataMessageType type, + ChromecastInitData* chromecast_init_data_out) { + // Chromecast initData assumes a CENC data format and searches for PSSH boxes + // with SystemID |kChromecastPlayreadyUuid|. The PSSH box content is as + // follows: + // * |type| (2 bytes, InitDataMessageType) + // * |data| (all remaining bytes) + // Data may or may not be present and is specific to the given |type|. + + std::vector<uint8_t> pssh_data; + if (!::media::GetPsshData( + init_data, std::vector<uint8_t>(kChromecastPlayreadyUuid, + kChromecastPlayreadyUuid + + sizeof(kChromecastPlayreadyUuid)), + &pssh_data)) { + return false; + } + + ::media::BitReader reader(vector_as_array(&pssh_data), pssh_data.size()); + + uint16_t msg_type; + RCHECK(reader.ReadBits(2 * 8, &msg_type)); + RCHECK(msg_type < static_cast<uint16_t>(InitDataMessageType::END)); + RCHECK(msg_type == static_cast<uint16_t>(type)); + + chromecast_init_data_out->type = static_cast<InitDataMessageType>(msg_type); + chromecast_init_data_out->data.assign( + pssh_data.begin() + reader.bits_read() / 8, pssh_data.end()); + return true; +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cdm/chromecast_init_data.h b/chromium/chromecast/media/cdm/chromecast_init_data.h new file mode 100644 index 00000000000..d7de627e64f --- /dev/null +++ b/chromium/chromecast/media/cdm/chromecast_init_data.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_MEDIA_CDM_INIT_DATA_H_ +#define CHROMECAST_MEDIA_CDM_INIT_DATA_H_ + +#include <stdint.h> + +#include <vector> + +namespace chromecast { +namespace media { + +enum class InitDataMessageType { + UNKNOWN = 0x0, + CUSTOM_DATA = 0x1, + ENABLE_SECURE_STOP = 0x2, + END +}; + +// Structured data for EME initialization as parsed from an initData blob. +struct ChromecastInitData { + ChromecastInitData(); + ~ChromecastInitData(); + + InitDataMessageType type; + std::vector<uint8_t> data; +}; + +// Searches for a ChromecastInitData blob inside a CENC |init_data| message of +// type |type|. If such a blob is found, returns true and fills +// |chromecast_init_data_out|. If not found, returns false. +bool FindChromecastInitData(const std::vector<uint8_t>& init_data, + InitDataMessageType type, + ChromecastInitData* chromecast_init_data_out); + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CDM_INIT_DATA_H_ diff --git a/chromium/chromecast/media/cdm/chromecast_init_data_unittest.cc b/chromium/chromecast/media/cdm/chromecast_init_data_unittest.cc new file mode 100644 index 00000000000..640b95c8376 --- /dev/null +++ b/chromium/chromecast/media/cdm/chromecast_init_data_unittest.cc @@ -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. + +#include "chromecast/media/cdm/chromecast_init_data.h" + +#include <vector> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { +namespace media { + +TEST(ChromecastInitDataTest, TestPsshCustomData) { + const uint8_t kInitDataBlob[] = { + 0x00, 0x00, 0x00, 0x32, // length + 0x70, 0x73, 0x73, 0x68, // 'pssh' + 0x00, 0x00, 0x00, 0x00, // version / flags + 0x2B, 0xF8, 0x66, 0x80, 0xC6, 0xE5, 0x4E, 0x24, 0xBE, + 0x23, 0x0F, 0x81, 0x5A, 0x60, 0x6E, 0xB2, // UUID + 0x00, 0x00, 0x00, 0x12, // data size + 0x00, 0x01, // message type (CUSTOM_DATA) + 0x54, 0x65, 0x73, 0x74, 0x20, 0x63, 0x75, 0x73, 0x74, + 0x6F, 0x6D, 0x20, 0x64, 0x61, 0x74, 0x61 // 'Test custom data' + }; + + ChromecastInitData init_data; + EXPECT_TRUE(FindChromecastInitData( + std::vector<uint8_t>(kInitDataBlob, + kInitDataBlob + sizeof(kInitDataBlob)), + InitDataMessageType::CUSTOM_DATA, &init_data)); + + EXPECT_EQ(InitDataMessageType::CUSTOM_DATA, init_data.type); + EXPECT_EQ(16u, init_data.data.size()); + EXPECT_EQ("Test custom data", + std::string(init_data.data.begin(), init_data.data.end())); +} + +TEST(ChromecastInitDataTest, TestPsshCustomData_NoSize) { + const uint8_t kInitDataBlob[] = { + 0x00, 0x00, 0x00, 0x2E, // length + 0x70, 0x73, 0x73, 0x68, // 'pssh' + 0x00, 0x00, 0x00, 0x00, // version / flags + 0x2B, 0xF8, 0x66, 0x80, 0xC6, 0xE5, 0x4E, 0x24, 0xBE, + 0x23, 0x0F, 0x81, 0x5A, 0x60, 0x6E, 0xB2, // UUID + // [missing size should be present here]. + 0x00, 0x01, // message type (CUSTOM_DATA) + 0x54, 0x65, 0x73, 0x74, 0x20, 0x63, 0x75, 0x73, 0x74, + 0x6F, 0x6D, 0x20, 0x64, 0x61, 0x74, 0x61 // 'Test custom data' + }; + + ChromecastInitData init_data; + EXPECT_FALSE(FindChromecastInitData( + std::vector<uint8_t>(kInitDataBlob, + kInitDataBlob + sizeof(kInitDataBlob)), + InitDataMessageType::CUSTOM_DATA, &init_data)); +} + +TEST(ChromecastInitDataTest, TestPsshSecureStop) { + const uint8_t kInitDataBlob[] = { + 0x00, 0x00, 0x00, 0x22, // length + 0x70, 0x73, 0x73, 0x68, // 'pssh' + 0x00, 0x00, 0x00, 0x00, // version / flags + 0x2B, 0xF8, 0x66, 0x80, 0xC6, 0xE5, 0x4E, 0x24, + 0xBE, 0x23, 0x0F, 0x81, 0x5A, 0x60, 0x6E, 0xB2, // UUID + 0x00, 0x00, 0x00, 0x02, // data size + 0x00, 0x02, // message type (ENABLE_SECURE_STOP) + }; + + ChromecastInitData init_data; + EXPECT_TRUE(FindChromecastInitData( + std::vector<uint8_t>(kInitDataBlob, + kInitDataBlob + sizeof(kInitDataBlob)), + InitDataMessageType::ENABLE_SECURE_STOP, &init_data)); + + EXPECT_EQ(InitDataMessageType::ENABLE_SECURE_STOP, init_data.type); + EXPECT_EQ(0u, init_data.data.size()); +} + +TEST(ChromecastInitDataTest, TestPsshSecureStop_NoSize) { + const uint8_t kInitDataBlob[] = { + 0x00, 0x00, 0x00, 0x1E, // length + 0x70, 0x73, 0x73, 0x68, // 'pssh' + 0x00, 0x00, 0x00, 0x00, // version / flags + 0x2B, 0xF8, 0x66, 0x80, 0xC6, 0xE5, 0x4E, 0x24, + 0xBE, 0x23, 0x0F, 0x81, 0x5A, 0x60, 0x6E, 0xB2, // UUID + // [missing size should be present here]. + 0x00, 0x02, // message type (ENABLE_SECURE_STOP) + }; + + ChromecastInitData init_data; + EXPECT_FALSE(FindChromecastInitData( + std::vector<uint8_t>(kInitDataBlob, + kInitDataBlob + sizeof(kInitDataBlob)), + InitDataMessageType::ENABLE_SECURE_STOP, &init_data)); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cdm/playready_drm_delegate_android.cc b/chromium/chromecast/media/cdm/playready_drm_delegate_android.cc index 135fc44bd52..2cb856bdefc 100644 --- a/chromium/chromecast/media/cdm/playready_drm_delegate_android.cc +++ b/chromium/chromecast/media/cdm/playready_drm_delegate_android.cc @@ -5,7 +5,7 @@ #include "chromecast/media/cdm/playready_drm_delegate_android.h" #include "base/logging.h" -#include "media/base/bit_reader.h" +#include "chromecast/media/cdm/chromecast_init_data.h" namespace chromecast { namespace media { @@ -14,13 +14,6 @@ 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() { } @@ -34,47 +27,18 @@ const ::media::UUID PlayreadyDrmDelegateAndroid::GetUUID() const { 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) { + 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; - + ChromecastInitData custom_data; + if (FindChromecastInitData(init_data, InitDataMessageType::CUSTOM_DATA, + &custom_data)) { 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 + std::string(custom_data.data.begin(), custom_data.data.end())); + LOG(INFO) << "Including " << custom_data.data.size() << " bytes of custom PlayReady data"; } } diff --git a/chromium/chromecast/media/cma/backend/BUILD.gn b/chromium/chromecast/media/cma/backend/BUILD.gn index 6526a1f746b..51e62441010 100644 --- a/chromium/chromecast/media/cma/backend/BUILD.gn +++ b/chromium/chromecast/media/cma/backend/BUILD.gn @@ -6,24 +6,28 @@ source_set("backend") { sources = [ "audio_pipeline_device.cc", "audio_pipeline_device.h", + "audio_pipeline_device_default.cc", + "audio_pipeline_device_default.h", "media_clock_device.cc", "media_clock_device.h", + "media_clock_device_default.cc", + "media_clock_device_default.h", "media_component_device.cc", "media_component_device.h", + "media_component_device_default.cc", + "media_component_device_default.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_factory.h", + "media_pipeline_device_factory_default.cc", + "media_pipeline_device_factory_default.h", + "media_pipeline_device_factory_simple.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", + "video_pipeline_device_default.cc", + "video_pipeline_device_default.h", ] deps = [ diff --git a/chromium/chromecast/media/cma/backend/audio_pipeline_device_default.cc b/chromium/chromecast/media/cma/backend/audio_pipeline_device_default.cc new file mode 100644 index 00000000000..6d1bb8d500b --- /dev/null +++ b/chromium/chromecast/media/cma/backend/audio_pipeline_device_default.cc @@ -0,0 +1,84 @@ +// 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/backend/audio_pipeline_device_default.h" + +#include "chromecast/media/cma/backend/media_component_device_default.h" + +namespace chromecast { +namespace media { + +AudioPipelineDeviceDefault::AudioPipelineDeviceDefault( + MediaClockDevice* media_clock_device) + : pipeline_(new MediaComponentDeviceDefault(media_clock_device)) { + DetachFromThread(); +} + +AudioPipelineDeviceDefault::~AudioPipelineDeviceDefault() { +} + +void AudioPipelineDeviceDefault::SetClient(const Client& client) { + pipeline_->SetClient(client); +} + +MediaComponentDevice::State AudioPipelineDeviceDefault::GetState() const { + return pipeline_->GetState(); +} + +bool AudioPipelineDeviceDefault::SetState(State new_state) { + bool success = pipeline_->SetState(new_state); + if (!success) + return false; + + if (new_state == kStateIdle) { + DCHECK(IsValidConfig(config_)); + } + if (new_state == kStateUninitialized) { + config_ = AudioConfig(); + } + return true; +} + +bool AudioPipelineDeviceDefault::SetStartPts(base::TimeDelta time) { + return pipeline_->SetStartPts(time); +} + +MediaComponentDevice::FrameStatus AudioPipelineDeviceDefault::PushFrame( + const scoped_refptr<DecryptContext>& decrypt_context, + const scoped_refptr<DecoderBufferBase>& buffer, + const FrameStatusCB& completion_cb) { + return pipeline_->PushFrame(decrypt_context, buffer, completion_cb); +} + +base::TimeDelta AudioPipelineDeviceDefault::GetRenderingTime() const { + return pipeline_->GetRenderingTime(); +} + +base::TimeDelta AudioPipelineDeviceDefault::GetRenderingDelay() const { + return pipeline_->GetRenderingDelay(); +} + +bool AudioPipelineDeviceDefault::SetConfig(const AudioConfig& config) { + DCHECK(CalledOnValidThread()); + 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; +} + +void AudioPipelineDeviceDefault::SetStreamVolumeMultiplier(float multiplier) { + DCHECK(CalledOnValidThread()); +} + +bool AudioPipelineDeviceDefault::GetStatistics(Statistics* stats) const { + return pipeline_->GetStatistics(stats); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/audio_pipeline_device_default.h b/chromium/chromecast/media/cma/backend/audio_pipeline_device_default.h new file mode 100644 index 00000000000..f8a1a84c1d5 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/audio_pipeline_device_default.h @@ -0,0 +1,52 @@ +// 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_BACKEND_AUDIO_PIPELINE_DEVICE_DEFAULT_H_ +#define CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_PIPELINE_DEVICE_DEFAULT_H_ + +#include <vector> + +#include "base/macros.h" +#include "chromecast/media/cma/backend/audio_pipeline_device.h" +#include "chromecast/public/media/decoder_config.h" + +namespace chromecast { +namespace media { + +class MediaClockDevice; +class MediaComponentDeviceDefault; + +class AudioPipelineDeviceDefault : public AudioPipelineDevice { + public: + explicit AudioPipelineDeviceDefault(MediaClockDevice* media_clock_device); + ~AudioPipelineDeviceDefault() override; + + // AudioPipelineDevice implementation. + 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; + 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<MediaComponentDeviceDefault> pipeline_; + + AudioConfig config_; + std::vector<uint8_t> config_extra_data_; + + DISALLOW_COPY_AND_ASSIGN(AudioPipelineDeviceDefault); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_PIPELINE_DEVICE_DEFAULT_H_ diff --git a/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc b/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc index 972d20af679..9c84b2f4ee4 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 @@ -14,14 +14,16 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/path_service.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" #include "base/time/time.h" #include "chromecast/media/base/decrypt_context.h" #include "chromecast/media/cma/backend/audio_pipeline_device.h" #include "chromecast/media/cma/backend/media_clock_device.h" #include "chromecast/media/cma/backend/media_pipeline_device.h" +#include "chromecast/media/cma/backend/media_pipeline_device_factory.h" #include "chromecast/media/cma/backend/media_pipeline_device_params.h" #include "chromecast/media/cma/backend/video_pipeline_device.h" #include "chromecast/media/cma/base/decoder_buffer_adapter.h" @@ -168,7 +170,8 @@ void AudioVideoPipelineDeviceTest::LoadAudioStream(std::string filename) { media_pipeline_device_->GetAudioPipelineDevice(); bool success = audio_pipeline_device->SetConfig( - DecoderConfigAdapter::ToCastAudioConfig(demux_result.audio_config)); + DecoderConfigAdapter::ToCastAudioConfig(kPrimary, + demux_result.audio_config)); ASSERT_TRUE(success); VLOG(2) << "Got " << frames.size() << " audio input frames"; @@ -207,8 +210,8 @@ void AudioVideoPipelineDeviceTest::LoadVideoStream(std::string filename, DemuxResult demux_result = FFmpegDemuxForTest(file_path, /*audio*/ false); frames = demux_result.frames; - video_config = - DecoderConfigAdapter::ToCastVideoConfig(demux_result.video_config); + video_config = DecoderConfigAdapter::ToCastVideoConfig( + kPrimary, demux_result.video_config); } VideoPipelineDevice* video_pipeline_device = @@ -237,18 +240,16 @@ void AudioVideoPipelineDeviceTest::Start() { pause_pattern_idx_ = 0; for (size_t i = 0; i < component_device_feeders_.size(); i++) { - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&MediaComponentDeviceFeederForTest::Feed, - base::Unretained(component_device_feeders_[i]))); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&MediaComponentDeviceFeederForTest::Feed, + base::Unretained(component_device_feeders_[i]))); } media_clock_device_->SetState(MediaClockDevice::kStateRunning); - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&AudioVideoPipelineDeviceTest::MonitorLoop, - base::Unretained(this))); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&AudioVideoPipelineDeviceTest::MonitorLoop, + base::Unretained(this))); } void AudioVideoPipelineDeviceTest::MonitorLoop() { @@ -265,19 +266,17 @@ void AudioVideoPipelineDeviceTest::MonitorLoop() { pause_pattern_[pause_pattern_idx_].length.InMilliseconds() << "ms"; // Wait for pause finish - base::MessageLoopProxy::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&AudioVideoPipelineDeviceTest::OnPauseCompleted, - base::Unretained(this)), + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&AudioVideoPipelineDeviceTest::OnPauseCompleted, + base::Unretained(this)), pause_pattern_[pause_pattern_idx_].length); return; } // Check state again in a little while - base::MessageLoopProxy::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&AudioVideoPipelineDeviceTest::MonitorLoop, - base::Unretained(this)), + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&AudioVideoPipelineDeviceTest::MonitorLoop, + base::Unretained(this)), kMonitorLoopDelay); } @@ -324,7 +323,9 @@ void AudioVideoPipelineDeviceTest::OnEos( void AudioVideoPipelineDeviceTest::Initialize() { // Create the media device. MediaPipelineDeviceParams params; - media_pipeline_device_.reset(CreateMediaPipelineDevice(params).release()); + scoped_ptr<MediaPipelineDeviceFactory> device_factory = + GetMediaPipelineDeviceFactory(params); + media_pipeline_device_.reset(new MediaPipelineDevice(device_factory.Pass())); media_clock_device_ = media_pipeline_device_->GetMediaClockDevice(); // Clock initialization and configuration. diff --git a/chromium/chromecast/media/cma/backend/media_clock_device_default.cc b/chromium/chromecast/media/cma/backend/media_clock_device_default.cc new file mode 100644 index 00000000000..086083dda3f --- /dev/null +++ b/chromium/chromecast/media/cma/backend/media_clock_device_default.cc @@ -0,0 +1,84 @@ +// 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/backend/media_clock_device_default.h" + +#include "media/base/buffers.h" + +namespace chromecast { +namespace media { + +MediaClockDeviceDefault::MediaClockDeviceDefault() + : state_(kStateUninitialized), + media_time_(::media::kNoTimestamp()) { + DetachFromThread(); +} + +MediaClockDeviceDefault::~MediaClockDeviceDefault() { +} + +MediaClockDevice::State MediaClockDeviceDefault::GetState() const { + DCHECK(CalledOnValidThread()); + return state_; +} + +bool MediaClockDeviceDefault::SetState(State new_state) { + DCHECK(CalledOnValidThread()); + if (!MediaClockDevice::IsValidStateTransition(state_, new_state)) + return false; + + if (new_state == state_) + return true; + + state_ = new_state; + + if (state_ == kStateRunning) { + stc_ = base::TimeTicks::Now(); + DCHECK(media_time_ != ::media::kNoTimestamp()); + return true; + } + + if (state_ == kStateIdle) { + media_time_ = ::media::kNoTimestamp(); + return true; + } + + return true; +} + +bool MediaClockDeviceDefault::ResetTimeline(base::TimeDelta time) { + DCHECK(CalledOnValidThread()); + DCHECK_EQ(state_, kStateIdle); + media_time_ = time; + return true; +} + +bool MediaClockDeviceDefault::SetRate(float rate) { + DCHECK(CalledOnValidThread()); + if (state_ == kStateRunning) { + base::TimeTicks now = base::TimeTicks::Now(); + media_time_ = media_time_ + (now - stc_) * rate_; + stc_ = now; + } + + rate_ = rate; + return true; +} + +base::TimeDelta MediaClockDeviceDefault::GetTime() { + DCHECK(CalledOnValidThread()); + if (state_ != kStateRunning) + return media_time_; + + if (media_time_ == ::media::kNoTimestamp()) + return ::media::kNoTimestamp(); + + base::TimeTicks now = base::TimeTicks::Now(); + base::TimeDelta interpolated_media_time = + media_time_ + (now - stc_) * rate_; + return interpolated_media_time; +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/media_clock_device_default.h b/chromium/chromecast/media/cma/backend/media_clock_device_default.h new file mode 100644 index 00000000000..1ac43875210 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/media_clock_device_default.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_MEDIA_CMA_BACKEND_MEDIA_CLOCK_DEVICE_DEFAULT_H_ +#define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_CLOCK_DEVICE_DEFAULT_H_ + +#include "base/macros.h" +#include "chromecast/media/cma/backend/media_clock_device.h" + +namespace chromecast { +namespace media { + +class MediaClockDeviceDefault : public MediaClockDevice { + public: + MediaClockDeviceDefault(); + ~MediaClockDeviceDefault() override; + + // MediaClockDevice implementation. + 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_; + + // Media time sampled at STC time |stc_|. + base::TimeDelta media_time_; + base::TimeTicks stc_; + + float rate_; + + DISALLOW_COPY_AND_ASSIGN(MediaClockDeviceDefault); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_CLOCK_DEVICE_DEFAULT_H_ diff --git a/chromium/chromecast/media/cma/backend/media_component_device_default.cc b/chromium/chromecast/media/cma/backend/media_component_device_default.cc new file mode 100644 index 00000000000..3ed5f01f395 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/media_component_device_default.cc @@ -0,0 +1,189 @@ +// 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/backend/media_component_device_default.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/thread_task_runner_handle.h" +#include "chromecast/media/cma/base/decoder_buffer_base.h" +#include "media/base/buffers.h" + +namespace chromecast { +namespace media { + +namespace { + +// Maximum number of frames that can be buffered. +const size_t kMaxFrameCount = 20; + +} // namespace + +MediaComponentDeviceDefault::DefaultDecoderBuffer::DefaultDecoderBuffer() + : size(0) { +} + +MediaComponentDeviceDefault::DefaultDecoderBuffer::~DefaultDecoderBuffer() { +} + +MediaComponentDeviceDefault::MediaComponentDeviceDefault( + MediaClockDevice* media_clock_device) + : media_clock_device_(media_clock_device), + state_(kStateUninitialized), + rendering_time_(::media::kNoTimestamp()), + decoded_frame_count_(0), + decoded_byte_count_(0), + scheduled_rendering_task_(false), + weak_factory_(this) { + weak_this_ = weak_factory_.GetWeakPtr(); + DetachFromThread(); +} + +MediaComponentDeviceDefault::~MediaComponentDeviceDefault() { +} + +void MediaComponentDeviceDefault::SetClient(const Client& client) { + DCHECK(CalledOnValidThread()); + client_ = client; +} + +MediaComponentDevice::State MediaComponentDeviceDefault::GetState() const { + DCHECK(CalledOnValidThread()); + return state_; +} + +bool MediaComponentDeviceDefault::SetState(State new_state) { + DCHECK(CalledOnValidThread()); + if (!MediaComponentDevice::IsValidStateTransition(state_, new_state)) + return false; + state_ = new_state; + + if (state_ == kStateIdle) { + // Back to the idle state: reset a bunch of parameters. + is_eos_ = false; + rendering_time_ = ::media::kNoTimestamp(); + decoded_frame_count_ = 0; + decoded_byte_count_ = 0; + frames_.clear(); + pending_buffer_ = scoped_refptr<DecoderBufferBase>(); + frame_pushed_cb_.Reset(); + return true; + } + + if (state_ == kStateRunning) { + if (!scheduled_rendering_task_) { + scheduled_rendering_task_ = true; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&MediaComponentDeviceDefault::RenderTask, weak_this_)); + } + return true; + } + + return true; +} + +bool MediaComponentDeviceDefault::SetStartPts(base::TimeDelta time) { + DCHECK(CalledOnValidThread()); + DCHECK_EQ(state_, kStateIdle); + rendering_time_ = time; + return true; +} + +MediaComponentDevice::FrameStatus MediaComponentDeviceDefault::PushFrame( + const scoped_refptr<DecryptContext>& decrypt_context, + const scoped_refptr<DecoderBufferBase>& buffer, + const FrameStatusCB& completion_cb) { + DCHECK(CalledOnValidThread()); + DCHECK(state_ == kStatePaused || state_ == kStateRunning); + DCHECK(!is_eos_); + DCHECK(!pending_buffer_.get()); + DCHECK(buffer.get()); + + if (buffer->end_of_stream()) { + is_eos_ = true; + return kFrameSuccess; + } + + if (frames_.size() > kMaxFrameCount) { + pending_buffer_ = buffer; + frame_pushed_cb_ = completion_cb; + return kFramePending; + } + + DefaultDecoderBuffer fake_buffer; + fake_buffer.size = buffer->data_size(); + fake_buffer.pts = buffer->timestamp(); + frames_.push_back(fake_buffer); + return kFrameSuccess; +} + +base::TimeDelta MediaComponentDeviceDefault::GetRenderingTime() const { + return rendering_time_; +} + +base::TimeDelta MediaComponentDeviceDefault::GetRenderingDelay() const { + NOTIMPLEMENTED(); + return ::media::kNoTimestamp(); +} + +void MediaComponentDeviceDefault::RenderTask() { + scheduled_rendering_task_ = false; + + if (state_ != kStateRunning) + return; + + base::TimeDelta media_time = media_clock_device_->GetTime(); + if (media_time == ::media::kNoTimestamp()) { + scheduled_rendering_task_ = true; + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&MediaComponentDeviceDefault::RenderTask, weak_this_), + base::TimeDelta::FromMilliseconds(50)); + return; + } + + while (!frames_.empty() && frames_.front().pts <= media_time) { + rendering_time_ = frames_.front().pts; + decoded_frame_count_++; + decoded_byte_count_ += frames_.front().size; + frames_.pop_front(); + if (pending_buffer_.get()) { + DefaultDecoderBuffer fake_buffer; + fake_buffer.size = pending_buffer_->data_size(); + fake_buffer.pts = pending_buffer_->timestamp(); + frames_.push_back(fake_buffer); + pending_buffer_ = scoped_refptr<DecoderBufferBase>(); + base::ResetAndReturn(&frame_pushed_cb_).Run(kFrameSuccess); + } + } + + if (frames_.empty() && is_eos_) { + if (!client_.eos_cb.is_null()) + client_.eos_cb.Run(); + return; + } + + scheduled_rendering_task_ = true; + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&MediaComponentDeviceDefault::RenderTask, weak_this_), + base::TimeDelta::FromMilliseconds(50)); +} + +bool MediaComponentDeviceDefault::GetStatistics(Statistics* stats) const { + if (state_ != kStateRunning) + return false; + + // Note: what is returned here is not the number of samples but the number of + // frames. The value is different for audio. + stats->decoded_bytes = decoded_byte_count_; + stats->decoded_samples = decoded_frame_count_; + stats->dropped_samples = 0; + return true; +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/media_component_device_default.h b/chromium/chromecast/media/cma/backend/media_component_device_default.h new file mode 100644 index 00000000000..5aea387ac13 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/media_component_device_default.h @@ -0,0 +1,84 @@ +// 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_BACKEND_MEDIA_COMPONENT_DEVICE_DEFAULT_H_ +#define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_COMPONENT_DEVICE_DEFAULT_H_ + +#include <list> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "chromecast/media/cma/backend/media_clock_device.h" +#include "chromecast/media/cma/backend/media_component_device.h" + +namespace chromecast { +namespace media { + +class MediaComponentDeviceDefault : public MediaComponentDevice { + public: + explicit MediaComponentDeviceDefault(MediaClockDevice* media_clock_device); + ~MediaComponentDeviceDefault() override; + + // MediaComponentDevice implementation. + 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; + base::TimeDelta GetRenderingTime() const override; + base::TimeDelta GetRenderingDelay() const override; + bool GetStatistics(Statistics* stats) const override; + + private: + struct DefaultDecoderBuffer { + DefaultDecoderBuffer(); + ~DefaultDecoderBuffer(); + + // Buffer size. + size_t size; + + // Presentation timestamp. + base::TimeDelta pts; + }; + + void RenderTask(); + + MediaClockDevice* const media_clock_device_; + Client client_; + + State state_; + + // Indicate whether the end of stream has been received. + bool is_eos_; + + // Media time of the last rendered audio sample. + base::TimeDelta rendering_time_; + + // Frame decoded/rendered since the pipeline left the idle state. + uint64 decoded_frame_count_; + uint64 decoded_byte_count_; + + // List of frames not rendered yet. + std::list<DefaultDecoderBuffer> frames_; + + // Indicate whether there is a scheduled rendering task. + bool scheduled_rendering_task_; + + // Pending frame. + scoped_refptr<DecoderBufferBase> pending_buffer_; + FrameStatusCB frame_pushed_cb_; + + base::WeakPtr<MediaComponentDeviceDefault> weak_this_; + base::WeakPtrFactory<MediaComponentDeviceDefault> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(MediaComponentDeviceDefault); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_COMPONENT_DEVICE_DEFAULT_H_ diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device.cc index ea120c47829..ec2370b553d 100644 --- a/chromium/chromecast/media/cma/backend/media_pipeline_device.cc +++ b/chromium/chromecast/media/cma/backend/media_pipeline_device.cc @@ -4,10 +4,28 @@ #include "chromecast/media/cma/backend/media_pipeline_device.h" +#include "chromecast/media/cma/backend/audio_pipeline_device_default.h" +#include "chromecast/media/cma/backend/media_clock_device_default.h" +#include "chromecast/media/cma/backend/media_pipeline_device_factory.h" +#include "chromecast/media/cma/backend/video_pipeline_device_default.h" + namespace chromecast { namespace media { -MediaPipelineDevice::MediaPipelineDevice() { +MediaPipelineDevice::MediaPipelineDevice( + scoped_ptr<MediaPipelineDeviceFactory> factory) + : media_clock_device_(factory->CreateMediaClockDevice()), + audio_pipeline_device_(factory->CreateAudioPipelineDevice()), + video_pipeline_device_(factory->CreateVideoPipelineDevice()) { +} + +MediaPipelineDevice::MediaPipelineDevice( + scoped_ptr<MediaClockDevice> media_clock_device, + scoped_ptr<AudioPipelineDevice> audio_pipeline_device, + scoped_ptr<VideoPipelineDevice> video_pipeline_device) + : media_clock_device_(media_clock_device.Pass()), + audio_pipeline_device_(audio_pipeline_device.Pass()), + video_pipeline_device_(video_pipeline_device.Pass()) { } MediaPipelineDevice::~MediaPipelineDevice() { diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device.h b/chromium/chromecast/media/cma/backend/media_pipeline_device.h index 8a961dc7fda..e010176965c 100644 --- a/chromium/chromecast/media/cma/backend/media_pipeline_device.h +++ b/chromium/chromecast/media/cma/backend/media_pipeline_device.h @@ -5,6 +5,7 @@ #ifndef CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_H_ #define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_H_ +#include "base/callback.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" @@ -12,29 +13,45 @@ namespace chromecast { namespace media { class AudioPipelineDevice; class MediaClockDevice; +class MediaPipelineDeviceFactory; class MediaPipelineDeviceParams; class VideoPipelineDevice; // MediaPipelineDevice is the owner of the underlying audio/video/clock -// devices. +// devices. It ensures that the clock outlives the audio and video devices, so +// it is safe for them to reference the clock with a raw pointer. class MediaPipelineDevice { public: - MediaPipelineDevice(); - virtual ~MediaPipelineDevice(); - - virtual AudioPipelineDevice* GetAudioPipelineDevice() const = 0; - - virtual VideoPipelineDevice* GetVideoPipelineDevice() const = 0; - - virtual MediaClockDevice* GetMediaClockDevice() const = 0; + // Constructs object using clock, audio, video created by given factory. + explicit MediaPipelineDevice(scoped_ptr<MediaPipelineDeviceFactory> factory); + + // Constructs explicitly from separate clock, audio, video elements + MediaPipelineDevice(scoped_ptr<MediaClockDevice> media_clock_device, + scoped_ptr<AudioPipelineDevice> audio_pipeline_device, + scoped_ptr<VideoPipelineDevice> video_pipeline_device); + ~MediaPipelineDevice(); + + AudioPipelineDevice* GetAudioPipelineDevice() const { + return audio_pipeline_device_.get(); + } + VideoPipelineDevice* GetVideoPipelineDevice() const { + return video_pipeline_device_.get(); + } + MediaClockDevice* GetMediaClockDevice() const { + return media_clock_device_.get(); + } private: + scoped_ptr<MediaClockDevice> media_clock_device_; + scoped_ptr<AudioPipelineDevice> audio_pipeline_device_; + scoped_ptr<VideoPipelineDevice> video_pipeline_device_; + DISALLOW_COPY_AND_ASSIGN(MediaPipelineDevice); }; -// Factory to create a MediaPipelineDevice. -scoped_ptr<MediaPipelineDevice> CreateMediaPipelineDevice( - const MediaPipelineDeviceParams& params); +// Factory method to create a MediaPipelineDevice. +typedef base::Callback<scoped_ptr<MediaPipelineDevice>( + const MediaPipelineDeviceParams&)> CreatePipelineDeviceCB; } // namespace media } // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_factory.h b/chromium/chromecast/media/cma/backend/media_pipeline_device_factory.h new file mode 100644 index 00000000000..28da11994d9 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_factory.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_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_FACTORY_H_ +#define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_FACTORY_H_ + +#include "base/memory/scoped_ptr.h" + +namespace chromecast { +namespace media { + +class AudioPipelineDevice; +class MediaClockDevice; +class MediaPipelineDeviceParams; +class VideoPipelineDevice; + +// Factory for creating platform-specific clock, audio and video devices. +// A new factory will be instantiated for each media player instance. +class MediaPipelineDeviceFactory { + public: + MediaPipelineDeviceFactory() {} + virtual ~MediaPipelineDeviceFactory() {} + + virtual scoped_ptr<MediaClockDevice> CreateMediaClockDevice() = 0; + virtual scoped_ptr<AudioPipelineDevice> CreateAudioPipelineDevice() = 0; + virtual scoped_ptr<VideoPipelineDevice> CreateVideoPipelineDevice() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(MediaPipelineDeviceFactory); +}; + +// Creates the platform-specific pipeline device factory. +// TODO(halliwell): move into libcast_media +scoped_ptr<MediaPipelineDeviceFactory> GetMediaPipelineDeviceFactory( + const MediaPipelineDeviceParams& params); + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_FACTORY_H_ diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_factory_default.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device_factory_default.cc new file mode 100644 index 00000000000..78663fe4962 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_factory_default.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/cma/backend/media_pipeline_device_factory_default.h" + +#include "chromecast/media/cma/backend/audio_pipeline_device_default.h" +#include "chromecast/media/cma/backend/media_clock_device_default.h" +#include "chromecast/media/cma/backend/video_pipeline_device_default.h" + +namespace chromecast { +namespace media { + +MediaPipelineDeviceFactoryDefault::MediaPipelineDeviceFactoryDefault() + : clock_(nullptr) { +} + +scoped_ptr<MediaClockDevice> +MediaPipelineDeviceFactoryDefault::CreateMediaClockDevice() { + DCHECK(!clock_); + clock_ = new MediaClockDeviceDefault(); + return make_scoped_ptr(clock_); +} + +scoped_ptr<AudioPipelineDevice> +MediaPipelineDeviceFactoryDefault::CreateAudioPipelineDevice() { + DCHECK(clock_); + return make_scoped_ptr(new AudioPipelineDeviceDefault(clock_)); +} + +scoped_ptr<VideoPipelineDevice> +MediaPipelineDeviceFactoryDefault::CreateVideoPipelineDevice() { + DCHECK(clock_); + return make_scoped_ptr(new VideoPipelineDeviceDefault(clock_)); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_factory_default.h b/chromium/chromecast/media/cma/backend/media_pipeline_device_factory_default.h new file mode 100644 index 00000000000..1927847a99a --- /dev/null +++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_factory_default.h @@ -0,0 +1,36 @@ +// 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_BACKEND_MEDIA_PIPELINE_DEVICE_FACTORY_DEFAULT_H_ +#define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_FACTORY_DEFAULT_H_ + +#include "chromecast/media/cma/backend/media_pipeline_device_factory.h" + +namespace chromecast { +namespace media { + +// Factory that instantiates default (stub) media pipeline device elements. +class MediaPipelineDeviceFactoryDefault : public MediaPipelineDeviceFactory { + public: + MediaPipelineDeviceFactoryDefault(); + ~MediaPipelineDeviceFactoryDefault() override {} + + // MediaPipelineDeviceFactory implementation + scoped_ptr<MediaClockDevice> CreateMediaClockDevice() override; + scoped_ptr<AudioPipelineDevice> CreateAudioPipelineDevice() override; + scoped_ptr<VideoPipelineDevice> CreateVideoPipelineDevice() override; + + private: + // Owned by MediaPipelineDevice, but we hold raw pointer in order to wire up + // to audio and video devices. MediaPipelineDevice guarantees the clock will + // outlive the audio and video devices. + MediaClockDevice* clock_; + + DISALLOW_COPY_AND_ASSIGN(MediaPipelineDeviceFactoryDefault); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_FACTORY_DEFAULT_H_ diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_factory_simple.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device_factory_simple.cc new file mode 100644 index 00000000000..92efca15342 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/media_pipeline_device_factory_simple.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/media/cma/backend/media_pipeline_device_factory_default.h" + +namespace chromecast { +namespace media { + +// Creates default (no-op) media pipeline device elements. +scoped_ptr<MediaPipelineDeviceFactory> GetMediaPipelineDeviceFactory( + const MediaPipelineDeviceParams& params) { + return make_scoped_ptr(new MediaPipelineDeviceFactoryDefault()); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc deleted file mode 100644 index 60d9576764a..00000000000 --- a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.cc +++ /dev/null @@ -1,583 +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/media/cma/backend/media_pipeline_device_fake.h" - -#include <list> -#include <vector> - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/message_loop/message_loop_proxy.h" -#include "chromecast/media/cma/backend/audio_pipeline_device.h" -#include "chromecast/media/cma/backend/media_clock_device.h" -#include "chromecast/media/cma/backend/media_component_device.h" -#include "chromecast/media/cma/backend/video_pipeline_device.h" -#include "chromecast/media/cma/base/decoder_buffer_base.h" -#include "chromecast/public/media/decoder_config.h" -#include "media/base/buffers.h" - -namespace chromecast { -namespace media { - -class MediaClockDeviceFake : public MediaClockDevice { - public: - MediaClockDeviceFake(); - ~MediaClockDeviceFake() override; - - // MediaClockDevice implementation. - 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_; - - // Media time sampled at STC time |stc_|. - base::TimeDelta media_time_; - base::TimeTicks stc_; - - float rate_; - - DISALLOW_COPY_AND_ASSIGN(MediaClockDeviceFake); -}; - -MediaClockDeviceFake::MediaClockDeviceFake() - : state_(kStateUninitialized), - media_time_(::media::kNoTimestamp()) { - DetachFromThread(); -} - -MediaClockDeviceFake::~MediaClockDeviceFake() { -} - -MediaClockDevice::State MediaClockDeviceFake::GetState() const { - DCHECK(CalledOnValidThread()); - return state_; -} - -bool MediaClockDeviceFake::SetState(State new_state) { - DCHECK(CalledOnValidThread()); - if (!MediaClockDevice::IsValidStateTransition(state_, new_state)) - return false; - - if (new_state == state_) - return true; - - state_ = new_state; - - if (state_ == kStateRunning) { - stc_ = base::TimeTicks::Now(); - DCHECK(media_time_ != ::media::kNoTimestamp()); - return true; - } - - if (state_ == kStateIdle) { - media_time_ = ::media::kNoTimestamp(); - return true; - } - - return true; -} - -bool MediaClockDeviceFake::ResetTimeline(base::TimeDelta time) { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(state_, kStateIdle); - media_time_ = time; - return true; -} - -bool MediaClockDeviceFake::SetRate(float rate) { - DCHECK(CalledOnValidThread()); - if (state_ == kStateRunning) { - base::TimeTicks now = base::TimeTicks::Now(); - media_time_ = media_time_ + (now - stc_) * rate_; - stc_ = now; - } - - rate_ = rate; - return true; -} - -base::TimeDelta MediaClockDeviceFake::GetTime() { - DCHECK(CalledOnValidThread()); - if (state_ != kStateRunning) - return media_time_; - - if (media_time_ == ::media::kNoTimestamp()) - return ::media::kNoTimestamp(); - - base::TimeTicks now = base::TimeTicks::Now(); - base::TimeDelta interpolated_media_time = - media_time_ + (now - stc_) * rate_; - return interpolated_media_time; -} - - -namespace { - -// Maximum number of frames that can be buffered. -const size_t kMaxFrameCount = 20; - -} // namespace - -class MediaComponentDeviceFake : public MediaComponentDevice { - public: - explicit MediaComponentDeviceFake(MediaClockDeviceFake* media_clock_device); - ~MediaComponentDeviceFake() override; - - // MediaComponentDevice implementation. - 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; - base::TimeDelta GetRenderingTime() const override; - base::TimeDelta GetRenderingDelay() const override; - bool GetStatistics(Statistics* stats) const override; - - private: - struct FakeDecoderBuffer { - FakeDecoderBuffer(); - ~FakeDecoderBuffer(); - - // Buffer size. - size_t size; - - // Presentation timestamp. - base::TimeDelta pts; - }; - - void RenderTask(); - - MediaClockDeviceFake* const media_clock_device_; - Client client_; - - State state_; - - // Indicate whether the end of stream has been received. - bool is_eos_; - - // Media time of the last rendered audio sample. - base::TimeDelta rendering_time_; - - // Frame decoded/rendered since the pipeline left the idle state. - uint64 decoded_frame_count_; - uint64 decoded_byte_count_; - - // List of frames not rendered yet. - std::list<FakeDecoderBuffer> frames_; - - // Indicate whether there is a scheduled rendering task. - bool scheduled_rendering_task_; - - // Pending frame. - scoped_refptr<DecoderBufferBase> pending_buffer_; - FrameStatusCB frame_pushed_cb_; - - base::WeakPtr<MediaComponentDeviceFake> weak_this_; - base::WeakPtrFactory<MediaComponentDeviceFake> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(MediaComponentDeviceFake); -}; - -MediaComponentDeviceFake::FakeDecoderBuffer::FakeDecoderBuffer() - : size(0) { -} - -MediaComponentDeviceFake::FakeDecoderBuffer::~FakeDecoderBuffer() { -} - -MediaComponentDeviceFake::MediaComponentDeviceFake( - MediaClockDeviceFake* media_clock_device) - : media_clock_device_(media_clock_device), - state_(kStateUninitialized), - rendering_time_(::media::kNoTimestamp()), - decoded_frame_count_(0), - decoded_byte_count_(0), - scheduled_rendering_task_(false), - weak_factory_(this) { - weak_this_ = weak_factory_.GetWeakPtr(); - DetachFromThread(); -} - -MediaComponentDeviceFake::~MediaComponentDeviceFake() { -} - -void MediaComponentDeviceFake::SetClient(const Client& client) { - DCHECK(CalledOnValidThread()); - client_ = client; -} - -MediaComponentDevice::State MediaComponentDeviceFake::GetState() const { - DCHECK(CalledOnValidThread()); - return state_; -} - -bool MediaComponentDeviceFake::SetState(State new_state) { - DCHECK(CalledOnValidThread()); - if (!MediaComponentDevice::IsValidStateTransition(state_, new_state)) - return false; - state_ = new_state; - - if (state_ == kStateIdle) { - // Back to the idle state: reset a bunch of parameters. - is_eos_ = false; - rendering_time_ = ::media::kNoTimestamp(); - decoded_frame_count_ = 0; - decoded_byte_count_ = 0; - frames_.clear(); - pending_buffer_ = scoped_refptr<DecoderBufferBase>(); - frame_pushed_cb_.Reset(); - return true; - } - - if (state_ == kStateRunning) { - if (!scheduled_rendering_task_) { - scheduled_rendering_task_ = true; - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&MediaComponentDeviceFake::RenderTask, weak_this_)); - } - return true; - } - - return true; -} - -bool MediaComponentDeviceFake::SetStartPts(base::TimeDelta time) { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(state_, kStateIdle); - rendering_time_ = time; - return true; -} - -MediaComponentDevice::FrameStatus MediaComponentDeviceFake::PushFrame( - const scoped_refptr<DecryptContext>& decrypt_context, - const scoped_refptr<DecoderBufferBase>& buffer, - const FrameStatusCB& completion_cb) { - DCHECK(CalledOnValidThread()); - DCHECK(state_ == kStatePaused || state_ == kStateRunning); - DCHECK(!is_eos_); - DCHECK(!pending_buffer_.get()); - DCHECK(buffer.get()); - - if (buffer->end_of_stream()) { - is_eos_ = true; - return kFrameSuccess; - } - - if (frames_.size() > kMaxFrameCount) { - pending_buffer_ = buffer; - frame_pushed_cb_ = completion_cb; - return kFramePending; - } - - FakeDecoderBuffer fake_buffer; - fake_buffer.size = buffer->data_size(); - fake_buffer.pts = buffer->timestamp(); - frames_.push_back(fake_buffer); - return kFrameSuccess; -} - -base::TimeDelta MediaComponentDeviceFake::GetRenderingTime() const { - return rendering_time_; -} - -base::TimeDelta MediaComponentDeviceFake::GetRenderingDelay() const { - NOTIMPLEMENTED(); - return ::media::kNoTimestamp(); -} - -void MediaComponentDeviceFake::RenderTask() { - scheduled_rendering_task_ = false; - - if (state_ != kStateRunning) - return; - - base::TimeDelta media_time = media_clock_device_->GetTime(); - if (media_time == ::media::kNoTimestamp()) { - scheduled_rendering_task_ = true; - base::MessageLoopProxy::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&MediaComponentDeviceFake::RenderTask, weak_this_), - base::TimeDelta::FromMilliseconds(50)); - return; - } - - while (!frames_.empty() && frames_.front().pts <= media_time) { - rendering_time_ = frames_.front().pts; - decoded_frame_count_++; - decoded_byte_count_ += frames_.front().size; - frames_.pop_front(); - if (pending_buffer_.get()) { - FakeDecoderBuffer fake_buffer; - fake_buffer.size = pending_buffer_->data_size(); - fake_buffer.pts = pending_buffer_->timestamp(); - frames_.push_back(fake_buffer); - pending_buffer_ = scoped_refptr<DecoderBufferBase>(); - base::ResetAndReturn(&frame_pushed_cb_).Run(kFrameSuccess); - } - } - - if (frames_.empty() && is_eos_) { - if (!client_.eos_cb.is_null()) - client_.eos_cb.Run(); - return; - } - - scheduled_rendering_task_ = true; - base::MessageLoopProxy::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&MediaComponentDeviceFake::RenderTask, weak_this_), - base::TimeDelta::FromMilliseconds(50)); -} - -bool MediaComponentDeviceFake::GetStatistics(Statistics* stats) const { - if (state_ != kStateRunning) - return false; - - // Note: what is returned here is not the number of samples but the number of - // frames. The value is different for audio. - stats->decoded_bytes = decoded_byte_count_; - stats->decoded_samples = decoded_frame_count_; - stats->dropped_samples = 0; - return true; -} - - -class AudioPipelineDeviceFake : public AudioPipelineDevice { - public: - explicit AudioPipelineDeviceFake(MediaClockDeviceFake* media_clock_device); - ~AudioPipelineDeviceFake() override; - - // AudioPipelineDevice implementation. - 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; - 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_; - - AudioConfig config_; - std::vector<uint8_t> config_extra_data_; - - DISALLOW_COPY_AND_ASSIGN(AudioPipelineDeviceFake); -}; - -AudioPipelineDeviceFake::AudioPipelineDeviceFake( - MediaClockDeviceFake* media_clock_device) - : fake_pipeline_(new MediaComponentDeviceFake(media_clock_device)) { - DetachFromThread(); -} - -AudioPipelineDeviceFake::~AudioPipelineDeviceFake() { -} - -void AudioPipelineDeviceFake::SetClient(const Client& client) { - fake_pipeline_->SetClient(client); -} - -MediaComponentDevice::State AudioPipelineDeviceFake::GetState() const { - return fake_pipeline_->GetState(); -} - -bool AudioPipelineDeviceFake::SetState(State new_state) { - bool success = fake_pipeline_->SetState(new_state); - if (!success) - return false; - - if (new_state == kStateIdle) { - DCHECK(IsValidConfig(config_)); - } - if (new_state == kStateUninitialized) { - config_ = AudioConfig(); - } - return true; -} - -bool AudioPipelineDeviceFake::SetStartPts(base::TimeDelta time) { - return fake_pipeline_->SetStartPts(time); -} - -MediaComponentDevice::FrameStatus AudioPipelineDeviceFake::PushFrame( - const scoped_refptr<DecryptContext>& decrypt_context, - const scoped_refptr<DecoderBufferBase>& buffer, - const FrameStatusCB& completion_cb) { - return fake_pipeline_->PushFrame(decrypt_context, buffer, completion_cb); -} - -base::TimeDelta AudioPipelineDeviceFake::GetRenderingTime() const { - return fake_pipeline_->GetRenderingTime(); -} - -base::TimeDelta AudioPipelineDeviceFake::GetRenderingDelay() const { - return fake_pipeline_->GetRenderingDelay(); -} - -bool AudioPipelineDeviceFake::SetConfig(const AudioConfig& config) { - DCHECK(CalledOnValidThread()); - 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; -} - -void AudioPipelineDeviceFake::SetStreamVolumeMultiplier(float multiplier) { - DCHECK(CalledOnValidThread()); -} - -bool AudioPipelineDeviceFake::GetStatistics(Statistics* stats) const { - return fake_pipeline_->GetStatistics(stats); -} - - -class VideoPipelineDeviceFake : public VideoPipelineDevice { - public: - explicit VideoPipelineDeviceFake(MediaClockDeviceFake* media_clock_device); - ~VideoPipelineDeviceFake() override; - - // VideoPipelineDevice implementation. - 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; - 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_; - - VideoConfig config_; - std::vector<uint8_t> config_extra_data_; - - DISALLOW_COPY_AND_ASSIGN(VideoPipelineDeviceFake); -}; - -VideoPipelineDeviceFake::VideoPipelineDeviceFake( - MediaClockDeviceFake* media_clock_device) - : fake_pipeline_(new MediaComponentDeviceFake(media_clock_device)) { - DetachFromThread(); -} - -VideoPipelineDeviceFake::~VideoPipelineDeviceFake() { -} - -void VideoPipelineDeviceFake::SetClient(const Client& client) { - fake_pipeline_->SetClient(client); -} - -MediaComponentDevice::State VideoPipelineDeviceFake::GetState() const { - return fake_pipeline_->GetState(); -} - -bool VideoPipelineDeviceFake::SetState(State new_state) { - bool success = fake_pipeline_->SetState(new_state); - if (!success) - return false; - - if (new_state == kStateIdle) { - DCHECK(IsValidConfig(config_)); - } - if (new_state == kStateUninitialized) { - config_ = VideoConfig(); - } - return true; -} - -bool VideoPipelineDeviceFake::SetStartPts(base::TimeDelta time) { - return fake_pipeline_->SetStartPts(time); -} - -MediaComponentDevice::FrameStatus VideoPipelineDeviceFake::PushFrame( - const scoped_refptr<DecryptContext>& decrypt_context, - const scoped_refptr<DecoderBufferBase>& buffer, - const FrameStatusCB& completion_cb) { - return fake_pipeline_->PushFrame(decrypt_context, buffer, completion_cb); -} - -base::TimeDelta VideoPipelineDeviceFake::GetRenderingTime() const { - return fake_pipeline_->GetRenderingTime(); -} - -base::TimeDelta VideoPipelineDeviceFake::GetRenderingDelay() const { - return fake_pipeline_->GetRenderingDelay(); -} - -void VideoPipelineDeviceFake::SetVideoClient(const VideoClient& client) { -} - -bool VideoPipelineDeviceFake::SetConfig(const VideoConfig& config) { - DCHECK(CalledOnValidThread()); - 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; -} - -bool VideoPipelineDeviceFake::GetStatistics(Statistics* stats) const { - return fake_pipeline_->GetStatistics(stats); -} - - -MediaPipelineDeviceFake::MediaPipelineDeviceFake() - : media_clock_device_(new MediaClockDeviceFake()), - audio_pipeline_device_( - new AudioPipelineDeviceFake(media_clock_device_.get())), - video_pipeline_device_( - new VideoPipelineDeviceFake(media_clock_device_.get())) { -} - -MediaPipelineDeviceFake::~MediaPipelineDeviceFake() { -} - -AudioPipelineDevice* MediaPipelineDeviceFake::GetAudioPipelineDevice() const { - return audio_pipeline_device_.get(); -} - -VideoPipelineDevice* MediaPipelineDeviceFake::GetVideoPipelineDevice() const { - return video_pipeline_device_.get(); -} - -MediaClockDevice* MediaPipelineDeviceFake::GetMediaClockDevice() const { - return media_clock_device_.get(); -} - -} // namespace media -} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h deleted file mode 100644 index 248c9a4322e..00000000000 --- a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake.h +++ /dev/null @@ -1,40 +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_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_FAKE_H_ -#define CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_FAKE_H_ - -#include "chromecast/media/cma/backend/media_pipeline_device.h" - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" - -namespace chromecast { -namespace media { -class AudioPipelineDeviceFake; -class MediaClockDeviceFake; -class VideoPipelineDeviceFake; - -class MediaPipelineDeviceFake : public MediaPipelineDevice { - public: - MediaPipelineDeviceFake(); - ~MediaPipelineDeviceFake() override; - - // MediaPipelineDevice implementation. - AudioPipelineDevice* GetAudioPipelineDevice() const override; - VideoPipelineDevice* GetVideoPipelineDevice() const override; - MediaClockDevice* GetMediaClockDevice() const override; - - private: - scoped_ptr<MediaClockDeviceFake> media_clock_device_; - scoped_ptr<AudioPipelineDeviceFake> audio_pipeline_device_; - scoped_ptr<VideoPipelineDeviceFake> video_pipeline_device_; - - DISALLOW_COPY_AND_ASSIGN(MediaPipelineDeviceFake); -}; - -} // namespace media -} // namespace chromecast - -#endif // CHROMECAST_MEDIA_CMA_BACKEND_MEDIA_PIPELINE_DEVICE_H_ diff --git a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake_factory.cc b/chromium/chromecast/media/cma/backend/media_pipeline_device_fake_factory.cc deleted file mode 100644 index 646a4637158..00000000000 --- a/chromium/chromecast/media/cma/backend/media_pipeline_device_fake_factory.cc +++ /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. - -#include "chromecast/media/cma/backend/media_pipeline_device_fake.h" - -#include "base/memory/scoped_ptr.h" - -namespace chromecast { -namespace media { - -scoped_ptr<MediaPipelineDevice> CreateMediaPipelineDevice( - const MediaPipelineDeviceParams& params) { - return scoped_ptr<MediaPipelineDevice>(new MediaPipelineDeviceFake()); -} - -} // namespace media -} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/video_pipeline_device_default.cc b/chromium/chromecast/media/cma/backend/video_pipeline_device_default.cc new file mode 100644 index 00000000000..90970cbdbd1 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/video_pipeline_device_default.cc @@ -0,0 +1,83 @@ +// 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/backend/video_pipeline_device_default.h" + +#include "chromecast/media/cma/backend/media_component_device_default.h" + +namespace chromecast { +namespace media { + +VideoPipelineDeviceDefault::VideoPipelineDeviceDefault( + MediaClockDevice* media_clock_device) + : pipeline_(new MediaComponentDeviceDefault(media_clock_device)) { + DetachFromThread(); +} + +VideoPipelineDeviceDefault::~VideoPipelineDeviceDefault() { +} + +void VideoPipelineDeviceDefault::SetClient(const Client& client) { + pipeline_->SetClient(client); +} + +MediaComponentDevice::State VideoPipelineDeviceDefault::GetState() const { + return pipeline_->GetState(); +} + +bool VideoPipelineDeviceDefault::SetState(State new_state) { + bool success = pipeline_->SetState(new_state); + if (!success) + return false; + + if (new_state == kStateIdle) { + DCHECK(IsValidConfig(config_)); + } + if (new_state == kStateUninitialized) { + config_ = VideoConfig(); + } + return true; +} + +bool VideoPipelineDeviceDefault::SetStartPts(base::TimeDelta time) { + return pipeline_->SetStartPts(time); +} + +MediaComponentDevice::FrameStatus VideoPipelineDeviceDefault::PushFrame( + const scoped_refptr<DecryptContext>& decrypt_context, + const scoped_refptr<DecoderBufferBase>& buffer, + const FrameStatusCB& completion_cb) { + return pipeline_->PushFrame(decrypt_context, buffer, completion_cb); +} + +base::TimeDelta VideoPipelineDeviceDefault::GetRenderingTime() const { + return pipeline_->GetRenderingTime(); +} + +base::TimeDelta VideoPipelineDeviceDefault::GetRenderingDelay() const { + return pipeline_->GetRenderingDelay(); +} + +void VideoPipelineDeviceDefault::SetVideoClient(const VideoClient& client) { +} + +bool VideoPipelineDeviceDefault::SetConfig(const VideoConfig& config) { + DCHECK(CalledOnValidThread()); + 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; +} + +bool VideoPipelineDeviceDefault::GetStatistics(Statistics* stats) const { + return pipeline_->GetStatistics(stats); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/video_pipeline_device_default.h b/chromium/chromecast/media/cma/backend/video_pipeline_device_default.h new file mode 100644 index 00000000000..49a4a0316ee --- /dev/null +++ b/chromium/chromecast/media/cma/backend/video_pipeline_device_default.h @@ -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. + +#ifndef CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_PIPELINE_DEVICE_DEFAULT_H_ +#define CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_PIPELINE_DEVICE_DEFAULT_H_ + +#include <vector> + +#include "base/macros.h" +#include "chromecast/media/cma/backend/video_pipeline_device.h" +#include "chromecast/public/media/decoder_config.h" + +namespace chromecast { +namespace media { + +class MediaClockDevice; +class MediaComponentDeviceDefault; + +class VideoPipelineDeviceDefault : public VideoPipelineDevice { + public: + explicit VideoPipelineDeviceDefault(MediaClockDevice* media_clock_device); + ~VideoPipelineDeviceDefault() override; + + // VideoPipelineDevice implementation. + 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; + 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<MediaComponentDeviceDefault> pipeline_; + + VideoConfig config_; + std::vector<uint8_t> config_extra_data_; + + DISALLOW_COPY_AND_ASSIGN(VideoPipelineDeviceDefault); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_PIPELINE_DEVICE_DEFAULT_H_ + diff --git a/chromium/chromecast/media/cma/backend/video_plane.cc b/chromium/chromecast/media/cma/backend/video_plane.cc deleted file mode 100644 index 5d8097f2a62..00000000000 --- a/chromium/chromecast/media/cma/backend/video_plane.cc +++ /dev/null @@ -1,54 +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/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 deleted file mode 100644 index a5f864c6d50..00000000000 --- a/chromium/chromecast/media/cma/backend/video_plane.h +++ /dev/null @@ -1,62 +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_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 deleted file mode 100644 index 73fa39ef5b6..00000000000 --- a/chromium/chromecast/media/cma/backend/video_plane_fake.cc +++ /dev/null @@ -1,33 +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/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 deleted file mode 100644 index 5fe04e6accb..00000000000 --- a/chromium/chromecast/media/cma/backend/video_plane_fake.h +++ /dev/null @@ -1,31 +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_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 deleted file mode 100644 index 310c8bc878a..00000000000 --- a/chromium/chromecast/media/cma/backend/video_plane_fake_factory.cc +++ /dev/null @@ -1,15 +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/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 index b919439cde6..49dbbdedfe4 100644 --- a/chromium/chromecast/media/cma/base/BUILD.gn +++ b/chromium/chromecast/media/cma/base/BUILD.gn @@ -21,15 +21,21 @@ source_set("base") { "decoder_buffer_adapter.h", "decoder_buffer_base.cc", "decoder_buffer_base.h", + "decoder_config_adapter.cc", + "decoder_config_adapter.h", "media_task_runner.cc", "media_task_runner.h", + "simple_media_task_runner.cc", + "simple_media_task_runner.h", ] + configs += [ "//chromecast:config" ] + deps = [ "//base", "//chromecast/base", + "//chromecast/base/metrics", "//media", + "//media:shared_memory_support", ] - - 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 f365534e457..3aba0730afe 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 @@ -244,8 +244,14 @@ void BalancedMediaTaskRunnerFactory::OnNewTask() { void BalancedMediaTaskRunnerFactory::UnregisterMediaTaskRunner( const scoped_refptr<BalancedMediaTaskRunner>& media_task_runner) { - base::AutoLock auto_lock(lock_); - task_runners_.erase(media_task_runner); + { + base::AutoLock auto_lock(lock_); + task_runners_.erase(media_task_runner); + } + // After removing one of the task runners some of the other task runners might + // need to be waken up, if they are no longer blocked by the balancing + // restrictions with the old stream. + OnNewTask(); } } // namespace media 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 b572e500247..02e29d4334f 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 @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" #include "base/time/time.h" #include "chromecast/media/cma/base/balanced_media_task_runner_factory.h" @@ -100,7 +101,7 @@ void BalancedMediaTaskRunnerTest::SetupTest( for (size_t k = 0; k < n; k++) { contexts_[k].media_task_runner = media_task_runner_factory_->CreateMediaTaskRunner( - base::MessageLoopProxy::current()); + base::ThreadTaskRunnerHandle::Get()); contexts_[k].is_pending_task = false; contexts_[k].task_index = 0; contexts_[k].task_timestamp_list.resize( @@ -149,10 +150,9 @@ void BalancedMediaTaskRunnerTest::ScheduleTask() { if (context.task_index >= context.task_timestamp_list.size() || context.is_pending_task) { pattern_index_ = next_pattern_index; - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&BalancedMediaTaskRunnerTest::ScheduleTask, - base::Unretained(this))); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&BalancedMediaTaskRunnerTest::ScheduleTask, + base::Unretained(this))); return; } @@ -177,10 +177,9 @@ void BalancedMediaTaskRunnerTest::ScheduleTask() { context.task_index++; pattern_index_ = next_pattern_index; - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&BalancedMediaTaskRunnerTest::ScheduleTask, - base::Unretained(this))); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&BalancedMediaTaskRunnerTest::ScheduleTask, + base::Unretained(this))); } void BalancedMediaTaskRunnerTest::Task( @@ -190,6 +189,13 @@ void BalancedMediaTaskRunnerTest::Task( expected_task_timestamps_.pop_front(); contexts_[task_runner_id].is_pending_task = false; + + // Release task runner if the task has ended + // otherwise, the task runner may may block other streams + auto& context = contexts_[task_runner_id]; + if (context.task_index >= context.task_timestamp_list.size()) { + context.media_task_runner = nullptr; + } } void BalancedMediaTaskRunnerTest::OnTestTimeout() { @@ -259,5 +265,29 @@ TEST_F(BalancedMediaTaskRunnerTest, TwoTaskRunnerUnbalanced) { EXPECT_TRUE(expected_task_timestamps_.empty()); } +TEST_F(BalancedMediaTaskRunnerTest, TwoStreamsOfDifferentLength) { + scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); + + std::vector<std::vector<int>> timestamps = { + // One longer stream and one shorter stream. + // The longer stream runs first, then the shorter stream begins. + // After shorter stream ends, it shouldn't block the longer one. + {0, 20, 40, 60, 80, 100, 120, 140, 160}, + {51, 61, 71, 81}, + }; + + std::vector<int> expected_timestamps = { + 0, 20, 40, 60, 51, 80, 61, 71, 81, 100, 120, 140, 160}; + + std::vector<size_t> scheduling_pattern = { + 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0}; + + SetupTest(base::TimeDelta::FromMilliseconds(30), timestamps, + scheduling_pattern, expected_timestamps); + ProcessAllTasks(); + message_loop->Run(); + EXPECT_TRUE(expected_task_timestamps_.empty()); +} + } // namespace media } // namespace chromecast diff --git a/chromium/chromecast/media/cma/base/buffering_controller.cc b/chromium/chromecast/media/cma/base/buffering_controller.cc index 9e7cb6c9c01..3cd47bc9593 100644 --- a/chromium/chromecast/media/cma/base/buffering_controller.cc +++ b/chromium/chromecast/media/cma/base/buffering_controller.cc @@ -6,7 +6,7 @@ #include "base/bind.h" #include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" #include "chromecast/base/metrics/cast_metrics_helper.h" #include "chromecast/media/cma/base/buffering_state.h" #include "chromecast/media/cma/base/cma_logging.h" diff --git a/chromium/chromecast/media/cma/base/buffering_frame_provider.cc b/chromium/chromecast/media/cma/base/buffering_frame_provider.cc index b8d56985da4..7371426fc5f 100644 --- a/chromium/chromecast/media/cma/base/buffering_frame_provider.cc +++ b/chromium/chromecast/media/cma/base/buffering_frame_provider.cc @@ -114,7 +114,7 @@ void BufferingFrameProvider::RequestBufferIfNeeded() { return; is_pending_request_ = true; - coded_frame_provider_->Read(BindToCurrentLoop( + coded_frame_provider_->Read(::media::BindToCurrentLoop( base::Bind(&BufferingFrameProvider::OnNewBuffer, weak_this_))); } diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc index 45095da169b..ed8a779b5c0 100644 --- a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc +++ b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc @@ -10,17 +10,31 @@ namespace chromecast { namespace media { DecoderBufferAdapter::DecoderBufferAdapter( - const scoped_refptr< ::media::DecoderBuffer>& buffer) - : buffer_(buffer) { + const scoped_refptr<::media::DecoderBuffer>& buffer) + : DecoderBufferAdapter(kPrimary, buffer) { +} + +DecoderBufferAdapter::DecoderBufferAdapter( + StreamId stream_id, const scoped_refptr<::media::DecoderBuffer>& buffer) + : stream_id_(stream_id), + buffer_(buffer) { } DecoderBufferAdapter::~DecoderBufferAdapter() { } +StreamId DecoderBufferAdapter::stream_id() const { + return stream_id_; +} + base::TimeDelta DecoderBufferAdapter::timestamp() const { return buffer_->timestamp(); } +void DecoderBufferAdapter::set_timestamp(const base::TimeDelta& timestamp) { + buffer_->set_timestamp(timestamp); +} + const uint8* DecoderBufferAdapter::data() const { return buffer_->data(); } diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h index 054e60ee59e..7c48ec8a40b 100644 --- a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h +++ b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h @@ -20,11 +20,17 @@ namespace media { // into a DecoderBufferBase. class DecoderBufferAdapter : public DecoderBufferBase { public: + // Using explicit constructor without providing stream Id will set it to + // kPrimary by default. explicit DecoderBufferAdapter( - const scoped_refptr< ::media::DecoderBuffer>& buffer); + const scoped_refptr<::media::DecoderBuffer>& buffer); + DecoderBufferAdapter( + StreamId stream_id, const scoped_refptr<::media::DecoderBuffer>& buffer); // DecoderBufferBase implementation. + StreamId stream_id() const override; base::TimeDelta timestamp() const override; + void set_timestamp(const base::TimeDelta& timestamp) override; const uint8* data() const override; uint8* writable_data() const override; size_t data_size() const override; @@ -34,7 +40,8 @@ class DecoderBufferAdapter : public DecoderBufferBase { private: ~DecoderBufferAdapter() override; - scoped_refptr< ::media::DecoderBuffer> const buffer_; + StreamId stream_id_; + scoped_refptr<::media::DecoderBuffer> const buffer_; DISALLOW_COPY_AND_ASSIGN(DecoderBufferAdapter); }; diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_base.h b/chromium/chromecast/media/cma/base/decoder_buffer_base.h index 099b44d7a9e..1fca1b27e91 100644 --- a/chromium/chromecast/media/cma/base/decoder_buffer_base.h +++ b/chromium/chromecast/media/cma/base/decoder_buffer_base.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/time/time.h" +#include "chromecast/public/media/stream_id.h" namespace media { class DecryptConfig; @@ -25,9 +26,16 @@ class DecoderBufferBase public: DecoderBufferBase(); + // Returns the stream id of this decoder buffer belonging to. it's optional + // and default value is kPrimary. + virtual StreamId stream_id() const = 0; + // Returns the PTS of the frame. virtual base::TimeDelta timestamp() const = 0; + // Sets the PTS of the frame. + virtual void set_timestamp(const base::TimeDelta& timestamp) = 0; + // Gets the frame data. virtual const uint8* data() const = 0; virtual uint8* writable_data() const = 0; diff --git a/chromium/chromecast/media/cma/base/decoder_config_adapter.cc b/chromium/chromecast/media/cma/base/decoder_config_adapter.cc index 793395c9c14..b75d5f2be86 100644 --- a/chromium/chromecast/media/cma/base/decoder_config_adapter.cc +++ b/chromium/chromecast/media/cma/base/decoder_config_adapter.cc @@ -32,6 +32,29 @@ AudioCodec ToAudioCodec(const ::media::AudioCodec audio_codec) { return kAudioCodecUnknown; } +SampleFormat ToSampleFormat(const ::media::SampleFormat sample_format) { + switch (sample_format) { + case ::media::kUnknownSampleFormat: + return kUnknownSampleFormat; + case ::media::kSampleFormatU8: + return kSampleFormatU8; + case ::media::kSampleFormatS16: + return kSampleFormatS16; + case ::media::kSampleFormatS32: + return kSampleFormatS32; + case ::media::kSampleFormatF32: + return kSampleFormatF32; + case ::media::kSampleFormatPlanarS16: + return kSampleFormatPlanarS16; + case ::media::kSampleFormatPlanarF32: + return kSampleFormatPlanarF32; + case ::media::kSampleFormatPlanarS32: + return kSampleFormatPlanarS32; + } + NOTREACHED(); + return kUnknownSampleFormat; +} + // 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) { @@ -83,36 +106,108 @@ VideoProfile ToVideoProfile(const ::media::VideoCodecProfile codec_profile) { return kVideoProfileUnknown; } +::media::ChannelLayout ToMediaChannelLayout(int channel_number) { + switch (channel_number) { + case 1: + return ::media::ChannelLayout::CHANNEL_LAYOUT_MONO; + case 2: + return ::media::ChannelLayout::CHANNEL_LAYOUT_STEREO; + default: + return ::media::ChannelLayout::CHANNEL_LAYOUT_UNSUPPORTED; + } +} + +::media::SampleFormat ToMediaSampleFormat(const SampleFormat sample_format) { + switch (sample_format) { + case kUnknownSampleFormat: + return ::media::kUnknownSampleFormat; + case kSampleFormatU8: + return ::media::kSampleFormatU8; + case kSampleFormatS16: + return ::media::kSampleFormatS16; + case kSampleFormatS32: + return ::media::kSampleFormatS32; + case kSampleFormatF32: + return ::media::kSampleFormatF32; + case kSampleFormatPlanarS16: + return ::media::kSampleFormatPlanarS16; + case kSampleFormatPlanarF32: + return ::media::kSampleFormatPlanarF32; + case kSampleFormatPlanarS32: + return ::media::kSampleFormatPlanarS32; + default: + NOTREACHED(); + return ::media::kUnknownSampleFormat; + } +} + +::media::AudioCodec ToMediaAudioCodec( + const chromecast::media::AudioCodec codec) { + switch (codec) { + case kAudioCodecUnknown: + return ::media::kUnknownAudioCodec; + case kCodecAAC: + return ::media::kCodecAAC; + case kCodecMP3: + return ::media::kCodecMP3; + case kCodecPCM: + return ::media::kCodecPCM; + case kCodecPCM_S16BE: + return ::media::kCodecPCM_S16BE; + case kCodecVorbis: + return ::media::kCodecVorbis; + case kCodecOpus: + return ::media::kCodecOpus; + default: + return ::media::kUnknownAudioCodec; + } +} + } // namespace // static AudioConfig DecoderConfigAdapter::ToCastAudioConfig( + StreamId id, const ::media::AudioDecoderConfig& config) { AudioConfig audio_config; if (!config.IsValidConfig()) { return audio_config; } + audio_config.id = id; audio_config.codec = ToAudioCodec(config.codec()); + audio_config.sample_format = ToSampleFormat(config.sample_format()); 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.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 +::media::AudioDecoderConfig DecoderConfigAdapter::ToMediaAudioDecoderConfig( + const AudioConfig& config) { + return ::media::AudioDecoderConfig( + ToMediaAudioCodec(config.codec), + ToMediaSampleFormat(config.sample_format), + ToMediaChannelLayout(config.channel_number), config.samples_per_second, + config.extra_data, config.extra_data_size, config.is_encrypted); +} + +// static VideoConfig DecoderConfigAdapter::ToCastVideoConfig( + StreamId id, const ::media::VideoDecoderConfig& config) { VideoConfig video_config; if (!config.IsValidConfig()) { return video_config; } + video_config.id = id; video_config.codec = ToVideoCodec(config.codec()); video_config.profile = ToVideoProfile(config.profile()); video_config.extra_data = (config.extra_data_size() > 0) ? diff --git a/chromium/chromecast/media/cma/base/decoder_config_adapter.h b/chromium/chromecast/media/cma/base/decoder_config_adapter.h index f04c1c2f1d3..07a54a6dd6c 100644 --- a/chromium/chromecast/media/cma/base/decoder_config_adapter.h +++ b/chromium/chromecast/media/cma/base/decoder_config_adapter.h @@ -16,10 +16,16 @@ class DecoderConfigAdapter { public: // Converts ::media::AudioDecoderConfig to chromecast::media::AudioConfig. static AudioConfig ToCastAudioConfig( + StreamId id, const ::media::AudioDecoderConfig& config); + // Converts chromecast::media::AudioConfig to ::media::AudioDecoderConfig. + static ::media::AudioDecoderConfig ToMediaAudioDecoderConfig( + const AudioConfig& config); + // Converts ::media::VideoDecoderConfig to chromecast::media::VideoConfig. static VideoConfig ToCastVideoConfig( + StreamId id, const ::media::VideoDecoderConfig& config); }; diff --git a/chromium/chromecast/media/cma/base/simple_media_task_runner.cc b/chromium/chromecast/media/cma/base/simple_media_task_runner.cc new file mode 100644 index 00000000000..c4a54395f66 --- /dev/null +++ b/chromium/chromecast/media/cma/base/simple_media_task_runner.cc @@ -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. + +#include "chromecast/media/cma/base/simple_media_task_runner.h" + +#include "base/single_thread_task_runner.h" + +namespace chromecast { +namespace media { + +SimpleMediaTaskRunner::SimpleMediaTaskRunner( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) + : task_runner_(task_runner) { +} + +SimpleMediaTaskRunner::~SimpleMediaTaskRunner() { +} + +bool SimpleMediaTaskRunner::PostMediaTask( + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta timestamp) { + return task_runner_->PostTask(from_here, task); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/base/simple_media_task_runner.h b/chromium/chromecast/media/cma/base/simple_media_task_runner.h new file mode 100644 index 00000000000..67979f70f1d --- /dev/null +++ b/chromium/chromecast/media/cma/base/simple_media_task_runner.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_MEDIA_CMA_BASE_SIMPLE_MEDIA_TASK_RUNNER_H_ +#define CHROMECAST_MEDIA_CMA_BASE_SIMPLE_MEDIA_TASK_RUNNER_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "chromecast/media/cma/base/media_task_runner.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace chromecast { +namespace media { + +// This is a light version of task runner which post tasks immediately +// by ignoring the timestamps once receiving the request. +class SimpleMediaTaskRunner : public MediaTaskRunner { + public: + SimpleMediaTaskRunner( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); + + // MediaTaskRunner implementation. + bool PostMediaTask(const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta timestamp) override; + + private: + ~SimpleMediaTaskRunner() override; + + scoped_refptr<base::SingleThreadTaskRunner> const task_runner_; + + DISALLOW_COPY_AND_ASSIGN(SimpleMediaTaskRunner); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BASE_SIMPLE_MEDIA_TASK_RUNNER_H_ diff --git a/chromium/chromecast/media/cma/filters/BUILD.gn b/chromium/chromecast/media/cma/filters/BUILD.gn index 605075533f9..7126bbb2941 100644 --- a/chromium/chromecast/media/cma/filters/BUILD.gn +++ b/chromium/chromecast/media/cma/filters/BUILD.gn @@ -8,13 +8,20 @@ source_set("filters") { "cma_renderer.h", "demuxer_stream_adapter.cc", "demuxer_stream_adapter.h", + "hole_frame_factory.cc", + "hole_frame_factory.h", ] + configs += [ "//chromecast:config" ] + deps = [ "//base", + "//chromecast/base", "//chromecast/media/cma/base", + "//chromecast/media/cma/pipeline", + "//gpu/command_buffer/client:gles2_interface", + "//gpu/command_buffer/common", "//media", + "//ui/gfx/geometry", ] - - configs += [ "//chromecast:config" ] } diff --git a/chromium/chromecast/media/cma/filters/cma_renderer.cc b/chromium/chromecast/media/cma/filters/cma_renderer.cc index b8e7b42ba9a..d2ec2bd82e3 100644 --- a/chromium/chromecast/media/cma/filters/cma_renderer.cc +++ b/chromium/chromecast/media/cma/filters/cma_renderer.cc @@ -7,10 +7,12 @@ #include "base/bind.h" #include "base/callback_helpers.h" #include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.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/filters/hole_frame_factory.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" @@ -21,8 +23,8 @@ #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 "media/renderers/gpu_video_accelerator_factories.h" #include "ui/gfx/geometry/size.h" namespace chromecast { @@ -37,8 +39,10 @@ const base::TimeDelta kMaxDeltaFetcher( } // namespace -CmaRenderer::CmaRenderer(scoped_ptr<MediaPipeline> media_pipeline, - ::media::VideoRendererSink* video_renderer_sink) +CmaRenderer::CmaRenderer( + scoped_ptr<MediaPipeline> media_pipeline, + ::media::VideoRendererSink* video_renderer_sink, + const scoped_refptr<::media::GpuVideoAcceleratorFactories>& gpu_factories) : media_task_runner_factory_( new BalancedMediaTaskRunnerFactory(kMaxDeltaFetcher)), media_pipeline_(media_pipeline.Pass()), @@ -53,6 +57,7 @@ CmaRenderer::CmaRenderer(scoped_ptr<MediaPipeline> media_pipeline, received_video_eos_(false), initial_natural_size_(gfx::Size()), initial_video_hole_created_(false), + gpu_factories_(gpu_factories), time_interpolator_( new ::media::TimeDeltaInterpolator(&default_tick_clock_)), playback_rate_(1.0), @@ -94,6 +99,9 @@ void CmaRenderer::Initialize( DCHECK(demuxer_stream_provider->GetStream(::media::DemuxerStream::AUDIO) || demuxer_stream_provider->GetStream(::media::DemuxerStream::VIDEO)); + // Deferred from ctor so as to initialise on correct thread. + hole_frame_factory_.reset(new HoleFrameFactory(gpu_factories_)); + BeginStateTransition(); demuxer_stream_provider_ = demuxer_stream_provider; @@ -151,7 +159,6 @@ void CmaRenderer::StartPlayingFrom(base::TimeDelta time) { 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) @@ -162,9 +169,8 @@ void CmaRenderer::StartPlayingFrom(base::TimeDelta time) { if (!initial_video_hole_created_) { initial_video_hole_created_ = true; video_renderer_sink_->PaintFrameUsingOldRenderingPath( - ::media::VideoFrame::CreateHoleFrame(initial_natural_size_)); + hole_frame_factory_->CreateHoleFrame(initial_natural_size_)); } -#endif { base::AutoLock auto_lock(time_interpolator_lock_); @@ -232,13 +238,14 @@ void CmaRenderer::InitializeAudioPipeline() { 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); + ::media::PipelineStatusCB audio_initialization_done_cb = + ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnAudioPipelineInitializeDone, weak_this_, + stream != nullptr)); if (!stream) { + CMALOG(kLogControl) << __FUNCTION__ << ": no audio stream, skipping init."; audio_initialization_done_cb.Run(::media::PIPELINE_OK); return; } @@ -253,11 +260,8 @@ void CmaRenderer::InitializeAudioPipeline() { 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)); + scoped_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter( + base::ThreadTaskRunnerHandle::Get(), media_task_runner_factory_, stream)); const ::media::AudioDecoderConfig& config = stream->audio_decoder_config(); if (config.codec() == ::media::kCodecAAC) @@ -268,7 +272,9 @@ void CmaRenderer::InitializeAudioPipeline() { } void CmaRenderer::OnAudioPipelineInitializeDone( + bool audio_stream_present, ::media::PipelineStatus status) { + CMALOG(kLogControl) << __FUNCTION__ << ": state=" << state_; DCHECK(thread_checker_.CalledOnValidThread()); // OnError() may be fired at any time, even before initialization is complete. @@ -281,8 +287,8 @@ void CmaRenderer::OnAudioPipelineInitializeDone( base::ResetAndReturn(&init_cb_).Run(status); return; } - has_audio_ = true; + has_audio_ = audio_stream_present; InitializeVideoPipeline(); } @@ -291,13 +297,14 @@ void CmaRenderer::InitializeVideoPipeline() { 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); + ::media::PipelineStatusCB video_initialization_done_cb = + ::media::BindToCurrentLoop( + base::Bind(&CmaRenderer::OnVideoPipelineInitializeDone, weak_this_, + stream != nullptr)); if (!stream) { + CMALOG(kLogControl) << __FUNCTION__ << ": no video stream, skipping init."; video_initialization_done_cb.Run(::media::PIPELINE_OK); return; } @@ -314,11 +321,8 @@ void CmaRenderer::InitializeVideoPipeline() { 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)); + scoped_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter( + base::ThreadTaskRunnerHandle::Get(), media_task_runner_factory_, stream)); const ::media::VideoDecoderConfig& config = stream->video_decoder_config(); if (config.codec() == ::media::kCodecH264) @@ -326,14 +330,18 @@ void CmaRenderer::InitializeVideoPipeline() { initial_natural_size_ = config.natural_size(); + std::vector<::media::VideoDecoderConfig> configs; + configs.push_back(config); media_pipeline_->InitializeVideo( - config, + configs, frame_provider.Pass(), video_initialization_done_cb); } void CmaRenderer::OnVideoPipelineInitializeDone( + bool video_stream_present, ::media::PipelineStatus status) { + CMALOG(kLogControl) << __FUNCTION__ << ": state=" << state_; DCHECK(thread_checker_.CalledOnValidThread()); // OnError() may be fired at any time, even before initialization is complete. @@ -346,17 +354,16 @@ void CmaRenderer::OnVideoPipelineInitializeDone( base::ResetAndReturn(&init_cb_).Run(status); return; } - has_video_ = true; + has_video_ = video_stream_present; 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"; + LOG(WARNING) << __FUNCTION__ << " Ignoring a late EOS event"; return; } @@ -370,6 +377,9 @@ void CmaRenderer::OnEosReached(bool is_audio) { bool audio_finished = !has_audio_ || received_audio_eos_; bool video_finished = !has_video_ || received_video_eos_; + CMALOG(kLogControl) << __FUNCTION__ + << " audio_finished=" << audio_finished + << " video_finished=" << video_finished; if (audio_finished && video_finished) ended_cb_.Run(); } @@ -382,10 +392,8 @@ void CmaRenderer::OnStatisticsUpdated( void CmaRenderer::OnNaturalSizeChanged(const gfx::Size& size) { DCHECK(thread_checker_.CalledOnValidThread()); -#if defined(VIDEO_HOLE) video_renderer_sink_->PaintFrameUsingOldRenderingPath( - ::media::VideoFrame::CreateHoleFrame(size)); -#endif + hole_frame_factory_->CreateHoleFrame(size)); } void CmaRenderer::OnPlaybackTimeUpdated( diff --git a/chromium/chromecast/media/cma/filters/cma_renderer.h b/chromium/chromecast/media/cma/filters/cma_renderer.h index d33699ea586..f76226bd215 100644 --- a/chromium/chromecast/media/cma/filters/cma_renderer.h +++ b/chromium/chromecast/media/cma/filters/cma_renderer.h @@ -16,11 +16,12 @@ #include "ui/gfx/geometry/size.h" namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; } namespace media { class DemuxerStreamProvider; +class GpuVideoAcceleratorFactories; class TimeDeltaInterpolator; class VideoFrame; class VideoRendererSink; @@ -30,13 +31,16 @@ namespace chromecast { namespace media { class AudioPipeline; class BalancedMediaTaskRunnerFactory; +class HoleFrameFactory; class MediaPipeline; class VideoPipeline; class CmaRenderer : public ::media::Renderer { public: CmaRenderer(scoped_ptr<MediaPipeline> media_pipeline, - ::media::VideoRendererSink* video_renderer_sink); + ::media::VideoRendererSink* video_renderer_sink, + const scoped_refptr<::media::GpuVideoAcceleratorFactories>& + gpu_factories); ~CmaRenderer() override; // ::media::Renderer implementation: @@ -70,9 +74,11 @@ class CmaRenderer : public ::media::Renderer { // 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 OnAudioPipelineInitializeDone(bool audio_stream_present, + ::media::PipelineStatus status); void InitializeVideoPipeline(); - void OnVideoPipelineInitializeDone(::media::PipelineStatus status); + void OnVideoPipelineInitializeDone(bool video_stream_present, + ::media::PipelineStatus status); // Callbacks for AvPipelineClient. void OnEosReached(bool is_audio); @@ -123,9 +129,11 @@ class CmaRenderer : public ::media::Renderer { bool received_audio_eos_; bool received_video_eos_; - // Data members for helping the creation of the initial video hole frame. + // Data members for helping the creation of the video hole frame. gfx::Size initial_natural_size_; bool initial_video_hole_created_; + scoped_refptr<::media::GpuVideoAcceleratorFactories> gpu_factories_; + scoped_ptr<HoleFrameFactory> hole_frame_factory_; // Lock protecting access to |time_interpolator_|. base::Lock time_interpolator_lock_; diff --git a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc index c2fd5a47861..40725237bb7 100644 --- a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc +++ b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter.cc @@ -10,7 +10,7 @@ #include "chromecast/media/cma/base/balanced_media_task_runner_factory.h" #include "chromecast/media/cma/base/cma_logging.h" #include "chromecast/media/cma/base/decoder_buffer_adapter.h" -#include "chromecast/media/cma/base/media_task_runner.h" +#include "chromecast/media/cma/base/simple_media_task_runner.h" #include "media/base/bind_to_current_loop.h" #include "media/base/buffers.h" #include "media/base/decoder_buffer.h" @@ -19,52 +19,14 @@ namespace chromecast { namespace media { -namespace { - -class DummyMediaTaskRunner : public MediaTaskRunner { - public: - DummyMediaTaskRunner( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); - - // MediaTaskRunner implementation. - bool PostMediaTask( - const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta timestamp) override; - - private: - ~DummyMediaTaskRunner() override; - - scoped_refptr<base::SingleThreadTaskRunner> const task_runner_; - - DISALLOW_COPY_AND_ASSIGN(DummyMediaTaskRunner); -}; - -DummyMediaTaskRunner::DummyMediaTaskRunner( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) - : task_runner_(task_runner) { -} - -DummyMediaTaskRunner::~DummyMediaTaskRunner() { -} - -bool DummyMediaTaskRunner::PostMediaTask( - const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta timestamp) { - return task_runner_->PostTask(from_here, task); -} - -} // namespace - DemuxerStreamAdapter::DemuxerStreamAdapter( const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, const scoped_refptr<BalancedMediaTaskRunnerFactory>& - media_task_runner_factory, + media_task_runner_factory, ::media::DemuxerStream* demuxer_stream) : task_runner_(task_runner), media_task_runner_factory_(media_task_runner_factory), - media_task_runner_(new DummyMediaTaskRunner(task_runner)), + media_task_runner_(new SimpleMediaTaskRunner(task_runner)), demuxer_stream_(demuxer_stream), is_pending_read_(false), is_pending_demuxer_read_(false), @@ -152,7 +114,7 @@ void DemuxerStreamAdapter::RequestBuffer(const ReadCB& read_cb) { void DemuxerStreamAdapter::OnNewBuffer( const ReadCB& read_cb, ::media::DemuxerStream::Status status, - const scoped_refptr< ::media::DecoderBuffer>& input) { + const scoped_refptr<::media::DecoderBuffer>& input) { DCHECK(thread_checker_.CalledOnValidThread()); is_pending_demuxer_read_ = false; 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 084acd7e3a4..f7d668a1973 100644 --- a/chromium/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc +++ b/chromium/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc @@ -8,11 +8,14 @@ #include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" #include "base/time/time.h" #include "chromecast/media/cma/base/balanced_media_task_runner_factory.h" #include "chromecast/media/cma/base/decoder_buffer_base.h" #include "chromecast/media/cma/filters/demuxer_stream_adapter.h" +#include "chromecast/media/cma/test/demuxer_stream_for_test.h" #include "media/base/audio_decoder_config.h" #include "media/base/decoder_buffer.h" #include "media/base/demuxer_stream.h" @@ -22,134 +25,6 @@ namespace chromecast { namespace media { -namespace { - -class DummyDemuxerStream : public ::media::DemuxerStream { - public: - // Creates a demuxer stream which provides frames either with a delay - // or instantly. The scheduling pattern is the following: - // - provides |delayed_frame_count| frames with a delay, - // - then provides the following |cycle_count| - |delayed_frame_count| - // instantly, - // - then provides |delayed_frame_count| frames with a delay, - // - ... and so on. - // Special cases: - // - all frames are delayed: |delayed_frame_count| = |cycle_count| - // - all frames are provided instantly: |delayed_frame_count| = 0 - // |config_idx| is a list of frame index before which there is - // a change of decoder configuration. - DummyDemuxerStream(int cycle_count, - int delayed_frame_count, - const std::list<int>& config_idx); - ~DummyDemuxerStream() override; - - // ::media::DemuxerStream implementation. - 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_; - } - - private: - void DoRead(const ReadCB& read_cb); - - // Demuxer configuration. - const int cycle_count_; - const int delayed_frame_count_; - std::list<int> config_idx_; - - // Number of frames sent so far. - int frame_count_; - - bool has_pending_read_; - - DISALLOW_COPY_AND_ASSIGN(DummyDemuxerStream); -}; - -DummyDemuxerStream::DummyDemuxerStream( - int cycle_count, - int delayed_frame_count, - const std::list<int>& config_idx) - : cycle_count_(cycle_count), - delayed_frame_count_(delayed_frame_count), - config_idx_(config_idx), - frame_count_(0), - has_pending_read_(false) { - DCHECK_LE(delayed_frame_count, cycle_count); -} - -DummyDemuxerStream::~DummyDemuxerStream() { -} - -void DummyDemuxerStream::Read(const ReadCB& read_cb) { - has_pending_read_ = true; - if (!config_idx_.empty() && config_idx_.front() == frame_count_) { - config_idx_.pop_front(); - has_pending_read_ = false; - read_cb.Run(kConfigChanged, - scoped_refptr< ::media::DecoderBuffer>()); - return; - } - - if ((frame_count_ % cycle_count_) < delayed_frame_count_) { - base::MessageLoopProxy::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&DummyDemuxerStream::DoRead, base::Unretained(this), - read_cb), - base::TimeDelta::FromMilliseconds(20)); - return; - } - DoRead(read_cb); -} - -::media::AudioDecoderConfig DummyDemuxerStream::audio_decoder_config() { - LOG(FATAL) << "DummyDemuxerStream is a video DemuxerStream"; - return ::media::AudioDecoderConfig(); -} - -::media::VideoDecoderConfig DummyDemuxerStream::video_decoder_config() { - gfx::Size coded_size(640, 480); - gfx::Rect visible_rect(640, 480); - gfx::Size natural_size(640, 480); - return ::media::VideoDecoderConfig( - ::media::kCodecH264, - ::media::VIDEO_CODEC_PROFILE_UNKNOWN, - ::media::VideoFrame::YV12, - coded_size, - visible_rect, - natural_size, - NULL, 0, - false); -} - -::media::DemuxerStream::Type DummyDemuxerStream::type() const { - return VIDEO; -} - -bool DummyDemuxerStream::SupportsConfigChanges() { - return true; -} - -::media::VideoRotation DummyDemuxerStream::video_rotation() { - return ::media::VIDEO_ROTATION_0; -} - -void DummyDemuxerStream::DoRead(const ReadCB& read_cb) { - has_pending_read_ = false; - scoped_refptr< ::media::DecoderBuffer> buffer( - new ::media::DecoderBuffer(16)); - buffer->set_timestamp(frame_count_ * base::TimeDelta::FromMilliseconds(40)); - frame_count_++; - read_cb.Run(kOk, buffer); -} - -} // namespace - class DemuxerStreamAdapterTest : public testing::Test { public: DemuxerStreamAdapterTest(); @@ -181,7 +56,7 @@ class DemuxerStreamAdapterTest : public testing::Test { // List of expected frame indices with decoder config changes. std::list<int> config_idx_; - scoped_ptr<DummyDemuxerStream> demuxer_stream_; + scoped_ptr<DemuxerStreamForTest> demuxer_stream_; scoped_ptr<CodedFrameProvider> coded_frame_provider_; @@ -197,11 +72,9 @@ DemuxerStreamAdapterTest::~DemuxerStreamAdapterTest() { void DemuxerStreamAdapterTest::Initialize( ::media::DemuxerStream* demuxer_stream) { - coded_frame_provider_.reset( - new DemuxerStreamAdapter( - base::MessageLoopProxy::current(), - scoped_refptr<BalancedMediaTaskRunnerFactory>(), - demuxer_stream)); + coded_frame_provider_.reset(new DemuxerStreamAdapter( + base::ThreadTaskRunnerHandle::Get(), + scoped_refptr<BalancedMediaTaskRunnerFactory>(), demuxer_stream)); } void DemuxerStreamAdapterTest::Start() { @@ -286,9 +159,8 @@ TEST_F(DemuxerStreamAdapterTest, NoDelay) { int cycle_count = 1; int delayed_frame_count = 0; - demuxer_stream_.reset( - new DummyDemuxerStream( - cycle_count, delayed_frame_count, config_idx_)); + demuxer_stream_.reset(new DemuxerStreamForTest( + -1, cycle_count, delayed_frame_count, config_idx_)); scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); Initialize(demuxer_stream_.get()); @@ -307,9 +179,8 @@ TEST_F(DemuxerStreamAdapterTest, AllDelayed) { int cycle_count = 1; int delayed_frame_count = 1; - demuxer_stream_.reset( - new DummyDemuxerStream( - cycle_count, delayed_frame_count, config_idx_)); + demuxer_stream_.reset(new DemuxerStreamForTest( + -1, cycle_count, delayed_frame_count, config_idx_)); scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); Initialize(demuxer_stream_.get()); @@ -329,9 +200,8 @@ TEST_F(DemuxerStreamAdapterTest, AllDelayedEarlyFlush) { int cycle_count = 1; int delayed_frame_count = 1; - demuxer_stream_.reset( - new DummyDemuxerStream( - cycle_count, delayed_frame_count, config_idx_)); + demuxer_stream_.reset(new DemuxerStreamForTest( + -1, cycle_count, delayed_frame_count, config_idx_)); scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); Initialize(demuxer_stream_.get()); diff --git a/chromium/chromecast/media/cma/filters/hole_frame_factory.cc b/chromium/chromecast/media/cma/filters/hole_frame_factory.cc new file mode 100644 index 00000000000..caad1886dec --- /dev/null +++ b/chromium/chromecast/media/cma/filters/hole_frame_factory.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/media/cma/filters/hole_frame_factory.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/location.h" +#include "chromecast/base/chromecast_switches.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "gpu/command_buffer/client/gles2_interface.h" +#include "media/base/video_frame.h" +#include "media/renderers/gpu_video_accelerator_factories.h" + +namespace chromecast { +namespace media { + +HoleFrameFactory::HoleFrameFactory( + const scoped_refptr<::media::GpuVideoAcceleratorFactories>& gpu_factories) + : gpu_factories_(gpu_factories), + texture_(0), + image_id_(0), + sync_point_(0), + use_legacy_hole_punching_( + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableLegacyHolePunching)) { + if (!use_legacy_hole_punching_) { + gpu::gles2::GLES2Interface* gl = gpu_factories_->GetGLES2Interface(); + CHECK(gl); + + gl->GenTextures(1, &texture_); + gl->BindTexture(GL_TEXTURE_2D, texture_); + image_id_ = gl->CreateGpuMemoryBufferImageCHROMIUM(1, 1, GL_RGBA, + GL_SCANOUT_CHROMIUM); + gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id_); + + gl->GenMailboxCHROMIUM(mailbox_.name); + gl->ProduceTextureDirectCHROMIUM(texture_, GL_TEXTURE_2D, mailbox_.name); + + sync_point_ = gl->InsertSyncPointCHROMIUM(); + } +} + +HoleFrameFactory::~HoleFrameFactory() { + if (texture_ != 0) { + gpu::gles2::GLES2Interface* gl = gpu_factories_->GetGLES2Interface(); + gl->BindTexture(GL_TEXTURE_2D, texture_); + gl->ReleaseTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id_); + gl->DeleteTextures(1, &texture_); + gl->DestroyImageCHROMIUM(image_id_); + } +} + +scoped_refptr<::media::VideoFrame> HoleFrameFactory::CreateHoleFrame( + const gfx::Size& size) { + if (use_legacy_hole_punching_) { +#if defined(VIDEO_HOLE) + return ::media::VideoFrame::CreateHoleFrame(size); +#endif + NOTREACHED(); + } + + scoped_refptr<::media::VideoFrame> frame = + ::media::VideoFrame::WrapNativeTexture( + ::media::VideoFrame::XRGB, + gpu::MailboxHolder(mailbox_, GL_TEXTURE_2D, sync_point_), + ::media::VideoFrame::ReleaseMailboxCB(), + size, // coded_size + gfx::Rect(size), // visible rect + size, // natural size + base::TimeDelta()); // timestamp + frame->metadata()->SetBoolean(::media::VideoFrameMetadata::ALLOW_OVERLAY, + true); + return frame; +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/filters/hole_frame_factory.h b/chromium/chromecast/media/cma/filters/hole_frame_factory.h new file mode 100644 index 00000000000..e817a3e93f3 --- /dev/null +++ b/chromium/chromecast/media/cma/filters/hole_frame_factory.h @@ -0,0 +1,51 @@ +// 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_FILTERS_HOLE_FRAME_FACTORY_H_ +#define CHROMECAST_MEDIA_CMA_FILTERS_HOLE_FRAME_FACTORY_H_ + +#include <GLES2/gl2.h> + +#include "base/memory/ref_counted.h" +#include "gpu/command_buffer/common/mailbox.h" + +namespace gfx { +class Size; +} + +namespace media { +class GpuVideoAcceleratorFactories; +class VideoFrame; +} + +namespace chromecast { +namespace media { + +// Creates VideoFrames for CMA - currently supports both overlay frames +// (native textures that get turned into transparent holes in the browser +// compositor), and legacy VIDEO_HOLE codepath. +// All calls (including ctor/dtor) must be on media thread. +class HoleFrameFactory { + public: + explicit HoleFrameFactory(const scoped_refptr< + ::media::GpuVideoAcceleratorFactories>& gpu_factories); + ~HoleFrameFactory(); + + scoped_refptr<::media::VideoFrame> CreateHoleFrame(const gfx::Size& size); + + private: + scoped_refptr<::media::GpuVideoAcceleratorFactories> gpu_factories_; + gpu::Mailbox mailbox_; + GLuint texture_; + GLuint image_id_; + GLuint sync_point_; + bool use_legacy_hole_punching_; + + DISALLOW_COPY_AND_ASSIGN(HoleFrameFactory); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_FILTERS_HOLE_FRAME_FACTORY_H_ diff --git a/chromium/chromecast/media/cma/filters/multi_demuxer_stream_adapter_unittest.cc b/chromium/chromecast/media/cma/filters/multi_demuxer_stream_adapter_unittest.cc new file mode 100644 index 00000000000..6aaf213228a --- /dev/null +++ b/chromium/chromecast/media/cma/filters/multi_demuxer_stream_adapter_unittest.cc @@ -0,0 +1,162 @@ +// 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/basictypes.h" +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "chromecast/media/cma/base/balanced_media_task_runner_factory.h" +#include "chromecast/media/cma/base/decoder_buffer_base.h" +#include "chromecast/media/cma/filters/demuxer_stream_adapter.h" +#include "chromecast/media/cma/test/demuxer_stream_for_test.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/decoder_buffer.h" +#include "media/base/demuxer_stream.h" +#include "media/base/video_decoder_config.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { +namespace media { + +namespace { +// Maximum pts diff between frames +const int kMaxPtsDiffMs = 2000; +} // namespace + +// Test for multiple streams +class MultiDemuxerStreamAdaptersTest : public testing::Test { + public: + MultiDemuxerStreamAdaptersTest(); + ~MultiDemuxerStreamAdaptersTest() override; + + void Start(); + + protected: + void OnTestTimeout(); + void OnNewFrame(CodedFrameProvider* frame_provider, + const scoped_refptr<DecoderBufferBase>& buffer, + const ::media::AudioDecoderConfig& audio_config, + const ::media::VideoDecoderConfig& video_config); + + // Number of expected read frames. + int total_expected_frames_; + + // Number of frames actually read so far. + int frame_received_count_; + + // List of expected frame indices with decoder config changes. + std::list<int> config_idx_; + + ScopedVector<DemuxerStreamForTest> demuxer_streams_; + + ScopedVector<CodedFrameProvider> coded_frame_providers_; + + private: + // exit if all of the streams end + void OnEos(); + + // Number of reading-streams + int running_stream_count_; + + scoped_refptr<BalancedMediaTaskRunnerFactory> media_task_runner_factory_; + DISALLOW_COPY_AND_ASSIGN(MultiDemuxerStreamAdaptersTest); +}; + +MultiDemuxerStreamAdaptersTest::MultiDemuxerStreamAdaptersTest() { +} + +MultiDemuxerStreamAdaptersTest::~MultiDemuxerStreamAdaptersTest() { +} + +void MultiDemuxerStreamAdaptersTest::Start() { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&MultiDemuxerStreamAdaptersTest::OnTestTimeout, + base::Unretained(this)), + base::TimeDelta::FromSeconds(5)); + + media_task_runner_factory_ = new BalancedMediaTaskRunnerFactory( + base::TimeDelta::FromMilliseconds(kMaxPtsDiffMs)); + + coded_frame_providers_.clear(); + frame_received_count_ = 0; + + for (auto& stream : demuxer_streams_) { + coded_frame_providers_.push_back(make_scoped_ptr( + new DemuxerStreamAdapter(base::ThreadTaskRunnerHandle::Get(), + media_task_runner_factory_, stream))); + } + running_stream_count_ = coded_frame_providers_.size(); + + // read each stream + for (auto& code_frame_provider : coded_frame_providers_) { + auto read_cb = base::Bind(&MultiDemuxerStreamAdaptersTest::OnNewFrame, + base::Unretained(this), code_frame_provider); + + base::Closure task = + base::Bind(&CodedFrameProvider::Read, + base::Unretained(code_frame_provider), read_cb); + + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, task); + } +} + +void MultiDemuxerStreamAdaptersTest::OnTestTimeout() { + if (running_stream_count_ != 0) { + ADD_FAILURE() << "Test timed out"; + } +} + +void MultiDemuxerStreamAdaptersTest::OnNewFrame( + CodedFrameProvider* frame_provider, + const scoped_refptr<DecoderBufferBase>& buffer, + const ::media::AudioDecoderConfig& audio_config, + const ::media::VideoDecoderConfig& video_config) { + if (buffer->end_of_stream()) { + OnEos(); + return; + } + + frame_received_count_++; + auto read_cb = base::Bind(&MultiDemuxerStreamAdaptersTest::OnNewFrame, + base::Unretained(this), frame_provider); + frame_provider->Read(read_cb); +} + +void MultiDemuxerStreamAdaptersTest::OnEos() { + running_stream_count_--; + ASSERT_GE(running_stream_count_, 0); + if (running_stream_count_ == 0) { + ASSERT_EQ(frame_received_count_, total_expected_frames_); + base::MessageLoop::current()->QuitWhenIdle(); + } +} + +TEST_F(MultiDemuxerStreamAdaptersTest, EarlyEos) { + // We have more than one streams here. One of them is much shorter than the + // others. When the shortest stream reaches EOS, other streams should still + // run as usually. BalancedTaskRunner should not be blocked. + int frame_count_short = 2; + int frame_count_long = + frame_count_short + + kMaxPtsDiffMs / DemuxerStreamForTest::kDemuxerStreamForTestFrameDuration + + 100; + demuxer_streams_.push_back(scoped_ptr<DemuxerStreamForTest>( + new DemuxerStreamForTest(frame_count_short, 2, 0, config_idx_))); + demuxer_streams_.push_back(scoped_ptr<DemuxerStreamForTest>( + new DemuxerStreamForTest(frame_count_long, 10, 0, config_idx_))); + + total_expected_frames_ = frame_count_short + frame_count_long; + + scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); + message_loop->PostTask(FROM_HERE, + base::Bind(&MultiDemuxerStreamAdaptersTest::Start, + base::Unretained(this))); + message_loop->Run(); +} +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/ipc/BUILD.gn b/chromium/chromecast/media/cma/ipc/BUILD.gn index 037b3bdd6a7..85ed2ac0376 100644 --- a/chromium/chromecast/media/cma/ipc/BUILD.gn +++ b/chromium/chromecast/media/cma/ipc/BUILD.gn @@ -13,9 +13,10 @@ source_set("ipc") { "media_message_type.h", ] + configs += [ "//chromecast:config" ] + deps = [ "//base", + "//chromecast/media/cma/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 5e4b239bc5d..bd68f3f8673 100644 --- a/chromium/chromecast/media/cma/ipc/media_message_fifo.cc +++ b/chromium/chromecast/media/cma/ipc/media_message_fifo.cc @@ -8,7 +8,8 @@ #include "base/bind.h" #include "base/location.h" #include "base/logging.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "chromecast/media/cma/base/cma_logging.h" #include "chromecast/media/cma/ipc/media_memory_chunk.h" #include "chromecast/media/cma/ipc/media_message.h" @@ -78,15 +79,15 @@ class FifoOwnedMemory : public MediaMemoryChunk { DISALLOW_COPY_AND_ASSIGN(FifoOwnedMemory); }; -FifoOwnedMemory::FifoOwnedMemory( - void* data, size_t size, - const scoped_refptr<MediaMessageFlag>& flag, - const base::Closure& release_msg_cb) - : task_runner_(base::MessageLoopProxy::current()), - release_msg_cb_(release_msg_cb), - data_(data), - size_(size), - flag_(flag) { +FifoOwnedMemory::FifoOwnedMemory(void* data, + size_t size, + const scoped_refptr<MediaMessageFlag>& flag, + const base::Closure& release_msg_cb) + : task_runner_(base::ThreadTaskRunnerHandle::Get()), + release_msg_cb_(release_msg_cb), + data_(data), + size_(size), + flag_(flag) { } FifoOwnedMemory::~FifoOwnedMemory() { 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 ed758182ce4..6680451374b 100644 --- a/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc +++ b/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc @@ -135,12 +135,9 @@ TEST(MediaMessageFifoTest, AlternateWriteRead) { false)); base::WaitableEvent event(false, false); - thread->message_loop_proxy()->PostTask( - FROM_HERE, - base::Bind(&MsgProducerConsumer, - base::Passed(&producer_fifo), - base::Passed(&consumer_fifo), - &event)); + thread->task_runner()->PostTask( + FROM_HERE, base::Bind(&MsgProducerConsumer, base::Passed(&producer_fifo), + base::Passed(&consumer_fifo), &event)); event.Wait(); thread.reset(); @@ -170,18 +167,12 @@ TEST(MediaMessageFifoTest, MultiThreaded) { base::WaitableEvent consumer_event_done(false, false); const int msg_count = 2048; - producer_thread->message_loop_proxy()->PostTask( - FROM_HERE, - base::Bind(&MsgProducer, - base::Passed(&producer_fifo), - msg_count, - &producer_event_done)); - consumer_thread->message_loop_proxy()->PostTask( - FROM_HERE, - base::Bind(&MsgConsumer, - base::Passed(&consumer_fifo), - msg_count, - &consumer_event_done)); + producer_thread->task_runner()->PostTask( + FROM_HERE, base::Bind(&MsgProducer, base::Passed(&producer_fifo), + msg_count, &producer_event_done)); + consumer_thread->task_runner()->PostTask( + FROM_HERE, base::Bind(&MsgConsumer, base::Passed(&consumer_fifo), + msg_count, &consumer_event_done)); producer_event_done.Wait(); consumer_event_done.Wait(); diff --git a/chromium/chromecast/media/cma/ipc_streamer/BUILD.gn b/chromium/chromecast/media/cma/ipc_streamer/BUILD.gn index aa87709333e..4601f073399 100644 --- a/chromium/chromecast/media/cma/ipc_streamer/BUILD.gn +++ b/chromium/chromecast/media/cma/ipc_streamer/BUILD.gn @@ -18,11 +18,13 @@ source_set("ipc_streamer") { "video_decoder_config_marshaller.h", ] + configs += [ "//chromecast:config" ] + deps = [ "//base", "//chromecast/media/cma/base", + "//chromecast/media/cma/ipc", "//media", + "//ui/gfx/geometry", ] - - configs += [ "//chromecast:config" ] } diff --git a/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.cc b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.cc index 6bc25edc386..09fc5b5e99c 100644 --- a/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.cc +++ b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.cc @@ -6,7 +6,8 @@ #include "base/bind.h" #include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "chromecast/media/cma/base/coded_frame_provider.h" #include "chromecast/media/cma/base/decoder_buffer_base.h" #include "chromecast/media/cma/ipc/media_memory_chunk.h" @@ -55,7 +56,8 @@ void AvStreamerProxy::Start() { } void AvStreamerProxy::StopAndFlush(const base::Closure& done_cb) { - is_running_ = false; + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!done_cb.is_null()); pending_av_data_ = false; pending_audio_config_ = ::media::AudioDecoderConfig(); @@ -63,7 +65,27 @@ void AvStreamerProxy::StopAndFlush(const base::Closure& done_cb) { pending_buffer_ = scoped_refptr<DecoderBufferBase>(); pending_read_ = false; - frame_provider_->Flush(done_cb); + is_running_ = false; + + // If there's another pending Flush, for example, the pipeline is stopped + // while another seek is pending, then we don't need to call Flush again. Save + // the callback and fire it later when Flush is done. + pending_stop_flush_cb_list_.push_back(done_cb); + if (pending_stop_flush_cb_list_.size() == 1) { + frame_provider_->Flush( + base::Bind(&AvStreamerProxy::OnStopAndFlushDone, weak_this_)); + } +} + +void AvStreamerProxy::OnStopAndFlushDone() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Flush is done. Fire all the "flush done" callbacks in order. This is + // necessary to guarantee proper state transition in pipeline. + for (const auto& cb : pending_stop_flush_cb_list_) { + cb.Run(); + } + pending_stop_flush_cb_list_.clear(); } void AvStreamerProxy::OnFifoReadEvent() { @@ -128,7 +150,7 @@ void AvStreamerProxy::ProcessPendingData() { } pending_av_data_ = false; - base::MessageLoopProxy::current()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&AvStreamerProxy::RequestBufferIfNeeded, weak_this_)); } diff --git a/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.h b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.h index e750c85ae8c..9d24abc7e0e 100644 --- a/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.h +++ b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_proxy.h @@ -5,6 +5,8 @@ #ifndef CHROMECAST_MEDIA_CMA_IPC_STREAMER_AV_STREAMER_PROXY_H_ #define CHROMECAST_MEDIA_CMA_IPC_STREAMER_AV_STREAMER_PROXY_H_ +#include <list> + #include "base/callback.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" @@ -55,6 +57,8 @@ class AvStreamerProxy { bool SendVideoDecoderConfig(const ::media::VideoDecoderConfig& config); bool SendBuffer(const scoped_refptr<DecoderBufferBase>& buffer); + void OnStopAndFlushDone(); + base::ThreadChecker thread_checker_; scoped_ptr<CodedFrameProvider> frame_provider_; @@ -76,6 +80,9 @@ class AvStreamerProxy { ::media::VideoDecoderConfig pending_video_config_; scoped_refptr<DecoderBufferBase> pending_buffer_; + // List of pending callbacks in StopAndFlush + std::list<base::Closure> pending_stop_flush_cb_list_; + base::WeakPtr<AvStreamerProxy> weak_this_; base::WeakPtrFactory<AvStreamerProxy> weak_factory_; 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 c8720561c1a..9aff31d3be9 100644 --- a/chromium/chromecast/media/cma/ipc_streamer/av_streamer_unittest.cc +++ b/chromium/chromecast/media/cma/ipc_streamer/av_streamer_unittest.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" #include "base/time/time.h" #include "chromecast/media/cma/base/decoder_buffer_base.h" @@ -54,14 +55,20 @@ class AvStreamerTest : public testing::Test { ~AvStreamerTest() override; // Setups the test. - void Configure( - size_t frame_count, - const std::vector<bool>& provider_delayed_pattern, - const std::vector<bool>& consumer_delayed_pattern); + void Configure(size_t frame_count, + const std::vector<bool>& provider_delayed_pattern, + const std::vector<bool>& consumer_delayed_pattern, + bool delay_flush); // Starts the test. void Start(); + // Back to back flush + void FlushThenStop(); + + // Timeout indicates test failure + void OnTestTimeout(); + protected: scoped_ptr<uint64[]> fifo_mem_; @@ -69,13 +76,17 @@ class AvStreamerTest : public testing::Test { scoped_ptr<CodedFrameProviderHost> coded_frame_provider_host_; scoped_ptr<MockFrameConsumer> frame_consumer_; + // number of pending cb in StopAndFlush + int stop_and_flush_cb_count_; + private: - void OnTestTimeout(); void OnTestCompleted(); void OnFifoRead(); void OnFifoWrite(); + void OnStopAndFlush(); + DISALLOW_COPY_AND_ASSIGN(AvStreamerTest); }; @@ -88,7 +99,8 @@ AvStreamerTest::~AvStreamerTest() { void AvStreamerTest::Configure( size_t frame_count, const std::vector<bool>& provider_delayed_pattern, - const std::vector<bool>& consumer_delayed_pattern) { + const std::vector<bool>& consumer_delayed_pattern, + bool delay_flush) { // Frame generation on the producer and consumer side. std::vector<FrameGeneratorForTest::FrameSpec> frame_specs; frame_specs.resize(frame_count); @@ -108,6 +120,7 @@ void AvStreamerTest::Configure( scoped_ptr<MockFrameProvider> frame_provider(new MockFrameProvider()); frame_provider->Configure(provider_delayed_pattern, frame_generator_provider.Pass()); + frame_provider->SetDelayFlush(delay_flush); size_t fifo_size_div_8 = 512; fifo_mem_.reset(new uint64[fifo_size_div_8]); @@ -141,19 +154,33 @@ void AvStreamerTest::Configure( consumer_delayed_pattern, false, frame_generator_consumer.Pass()); + + stop_and_flush_cb_count_ = 0; } void AvStreamerTest::Start() { - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&AvStreamerProxy::Start, - base::Unretained(av_buffer_proxy_.get()))); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&AvStreamerProxy::Start, + base::Unretained(av_buffer_proxy_.get()))); frame_consumer_->Start( base::Bind(&AvStreamerTest::OnTestCompleted, base::Unretained(this))); } +void AvStreamerTest::FlushThenStop() { + base::Closure cb = + base::Bind(&AvStreamerTest::OnStopAndFlush, base::Unretained(this)); + + stop_and_flush_cb_count_++; + av_buffer_proxy_->StopAndFlush(cb); + + ASSERT_EQ(stop_and_flush_cb_count_, 1); + + stop_and_flush_cb_count_++; + av_buffer_proxy_->StopAndFlush(cb); +} + void AvStreamerTest::OnTestTimeout() { ADD_FAILURE() << "Test timed out"; if (base::MessageLoop::current()) @@ -165,17 +192,23 @@ void AvStreamerTest::OnTestCompleted() { } void AvStreamerTest::OnFifoWrite() { - base::MessageLoopProxy::current()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&CodedFrameProviderHost::OnFifoWriteEvent, base::Unretained(coded_frame_provider_host_.get()))); } void AvStreamerTest::OnFifoRead() { - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&AvStreamerProxy::OnFifoReadEvent, - base::Unretained(av_buffer_proxy_.get()))); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&AvStreamerProxy::OnFifoReadEvent, + base::Unretained(av_buffer_proxy_.get()))); +} + +void AvStreamerTest::OnStopAndFlush() { + stop_and_flush_cb_count_--; + if (stop_and_flush_cb_count_ == 0) { + OnTestCompleted(); + } } TEST_F(AvStreamerTest, FastProviderSlowConsumer) { @@ -183,14 +216,14 @@ TEST_F(AvStreamerTest, FastProviderSlowConsumer) { bool consumer_delayed_pattern[] = { true }; const size_t frame_count = 100u; - Configure( - frame_count, - std::vector<bool>( - provider_delayed_pattern, - provider_delayed_pattern + arraysize(provider_delayed_pattern)), - std::vector<bool>( - consumer_delayed_pattern, - consumer_delayed_pattern + arraysize(consumer_delayed_pattern))); + Configure(frame_count, + std::vector<bool>( + provider_delayed_pattern, + provider_delayed_pattern + arraysize(provider_delayed_pattern)), + std::vector<bool>( + consumer_delayed_pattern, + consumer_delayed_pattern + arraysize(consumer_delayed_pattern)), + false); scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); message_loop->PostTask( @@ -204,14 +237,14 @@ TEST_F(AvStreamerTest, SlowProviderFastConsumer) { bool consumer_delayed_pattern[] = { false }; const size_t frame_count = 100u; - Configure( - frame_count, - std::vector<bool>( - provider_delayed_pattern, - provider_delayed_pattern + arraysize(provider_delayed_pattern)), - std::vector<bool>( - consumer_delayed_pattern, - consumer_delayed_pattern + arraysize(consumer_delayed_pattern))); + Configure(frame_count, + std::vector<bool>( + provider_delayed_pattern, + provider_delayed_pattern + arraysize(provider_delayed_pattern)), + std::vector<bool>( + consumer_delayed_pattern, + consumer_delayed_pattern + arraysize(consumer_delayed_pattern)), + false); scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); message_loop->PostTask( @@ -233,14 +266,14 @@ TEST_F(AvStreamerTest, SlowFastProducerConsumer) { }; const size_t frame_count = 100u; - Configure( - frame_count, - std::vector<bool>( - provider_delayed_pattern, - provider_delayed_pattern + arraysize(provider_delayed_pattern)), - std::vector<bool>( - consumer_delayed_pattern, - consumer_delayed_pattern + arraysize(consumer_delayed_pattern))); + Configure(frame_count, + std::vector<bool>( + provider_delayed_pattern, + provider_delayed_pattern + arraysize(provider_delayed_pattern)), + std::vector<bool>( + consumer_delayed_pattern, + consumer_delayed_pattern + arraysize(consumer_delayed_pattern)), + false); scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); message_loop->PostTask( @@ -249,5 +282,41 @@ TEST_F(AvStreamerTest, SlowFastProducerConsumer) { message_loop->Run(); }; +// Test case for when AvStreamerProxy::StopAndFlush is invoked while a previous +// flush is pending. This can happen when pipeline is stopped while a seek/flush +// is pending. +TEST_F(AvStreamerTest, StopInFlush) { + // We don't care about the delayed pattern. Setting to true to make sure the + // test won't exit before flush is called. + bool dummy_delayed_pattern[] = {true}; + std::vector<bool> dummy_delayed_pattern_vector( + dummy_delayed_pattern, + dummy_delayed_pattern + arraysize(dummy_delayed_pattern)); + const size_t frame_count = 100u; + + // Delay flush callback in frame provider + Configure(frame_count, dummy_delayed_pattern_vector, + dummy_delayed_pattern_vector, true); + + scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); + + // Flush takes 10ms to finish. 1s timeout is enough for this test. + message_loop->PostDelayedTask( + FROM_HERE, + base::Bind(&AvStreamerTest::OnTestTimeout, base::Unretained(this)), + base::TimeDelta::FromSeconds(1)); + + message_loop->PostTask( + FROM_HERE, base::Bind(&AvStreamerTest::Start, base::Unretained(this))); + + // Let AvStreamerProxy run for a while. Fire flush and stop later + message_loop->PostDelayedTask( + FROM_HERE, + base::Bind(&AvStreamerTest::FlushThenStop, base::Unretained(this)), + base::TimeDelta::FromMilliseconds(10)); + + message_loop->Run(); +} + } // namespace media } // namespace chromecast 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 a377ae66337..ab3a2587851 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,7 +24,9 @@ class DecoderBufferFromMsg : public DecoderBufferBase { void Initialize(); // DecoderBufferBase implementation. + StreamId stream_id() const override; base::TimeDelta timestamp() const override; + void set_timestamp(const base::TimeDelta& timestamp) override; const uint8* data() const override; uint8* writable_data() const override; size_t data_size() const override; @@ -37,6 +39,9 @@ class DecoderBufferFromMsg : public DecoderBufferBase { // Indicates whether this is an end of stream frame. bool is_eos_; + // Stream Id this decoder buffer belongs to. + StreamId stream_id_; + // Frame timestamp. base::TimeDelta pts_; @@ -56,6 +61,7 @@ class DecoderBufferFromMsg : public DecoderBufferBase { DecoderBufferFromMsg::DecoderBufferFromMsg( scoped_ptr<MediaMessage> msg) : is_eos_(true), + stream_id_(kPrimary), msg_(msg.Pass()), data_(NULL) { CHECK(msg_); @@ -71,6 +77,8 @@ void DecoderBufferFromMsg::Initialize() { if (is_eos_) return; + CHECK(msg_->ReadPod(&stream_id_)); + int64 pts_internal = 0; CHECK(msg_->ReadPod(&pts_internal)); pts_ = base::TimeDelta::FromInternalValue(pts_internal); @@ -99,10 +107,18 @@ void DecoderBufferFromMsg::Initialize() { } } +StreamId DecoderBufferFromMsg::stream_id() const { + return stream_id_; +} + base::TimeDelta DecoderBufferFromMsg::timestamp() const { return pts_; } +void DecoderBufferFromMsg::set_timestamp(const base::TimeDelta& timestamp) { + pts_ = timestamp; +} + const uint8* DecoderBufferFromMsg::data() const { CHECK(msg_->IsSerializedMsgAvailable()); return data_; @@ -135,6 +151,7 @@ void DecoderBufferBaseMarshaller::Write( if (buffer->end_of_stream()) return; + CHECK(msg->WritePod(buffer->stream_id())); CHECK(msg->WritePod(buffer->timestamp().ToInternalValue())); bool has_decrypt_config = diff --git a/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.cc index 3dbc028fc6c..006ea820437 100644 --- a/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.cc +++ b/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.cc @@ -109,8 +109,9 @@ void AudioPipelineImpl::Initialize( if (frame_provider) SetCodedFrameProvider(frame_provider.Pass()); + DCHECK(audio_config.IsValidConfig()); if (!audio_device_->SetConfig( - DecoderConfigAdapter::ToCastAudioConfig(audio_config)) || + DecoderConfigAdapter::ToCastAudioConfig(kPrimary, audio_config)) || !av_pipeline_impl_->Initialize()) { status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); return; @@ -124,14 +125,15 @@ void AudioPipelineImpl::SetVolume(float volume) { } void AudioPipelineImpl::OnUpdateConfig( + StreamId id, const ::media::AudioDecoderConfig& audio_config, const ::media::VideoDecoderConfig& video_config) { if (audio_config.IsValidConfig()) { - CMALOG(kLogControl) << "AudioPipelineImpl::OnUpdateConfig " + CMALOG(kLogControl) << "AudioPipelineImpl::OnUpdateConfig id:" << id << " " << audio_config.AsHumanReadableString(); bool success = audio_device_->SetConfig( - DecoderConfigAdapter::ToCastAudioConfig(audio_config)); + DecoderConfigAdapter::ToCastAudioConfig(id, audio_config)); if (!success && !audio_client_.playback_error_cb.is_null()) audio_client_.playback_error_cb.Run(::media::PIPELINE_ERROR_DECODE); } diff --git a/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.h b/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.h index 7d2e63a1314..31cadf91680 100644 --- a/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.h +++ b/chromium/chromecast/media/cma/pipeline/audio_pipeline_impl.h @@ -12,6 +12,7 @@ #include "base/threading/thread_checker.h" #include "chromecast/media/cma/pipeline/audio_pipeline.h" #include "chromecast/media/cma/pipeline/av_pipeline_client.h" +#include "chromecast/public/media/stream_id.h" namespace media { class AudioDecoderConfig; @@ -57,7 +58,8 @@ class AudioPipelineImpl : public AudioPipeline { private: void OnFlushDone(const ::media::PipelineStatusCB& status_cb); - void OnUpdateConfig(const ::media::AudioDecoderConfig& audio_config, + void OnUpdateConfig(StreamId id, + const ::media::AudioDecoderConfig& audio_config, const ::media::VideoDecoderConfig& video_config); AudioPipelineDevice* audio_device_; 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 index 9c17c7b2330..9b9dc120e71 100644 --- a/chromium/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc +++ b/chromium/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc @@ -9,12 +9,13 @@ #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/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.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/backend/media_pipeline_device_factory_default.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" @@ -59,8 +60,10 @@ class AudioVideoPipelineImplTest : public testing::Test { AudioVideoPipelineImplTest::AudioVideoPipelineImplTest() : media_pipeline_(new MediaPipelineImpl()) { - scoped_ptr<MediaPipelineDevice> media_pipeline_device( - new MediaPipelineDeviceFake()); + scoped_ptr<MediaPipelineDeviceFactory> factory = + make_scoped_ptr(new MediaPipelineDeviceFactoryDefault()); + scoped_ptr<MediaPipelineDevice> media_pipeline_device = + make_scoped_ptr(new MediaPipelineDevice(factory.Pass())); media_pipeline_->Initialize(kLoadTypeURL, media_pipeline_device.Pass()); media_pipeline_->SetPlaybackRate(1.0); } @@ -90,14 +93,15 @@ void AudioVideoPipelineImplTest::Initialize( ::media::CHANNEL_LAYOUT_STEREO, 44100, NULL, 0, false); - ::media::VideoDecoderConfig video_config( + std::vector<::media::VideoDecoderConfig> video_configs; + video_configs.push_back(::media::VideoDecoderConfig( ::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); + NULL, 0, false)); // Frame generation on the producer side. std::vector<FrameGeneratorForTest::FrameSpec> frame_specs; @@ -134,11 +138,11 @@ void AudioVideoPipelineImplTest::Initialize( next_task) : base::Bind(&MediaPipeline::InitializeVideo, base::Unretained(media_pipeline_.get()), - video_config, + video_configs, base::Passed(&frame_provider_base), next_task); - base::MessageLoopProxy::current()->PostTask(FROM_HERE, task); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, task); } void AudioVideoPipelineImplTest::StartPlaying( @@ -159,10 +163,9 @@ void AudioVideoPipelineImplTest::Flush( ::media::PipelineStatusCB next_task = base::Bind(&AudioVideoPipelineImplTest::Stop, base::Unretained(this), done_cb); - base::MessageLoopProxy::current()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&MediaPipeline::Flush, - base::Unretained(media_pipeline_.get()), + base::Bind(&MediaPipeline::Flush, base::Unretained(media_pipeline_.get()), next_task)); } diff --git a/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc index 9ff88e99225..5c6c69538a2 100644 --- a/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc +++ b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc @@ -6,8 +6,9 @@ #include "base/bind.h" #include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" +#include "base/thread_task_runner_handle.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" @@ -124,9 +125,8 @@ bool AvPipelineImpl::StartPlayingFrom( // Start feeding the pipeline. enable_feeding_ = true; - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); return true; } @@ -221,14 +221,13 @@ void AvPipelineImpl::OnNewFrame( pending_read_ = false; if (audio_config.IsValidConfig() || video_config.IsValidConfig()) - update_config_cb_.Run(audio_config, video_config); + update_config_cb_.Run(buffer->stream_id(), audio_config, video_config); pending_buffer_ = buffer; ProcessPendingBuffer(); - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); } void AvPipelineImpl::ProcessPendingBuffer() { @@ -237,7 +236,7 @@ void AvPipelineImpl::ProcessPendingBuffer() { // Initiate a read if there isn't already one. if (!pending_buffer_.get() && !pending_read_) { - base::MessageLoopProxy::current()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); return; @@ -307,9 +306,8 @@ void AvPipelineImpl::OnFramePushed(MediaComponentDevice::FrameStatus status) { state_ = kError; return; } - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&AvPipelineImpl::ProcessPendingBuffer, weak_this_)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&AvPipelineImpl::ProcessPendingBuffer, weak_this_)); } void AvPipelineImpl::OnCdmStateChanged() { diff --git a/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h index 20b3fc4feda..70395fc95d6 100644 --- a/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h +++ b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h @@ -14,6 +14,7 @@ #include "base/threading/thread_checker.h" #include "chromecast/media/cma/backend/media_component_device.h" #include "chromecast/media/cma/pipeline/av_pipeline_client.h" +#include "chromecast/public/media/stream_id.h" namespace media { class AudioDecoderConfig; @@ -42,7 +43,8 @@ class AvPipelineImpl { }; typedef base::Callback< - void(const ::media::AudioDecoderConfig&, + void(StreamId id, + const ::media::AudioDecoderConfig&, const ::media::VideoDecoderConfig&)> UpdateConfigCB; AvPipelineImpl( diff --git a/chromium/chromecast/media/cma/pipeline/decrypt_util.cc b/chromium/chromecast/media/cma/pipeline/decrypt_util.cc index 1f1fbe85c62..abd466148a4 100644 --- a/chromium/chromecast/media/cma/pipeline/decrypt_util.cc +++ b/chromium/chromecast/media/cma/pipeline/decrypt_util.cc @@ -22,7 +22,9 @@ class DecoderBufferClear : public DecoderBufferBase { explicit DecoderBufferClear(const scoped_refptr<DecoderBufferBase>& buffer); // DecoderBufferBase implementation. + StreamId stream_id() const override; base::TimeDelta timestamp() const override; + void set_timestamp(const base::TimeDelta& timestamp) override; const uint8* data() const override; uint8* writable_data() const override; size_t data_size() const override; @@ -45,10 +47,18 @@ DecoderBufferClear::DecoderBufferClear( DecoderBufferClear::~DecoderBufferClear() { } +StreamId DecoderBufferClear::stream_id() const { + return buffer_->stream_id(); +} + base::TimeDelta DecoderBufferClear::timestamp() const { return buffer_->timestamp(); } +void DecoderBufferClear::set_timestamp(const base::TimeDelta& timestamp) { + buffer_->set_timestamp(timestamp); +} + const uint8* DecoderBufferClear::data() const { return buffer_->data(); } diff --git a/chromium/chromecast/media/cma/pipeline/media_pipeline.h b/chromium/chromecast/media/cma/pipeline/media_pipeline.h index 6b127cb276b..6026cdcc874 100644 --- a/chromium/chromecast/media/cma/pipeline/media_pipeline.h +++ b/chromium/chromecast/media/cma/pipeline/media_pipeline.h @@ -47,7 +47,7 @@ class MediaPipeline { scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb) = 0; virtual void InitializeVideo( - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb) = 0; diff --git a/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc index ced7df14cc0..fe5a2ebd10d 100644 --- a/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc +++ b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc @@ -9,7 +9,8 @@ #include "base/callback_helpers.h" #include "base/location.h" #include "base/logging.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "base/time/time.h" #include "chromecast/media/cdm/browser_cdm_cast.h" #include "chromecast/media/cma/backend/media_clock_device.h" @@ -143,7 +144,7 @@ void MediaPipelineImpl::InitializeAudio( } void MediaPipelineImpl::InitializeVideo( - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb) { DCHECK(thread_checker_.CalledOnValidThread()); @@ -154,14 +155,14 @@ void MediaPipelineImpl::InitializeVideo( return; } has_video_ = true; - video_pipeline_->Initialize(config, frame_provider.Pass(), status_cb); + video_pipeline_->Initialize(configs, 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_); + DCHECK(!pending_flush_callbacks_); // Reset the start of the timeline. DCHECK_EQ(clock_device_->GetState(), MediaClockDevice::kStateIdle); @@ -179,9 +180,8 @@ void MediaPipelineImpl::StartPlayingFrom(base::TimeDelta time) { 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_)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_)); } // Setup the audio and video pipeline for the new timeline. @@ -209,7 +209,7 @@ void MediaPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) { CMALOG(kLogControl) << __FUNCTION__; DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(has_audio_ || has_video_); - DCHECK(!pending_callbacks_); + DCHECK(!pending_flush_callbacks_); // No need to update media time anymore. enable_time_update_ = false; @@ -236,7 +236,7 @@ void MediaPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) { } ::media::PipelineStatusCB transition_cb = base::Bind(&MediaPipelineImpl::StateTransition, weak_this_, status_cb); - pending_callbacks_ = + pending_flush_callbacks_ = ::media::SerialRunner::Run(bound_fns, transition_cb); } @@ -244,7 +244,11 @@ void MediaPipelineImpl::Stop() { CMALOG(kLogControl) << __FUNCTION__; DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(has_audio_ || has_video_); - DCHECK(!pending_callbacks_); + + // Cancel pending flush callbacks since we are about to stop/shutdown + // audio/video pipelines. This will ensure A/V Flush won't happen in + // stopped state. + pending_flush_callbacks_.reset(); // No need to update media time anymore. enable_time_update_ = false; @@ -282,7 +286,7 @@ VideoPipelineImpl* MediaPipelineImpl::GetVideoPipelineImpl() const { void MediaPipelineImpl::StateTransition( const ::media::PipelineStatusCB& status_cb, ::media::PipelineStatus status) { - pending_callbacks_.reset(); + pending_flush_callbacks_.reset(); status_cb.Run(status); } @@ -326,9 +330,8 @@ void MediaPipelineImpl::UpdateMediaTime() { 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_), + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_), kTimeUpdateInterval); return; } @@ -356,9 +359,8 @@ void MediaPipelineImpl::UpdateMediaTime() { 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_), + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_), kTimeUpdateInterval); } diff --git a/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h index eb14a555b98..71a5384ee66 100644 --- a/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h +++ b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h @@ -45,7 +45,7 @@ class MediaPipelineImpl : public MediaPipeline { scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb) override; void InitializeVideo( - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb) override; void StartPlayingFrom(base::TimeDelta time) override; @@ -83,7 +83,7 @@ class MediaPipelineImpl : public MediaPipeline { bool has_video_; scoped_ptr<AudioPipelineImpl> audio_pipeline_; scoped_ptr<VideoPipelineImpl> video_pipeline_; - scoped_ptr< ::media::SerialRunner> pending_callbacks_; + scoped_ptr< ::media::SerialRunner> pending_flush_callbacks_; // Playback rate set by the upper layer. float target_playback_rate_; diff --git a/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc index fda8b445ba4..38335318bd3 100644 --- a/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc +++ b/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc @@ -101,11 +101,10 @@ void VideoPipelineImpl::SetClient(const VideoPipelineClient& client) { } void VideoPipelineImpl::Initialize( - const ::media::VideoDecoderConfig& video_config, + const std::vector<::media::VideoDecoderConfig>& configs, scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb) { - CMALOG(kLogControl) << "VideoPipelineImpl::Initialize " - << video_config.AsHumanReadableString(); + CMALOG(kLogControl) << __FUNCTION__ << " config (" << configs.size() << ")"; VideoPipelineDevice::VideoClient client; client.natural_size_changed_cb = base::Bind(&VideoPipelineImpl::OnNaturalSizeChanged, weak_this_); @@ -113,8 +112,23 @@ void VideoPipelineImpl::Initialize( if (frame_provider) SetCodedFrameProvider(frame_provider.Pass()); - if (!video_device_->SetConfig( - DecoderConfigAdapter::ToCastVideoConfig(video_config)) || + if (configs.empty()) { + status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + DCHECK(configs.size() <= 2); + DCHECK(configs[0].IsValidConfig()); + VideoConfig video_config = + DecoderConfigAdapter::ToCastVideoConfig(kPrimary, configs[0]); + VideoConfig secondary_config; + if (configs.size() == 2) { + DCHECK(configs[1].IsValidConfig()); + secondary_config = DecoderConfigAdapter::ToCastVideoConfig(kSecondary, + configs[1]); + video_config.additional_config = &secondary_config; + } + + if (!video_device_->SetConfig(video_config) || !av_pipeline_impl_->Initialize()) { status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); return; @@ -124,14 +138,15 @@ void VideoPipelineImpl::Initialize( } void VideoPipelineImpl::OnUpdateConfig( + StreamId id, const ::media::AudioDecoderConfig& audio_config, const ::media::VideoDecoderConfig& video_config) { if (video_config.IsValidConfig()) { - CMALOG(kLogControl) << "VideoPipelineImpl::OnUpdateConfig " + CMALOG(kLogControl) << "VideoPipelineImpl::OnUpdateConfig id:" << id << " " << video_config.AsHumanReadableString(); bool success = video_device_->SetConfig( - DecoderConfigAdapter::ToCastVideoConfig(video_config)); + DecoderConfigAdapter::ToCastVideoConfig(id, video_config)); if (!success && !video_client_.av_pipeline_client.playback_error_cb.is_null()) { video_client_.av_pipeline_client.playback_error_cb.Run( diff --git a/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.h b/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.h index e065ac1df20..25f875fcf63 100644 --- a/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.h +++ b/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.h @@ -11,6 +11,7 @@ #include "base/threading/thread_checker.h" #include "chromecast/media/cma/pipeline/video_pipeline.h" #include "chromecast/media/cma/pipeline/video_pipeline_client.h" +#include "chromecast/public/media/stream_id.h" namespace gfx { class Size; @@ -43,7 +44,7 @@ class VideoPipelineImpl : public VideoPipeline { // Functions to control the state of the audio pipeline. void Initialize( - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb); bool StartPlayingFrom(base::TimeDelta time, @@ -59,7 +60,8 @@ class VideoPipelineImpl : public VideoPipeline { private: void OnFlushDone(const ::media::PipelineStatusCB& status_cb); - void OnUpdateConfig(const ::media::AudioDecoderConfig& audio_config, + void OnUpdateConfig(StreamId id, + const ::media::AudioDecoderConfig& audio_config, const ::media::VideoDecoderConfig& video_config); void OnNaturalSizeChanged(const gfx::Size& size); diff --git a/chromium/chromecast/media/empty.cc b/chromium/chromecast/media/empty.cc new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/chromium/chromecast/media/empty.cc diff --git a/chromium/chromecast/media/media.gyp b/chromium/chromecast/media/media.gyp index 20b1dc0c134..86879e5b24a 100644 --- a/chromium/chromecast/media/media.gyp +++ b/chromium/chromecast/media/media.gyp @@ -10,6 +10,13 @@ 'use_default_libcast_media%': 1, }, 'targets': [ + # TODO(gunsch): delete this target once Chromecast M44/earlier is obsolete. + # See: b/21639416 + { + 'target_name': 'libffmpegsumo', + 'type': 'loadable_module', + 'sources': ['empty.cc'], + }, { 'target_name': 'media_base', 'type': '<(component)', @@ -30,6 +37,8 @@ 'base/media_caps.h', 'base/media_codec_support.cc', 'base/media_codec_support.h', + 'base/media_message_loop.cc', + 'base/media_message_loop.h', 'base/switching_media_renderer.cc', 'base/switching_media_renderer.h', ], @@ -56,6 +65,8 @@ 'sources': [ 'cdm/browser_cdm_cast.cc', 'cdm/browser_cdm_cast.h', + 'cdm/chromecast_init_data.cc', + 'cdm/chromecast_init_data.h', ], 'conditions': [ ['use_playready==1', { @@ -99,6 +110,8 @@ 'cma/base/decoder_config_adapter.h', 'cma/base/media_task_runner.cc', 'cma/base/media_task_runner.h', + 'cma/base/simple_media_task_runner.cc', + 'cma/base/simple_media_task_runner.h', ], }, { @@ -116,22 +129,27 @@ 'sources': [ 'cma/backend/audio_pipeline_device.cc', 'cma/backend/audio_pipeline_device.h', + 'cma/backend/audio_pipeline_device_default.cc', + 'cma/backend/audio_pipeline_device_default.h', 'cma/backend/media_clock_device.cc', 'cma/backend/media_clock_device.h', + 'cma/backend/media_clock_device_default.cc', + 'cma/backend/media_clock_device_default.h', 'cma/backend/media_component_device.cc', 'cma/backend/media_component_device.h', + 'cma/backend/media_component_device_default.cc', + 'cma/backend/media_component_device_default.h', 'cma/backend/media_pipeline_device.cc', 'cma/backend/media_pipeline_device.h', - 'cma/backend/media_pipeline_device_fake.cc', - 'cma/backend/media_pipeline_device_fake.h', + 'cma/backend/media_pipeline_device_factory.h', + 'cma/backend/media_pipeline_device_factory_default.cc', + 'cma/backend/media_pipeline_device_factory_default.h', 'cma/backend/media_pipeline_device_params.cc', 'cma/backend/media_pipeline_device_params.h', 'cma/backend/video_pipeline_device.cc', + 'cma/backend/video_pipeline_device_default.cc', + 'cma/backend/video_pipeline_device_default.h', '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"', { @@ -140,8 +158,7 @@ ], }, { 'sources': [ - 'cma/backend/media_pipeline_device_fake_factory.cc', - 'cma/backend/video_plane_fake_factory.cc', + 'cma/backend/media_pipeline_device_factory_simple.cc' ], }], ], @@ -195,17 +212,7 @@ '../../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', - ], - }], + '../../third_party/boringssl/boringssl.gyp:boringssl', ], 'sources': [ 'cma/pipeline/audio_pipeline.cc', @@ -245,6 +252,8 @@ 'cma/filters/cma_renderer.h', 'cma/filters/demuxer_stream_adapter.cc', 'cma/filters/demuxer_stream_adapter.h', + 'cma/filters/hole_frame_factory.cc', + 'cma/filters/hole_frame_factory.h', ], }, { @@ -269,21 +278,28 @@ '../../base/base.gyp:base_i18n', '../../base/base.gyp:test_support_base', '../../chromecast/chromecast.gyp:cast_metrics_test_support', + '../../gpu/gpu.gyp:gpu_unittest_utils', '../../media/media.gyp:media_test_support', '../../testing/gmock.gyp:gmock', '../../testing/gtest.gyp:gtest', '../../testing/gtest.gyp:gtest_main', + '../../ui/gfx/gfx.gyp:gfx_test_support', ], 'sources': [ + 'cdm/chromecast_init_data_unittest.cc', '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/filters/multi_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/cma_end_to_end_test.cc', + 'cma/test/demuxer_stream_for_test.cc', + 'cma/test/demuxer_stream_for_test.h', 'cma/test/frame_generator_for_test.cc', 'cma/test/frame_generator_for_test.h', 'cma/test/frame_segmenter_for_test.cc', diff --git a/chromium/chromecast/net/BUILD.gn b/chromium/chromecast/net/BUILD.gn new file mode 100644 index 00000000000..a4818650823 --- /dev/null +++ b/chromium/chromecast/net/BUILD.gn @@ -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. + +source_set("net") { + sources = [ + "connectivity_checker.cc", + "connectivity_checker.h", + "connectivity_checker_impl.cc", + "connectivity_checker_impl.h", + "fake_connectivity_checker.cc", + "fake_connectivity_checker.h", + "net_switches.cc", + "net_switches.h", + "net_util_cast.cc", + "net_util_cast.h", + ] + + if (!is_android) { + sources += [ + "network_change_notifier_factory_cast.cc", + "network_change_notifier_factory_cast.h", + ] + } + + deps = [ + "//base", + "//chromecast/base:cast_sys_info", + "//chromecast/public", + "//net", + ] +} diff --git a/chromium/chromecast/net/connectivity_checker.cc b/chromium/chromecast/net/connectivity_checker.cc index de3081291f3..873ab1e1a57 100644 --- a/chromium/chromecast/net/connectivity_checker.cc +++ b/chromium/chromecast/net/connectivity_checker.cc @@ -4,73 +4,16 @@ #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" +#include "chromecast/net/connectivity_checker_impl.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) +ConnectivityChecker::ConnectivityChecker() : 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)); + new base::ObserverListThreadSafe<ConnectivityObserver>()) { } 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( @@ -83,102 +26,16 @@ void ConnectivityChecker::RemoveConnectivityObserver( connectivity_observer_list_->RemoveObserver(observer); } -bool ConnectivityChecker::Connected() const { - return connected_; -} - -void ConnectivityChecker::SetConnectivity(bool connected) { - if (connected_ == connected) - return; - - connected_ = connected; +void ConnectivityChecker::Notify(bool connected) { + DCHECK(connectivity_observer_list_.get()); 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. - } +// static +scoped_refptr<ConnectivityChecker> ConnectivityChecker::Create( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { + return make_scoped_refptr(new ConnectivityCheckerImpl(task_runner)); } } // namespace chromecast diff --git a/chromium/chromecast/net/connectivity_checker.h b/chromium/chromecast/net/connectivity_checker.h index c52b8a624da..fcfb878bde3 100644 --- a/chromium/chromecast/net/connectivity_checker.h +++ b/chromium/chromecast/net/connectivity_checker.h @@ -7,33 +7,23 @@ #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" +#include "base/observer_list_threadsafe.h" class GURL; namespace base { -class MessageLoopProxy; -} - -namespace net { -class URLRequestContext; +class SingleThreadTaskRunner; } namespace chromecast { -// Simple class to check network connectivity by sending a HEAD http request -// to given url. +// Checks if internet connectivity is available. class ConnectivityChecker - : public base::RefCountedThreadSafe<ConnectivityChecker>, - public net::URLRequest::Delegate, - public net::NetworkChangeNotifier::ConnectionTypeObserver, - public net::NetworkChangeNotifier::IPAddressObserver { + : public base::RefCountedThreadSafe<ConnectivityChecker> { public: class ConnectivityObserver { public: - // Will be called when internet connectivity changes + // Will be called when internet connectivity changes. virtual void OnConnectivityChanged(bool connected) = 0; protected: @@ -44,52 +34,31 @@ class ConnectivityChecker DISALLOW_COPY_AND_ASSIGN(ConnectivityObserver); }; - explicit ConnectivityChecker( - const scoped_refptr<base::MessageLoopProxy>& loop_proxy); + static scoped_refptr<ConnectivityChecker> Create( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); + + ConnectivityChecker(); void AddConnectivityObserver(ConnectivityObserver* observer); void RemoveConnectivityObserver(ConnectivityObserver* observer); - // Returns if there is internet connectivity - bool Connected() const; + // Returns if there is internet connectivity. + virtual bool Connected() const = 0; - // Checks for connectivity - void Check(); + // Checks for connectivity. + virtual void Check() = 0; protected: - ~ConnectivityChecker() override; + virtual ~ConnectivityChecker(); + + // Notifies observes that connectivity has changed. + void Notify(bool connected); 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> > + const scoped_refptr<base::ObserverListThreadSafe<ConnectivityObserver>> connectivity_observer_list_; - const scoped_refptr<base::MessageLoopProxy> loop_proxy_; - bool connected_; - unsigned int bad_responses_; DISALLOW_COPY_AND_ASSIGN(ConnectivityChecker); }; diff --git a/chromium/chromecast/net/connectivity_checker_impl.cc b/chromium/chromecast/net/connectivity_checker_impl.cc new file mode 100644 index 00000000000..d298d1bbfde --- /dev/null +++ b/chromium/chromecast/net/connectivity_checker_impl.cc @@ -0,0 +1,195 @@ +// 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_impl.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 connectivity check errors before status is changed +// to offline. +const unsigned int kNumErrorsToNotifyOffline = 3; + +// Request timeout value in seconds. +const unsigned int kRequestTimeoutInSeconds = 3; + +// Default url for connectivity checking. +const char kDefaultConnectivityCheckUrl[] = + "https://clients3.google.com/generate_204"; + +} // namespace + +ConnectivityCheckerImpl::ConnectivityCheckerImpl( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) + : ConnectivityChecker(), + task_runner_(task_runner), + connected_(false), + check_errors_(0) { + DCHECK(task_runner_.get()); + task_runner->PostTask(FROM_HERE, + base::Bind(&ConnectivityCheckerImpl::Initialize, this)); +} + +void ConnectivityCheckerImpl::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::AddNetworkChangeObserver(this); + task_runner_->PostTask(FROM_HERE, + base::Bind(&ConnectivityCheckerImpl::Check, this)); +} + +ConnectivityCheckerImpl::~ConnectivityCheckerImpl() { + DCHECK(task_runner_.get()); + net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); + task_runner_->DeleteSoon(FROM_HERE, url_request_.release()); + task_runner_->DeleteSoon(FROM_HERE, url_request_context_.release()); +} + +bool ConnectivityCheckerImpl::Connected() const { + return connected_; +} + +void ConnectivityCheckerImpl::SetConnected(bool connected) { + if (connected_ == connected) + return; + + connected_ = connected; + Notify(connected); + LOG(INFO) << "Global connection is: " << (connected ? "Up" : "Down"); +} + +void ConnectivityCheckerImpl::Check() { + if (!task_runner_->BelongsToCurrentThread()) { + task_runner_->PostTask(FROM_HERE, + base::Bind(&ConnectivityCheckerImpl::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(); + + timeout_.Reset(base::Bind(&ConnectivityCheckerImpl::OnUrlRequestTimeout, + this)); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + timeout_.callback(), + base::TimeDelta::FromSeconds(kRequestTimeoutInSeconds)); +} + +void ConnectivityCheckerImpl::OnNetworkChanged( + net::NetworkChangeNotifier::ConnectionType type) { + VLOG(2) << "OnNetworkChanged " << type; + Cancel(); + + if (type == net::NetworkChangeNotifier::CONNECTION_NONE) { + SetConnected(false); + return; + } + + Check(); +} + +void ConnectivityCheckerImpl::OnResponseStarted(net::URLRequest* request) { + timeout_.Cancel(); + 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"; + check_errors_ = 0; + SetConnected(true); + return; + } + VLOG(1) << "Connectivity check failed: " << http_response_code; + OnUrlRequestError(); +} + +void ConnectivityCheckerImpl::OnReadCompleted(net::URLRequest* request, + int bytes_read) { + NOTREACHED(); +} + +void ConnectivityCheckerImpl::OnSSLCertificateError( + net::URLRequest* request, + const net::SSLInfo& ssl_info, + bool fatal) { + LOG(ERROR) << "OnSSLCertificateError: cert_status=" << ssl_info.cert_status; + timeout_.Cancel(); + OnUrlRequestError(); +} + +void ConnectivityCheckerImpl::OnUrlRequestError() { + ++check_errors_; + if (check_errors_ > kNumErrorsToNotifyOffline) { + check_errors_ = kNumErrorsToNotifyOffline; + SetConnected(false); + } + url_request_.reset(NULL); + // Check again. + task_runner_->PostDelayedTask( + FROM_HERE, base::Bind(&ConnectivityCheckerImpl::Check, this), + base::TimeDelta::FromSeconds(kConnectivityPeriodSeconds)); +} + +void ConnectivityCheckerImpl::OnUrlRequestTimeout() { + LOG(ERROR) << "time out"; + OnUrlRequestError(); +} + +void ConnectivityCheckerImpl::Cancel() { + if (!url_request_.get()) + return; + VLOG(2) << "Cancel connectivity check in progress"; + timeout_.Cancel(); + url_request_.reset(NULL); // URLRequest::Cancel() is called in destructor. +} + +} // namespace chromecast diff --git a/chromium/chromecast/net/connectivity_checker_impl.h b/chromium/chromecast/net/connectivity_checker_impl.h new file mode 100644 index 00000000000..da15039de75 --- /dev/null +++ b/chromium/chromecast/net/connectivity_checker_impl.h @@ -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. + +#ifndef CHROMECAST_NET_CONNECTIVITY_CHECKER_IMPL_H_ +#define CHROMECAST_NET_CONNECTIVITY_CHECKER_IMPL_H_ + +#include "base/cancelable_callback.h" +#include "chromecast/net/connectivity_checker.h" +#include "net/base/network_change_notifier.h" +#include "net/url_request/url_request.h" + +class GURL; + +namespace base { +class SingleThreadTaskRunner; +} + +namespace net { +class SSLInfo; +class URLRequest; +class URLRequestContext; +} + +namespace chromecast { + +// Simple class to check network connectivity by sending a HEAD http request +// to given url. +class ConnectivityCheckerImpl + : public ConnectivityChecker, + public net::URLRequest::Delegate, + public net::NetworkChangeNotifier::NetworkChangeObserver { + public: + explicit ConnectivityCheckerImpl( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); + + // ConnectivityChecker implementation: + bool Connected() const override; + void Check() override; + + protected: + ~ConnectivityCheckerImpl() override; + + private: + // UrlRequest::Delegate implementation: + void OnResponseStarted(net::URLRequest* request) override; + void OnReadCompleted(net::URLRequest* request, int bytes_read) override; + void OnSSLCertificateError(net::URLRequest* request, + const net::SSLInfo& ssl_info, + bool fatal) override; + + // Initializes ConnectivityChecker + void Initialize(); + + // net::NetworkChangeNotifier::NetworkChangeObserver implementation: + void OnNetworkChanged( + net::NetworkChangeNotifier::ConnectionType type) override; + + // Cancels current connectivity checking in progress. + void Cancel(); + + // Sets connectivity and alerts observers if it has changed + void SetConnected(bool connected); + + // Called when URL request failed. + void OnUrlRequestError(); + + // Called when URL request timed out. + void OnUrlRequestTimeout(); + + scoped_ptr<GURL> connectivity_check_url_; + scoped_ptr<net::URLRequestContext> url_request_context_; + scoped_ptr<net::URLRequest> url_request_; + const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + bool connected_; + // Number of connectivity check errors. + unsigned int check_errors_; + base::CancelableCallback<void()> timeout_; + + DISALLOW_COPY_AND_ASSIGN(ConnectivityCheckerImpl); +}; + +} // namespace chromecast + +#endif // CHROMECAST_NET_CONNECTIVITY_CHECKER_IMPL_H_ diff --git a/chromium/chromecast/net/fake_connectivity_checker.cc b/chromium/chromecast/net/fake_connectivity_checker.cc new file mode 100644 index 00000000000..fc3caed6ae3 --- /dev/null +++ b/chromium/chromecast/net/fake_connectivity_checker.cc @@ -0,0 +1,31 @@ +// 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/fake_connectivity_checker.h" + +namespace chromecast { + +FakeConnectivityChecker::FakeConnectivityChecker() + : ConnectivityChecker(), + connected_(true) { +} + +FakeConnectivityChecker::~FakeConnectivityChecker() {} + +bool FakeConnectivityChecker::Connected() const { + return connected_; +} + +void FakeConnectivityChecker::Check() { +} + +void FakeConnectivityChecker::SetConnectedForTest(bool connected) { + if (connected_ == connected) + return; + + connected_ = connected; + Notify(connected); +} + +} // namespace chromecast diff --git a/chromium/chromecast/net/fake_connectivity_checker.h b/chromium/chromecast/net/fake_connectivity_checker.h new file mode 100644 index 00000000000..7381b8f0ddc --- /dev/null +++ b/chromium/chromecast/net/fake_connectivity_checker.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. + +#include "chromecast/net/connectivity_checker.h" + +namespace chromecast { + +// A simple fake connectivity checker for testing. Will appeared to be +// connected by default. +class FakeConnectivityChecker : public ConnectivityChecker { + public: + FakeConnectivityChecker(); + + // ConnectivityChecker implementation: + bool Connected() const override; + void Check() override; + + // Sets connectivity and notifies observers if it has changed. + void SetConnectedForTest(bool connected); + + protected: + ~FakeConnectivityChecker() override; + + private: + friend class base::RefCountedThreadSafe<FakeConnectivityChecker>; + bool connected_; + + DISALLOW_COPY_AND_ASSIGN(FakeConnectivityChecker); +}; + +} // namespace chromecast diff --git a/chromium/chromecast/public/BUILD.gn b/chromium/chromecast/public/BUILD.gn new file mode 100644 index 00000000000..5ad19d36e33 --- /dev/null +++ b/chromium/chromecast/public/BUILD.gn @@ -0,0 +1,19 @@ +# 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("public") { + sources = [ + "cast_egl_platform.h", + "cast_egl_platform_shlib.h", + "cast_media_shlib.h", + "cast_sys_info.h", + "chromecast_export.h", + "graphics_properties_shlib.h", + "graphics_types.h", + "osd_plane.h", + "osd_plane_shlib.h", + "osd_surface.h", + "video_plane.h", + ] +} diff --git a/chromium/chromecast/public/cast_egl_platform.h b/chromium/chromecast/public/cast_egl_platform.h index c1350f6964f..a597f9f8ed2 100644 --- a/chromium/chromecast/public/cast_egl_platform.h +++ b/chromium/chromecast/public/cast_egl_platform.h @@ -53,6 +53,11 @@ class CastEglPlatform { virtual NativeWindowType CreateWindow(NativeDisplayType display_type, const Size& size) = 0; virtual void DestroyWindow(NativeWindowType window) = 0; + + // Specifies if creating multiple surfaces on a window is broken on this + // platform and a new window is required. This should return false on most + // implementations. + virtual bool MultipleSurfaceUnsupported() = 0; }; } // namespace chromecast diff --git a/chromium/chromecast/public/cast_media_shlib.h b/chromium/chromecast/public/cast_media_shlib.h index 6f2d60d6cd1..8b776da8a43 100644 --- a/chromium/chromecast/public/cast_media_shlib.h +++ b/chromium/chromecast/public/cast_media_shlib.h @@ -13,6 +13,8 @@ namespace chromecast { namespace media { +class VideoPlane; + // 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 @@ -32,6 +34,11 @@ class CHROMECAST_EXPORT CastMediaShlib { // state. The implementation must release all media-related hardware // resources. static void Finalize(); + + // Gets the VideoPlane instance for managing the hardware video plane. + // While an implementation is in an initialized state, this function may be + // called at any time. The VideoPlane object must be destroyed in Finalize. + static VideoPlane* GetVideoPlane(); }; } // namespace media diff --git a/chromium/chromecast/public/graphics_types.h b/chromium/chromecast/public/graphics_types.h index 09ac12ba29b..841dc00ac81 100644 --- a/chromium/chromecast/public/graphics_types.h +++ b/chromium/chromecast/public/graphics_types.h @@ -18,6 +18,17 @@ struct Rect { int height; }; +struct RectF { + RectF(float w, float h) : x(0), y(0), width(w), height(h) {} + RectF(float arg_x, float arg_y, float w, float h) + : x(arg_x), y(arg_y), width(w), height(h) {} + + float x; + float y; + float width; + float height; +}; + struct Size { Size(int w, int h) : width(w), height(h) {} diff --git a/chromium/chromecast/public/media/decoder_config.h b/chromium/chromecast/public/media/decoder_config.h index b1077af5763..45fd1d4cab4 100644 --- a/chromium/chromecast/public/media/decoder_config.h +++ b/chromium/chromecast/public/media/decoder_config.h @@ -8,24 +8,20 @@ #include <stdint.h> #include <vector> +#include "chromecast/public/media/stream_id.h" + 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, + kAudioCodecUnknown = 0, + kCodecAAC, kCodecMP3, kCodecPCM, kCodecPCM_S16BE, @@ -34,14 +30,28 @@ enum AudioCodec { kCodecEAC3, kCodecAC3, kCodecDTS, + + kAudioCodecMin = kAudioCodecUnknown, kAudioCodecMax = kCodecDTS, }; -enum VideoCodec { - kVideoCodecUnknown = -1, +enum SampleFormat { + kUnknownSampleFormat = 0, + kSampleFormatU8, // Unsigned 8-bit w/ bias of 128. + kSampleFormatS16, // Signed 16-bit. + kSampleFormatS32, // Signed 32-bit. + kSampleFormatF32, // Float 32-bit. + kSampleFormatPlanarS16, // Signed 16-bit planar. + kSampleFormatPlanarF32, // Float 32-bit planar. + kSampleFormatPlanarS32, // Signed 32-bit planar. + + kSampleFormatMin = kUnknownSampleFormat, + kSampleFormatMax = kSampleFormatPlanarS32, +}; - kVideoCodecMin = 0, - kCodecH264 = kVideoCodecMin, +enum VideoCodec { + kVideoCodecUnknown = 0, + kCodecH264, kCodecVC1, kCodecMPEG2, kCodecMPEG4, @@ -49,15 +59,15 @@ enum VideoCodec { kCodecVP8, kCodecVP9, kCodecHEVC, + + kVideoCodecMin = kVideoCodecUnknown, kVideoCodecMax = kCodecHEVC, }; // Profile for Video codec. enum VideoProfile { - kVideoProfileUnknown = -1, - - kVideoProfileMin = 0, - kH264Baseline = kVideoProfileMin, + kVideoProfileUnknown = 0, + kH264Baseline, kH264Main, kH264Extended, kH264High, @@ -70,6 +80,8 @@ enum VideoProfile { kH264MultiviewHigh, kVP8ProfileAny, kVP9ProfileAny, + + kVideoProfileMin = kVideoProfileUnknown, kVideoProfileMax = kVP9ProfileAny, }; @@ -78,16 +90,22 @@ enum VideoProfile { // 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) {} - + : id(kPrimary), + codec(kAudioCodecUnknown), + sample_format(kUnknownSampleFormat), + bytes_per_channel(0), + channel_number(0), + samples_per_second(0), + extra_data(nullptr), + extra_data_size(0), + is_encrypted(false) {} + + // Stream id. + StreamId id; // Audio codec. AudioCodec codec; + // The format of each audio sample. + SampleFormat sample_format; // Number of bytes in each channel. int bytes_per_channel; // Number of channels in this audio stream. @@ -109,13 +127,16 @@ struct AudioConfig { // determine if the configuration is still valid or not. struct VideoConfig { VideoConfig() - : codec(kVideoCodecUnknown), + : id(kPrimary), + codec(kVideoCodecUnknown), profile(kVideoProfileUnknown), additional_config(nullptr), extra_data(nullptr), extra_data_size(0), is_encrypted(false) {} + // Stream Id. + StreamId id; // Video codec. VideoCodec codec; // Video codec profile. @@ -140,6 +161,10 @@ struct VideoConfig { inline bool IsValidConfig(const AudioConfig& config) { return config.codec >= kAudioCodecMin && config.codec <= kAudioCodecMax && + config.codec != kAudioCodecUnknown && + config.sample_format >= kSampleFormatMin && + config.sample_format <= kSampleFormatMax && + config.sample_format != kUnknownSampleFormat && config.channel_number > 0 && config.bytes_per_channel > 0 && config.bytes_per_channel <= kMaxBytesPerSample && @@ -148,7 +173,9 @@ inline bool IsValidConfig(const AudioConfig& config) { } inline bool IsValidConfig(const VideoConfig& config) { - return config.codec >= kVideoCodecMin && config.codec <= kVideoCodecMax; + return config.codec >= kVideoCodecMin && + config.codec <= kVideoCodecMax && + config.codec != kVideoCodecUnknown; } } // namespace media diff --git a/chromium/chromecast/public/media/stream_id.h b/chromium/chromecast/public/media/stream_id.h new file mode 100644 index 00000000000..6641fd54ca9 --- /dev/null +++ b/chromium/chromecast/public/media/stream_id.h @@ -0,0 +1,19 @@ +// 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_STREAM_ID_H_ +#define CHROMECAST_PUBLIC_MEDIA_STREAM_ID_H_ + +namespace chromecast { +namespace media { + +enum StreamId { + kPrimary = 0, + kSecondary +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_MEDIA_STREAM_ID_H_ diff --git a/chromium/chromecast/public/video_plane.h b/chromium/chromecast/public/video_plane.h new file mode 100644 index 00000000000..890e7a051b8 --- /dev/null +++ b/chromium/chromecast/public/video_plane.h @@ -0,0 +1,55 @@ +// 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_VIDEO_PLANE_H_ +#define CHROMECAST_PUBLIC_VIDEO_PLANE_H_ + +namespace chromecast { +struct RectF; +struct Size; + +namespace media { + +class VideoPlane { + public: + // List of possible hardware transforms that can be applied to video. + // Rotations are anticlockwise. + enum Transform { + TRANSFORM_NONE, + ROTATE_90, + ROTATE_180, + ROTATE_270, + FLIP_HORIZONTAL, + FLIP_VERTICAL, + }; + + enum CoordinateType { + // Graphics plane as coordinate type. + COORDINATE_TYPE_GRAPHICS_PLANE = 0, + // Output display screen as coordinate type. + COORDINATE_TYPE_SCREEN_RESOLUTION = 1, + }; + + virtual ~VideoPlane() {} + + // Gets output screen resolution. + virtual Size GetScreenResolution() = 0; + + // Updates the video plane geometry. + // |display_rect| specifies the rectangle that the video should occupy. + // |coordinate_type| gives the coordinate space of |display_rect|. + // |transform| specifies how the video should be transformed within that + // rectangle. + virtual void SetGeometry(const RectF& display_rect, + CoordinateType coordinate_type, + Transform transform) = 0; + + // Notifies VideoPlane that screen resolution has changed. + virtual void OnScreenResolutionChanged(const Size& screen_res) = 0; +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_VIDEO_PLANE_H_ diff --git a/chromium/chromecast/renderer/cast_content_renderer_client.cc b/chromium/chromecast/renderer/cast_content_renderer_client.cc index cfce89371aa..1f7c4a68d19 100644 --- a/chromium/chromecast/renderer/cast_content_renderer_client.cc +++ b/chromium/chromecast/renderer/cast_content_renderer_client.cc @@ -7,9 +7,11 @@ #include <sys/sysinfo.h> #include "base/command_line.h" +#include "base/location.h" #include "base/memory/memory_pressure_listener.h" #include "base/strings/string_number_conversions.h" -#include "chromecast/common/chromecast_switches.h" +#include "base/thread_task_runner_handle.h" +#include "chromecast/base/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" @@ -58,9 +60,8 @@ void PlatformPollFreemem(void) { } // Setup next poll. - base::MessageLoopProxy::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&PlatformPollFreemem), + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&PlatformPollFreemem), base::TimeDelta::FromMilliseconds(kPollingIntervalMS)); } #endif @@ -71,18 +72,27 @@ const blink::WebColor kColorBlack = 0xFF000000; class CastRenderViewObserver : content::RenderViewObserver { public: - explicit CastRenderViewObserver(content::RenderView* render_view); + CastRenderViewObserver(CastContentRendererClient* client, + content::RenderView* render_view); ~CastRenderViewObserver() override {} void DidClearWindowObject(blink::WebLocalFrame* frame) override; + + private: + CastContentRendererClient* const client_; + + DISALLOW_COPY_AND_ASSIGN(CastRenderViewObserver); }; -CastRenderViewObserver::CastRenderViewObserver(content::RenderView* render_view) - : content::RenderViewObserver(render_view) { +CastRenderViewObserver::CastRenderViewObserver( + CastContentRendererClient* client, + content::RenderView* render_view) + : content::RenderViewObserver(render_view), + client_(client) { } void CastRenderViewObserver::DidClearWindowObject(blink::WebLocalFrame* frame) { - PlatformAddRendererNativeBindings(frame); + client_->AddRendererNativeBindings(frame); } } // namespace @@ -93,9 +103,18 @@ CastContentRendererClient::CastContentRendererClient() { CastContentRendererClient::~CastContentRendererClient() { } +void CastContentRendererClient::AddRendererNativeBindings( + blink::WebLocalFrame* frame) { +} + +std::vector<scoped_refptr<IPC::MessageFilter>> +CastContentRendererClient::GetRendererMessageFilters() { + return std::vector<scoped_refptr<IPC::MessageFilter>>(); +} + void CastContentRendererClient::RenderThreadStarted() { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); -#if defined(USE_NSS_CERTS) +#if !defined(USE_OPENSSL) // Note: Copied from chrome_render_process_observer.cc to fix b/8676652. // // On platforms where the system NSS shared libraries are used, @@ -120,7 +139,7 @@ void CastContentRendererClient::RenderThreadStarted() { } cast_observer_.reset( - new CastRenderProcessObserver(PlatformGetRendererMessageFilters())); + new CastRenderProcessObserver(GetRendererMessageFilters())); prescient_networking_dispatcher_.reset( new network_hints::PrescientNetworkingDispatcher()); @@ -160,19 +179,19 @@ void CastContentRendererClient::RenderViewCreated( } // Note: RenderView will own the lifetime of its observer. - new CastRenderViewObserver(render_view); + new CastRenderViewObserver(this, render_view); } void CastContentRendererClient::AddKeySystems( std::vector< ::media::KeySystemInfo>* key_systems) { AddChromecastKeySystems(key_systems); - AddChromecastPlatformKeySystems(key_systems); } #if !defined(OS_ANDROID) scoped_ptr<::media::RendererFactory> CastContentRendererClient::CreateMediaRendererFactory( ::content::RenderFrame* render_frame, + const scoped_refptr<::media::GpuVideoAcceleratorFactories>& gpu_factories, const scoped_refptr<::media::MediaLog>& media_log) { const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); if (!cmd_line->HasSwitch(switches::kEnableCmaMediaPipeline)) @@ -180,7 +199,7 @@ CastContentRendererClient::CreateMediaRendererFactory( return scoped_ptr<::media::RendererFactory>( new chromecast::media::ChromecastMediaRendererFactory( - media_log, render_frame->GetRoutingID())); + gpu_factories, media_log, render_frame->GetRoutingID())); } #endif diff --git a/chromium/chromecast/renderer/cast_content_renderer_client.h b/chromium/chromecast/renderer/cast_content_renderer_client.h index efdc3a5a9f6..0ee87ed6bd5 100644 --- a/chromium/chromecast/renderer/cast_content_renderer_client.h +++ b/chromium/chromecast/renderer/cast_content_renderer_client.h @@ -22,18 +22,21 @@ 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(); + // Creates an implementation of CastContentRendererClient. Platform should + // link in an implementation as needed. + static scoped_ptr<CastContentRendererClient> Create(); + ~CastContentRendererClient() override; + // Adds any platform-specific bindings to the current frame. + virtual void AddRendererNativeBindings(blink::WebLocalFrame* frame); + // Returns any MessageFilters from the platform implementation that should // be added to the render process. - std::vector<scoped_refptr<IPC::MessageFilter>> - PlatformGetRendererMessageFilters(); + virtual std::vector<scoped_refptr<IPC::MessageFilter>> + GetRendererMessageFilters(); // ContentRendererClient implementation: void RenderThreadStarted() override; @@ -41,14 +44,18 @@ class CastContentRendererClient : public content::ContentRendererClient { void AddKeySystems( std::vector< ::media::KeySystemInfo>* key_systems) override; #if !defined(OS_ANDROID) - scoped_ptr<media::RendererFactory> CreateMediaRendererFactory( + scoped_ptr<::media::RendererFactory> CreateMediaRendererFactory( content::RenderFrame* render_frame, - const scoped_refptr<media::MediaLog>& media_log) override; + const scoped_refptr<::media::GpuVideoAcceleratorFactories>& gpu_factories, + const scoped_refptr<::media::MediaLog>& media_log) override; #endif blink::WebPrescientNetworking* GetPrescientNetworking() override; void DeferMediaLoad(content::RenderFrame* render_frame, const base::Closure& closure) override; + protected: + CastContentRendererClient(); + private: scoped_ptr<network_hints::PrescientNetworkingDispatcher> prescient_networking_dispatcher_; diff --git a/chromium/chromecast/renderer/cast_content_renderer_client_simple.cc b/chromium/chromecast/renderer/cast_content_renderer_client_simple.cc index 5ad4fccb261..5f94e08e7ac 100644 --- a/chromium/chromecast/renderer/cast_content_renderer_client_simple.cc +++ b/chromium/chromecast/renderer/cast_content_renderer_client_simple.cc @@ -4,17 +4,15 @@ #include "chromecast/renderer/cast_content_renderer_client.h" +#include "base/memory/scoped_ptr.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>>(); +// static +scoped_ptr<CastContentRendererClient> CastContentRendererClient::Create() { + return make_scoped_ptr(new CastContentRendererClient()); } } // namespace shell diff --git a/chromium/chromecast/renderer/key_systems_cast.cc b/chromium/chromecast/renderer/key_systems_cast.cc index bede01890c5..bb27a6f11a2 100644 --- a/chromium/chromecast/renderer/key_systems_cast.cc +++ b/chromium/chromecast/renderer/key_systems_cast.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/logging.h" +#include "build/build_config.h" #include "chromecast/media/base/key_systems_common.h" #include "components/cdm/renderer/widevine_key_systems.h" #include "media/base/eme_constants.h" @@ -31,8 +32,13 @@ void AddKeySystemWithCodecs( ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1; info.max_audio_robustness = ::media::EmeRobustness::EMPTY; info.max_video_robustness = ::media::EmeRobustness::EMPTY; +#if defined(OS_ANDROID) info.persistent_license_support = ::media::EmeSessionTypeSupport::NOT_SUPPORTED; +#else + info.persistent_license_support = + ::media::EmeSessionTypeSupport::SUPPORTED; +#endif info.persistent_release_message_support = ::media::EmeSessionTypeSupport::NOT_SUPPORTED; info.persistent_state_support = ::media::EmeFeatureSupport::ALWAYS_ENABLED; diff --git a/chromium/chromecast/renderer/key_systems_cast.h b/chromium/chromecast/renderer/key_systems_cast.h index c64eb1db708..d59993f131d 100644 --- a/chromium/chromecast/renderer/key_systems_cast.h +++ b/chromium/chromecast/renderer/key_systems_cast.h @@ -22,10 +22,6 @@ void AddKeySystemWithCodecs( void AddChromecastKeySystems( std::vector<::media::KeySystemInfo>* key_systems_info); -// TODO(gunsch): Remove when prefixed EME is removed. -void AddChromecastPlatformKeySystems( - std::vector<::media::KeySystemInfo>* key_systems_info); - } // namespace shell } // namespace chromecast diff --git a/chromium/chromecast/renderer/key_systems_cast_simple.cc b/chromium/chromecast/renderer/key_systems_cast_simple.cc deleted file mode 100644 index e5ca3783c3e..00000000000 --- a/chromium/chromecast/renderer/key_systems_cast_simple.cc +++ /dev/null @@ -1,16 +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/renderer/key_systems_cast.h" - -namespace chromecast { -namespace shell { - -void AddChromecastPlatformKeySystems( - std::vector< ::media::KeySystemInfo>* key_systems_info) { - // Intentional no-op for public build. -} - -} // namespace shell -} // namespace chromecast diff --git a/chromium/chromecast/renderer/media/audio_pipeline_proxy.cc b/chromium/chromecast/renderer/media/audio_pipeline_proxy.cc index 30297a8a549..0193232d6f2 100644 --- a/chromium/chromecast/renderer/media/audio_pipeline_proxy.cc +++ b/chromium/chromecast/renderer/media/audio_pipeline_proxy.cc @@ -195,22 +195,20 @@ void AudioPipelineProxyInternal::OnStateChanged( 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__)) +// A macro runs current member function on |io_task_runner_| thread. +#define FORWARD_ON_IO_THREAD(param_fn, ...) \ + io_task_runner_->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<base::SingleThreadTaskRunner> io_task_runner, scoped_refptr<MediaChannelProxy> media_channel_proxy) - : io_message_loop_proxy_(io_message_loop_proxy), + : io_task_runner_(io_task_runner), proxy_(new AudioPipelineProxyInternal(media_channel_proxy)), audio_streamer_(new AvStreamerProxy()), weak_factory_(this) { - DCHECK(io_message_loop_proxy_.get()); + DCHECK(io_task_runner_.get()); weak_this_ = weak_factory_.GetWeakPtr(); thread_checker_.DetachFromThread(); } @@ -218,7 +216,7 @@ AudioPipelineProxy::AudioPipelineProxy( AudioPipelineProxy::~AudioPipelineProxy() { DCHECK(thread_checker_.CalledOnValidThread()); // Release the underlying object on the right thread. - io_message_loop_proxy_->PostTask( + io_task_runner_->PostTask( FROM_HERE, base::Bind(&AudioPipelineProxyInternal::Release, base::Passed(&proxy_))); } diff --git a/chromium/chromecast/renderer/media/audio_pipeline_proxy.h b/chromium/chromecast/renderer/media/audio_pipeline_proxy.h index b319ee1b8a7..7bcc6852e6a 100644 --- a/chromium/chromecast/renderer/media/audio_pipeline_proxy.h +++ b/chromium/chromecast/renderer/media/audio_pipeline_proxy.h @@ -15,7 +15,7 @@ #include "media/base/pipeline_status.h" namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; class SharedMemory; } @@ -33,9 +33,8 @@ class MediaChannelProxy; class AudioPipelineProxy : public AudioPipeline { public: - AudioPipelineProxy( - scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, - scoped_refptr<MediaChannelProxy> media_channel_proxy); + AudioPipelineProxy(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, + scoped_refptr<MediaChannelProxy> media_channel_proxy); ~AudioPipelineProxy() override; void Initialize( @@ -60,7 +59,7 @@ class AudioPipelineProxy : public AudioPipeline { void OnPipeWrite(); void OnPipeRead(); - scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; // |proxy_| main goal is to convert function calls to IPC messages. scoped_ptr<AudioPipelineProxyInternal> proxy_; diff --git a/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.cc b/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.cc index 62fe7716a3d..f2c9bfbd973 100644 --- a/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.cc +++ b/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.cc @@ -18,9 +18,11 @@ namespace chromecast { namespace media { ChromecastMediaRendererFactory::ChromecastMediaRendererFactory( + const scoped_refptr<::media::GpuVideoAcceleratorFactories>& gpu_factories, const scoped_refptr<::media::MediaLog>& media_log, int render_frame_id) : render_frame_id_(render_frame_id), + gpu_factories_(gpu_factories), media_log_(media_log) { } @@ -46,11 +48,12 @@ scoped_ptr<::media::Renderer> ChromecastMediaRendererFactory::CreateRenderer( ::media::CHANNEL_LAYOUT_STEREO, kDefaultSamplingRate, kDefaultBitsPerSample, buffer_size, ::media::AudioParameters::NO_EFFECTS); - ::media::AudioHardwareConfig audio_config(input_audio_params, - output_audio_params); + + audio_config_.reset(new ::media::AudioHardwareConfig(input_audio_params, + output_audio_params)); default_render_factory_.reset(new ::media::DefaultRendererFactory( - media_log_, /*gpu_factories*/ nullptr, audio_config)); + media_log_, /*gpu_factories*/ nullptr, *audio_config_)); } DCHECK(default_render_factory_); @@ -61,8 +64,8 @@ scoped_ptr<::media::Renderer> ChromecastMediaRendererFactory::CreateRenderer( 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<CmaRenderer> cma_renderer(new CmaRenderer( + cma_media_pipeline.Pass(), video_renderer_sink, gpu_factories_)); scoped_ptr<::media::Renderer> default_media_render( default_render_factory_->CreateRenderer(media_task_runner, audio_renderer_sink, diff --git a/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.h b/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.h index 820609478fa..ab2b078edad 100644 --- a/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.h +++ b/chromium/chromecast/renderer/media/chromecast_media_renderer_factory.h @@ -10,6 +10,8 @@ #include "media/base/renderer_factory.h" namespace media { +class AudioHardwareConfig; +class GpuVideoAcceleratorFactories; class MediaLog; class DefaultRendererFactory; } @@ -20,6 +22,7 @@ namespace media { class ChromecastMediaRendererFactory : public ::media::RendererFactory { public: ChromecastMediaRendererFactory( + const scoped_refptr<::media::GpuVideoAcceleratorFactories>& gpu_factories, const scoped_refptr<::media::MediaLog>& media_log, int render_frame_id); ~ChromecastMediaRendererFactory() final; @@ -32,9 +35,13 @@ class ChromecastMediaRendererFactory : public ::media::RendererFactory { private: int render_frame_id_; + scoped_refptr<::media::GpuVideoAcceleratorFactories> gpu_factories_; scoped_refptr<::media::MediaLog> media_log_; scoped_ptr<::media::DefaultRendererFactory> default_render_factory_; + // Audio config for the default media renderer. + scoped_ptr<::media::AudioHardwareConfig> audio_config_; + DISALLOW_COPY_AND_ASSIGN(ChromecastMediaRendererFactory); }; diff --git a/chromium/chromecast/renderer/media/cma_message_filter_proxy.cc b/chromium/chromecast/renderer/media/cma_message_filter_proxy.cc index f2701a98725..c502ea2cafb 100644 --- a/chromium/chromecast/renderer/media/cma_message_filter_proxy.cc +++ b/chromium/chromecast/renderer/media/cma_message_filter_proxy.cc @@ -5,7 +5,7 @@ #include "chromecast/renderer/media/cma_message_filter_proxy.h" #include "base/bind.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" #include "base/time/time.h" #include "chromecast/common/media/cma_messages.h" #include "ipc/ipc_logging.h" @@ -46,22 +46,21 @@ CmaMessageFilterProxy* CmaMessageFilterProxy::Get() { } CmaMessageFilterProxy::CmaMessageFilterProxy( - const scoped_refptr<base::MessageLoopProxy>& io_message_loop) - : sender_(NULL), - io_message_loop_(io_message_loop) { + const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) + : sender_(NULL), io_task_runner_(io_task_runner) { DCHECK(!filter_); filter_ = this; } int CmaMessageFilterProxy::CreateChannel() { - DCHECK(io_message_loop_->BelongsToCurrentThread()); + DCHECK(io_task_runner_->BelongsToCurrentThread()); DelegateEntry* entry = new DelegateEntry(); int id = delegates_.Add(entry); return id; } void CmaMessageFilterProxy::DestroyChannel(int id) { - DCHECK(io_message_loop_->BelongsToCurrentThread()); + DCHECK(io_task_runner_->BelongsToCurrentThread()); DelegateEntry* entry = delegates_.Lookup(id); if (!entry) return; @@ -71,7 +70,7 @@ void CmaMessageFilterProxy::DestroyChannel(int id) { bool CmaMessageFilterProxy::SetMediaDelegate( int id, const MediaDelegate& media_delegate) { - DCHECK(io_message_loop_->BelongsToCurrentThread()); + DCHECK(io_task_runner_->BelongsToCurrentThread()); DelegateEntry* entry = delegates_.Lookup(id); if (!entry) return false; @@ -81,7 +80,7 @@ bool CmaMessageFilterProxy::SetMediaDelegate( bool CmaMessageFilterProxy::SetAudioDelegate( int id, const AudioDelegate& audio_delegate) { - DCHECK(io_message_loop_->BelongsToCurrentThread()); + DCHECK(io_task_runner_->BelongsToCurrentThread()); DelegateEntry* entry = delegates_.Lookup(id); if (!entry) return false; @@ -91,7 +90,7 @@ bool CmaMessageFilterProxy::SetAudioDelegate( bool CmaMessageFilterProxy::SetVideoDelegate( int id, const VideoDelegate& video_delegate) { - DCHECK(io_message_loop_->BelongsToCurrentThread()); + DCHECK(io_task_runner_->BelongsToCurrentThread()); DelegateEntry* entry = delegates_.Lookup(id); if (!entry) return false; @@ -100,7 +99,7 @@ bool CmaMessageFilterProxy::SetVideoDelegate( } bool CmaMessageFilterProxy::Send(scoped_ptr<IPC::Message> message) { - DCHECK(io_message_loop_->BelongsToCurrentThread()); + DCHECK(io_task_runner_->BelongsToCurrentThread()); if (!sender_) return false; bool status = sender_->Send(message.release()); @@ -269,4 +268,4 @@ void CmaMessageFilterProxy::OnNaturalSizeChanged( } } // namespace media -} // namespace chromecast
\ No newline at end of file +} // namespace chromecast diff --git a/chromium/chromecast/renderer/media/cma_message_filter_proxy.h b/chromium/chromecast/renderer/media/cma_message_filter_proxy.h index 0930f19938e..ab007118e17 100644 --- a/chromium/chromecast/renderer/media/cma_message_filter_proxy.h +++ b/chromium/chromecast/renderer/media/cma_message_filter_proxy.h @@ -19,7 +19,7 @@ #include "media/base/pipeline_status.h" namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; } namespace chromecast { @@ -59,7 +59,7 @@ class CmaMessageFilterProxy : public IPC::MessageFilter { }; explicit CmaMessageFilterProxy( - const scoped_refptr<base::MessageLoopProxy>& io_message_loop); + const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner); // Getter for the one CmaMessageFilterHost object. static CmaMessageFilterProxy* Get(); @@ -124,7 +124,7 @@ class CmaMessageFilterProxy : public IPC::MessageFilter { IPC::Sender* sender_; - scoped_refptr<base::MessageLoopProxy> const io_message_loop_; + scoped_refptr<base::SingleThreadTaskRunner> const io_task_runner_; DISALLOW_COPY_AND_ASSIGN(CmaMessageFilterProxy); }; diff --git a/chromium/chromecast/renderer/media/cma_renderer_unittest.cc b/chromium/chromecast/renderer/media/cma_renderer_unittest.cc deleted file mode 100644 index 19ff6ecec62..00000000000 --- a/chromium/chromecast/renderer/media/cma_renderer_unittest.cc +++ /dev/null @@ -1,125 +0,0 @@ -// 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_pipeline_proxy.cc b/chromium/chromecast/renderer/media/media_pipeline_proxy.cc index 830ac0fe134..38a69980c6b 100644 --- a/chromium/chromecast/renderer/media/media_pipeline_proxy.cc +++ b/chromium/chromecast/renderer/media/media_pipeline_proxy.cc @@ -8,7 +8,7 @@ #include "base/callback_helpers.h" #include "base/location.h" #include "base/logging.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.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" @@ -149,44 +149,40 @@ void MediaPipelineProxyInternal::OnStateChanged( 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__)) +// A macro runs current member function on |io_task_runner_| thread. +#define FORWARD_ON_IO_THREAD(param_fn, ...) \ + io_task_runner_->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, + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, LoadType load_type) - : io_message_loop_proxy_(io_message_loop_proxy), + : io_task_runner_(io_task_runner), 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_)), + audio_pipeline_( + new AudioPipelineProxy(io_task_runner, media_channel_proxy_)), + video_pipeline_( + new VideoPipelineProxy(io_task_runner, media_channel_proxy_)), weak_factory_(this) { weak_this_ = weak_factory_.GetWeakPtr(); - io_message_loop_proxy_->PostTask( + io_task_runner_->PostTask( FROM_HERE, - base::Bind(&MediaChannelProxy::Open, media_channel_proxy_, - load_type)); + base::Bind(&MediaChannelProxy::Open, media_channel_proxy_, load_type)); thread_checker_.DetachFromThread(); } MediaPipelineProxy::~MediaPipelineProxy() { - io_message_loop_proxy_->PostTask( + io_task_runner_->PostTask( FROM_HERE, base::Bind(&MediaPipelineProxyInternal::Release, base::Passed(&proxy_))); - io_message_loop_proxy_->PostTask( - FROM_HERE, - base::Bind(&MediaChannelProxy::Close, media_channel_proxy_)); + io_task_runner_->PostTask( + FROM_HERE, base::Bind(&MediaChannelProxy::Close, media_channel_proxy_)); } void MediaPipelineProxy::SetClient( @@ -218,12 +214,12 @@ void MediaPipelineProxy::InitializeAudio( } void MediaPipelineProxy::InitializeVideo( - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, 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); + video_pipeline_->Initialize(configs, frame_provider.Pass(), status_cb); } void MediaPipelineProxy::StartPlayingFrom(base::TimeDelta time) { @@ -250,7 +246,7 @@ void MediaPipelineProxy::Flush(const ::media::PipelineStatusCB& status_cb) { } ::media::PipelineStatusCB cb = base::Bind(&MediaPipelineProxy::OnProxyFlushDone, weak_this_, status_cb); - pending_callbacks_ = ::media::SerialRunner::Run(bound_fns, cb); + pending_flush_callbacks_ = ::media::SerialRunner::Run(bound_fns, cb); } void MediaPipelineProxy::OnProxyFlushDone( @@ -258,7 +254,7 @@ void MediaPipelineProxy::OnProxyFlushDone( ::media::PipelineStatus status) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_EQ(status, ::media::PIPELINE_OK); - pending_callbacks_.reset(); + pending_flush_callbacks_.reset(); FORWARD_ON_IO_THREAD(Flush, status_cb); } @@ -266,6 +262,11 @@ void MediaPipelineProxy::Stop() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(has_audio_ || has_video_); + // Cancel pending flush callbacks since we are about to stop/shutdown + // audio/video pipelines. This will ensure A/V Flush won't happen in + // stopped state. + pending_flush_callbacks_.reset(); + if (has_audio_) audio_pipeline_->Stop(); if (has_video_) @@ -280,4 +281,4 @@ void MediaPipelineProxy::SetPlaybackRate(double playback_rate) { } } // namespace cma -} // namespace chromecast
\ No newline at end of file +} // namespace chromecast diff --git a/chromium/chromecast/renderer/media/media_pipeline_proxy.h b/chromium/chromecast/renderer/media/media_pipeline_proxy.h index decec653b86..1e8e9e6b7ab 100644 --- a/chromium/chromecast/renderer/media/media_pipeline_proxy.h +++ b/chromium/chromecast/renderer/media/media_pipeline_proxy.h @@ -16,7 +16,7 @@ #include "media/base/serial_runner.h" namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; } namespace chromecast { @@ -28,10 +28,9 @@ class VideoPipelineProxy; class MediaPipelineProxy : public MediaPipeline { public: - MediaPipelineProxy( - int render_frame_id, - scoped_refptr<base::MessageLoopProxy> message_loop_proxy, - LoadType load_type); + MediaPipelineProxy(int render_frame_id, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + LoadType load_type); ~MediaPipelineProxy() override; // MediaPipeline implementation. @@ -44,7 +43,7 @@ class MediaPipelineProxy : public MediaPipeline { scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb) override; void InitializeVideo( - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb) override; void StartPlayingFrom(base::TimeDelta time) override; @@ -58,7 +57,7 @@ class MediaPipelineProxy : public MediaPipeline { base::ThreadChecker thread_checker_; - scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; const int render_frame_id_; @@ -71,7 +70,7 @@ class MediaPipelineProxy : public MediaPipeline { bool has_video_; scoped_ptr<AudioPipelineProxy> audio_pipeline_; scoped_ptr<VideoPipelineProxy> video_pipeline_; - scoped_ptr< ::media::SerialRunner> pending_callbacks_; + scoped_ptr< ::media::SerialRunner> pending_flush_callbacks_; base::WeakPtr<MediaPipelineProxy> weak_this_; base::WeakPtrFactory<MediaPipelineProxy> weak_factory_; @@ -82,4 +81,4 @@ class MediaPipelineProxy : public MediaPipeline { } // namespace media } // namespace chromecast -#endif // CHROMECAST_RENDERER_MEDIA_MEDIA_PIPELINE_PROXY_H_
\ No newline at end of file +#endif // CHROMECAST_RENDERER_MEDIA_MEDIA_PIPELINE_PROXY_H_ diff --git a/chromium/chromecast/renderer/media/video_pipeline_proxy.cc b/chromium/chromecast/renderer/media/video_pipeline_proxy.cc index 6c924cd4e2e..bcef0838d26 100644 --- a/chromium/chromecast/renderer/media/video_pipeline_proxy.cc +++ b/chromium/chromecast/renderer/media/video_pipeline_proxy.cc @@ -56,7 +56,7 @@ class VideoPipelineProxyInternal { void SetClient(const base::Closure& pipe_read_cb, const VideoPipelineClient& client); void CreateAvPipe(const SharedMemCB& shared_mem_cb); - void Initialize(const ::media::VideoDecoderConfig& config, + void Initialize(const std::vector<::media::VideoDecoderConfig>& configs, const ::media::PipelineStatusCB& status_cb); private: @@ -166,12 +166,12 @@ void VideoPipelineProxyInternal::OnAvPipeCreated( } void VideoPipelineProxyInternal::Initialize( - const ::media::VideoDecoderConfig& arg1, + const std::vector<::media::VideoDecoderConfig>& configs, 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))); + kVideoTrackId, configs))); if (!success) { status_cb.Run( ::media::PIPELINE_ERROR_INITIALIZATION_FAILED); return; @@ -187,22 +187,20 @@ void VideoPipelineProxyInternal::OnStateChanged( 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__)) +// A macro runs current member function on |io_task_runner_| thread. +#define FORWARD_ON_IO_THREAD(param_fn, ...) \ + io_task_runner_->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<base::SingleThreadTaskRunner> io_task_runner, scoped_refptr<MediaChannelProxy> media_channel_proxy) - : io_message_loop_proxy_(io_message_loop_proxy), + : io_task_runner_(io_task_runner), proxy_(new VideoPipelineProxyInternal(media_channel_proxy)), video_streamer_(new AvStreamerProxy()), weak_factory_(this) { - DCHECK(io_message_loop_proxy_.get()); + DCHECK(io_task_runner_.get()); weak_this_ = weak_factory_.GetWeakPtr(); thread_checker_.DetachFromThread(); } @@ -210,7 +208,7 @@ VideoPipelineProxy::VideoPipelineProxy( VideoPipelineProxy::~VideoPipelineProxy() { DCHECK(thread_checker_.CalledOnValidThread()); // Release the underlying object on the right thread. - io_message_loop_proxy_->PostTask( + io_task_runner_->PostTask( FROM_HERE, base::Bind(&VideoPipelineProxyInternal::Release, base::Passed(&proxy_))); } @@ -225,7 +223,7 @@ void VideoPipelineProxy::SetClient( } void VideoPipelineProxy::Initialize( - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb) { CMALOG(kLogControl) << "VideoPipelineProxy::Initialize"; @@ -235,12 +233,12 @@ void VideoPipelineProxy::Initialize( VideoPipelineProxyInternal::SharedMemCB shared_mem_cb = ::media::BindToCurrentLoop(base::Bind( &VideoPipelineProxy::OnAvPipeCreated, weak_this_, - config, status_cb)); + configs, status_cb)); FORWARD_ON_IO_THREAD(CreateAvPipe, shared_mem_cb); } void VideoPipelineProxy::OnAvPipeCreated( - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, const ::media::PipelineStatusCB& status_cb, scoped_ptr<base::SharedMemory> shared_memory) { CMALOG(kLogControl) << "VideoPipelineProxy::OnAvPipeCreated"; @@ -262,7 +260,7 @@ void VideoPipelineProxy::OnAvPipeCreated( video_streamer_->SetMediaMessageFifo(video_pipe.Pass()); // Now proceed to the decoder/renderer initialization. - FORWARD_ON_IO_THREAD(Initialize, config, status_cb); + FORWARD_ON_IO_THREAD(Initialize, configs, status_cb); } void VideoPipelineProxy::StartFeeding() { diff --git a/chromium/chromecast/renderer/media/video_pipeline_proxy.h b/chromium/chromecast/renderer/media/video_pipeline_proxy.h index 88745525091..1dbf13ec0b8 100644 --- a/chromium/chromecast/renderer/media/video_pipeline_proxy.h +++ b/chromium/chromecast/renderer/media/video_pipeline_proxy.h @@ -14,7 +14,7 @@ #include "media/base/pipeline_status.h" namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; class SharedMemory; } @@ -32,12 +32,11 @@ class MediaChannelProxy; class VideoPipelineProxy : public VideoPipeline { public: - VideoPipelineProxy( - scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, - scoped_refptr<MediaChannelProxy> media_channel_proxy); + VideoPipelineProxy(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, + scoped_refptr<MediaChannelProxy> media_channel_proxy); ~VideoPipelineProxy() override; - void Initialize(const ::media::VideoDecoderConfig& config, + void Initialize(const std::vector<::media::VideoDecoderConfig>& configs, scoped_ptr<CodedFrameProvider> frame_provider, const ::media::PipelineStatusCB& status_cb); void StartFeeding(); @@ -51,13 +50,13 @@ class VideoPipelineProxy : public VideoPipeline { base::ThreadChecker thread_checker_; void OnAvPipeCreated( - const ::media::VideoDecoderConfig& config, + const std::vector<::media::VideoDecoderConfig>& configs, const ::media::PipelineStatusCB& status_cb, scoped_ptr<base::SharedMemory> shared_memory); void OnPipeWrite(); void OnPipeRead(); - scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; // |proxy_| main goal is to convert function calls to IPC messages. scoped_ptr<VideoPipelineProxyInternal> proxy_; |