diff options
39 files changed, 910 insertions, 228 deletions
diff --git a/android_webview/android_webview.gyp b/android_webview/android_webview.gyp index 83f165f..77aeafc 100644 --- a/android_webview/android_webview.gyp +++ b/android_webview/android_webview.gyp @@ -222,6 +222,8 @@ 'browser/aw_javascript_dialog_manager.h', 'browser/aw_login_delegate.cc', 'browser/aw_login_delegate.h', + 'browser/aw_media_client_android.cc', + 'browser/aw_media_client_android.h', 'browser/aw_message_port_message_filter.cc', 'browser/aw_message_port_message_filter.h', 'browser/aw_message_port_service.h', diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS index ba8fe5b..9fd4db1 100644 --- a/android_webview/browser/DEPS +++ b/android_webview/browser/DEPS @@ -24,6 +24,8 @@ include_rules = [ "+content/public/browser", "+content/public/test", + "+media/base/android", + "+printing", "+ui/gfx", diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc index b10b4d4..24f552b 100644 --- a/android_webview/browser/aw_browser_main_parts.cc +++ b/android_webview/browser/aw_browser_main_parts.cc @@ -6,7 +6,9 @@ #include "android_webview/browser/aw_browser_context.h" #include "android_webview/browser/aw_dev_tools_discovery_provider.h" +#include "android_webview/browser/aw_media_client_android.h" #include "android_webview/browser/aw_result_codes.h" +#include "android_webview/common/aw_resource.h" #include "android_webview/native/public/aw_assets.h" #include "base/android/build_info.h" #include "base/android/locale_utils.h" @@ -18,6 +20,7 @@ #include "content/public/common/content_switches.h" #include "content/public/common/result_codes.h" #include "content/public/common/url_utils.h" +#include "media/base/android/media_client_android.h" #include "net/android/network_change_notifier_factory_android.h" #include "net/base/network_change_notifier.h" #include "ui/base/l10n/l10n_util.h" @@ -96,6 +99,9 @@ void AwBrowserMainParts::PreMainMessageLoopRun() { AwDevToolsDiscoveryProvider::Install(); + media::SetMediaClientAndroid( + new AwMediaClientAndroid(AwResource::GetConfigKeySystemUuidMapping())); + // This is needed for WebView Classic backwards compatibility // See crbug.com/298495 content::SetMaxURLChars(20 * 1024 * 1024); diff --git a/android_webview/browser/aw_media_client_android.cc b/android_webview/browser/aw_media_client_android.cc new file mode 100644 index 0000000..bc71465 --- /dev/null +++ b/android_webview/browser/aw_media_client_android.cc @@ -0,0 +1,71 @@ +// 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 "android_webview/browser/aw_media_client_android.h" + +#include <utility> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" + +namespace android_webview { + +namespace { + +const size_t kGUIDLength = 36U; + +#define RCHECK(x) \ + if (!(x)) { \ + LOG(ERROR) << "Can't parse key-system mapping: " \ + << key_system_uuid_mapping; \ + return std::make_pair("", uuid); \ + } + +media::MediaClientAndroid::KeySystemUuidMap::value_type CreateMappingFromString( + const std::string& key_system_uuid_mapping) { + std::vector<uint8_t> uuid; + + std::vector<std::string> tokens; + Tokenize(key_system_uuid_mapping, ",", &tokens); + RCHECK(tokens.size() == 2); + + std::string key_system; + base::TrimWhitespaceASCII(tokens[0], base::TRIM_ALL, &key_system); + + std::string guid(tokens[1]); + RCHECK(guid.length() == kGUIDLength); + base::RemoveChars(guid, "-", &guid); + RCHECK(base::HexStringToBytes(guid, &uuid)); + + return std::make_pair(key_system, uuid); +} + +} // namespace + +AwMediaClientAndroid::AwMediaClientAndroid( + const std::vector<std::string>& key_system_uuid_mappings) + : key_system_uuid_mappings_(key_system_uuid_mappings) { +} + +AwMediaClientAndroid::~AwMediaClientAndroid() { +} + +void AwMediaClientAndroid::AddKeySystemUUIDMappings(KeySystemUuidMap* map) { + for (const std::string& key_system_uuid_mapping : key_system_uuid_mappings_) { + auto mapping = CreateMappingFromString(key_system_uuid_mapping); + if (!mapping.first.empty()) + map->insert(mapping); + } +} + +media::MediaDrmBridgeDelegate* AwMediaClientAndroid::GetMediaDrmBridgeDelegate( + const media::UUID& scheme_uuid) { + if (scheme_uuid == widevine_delegate_.GetUUID()) + return &widevine_delegate_; + return nullptr; +} + +} // namespace android_webview diff --git a/android_webview/browser/aw_media_client_android.h b/android_webview/browser/aw_media_client_android.h new file mode 100644 index 0000000..a2a6718 --- /dev/null +++ b/android_webview/browser/aw_media_client_android.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 ANDROID_WEBVIEW_BROWSER_AW_MEDIA_CLIENT_ANDROID_H_ +#define ANDROID_WEBVIEW_BROWSER_AW_MEDIA_CLIENT_ANDROID_H_ + +#include "base/macros.h" +#include "components/cdm/browser/widevine_drm_delegate_android.h" +#include "media/base/android/media_client_android.h" + +namespace android_webview { + +class AwMediaClientAndroid : public media::MediaClientAndroid { + public: + // |key_system_uuid_mappings| is a list of strings containing key-system/UUID + // pairs, in the format "key system name,UUID as string". + explicit AwMediaClientAndroid( + const std::vector<std::string>& key_system_uuid_mappings); + ~AwMediaClientAndroid() override; + + private: + // media::MediaClientAndroid implementation: + void AddKeySystemUUIDMappings(KeySystemUuidMap* map) override; + media::MediaDrmBridgeDelegate* GetMediaDrmBridgeDelegate( + const media::UUID& scheme_uuid) override; + + std::vector<std::string> key_system_uuid_mappings_; + cdm::WidevineDrmDelegateAndroid widevine_delegate_; + + DISALLOW_COPY_AND_ASSIGN(AwMediaClientAndroid); +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_AW_MEDIA_CLIENT_ANDROID_H_ diff --git a/android_webview/common/aw_resource.h b/android_webview/common/aw_resource.h index 00613c3..e5c04179 100644 --- a/android_webview/common/aw_resource.h +++ b/android_webview/common/aw_resource.h @@ -14,6 +14,7 @@ namespace AwResource { std::string GetLoadErrorPageContent(); std::string GetNoDomainPageContent(); +std::vector<std::string> GetConfigKeySystemUuidMapping(); } // namespace AwResource } // namsespace android_webview diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java index c1529e6..592e48f 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java +++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java @@ -5,7 +5,6 @@ package org.chromium.android_webview; import android.content.Context; -import android.util.Log; import org.chromium.base.PathUtils; import org.chromium.base.ThreadUtils; @@ -13,9 +12,6 @@ import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.library_loader.LibraryProcessType; import org.chromium.base.library_loader.ProcessInitException; import org.chromium.content.browser.BrowserStartupController; -import org.chromium.media.MediaDrmBridge; - -import java.util.UUID; /** * Wrapper for the steps needed to initialize the java and native sides of webview chromium. @@ -60,25 +56,10 @@ public abstract class AwBrowserProcess { try { BrowserStartupController.get(context, LibraryProcessType.PROCESS_WEBVIEW) .startBrowserProcessesSync(true); - initializePlatformKeySystem(); } catch (ProcessInitException e) { throw new RuntimeException("Cannot initialize WebView", e); } } }); } - - private static void initializePlatformKeySystem() { - String[] mappings = AwResource.getConfigKeySystemUuidMapping(); - for (String mapping : mappings) { - try { - String fragments[] = mapping.split(","); - String keySystem = fragments[0].trim(); - UUID uuid = UUID.fromString(fragments[1]); - MediaDrmBridge.addKeySystemUuidMapping(keySystem, uuid); - } catch (java.lang.RuntimeException e) { - Log.e(TAG, "Can't parse key-system mapping: " + mapping); - } - } - } } diff --git a/android_webview/java/src/org/chromium/android_webview/AwResource.java b/android_webview/java/src/org/chromium/android_webview/AwResource.java index e74206d..7939fe8 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwResource.java +++ b/android_webview/java/src/org/chromium/android_webview/AwResource.java @@ -60,16 +60,17 @@ public class AwResource { } @CalledByNative - public static String getNoDomainPageContent() { + private static String getNoDomainPageContent() { return getResource(sRawNoDomain, TYPE_RAW); } @CalledByNative - public static String getLoadErrorPageContent() { + private static String getLoadErrorPageContent() { return getResource(sRawLoadError, TYPE_RAW); } - public static String[] getConfigKeySystemUuidMapping() { + @CalledByNative + private static String[] getConfigKeySystemUuidMapping() { // No need to cache, since this should be called only once. return sResources.getStringArray(sStringArrayConfigKeySystemUUIDMapping); } diff --git a/android_webview/native/aw_resource.cc b/android_webview/native/aw_resource.cc index 498a201..75f75b5 100644 --- a/android_webview/native/aw_resource.cc +++ b/android_webview/native/aw_resource.cc @@ -4,6 +4,7 @@ #include "android_webview/native/aw_resource.h" +#include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "jni/AwResource_jni.h" @@ -30,6 +31,16 @@ std::string GetNoDomainPageContent() { return base::android::ConvertJavaStringToUTF8(content); } +std::vector<std::string> GetConfigKeySystemUuidMapping() { + JNIEnv* env = base::android::AttachCurrentThread(); + std::vector<std::string> key_system_uuid_mappings; + ScopedJavaLocalRef<jobjectArray> mappings = + Java_AwResource_getConfigKeySystemUuidMapping(env); + base::android::AppendJavaStringArrayToStringVector(env, mappings.obj(), + &key_system_uuid_mappings); + return key_system_uuid_mappings; +} + bool RegisterAwResource(JNIEnv* env) { return RegisterNativesImpl(env); } diff --git a/chrome/browser/android/chrome_media_client_android.cc b/chrome/browser/android/chrome_media_client_android.cc new file mode 100644 index 0000000..1f8a542 --- /dev/null +++ b/chrome/browser/android/chrome_media_client_android.cc @@ -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. + +#include "chrome/browser/android/chrome_media_client_android.h" + +ChromeMediaClientAndroid::ChromeMediaClientAndroid() { +} + +ChromeMediaClientAndroid::~ChromeMediaClientAndroid() { +} + +media::MediaDrmBridgeDelegate* +ChromeMediaClientAndroid::GetMediaDrmBridgeDelegate( + const std::vector<uint8_t>& scheme_uuid) { + if (scheme_uuid == widevine_delegate_.GetUUID()) + return &widevine_delegate_; + return nullptr; +} diff --git a/chrome/browser/android/chrome_media_client_android.h b/chrome/browser/android/chrome_media_client_android.h new file mode 100644 index 0000000..d489e42 --- /dev/null +++ b/chrome/browser/android/chrome_media_client_android.h @@ -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. + +#ifndef CHROME_BROWSER_ANDROID_CHROME_MEDIA_CLIENT_ANDROID_H_ +#define CHROME_BROWSER_ANDROID_CHROME_MEDIA_CLIENT_ANDROID_H_ + +#include "base/macros.h" +#include "components/cdm/browser/widevine_drm_delegate_android.h" +#include "media/base/android/media_client_android.h" + +class ChromeMediaClientAndroid : public media::MediaClientAndroid { + public: + ChromeMediaClientAndroid(); + ~ChromeMediaClientAndroid() override; + + private: + // media::MediaClientAndroid implementation: + media::MediaDrmBridgeDelegate* GetMediaDrmBridgeDelegate( + const std::vector<uint8_t>& scheme_uuid) override; + + cdm::WidevineDrmDelegateAndroid widevine_delegate_; + + DISALLOW_COPY_AND_ASSIGN(ChromeMediaClientAndroid); +}; + +#endif // CHROME_BROWSER_ANDROID_CHROME_MEDIA_CLIENT_ANDROID_H_ diff --git a/chrome/browser/chrome_browser_main_android.cc b/chrome/browser/chrome_browser_main_android.cc index 9f977fc..4e05a99 100644 --- a/chrome/browser/chrome_browser_main_android.cc +++ b/chrome/browser/chrome_browser_main_android.cc @@ -7,6 +7,7 @@ #include "base/command_line.h" #include "base/path_service.h" #include "base/trace_event/trace_event.h" +#include "chrome/browser/android/chrome_media_client_android.h" #include "chrome/browser/android/seccomp_support_detector.h" #include "chrome/browser/google/google_search_counter_android.h" #include "chrome/browser/signin/signin_manager_factory.h" @@ -18,6 +19,7 @@ #include "content/public/browser/android/compositor.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/main_function_params.h" +#include "media/base/android/media_client_android.h" #include "net/android/network_change_notifier_factory_android.h" #include "net/base/network_change_notifier.h" #include "ui/base/ui_base_paths.h" @@ -96,6 +98,12 @@ void ChromeBrowserMainPartsAndroid::PreEarlyInitialization() { ChromeBrowserMainParts::PreEarlyInitialization(); } +void ChromeBrowserMainPartsAndroid::PreMainMessageLoopRun() { + media::SetMediaClientAndroid(new ChromeMediaClientAndroid); + + ChromeBrowserMainParts::PreMainMessageLoopRun(); +} + void ChromeBrowserMainPartsAndroid::PostBrowserStart() { ChromeBrowserMainParts::PostBrowserStart(); diff --git a/chrome/browser/chrome_browser_main_android.h b/chrome/browser/chrome_browser_main_android.h index fbb3ab3..a20f090 100644 --- a/chrome/browser/chrome_browser_main_android.h +++ b/chrome/browser/chrome_browser_main_android.h @@ -23,6 +23,7 @@ class ChromeBrowserMainPartsAndroid : public ChromeBrowserMainParts { int PreCreateThreads() override; void PostProfileInit() override; void PreEarlyInitialization() override; + void PreMainMessageLoopRun() override; // ChromeBrowserMainParts overrides. void PostBrowserStart() override; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 4e7d857..6b2cbec 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -45,6 +45,8 @@ 'browser/android/bookmarks/partner_bookmarks_shim.h', 'browser/android/chrome_jni_registrar.cc', 'browser/android/chrome_jni_registrar.h', + 'browser/android/chrome_media_client_android.cc', + 'browser/android/chrome_media_client_android.h', 'browser/android/chrome_startup_flags.cc', 'browser/android/chrome_startup_flags.h', 'browser/android/chrome_web_contents_delegate_android.cc', diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS index 5306642..bd203a8 100644 --- a/chromecast/browser/DEPS +++ b/chromecast/browser/DEPS @@ -3,6 +3,7 @@ include_rules = [ # embedder and can include from all other chromecast/ directories. "+cc/base/switches.h", "+chromecast", + "+components/cdm/browser", "+components/crash", "+components/devtools_discovery", "+components/devtools_http_handler", diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastBrowserHelper.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastBrowserHelper.java index 937018d..7b56973 100644 --- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastBrowserHelper.java +++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastBrowserHelper.java @@ -19,11 +19,8 @@ import org.chromium.content.app.ContentApplication; import org.chromium.content.browser.BrowserStartupController; import org.chromium.content.browser.DeviceUtils; import org.chromium.content.common.ContentSwitches; -import org.chromium.media.MediaDrmBridge; import org.chromium.net.NetworkChangeNotifier; -import java.util.UUID; - /** * Static, one-time initialization for the browser process. */ @@ -32,10 +29,6 @@ public class CastBrowserHelper { public static final String COMMAND_LINE_ARGS_KEY = "commandLineArgs"; - private static final String PLAYREADY_KEY_SYSTEM_NAME = "com.chromecast.playready"; - private static final UUID PLAYREADY_UUID = - UUID.fromString("9A04F079-9840-4286-AB92-E65BE0885F95"); - private static boolean sIsBrowserInitialized = false; /** @@ -80,7 +73,6 @@ public class CastBrowserHelper { // Cast shell always expects to receive notifications to track network state. NetworkChangeNotifier.registerToReceiveNotificationsAlways(); sIsBrowserInitialized = true; - MediaDrmBridge.addKeySystemUuidMapping(PLAYREADY_KEY_SYSTEM_NAME, PLAYREADY_UUID); return true; } catch (ProcessInitException e) { Log.e(TAG, "Unable to launch browser process.", e); diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc index 45edf44..1c0ca15 100644 --- a/chromecast/browser/cast_browser_main_parts.cc +++ b/chromecast/browser/cast_browser_main_parts.cc @@ -29,6 +29,7 @@ #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/net/connectivity_checker.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_switches.h" @@ -36,8 +37,10 @@ #include "media/base/media_switches.h" #if defined(OS_ANDROID) +#include "chromecast/browser/media/cast_media_client_android.h" #include "chromecast/crash/android/crash_handler.h" #include "components/crash/browser/crash_dump_manager_android.h" +#include "media/base/android/media_client_android.h" #include "net/android/network_change_notifier_factory_android.h" #else #include "chromecast/browser/media/cast_browser_cdm_factory.h" @@ -259,11 +262,13 @@ void CastBrowserMainParts::PreMainMessageLoopRun() { cast_browser_process_->SetPrefService( PrefServiceHelper::CreatePrefService(pref_registry.get())); -#if !defined(OS_ANDROID) +#if defined(OS_ANDROID) + ::media::SetMediaClientAndroid(new media::CastMediaClientAndroid()); +#else const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); if (cmd_line->HasSwitch(switches::kEnableCmaMediaPipeline)) - ::media::SetBrowserCdmFactory(new media::CastBrowserCdmFactory); -#endif // !defined(OS_ANDROID) + ::media::SetBrowserCdmFactory(new media::CastBrowserCdmFactory()); +#endif // defined(OS_ANDROID) cast_browser_process_->SetConnectivityChecker( make_scoped_refptr(new ConnectivityChecker( diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc index 01430da..4a07649 100644 --- a/chromecast/browser/cast_content_browser_client.cc +++ b/chromecast/browser/cast_content_browser_client.cc @@ -44,9 +44,6 @@ #if defined(OS_ANDROID) #include "chromecast/browser/android/external_video_surface_container_impl.h" -#endif // defined(OS_ANDROID) - -#if defined(OS_ANDROID) #include "components/crash/browser/crash_dump_manager_android.h" #endif // defined(OS_ANDROID) diff --git a/chromecast/browser/media/cast_media_client_android.cc b/chromecast/browser/media/cast_media_client_android.cc new file mode 100644 index 0000000..696471e --- /dev/null +++ b/chromecast/browser/media/cast_media_client_android.cc @@ -0,0 +1,44 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/browser/media/cast_media_client_android.h" + +#include "chromecast/media/base/key_systems_common.h" + +namespace chromecast { +namespace media { + +CastMediaClientAndroid::CastMediaClientAndroid() { +} + +CastMediaClientAndroid::~CastMediaClientAndroid() { +} + +void CastMediaClientAndroid::AddKeySystemUUIDMappings(KeySystemUuidMap* map) { + // Note: MediaDrmBridge adds the Widevine UUID mapping automatically. +#if defined(PLAYREADY_CDM_AVAILABLE) + (*map)[kChromecastPlayreadyKeySystem] = playready_delegate_.GetUUID(); +#endif + + auto platform_mappings = GetPlatformKeySystemUUIDMappings(); + for (const auto& mapping : platform_mappings) + map->insert(mapping); +} + +::media::MediaDrmBridgeDelegate* +CastMediaClientAndroid::GetMediaDrmBridgeDelegate( + const ::media::UUID& scheme_uuid) { +#if defined(PLAYREADY_CDM_AVAILABLE) + if (scheme_uuid == playready_delegate_.GetUUID()) + return &playready_delegate_; +#endif + + if (scheme_uuid == widevine_delegate_.GetUUID()) + return &widevine_delegate_; + + return nullptr; +} + +} // namespace media +} // namespace chromecast diff --git a/chromecast/browser/media/cast_media_client_android.h b/chromecast/browser/media/cast_media_client_android.h new file mode 100644 index 0000000..a7aaa1d --- /dev/null +++ b/chromecast/browser/media/cast_media_client_android.h @@ -0,0 +1,41 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_BROWSER_MEDIA_CAST_MEDIA_CLIENT_ANDROID_H_ +#define CHROMECAST_BROWSER_MEDIA_CAST_MEDIA_CLIENT_ANDROID_H_ + +#include <map> + +#include "base/macros.h" +#include "chromecast/media/cdm/playready_drm_delegate_android.h" +#include "components/cdm/browser/widevine_drm_delegate_android.h" +#include "media/base/android/media_client_android.h" + +namespace chromecast { +namespace media { + +class CastMediaClientAndroid : public ::media::MediaClientAndroid { + public: + CastMediaClientAndroid(); + ~CastMediaClientAndroid() override; + + private: + // ::media::MediaClientAndroid implementation: + void AddKeySystemUUIDMappings(KeySystemUuidMap* map) override; + ::media::MediaDrmBridgeDelegate* GetMediaDrmBridgeDelegate( + const ::media::UUID& scheme_uuid) override; + +#if defined(PLAYREADY_CDM_AVAILABLE) + PlayreadyDrmDelegateAndroid playready_delegate_; +#endif + + cdm::WidevineDrmDelegateAndroid widevine_delegate_; + + DISALLOW_COPY_AND_ASSIGN(CastMediaClientAndroid); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_MEDIA_CAST_MEDIA_CLIENT_ANDROID_H_ diff --git a/chromecast/chromecast.gyp b/chromecast/chromecast.gyp index a9892ab..f882b8a 100644 --- a/chromecast/chromecast.gyp +++ b/chromecast/chromecast.gyp @@ -189,6 +189,7 @@ 'chromecast_locales.gyp:chromecast_locales_pak', 'chromecast_locales.gyp:chromecast_settings', 'media/media.gyp:media_base', + 'media/media.gyp:media_cdm', '../base/base.gyp:base', '../components/components.gyp:breakpad_host', '../components/components.gyp:cdm_renderer', @@ -239,6 +240,8 @@ 'browser/devtools/remote_debugging_server.h', 'browser/geolocation/cast_access_token_store.cc', 'browser/geolocation/cast_access_token_store.h', + 'browser/media/cast_media_client_android.cc', + 'browser/media/cast_media_client_android.h', 'browser/metrics/cast_metrics_prefs.cc', 'browser/metrics/cast_metrics_prefs.h', 'browser/metrics/cast_metrics_service_client.cc', @@ -319,6 +322,11 @@ '../ui/aura/aura.gyp:aura_test_support', ], }], + ['OS=="android"', { + 'dependencies': [ + '../components/components.gyp:cdm_browser', + ], + }], ], }, { diff --git a/chromecast/media/base/key_systems_common.h b/chromecast/media/base/key_systems_common.h index 4ab8ea7..1537afd 100644 --- a/chromecast/media/base/key_systems_common.h +++ b/chromecast/media/base/key_systems_common.h @@ -6,6 +6,11 @@ #define CHROMECAST_MEDIA_BASE_KEY_SYSTEMS_COMMON_H_ #include <string> +#include <utility> +#include <vector> + +#include "base/compiler_specific.h" +#include "media/base/android/media_client_android.h" namespace chromecast { namespace media { @@ -29,6 +34,13 @@ CastKeySystem GetKeySystemByName(const std::string& key_system_name); // TODO(gunsch): Remove when prefixed EME is removed. CastKeySystem GetPlatformKeySystemByName(const std::string& key_system_name); +// Translates a platform-specific key system string into a CastKeySystem. +// TODO(gunsch): Remove when prefixed EME is removed. +#if defined(OS_ANDROID) +std::vector<::media::MediaClientAndroid::KeySystemUuidMap::value_type> +GetPlatformKeySystemUUIDMappings(); +#endif + } // namespace media } // namespace chromecast diff --git a/chromecast/media/base/key_systems_common_simple.cc b/chromecast/media/base/key_systems_common_simple.cc index e6dbd02..671ce03 100644 --- a/chromecast/media/base/key_systems_common_simple.cc +++ b/chromecast/media/base/key_systems_common_simple.cc @@ -11,5 +11,13 @@ CastKeySystem GetPlatformKeySystemByName(const std::string& key_system_name) { return KEY_SYSTEM_NONE; } +#if defined(OS_ANDROID) +std::vector<::media::MediaClientAndroid::KeySystemUuidMap::value_type> +GetPlatformKeySystemUUIDMappings() { + return std::vector< + ::media::MediaClientAndroid::KeySystemUuidMap::value_type>(); +} +#endif + } // namespace media } // namespace chromecast diff --git a/chromecast/media/cdm/playready_drm_delegate_android.cc b/chromecast/media/cdm/playready_drm_delegate_android.cc new file mode 100644 index 0000000..135fc44 --- /dev/null +++ b/chromecast/media/cdm/playready_drm_delegate_android.cc @@ -0,0 +1,85 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/media/cdm/playready_drm_delegate_android.h" + +#include "base/logging.h" +#include "media/base/bit_reader.h" + +namespace chromecast { +namespace media { + +const uint8_t kPlayreadyUuid[16] = { + 0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, + 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95}; + +const uint8_t kPlayreadyCustomDataUuid[] = { + 0x2b, 0xf8, 0x66, 0x80, 0xc6, 0xe5, 0x4e, 0x24, + 0xbe, 0x23, 0x0f, 0x81, 0x5a, 0x60, 0x6e, 0xb2}; + +// ASCII "uuid" as an 4-byte integer +const uint32_t kBoxTypeUuid = 1970628964; + +PlayreadyDrmDelegateAndroid::PlayreadyDrmDelegateAndroid() { +} + +PlayreadyDrmDelegateAndroid::~PlayreadyDrmDelegateAndroid() { +} + +const ::media::UUID PlayreadyDrmDelegateAndroid::GetUUID() const { + return ::media::UUID(kPlayreadyUuid, + kPlayreadyUuid + arraysize(kPlayreadyUuid)); +} + +bool PlayreadyDrmDelegateAndroid::OnCreateSession( + const ::media::EmeInitDataType init_data_type, + const std::vector<uint8_t>& init_data, + std::vector<uint8_t>* /* init_data_out */, + std::vector<std::string>* optional_parameters_out) { + if (init_data_type == ::media::EmeInitDataType::CENC) { + ::media::BitReader reader(&init_data[0], init_data.size()); + while (reader.bits_available() > 64) { + uint32_t box_size; + uint32_t box_type; + reader.ReadBits(32, &box_size); + reader.ReadBits(32, &box_type); + int bytes_read = 8; + + if (box_type != kBoxTypeUuid) { + if (box_size < 8 + sizeof(kPlayreadyCustomDataUuid)) { + break; + } + // Box size includes the bytes already consumed + reader.SkipBits((box_size - bytes_read) * 8); + continue; + } + + // "uuid" was found, look for custom data format as per b/10246367 + reader.SkipBits(128); + bytes_read += 16; + if (!memcmp(&init_data[0] + reader.bits_read() / 8, + kPlayreadyCustomDataUuid, 16)) { + reader.SkipBits((box_size - bytes_read) * 8); + continue; + } + + int custom_data_size = box_size - bytes_read; + DCHECK(reader.bits_read() % 8 == 0); + int total_bytes_read = reader.bits_read() / 8; + + optional_parameters_out->clear(); + optional_parameters_out->push_back("PRCustomData"); + optional_parameters_out->push_back( + std::string(&init_data[0] + total_bytes_read, + &init_data[0] + total_bytes_read + custom_data_size)); + reader.SkipBits(custom_data_size * 8); + LOG(INFO) << "Including " << custom_data_size + << " bytes of custom PlayReady data"; + } + } + return true; +} + +} // namespace media +} // namespace chromecast diff --git a/chromecast/media/cdm/playready_drm_delegate_android.h b/chromecast/media/cdm/playready_drm_delegate_android.h new file mode 100644 index 0000000..3a934c45 --- /dev/null +++ b/chromecast/media/cdm/playready_drm_delegate_android.h @@ -0,0 +1,34 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_MEDIA_CDM_PLAYREADY_DRM_DELEGATE_ANDROID_H_ +#define CHROMECAST_MEDIA_CDM_PLAYREADY_DRM_DELEGATE_ANDROID_H_ + +#include "base/macros.h" +#include "media/base/android/media_drm_bridge_delegate.h" + +namespace chromecast { +namespace media { + +class PlayreadyDrmDelegateAndroid : public ::media::MediaDrmBridgeDelegate { + public: + PlayreadyDrmDelegateAndroid(); + ~PlayreadyDrmDelegateAndroid() override; + + // ::media::MediaDrmBridgeDelegate implementation: + const ::media::UUID GetUUID() const override; + bool OnCreateSession( + const ::media::EmeInitDataType init_data_type, + const std::vector<uint8_t>& init_data, + std::vector<uint8_t>* init_data_out, + std::vector<std::string>* optional_parameters_out) override; + + private: + DISALLOW_COPY_AND_ASSIGN(PlayreadyDrmDelegateAndroid); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CDM_PLAYREADY_DRM_DELEGATE_ANDROID_H_ diff --git a/chromecast/media/media.gyp b/chromecast/media/media.gyp index 8b98ec9..3ca9eff 100644 --- a/chromecast/media/media.gyp +++ b/chromecast/media/media.gyp @@ -54,6 +54,14 @@ 'cdm/browser_cdm_cast.cc', 'cdm/browser_cdm_cast.h', ], + 'conditions': [ + ['use_playready==1', { + 'sources': [ + 'cdm/playready_drm_delegate_android.cc', + 'cdm/playready_drm_delegate_android.h', + ], + }], + ], }, { 'target_name': 'cma_base', diff --git a/components/OWNERS b/components/OWNERS index 9180998..86bc6db 100644 --- a/components/OWNERS +++ b/components/OWNERS @@ -25,6 +25,10 @@ per-file browsing_data.gypi=mkwst@chromium.org per-file browsing_data.gypi=bauerb@chromium.org per-file browsing_data.gypi=michaeln@chromium.org +per-file cdm.gypi=ddorwin@chromium.org +per-file cdm.gypi=scherkus@chromium.org +per-file cdm.gypi=xhwang@chromium.org + per-file cloud_devices*=gene@chromium.org per-file cloud_devices*=vitalybuka@chromium.org diff --git a/components/cdm.gypi b/components/cdm.gypi index d6f640a..853ff35 100644 --- a/components/cdm.gypi +++ b/components/cdm.gypi @@ -64,6 +64,8 @@ 'sources': [ 'cdm/browser/cdm_message_filter_android.cc', 'cdm/browser/cdm_message_filter_android.h', + 'cdm/browser/widevine_drm_delegate_android.cc', + 'cdm/browser/widevine_drm_delegate_android.h', ], }, ], diff --git a/components/cdm/browser/BUILD.gn b/components/cdm/browser/BUILD.gn index e18b670..cea393b 100644 --- a/components/cdm/browser/BUILD.gn +++ b/components/cdm/browser/BUILD.gn @@ -6,6 +6,8 @@ source_set("browser") { sources = [ "cdm_message_filter_android.cc", "cdm_message_filter_android.h", + "widevine_drm_delegate_android.cc", + "widevine_drm_delegate_android.h", ] deps = [ diff --git a/components/cdm/browser/widevine_drm_delegate_android.cc b/components/cdm/browser/widevine_drm_delegate_android.cc new file mode 100644 index 0000000..64b90bf --- /dev/null +++ b/components/cdm/browser/widevine_drm_delegate_android.cc @@ -0,0 +1,163 @@ +// 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 "components/cdm/browser/widevine_drm_delegate_android.h" + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" + +namespace cdm { + +namespace { + +uint32_t ReadUint32(const uint8_t* data) { + uint32_t value = 0; + for (int i = 0; i < 4; ++i) + value = (value << 8) | data[i]; + return value; +} + +uint64_t ReadUint64(const uint8_t* data) { + uint64_t value = 0; + for (int i = 0; i < 8; ++i) + value = (value << 8) | data[i]; + return value; +} + +// The structure of an ISO CENC Protection System Specific Header (PSSH) box is +// as follows. (See ISO/IEC FDIS 23001-7:2011(E).) +// Note: ISO boxes use big-endian values. +// +// PSSH { +// uint32_t Size +// uint32_t Type +// uint64_t LargeSize # Field is only present if value(Size) == 1. +// uint32_t VersionAndFlags +// uint8_t[16] SystemId +// uint32_t DataSize +// uint8_t[DataSize] Data +// } +const int kBoxHeaderSize = 8; // Box's header contains Size and Type. +const int kBoxLargeSizeSize = 8; +const int kPsshVersionFlagSize = 4; +const int kPsshSystemIdSize = 16; +const int kPsshDataSizeSize = 4; +const uint32_t kTencType = 0x74656e63; +const uint32_t kPsshType = 0x70737368; + +const uint8_t kWidevineUuid[16] = { + 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, + 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED }; + +// Tries to find a PSSH box with the Widevine UUID, parses the +// "Data" of the box and put it in |pssh_data|. Returns true if such a box is +// found and successfully parsed. Returns false otherwise. +// Notes: +// 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box +// will be set in |pssh_data|. +// 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped. +bool GetPsshData(const std::vector<uint8_t>& data, + std::vector<uint8_t>* pssh_data) { + int bytes_left = base::checked_cast<int>(data.size()); + const uint8_t* cur = &data[0]; + const uint8_t* data_end = cur + bytes_left; + + while (bytes_left > 0) { + const uint8_t* box_head = cur; + + if (bytes_left < kBoxHeaderSize) + return false; + + uint64_t box_size = ReadUint32(cur); + uint32_t type = ReadUint32(cur + 4); + cur += kBoxHeaderSize; + bytes_left -= kBoxHeaderSize; + + if (box_size == 1) { // LargeSize is present. + if (bytes_left < kBoxLargeSizeSize) + return false; + + box_size = ReadUint64(cur); + cur += kBoxLargeSizeSize; + bytes_left -= kBoxLargeSizeSize; + } else if (box_size == 0) { + box_size = bytes_left + kBoxHeaderSize; + } + + const uint8_t* box_end = box_head + box_size; + if (data_end < box_end) + return false; + + if (type == kTencType) { + // Skip 'tenc' box. + cur = box_end; + bytes_left = data_end - cur; + continue; + } else if (type != kPsshType) { + return false; + } + + const int kPsshBoxMinimumSize = + kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize; + if (box_end < cur + kPsshBoxMinimumSize) + return false; + + uint32_t version_and_flags = ReadUint32(cur); + cur += kPsshVersionFlagSize; + bytes_left -= kPsshVersionFlagSize; + if (version_and_flags != 0) + return false; + + DCHECK_GE(bytes_left, kPsshSystemIdSize); + if (!std::equal(kWidevineUuid, + kWidevineUuid + sizeof(kWidevineUuid), cur)) { + cur = box_end; + bytes_left = data_end - cur; + continue; + } + + cur += kPsshSystemIdSize; + bytes_left -= kPsshSystemIdSize; + + uint32_t data_size = ReadUint32(cur); + cur += kPsshDataSizeSize; + bytes_left -= kPsshDataSizeSize; + + if (box_end < cur + data_size) + return false; + + pssh_data->assign(cur, cur + data_size); + return true; + } + + return false; +} + +} + +WidevineDrmDelegateAndroid::WidevineDrmDelegateAndroid() { +} + +WidevineDrmDelegateAndroid::~WidevineDrmDelegateAndroid() { +} + +const std::vector<uint8_t> WidevineDrmDelegateAndroid::GetUUID() const { + return std::vector<uint8_t>(kWidevineUuid, + kWidevineUuid + arraysize(kWidevineUuid)); +} + +bool WidevineDrmDelegateAndroid::OnCreateSession( + const media::EmeInitDataType init_data_type, + const std::vector<uint8_t>& init_data, + std::vector<uint8_t>* init_data_out, + std::vector<std::string>* /* optional_parameters_out */) { + if (init_data_type != media::EmeInitDataType::CENC) + return true; + + // Widevine MediaDrm plugin only accepts the "data" part of the PSSH box as + // the init data when using MP4 container. + return GetPsshData(init_data, init_data_out); +} + +} // namespace cdm diff --git a/components/cdm/browser/widevine_drm_delegate_android.h b/components/cdm/browser/widevine_drm_delegate_android.h new file mode 100644 index 0000000..09a54ae --- /dev/null +++ b/components/cdm/browser/widevine_drm_delegate_android.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 COMPONENTS_CDM_BROWSER_WIDEVINE_DRM_DELEGATE_ANDROID_H_ +#define COMPONENTS_CDM_BROWSER_WIDEVINE_DRM_DELEGATE_ANDROID_H_ + +#include "base/macros.h" +#include "media/base/android/media_drm_bridge_delegate.h" + +namespace cdm { + +class WidevineDrmDelegateAndroid : public media::MediaDrmBridgeDelegate { + public: + WidevineDrmDelegateAndroid(); + ~WidevineDrmDelegateAndroid() override; + + // media::MediaDrmBridgeDelegate implementation: + const std::vector<uint8_t> GetUUID() const override; + bool OnCreateSession( + const media::EmeInitDataType init_data_type, + const std::vector<uint8_t>& init_data, + std::vector<uint8_t>* init_data_out, + std::vector<std::string>* optional_parameters_out) override; + + private: + DISALLOW_COPY_AND_ASSIGN(WidevineDrmDelegateAndroid); +}; + +} // namespace cdm + +#endif // COMPONENTS_CDM_BROWSER_WIDEVINE_DRM_DELEGATE_ANDROID_H_ diff --git a/media/base/android/BUILD.gn b/media/base/android/BUILD.gn index 6108389..c407b17 100644 --- a/media/base/android/BUILD.gn +++ b/media/base/android/BUILD.gn @@ -19,12 +19,16 @@ source_set("android") { "demuxer_android.h", "demuxer_stream_player_params.cc", "demuxer_stream_player_params.h", + "media_client_android.cc", + "media_client_android.h", "media_codec_bridge.cc", "media_codec_bridge.h", "media_decoder_job.cc", "media_decoder_job.h", "media_drm_bridge.cc", "media_drm_bridge.h", + "media_drm_bridge_delegate.cc", + "media_drm_bridge_delegate.h", "media_jni_registrar.cc", "media_jni_registrar.h", "media_player_android.cc", diff --git a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java index 8f32ccb..9d49232 100644 --- a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java +++ b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java @@ -22,7 +22,6 @@ import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.ArrayDeque; import java.util.Arrays; import java.util.HashMap; @@ -107,11 +106,14 @@ public class MediaDrmBridge { private static class PendingCreateSessionData { private final byte[] mInitData; private final String mMimeType; + private final HashMap<String, String> mOptionalParameters; private final long mPromiseId; - private PendingCreateSessionData(byte[] initData, String mimeType, long promiseId) { + private PendingCreateSessionData(byte[] initData, String mimeType, + HashMap<String, String> optionalParameters, long promiseId) { mInitData = initData; mMimeType = mimeType; + mOptionalParameters = optionalParameters; mPromiseId = promiseId; } @@ -123,6 +125,10 @@ public class MediaDrmBridge { return mMimeType; } + private HashMap<String, String> optionalParameters() { + return mOptionalParameters; + } + private long promiseId() { return mPromiseId; } @@ -410,16 +416,20 @@ public class MediaDrmBridge { * @param sessionId ID of session on which we need to get the key request. * @param data Data needed to get the key request. * @param mime Mime type to get the key request. + * @param optionalParameters Optional parameters to pass to the DRM plugin. * * @return the key request. */ - private MediaDrm.KeyRequest getKeyRequest(byte[] sessionId, byte[] data, String mime) + private MediaDrm.KeyRequest getKeyRequest( + byte[] sessionId, byte[] data, String mime, HashMap<String, String> optionalParameters) throws android.media.NotProvisionedException { assert mMediaDrm != null; assert mMediaCrypto != null; assert !mProvisioningPending; - HashMap<String, String> optionalParameters = new HashMap<String, String>(); + if (optionalParameters == null) { + optionalParameters = new HashMap<String, String>(); + } MediaDrm.KeyRequest request = mMediaDrm.getKeyRequest( sessionId, data, mime, MediaDrm.KEY_TYPE_STREAMING, optionalParameters); String result = (request != null) ? "successed" : "failed"; @@ -433,12 +443,14 @@ public class MediaDrmBridge { * * @param initData Data needed to generate the key request. * @param mime Mime type. + * @param optionalParameters Optional parameters to pass to the DRM plugin. * @param promiseId Promise ID for the createSession() call. */ - private void savePendingCreateSessionData(byte[] initData, String mime, long promiseId) { + private void savePendingCreateSessionData(byte[] initData, String mime, + HashMap<String, String> optionalParameters, long promiseId) { Log.d(TAG, "savePendingCreateSessionData()"); mPendingCreateSessionDataQueue.offer( - new PendingCreateSessionData(initData, mime, promiseId)); + new PendingCreateSessionData(initData, mime, optionalParameters, promiseId)); } /** @@ -456,8 +468,9 @@ public class MediaDrmBridge { PendingCreateSessionData pendingData = mPendingCreateSessionDataQueue.poll(); byte[] initData = pendingData.initData(); String mime = pendingData.mimeType(); + HashMap<String, String> optionalParameters = pendingData.optionalParameters(); long promiseId = pendingData.promiseId(); - createSession(initData, mime, promiseId); + createSession(initData, mime, optionalParameters, promiseId); } } @@ -474,14 +487,35 @@ public class MediaDrmBridge { } /** + * createSession interface to be called from native using primitive types. + * @see createSession(byte[], String, HashMap<String, String>, long) + */ + @CalledByNative + private void createSessionFromNative( + byte[] initData, String mime, String[] optionalParamsArray, long promiseId) { + HashMap<String, String> optionalParameters = new HashMap<String, String>(); + if (optionalParamsArray != null) { + if (optionalParamsArray.length % 2 != 0) { + throw new IllegalArgumentException( + "Additional data array doesn't have equal keys/values"); + } + for (int i = 0; i < optionalParamsArray.length; i += 2) { + optionalParameters.put(optionalParamsArray[i], optionalParamsArray[i + 1]); + } + } + createSession(initData, mime, optionalParameters, promiseId); + } + + /** * Create a session, and generate a request with |initData| and |mime|. * * @param initData Data needed to generate the key request. * @param mime Mime type. + * @param optionalParameters Additional data to pass to getKeyRequest. * @param promiseId Promise ID for this call. */ - @CalledByNative - private void createSession(byte[] initData, String mime, long promiseId) { + private void createSession(byte[] initData, String mime, + HashMap<String, String> optionalParameters, long promiseId) { Log.d(TAG, "createSession()"); if (mMediaDrm == null) { Log.e(TAG, "createSession() called when MediaDrm is null."); @@ -490,7 +524,7 @@ public class MediaDrmBridge { if (mProvisioningPending) { assert mMediaCrypto == null; - savePendingCreateSessionData(initData, mime, promiseId); + savePendingCreateSessionData(initData, mime, optionalParameters, promiseId); return; } @@ -514,7 +548,7 @@ public class MediaDrmBridge { assert !sessionExists(sessionId); MediaDrm.KeyRequest request = null; - request = getKeyRequest(sessionId, initData, mime); + request = getKeyRequest(sessionId, initData, mime, optionalParameters); if (request == null) { mMediaDrm.closeSession(sessionId); onPromiseRejected(promiseId, "Generate request failed."); @@ -531,7 +565,7 @@ public class MediaDrmBridge { if (newSessionOpened) { mMediaDrm.closeSession(sessionId); } - savePendingCreateSessionData(initData, mime, promiseId); + savePendingCreateSessionData(initData, mime, optionalParameters, promiseId); startProvisioning(); } } @@ -832,7 +866,7 @@ public class MediaDrmBridge { String mime = mSessionIds.get(ByteBuffer.wrap(sessionId)); MediaDrm.KeyRequest request = null; try { - request = getKeyRequest(sessionId, data, mime); + request = getKeyRequest(sessionId, data, mime, null); } catch (android.media.NotProvisionedException e) { Log.e(TAG, "Device not provisioned", e); startProvisioning(); @@ -933,15 +967,6 @@ public class MediaDrmBridge { } } - public static void addKeySystemUuidMapping(String keySystem, UUID uuid) { - ByteBuffer uuidBuffer = ByteBuffer.allocateDirect(16); - // MSB (byte) should be positioned at the first element. - uuidBuffer.order(ByteOrder.BIG_ENDIAN); - uuidBuffer.putLong(uuid.getMostSignificantBits()); - uuidBuffer.putLong(uuid.getLeastSignificantBits()); - nativeAddKeySystemUuidMapping(keySystem, uuidBuffer); - } - private native void nativeOnMediaCryptoReady(long nativeMediaDrmBridge); private native void nativeOnPromiseResolved(long nativeMediaDrmBridge, long promiseId); @@ -960,6 +985,4 @@ public class MediaDrmBridge { private native void nativeOnResetDeviceCredentialsCompleted( long nativeMediaDrmBridge, boolean success); - - private static native void nativeAddKeySystemUuidMapping(String keySystem, ByteBuffer uuid); } diff --git a/media/base/android/media_client_android.cc b/media/base/android/media_client_android.cc new file mode 100644 index 0000000..ad4e11b --- /dev/null +++ b/media/base/android/media_client_android.cc @@ -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. + +#include "media/base/android/media_client_android.h" + +#include "base/logging.h" +#include "base/stl_util.h" + +namespace media { + +static MediaClientAndroid* g_media_client = nullptr; + +void SetMediaClientAndroid(MediaClientAndroid* media_client) { + DCHECK(!g_media_client); + g_media_client = media_client; +} + +MediaClientAndroid* GetMediaClientAndroid() { + return g_media_client; +} + +MediaClientAndroid::MediaClientAndroid() { +} + +MediaClientAndroid::~MediaClientAndroid() { +} + +void MediaClientAndroid::AddKeySystemUUIDMappings(KeySystemUuidMap* map) { +} + +media::MediaDrmBridgeDelegate* MediaClientAndroid::GetMediaDrmBridgeDelegate( + const std::vector<uint8_t>& scheme_uuid) { + return nullptr; +} + +} // namespace media diff --git a/media/base/android/media_client_android.h b/media/base/android/media_client_android.h new file mode 100644 index 0000000..0753b3b --- /dev/null +++ b/media/base/android/media_client_android.h @@ -0,0 +1,59 @@ +// 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 MEDIA_BASE_ANDROID_MEDIA_CLIENT_ANDROID_H_ +#define MEDIA_BASE_ANDROID_MEDIA_CLIENT_ANDROID_H_ + +#include <string> +#include <utility> +#include <vector> + +#include "base/containers/hash_tables.h" +#include "base/macros.h" +#include "media/base/media_export.h" + +namespace media { + +class MediaClientAndroid; +class MediaDrmBridgeDelegate; + +// Setter for MediaClientAndroid. This should be called early in embedder +// lifecycle, before any media playback could occur. +MEDIA_EXPORT void SetMediaClientAndroid(MediaClientAndroid* media_client); + +#if defined(MEDIA_IMPLEMENTATION) +// Getter for the client. Returns nullptr if no customized client is needed. +MediaClientAndroid* GetMediaClientAndroid(); +#endif + +using UUID = std::vector<uint8_t>; + +// A client interface for embedders (e.g. content/browser) to provide customized +// additions to Android's browser-side media handling. +class MEDIA_EXPORT MediaClientAndroid { + public: + typedef base::hash_map<std::string, UUID> KeySystemUuidMap; + + MediaClientAndroid(); + virtual ~MediaClientAndroid(); + + // Adds extra mappings from key-system name to Android UUID into |map|. + virtual void AddKeySystemUUIDMappings(KeySystemUuidMap* map); + + // Returns a MediaDrmBridgeDelegate that corresponds to |scheme_uuid|. + // MediaClientAndroid retains ownership. + virtual media::MediaDrmBridgeDelegate* GetMediaDrmBridgeDelegate( + const UUID& scheme_uuid); + + private: + friend class KeySystemManager; + + base::hash_map<std::string, UUID> key_system_uuid_map_; + + DISALLOW_COPY_AND_ASSIGN(MediaClientAndroid); +}; + +} // namespace media + +#endif // MEDIA_BASE_ANDROID_MEDIA_CLIENT_ANDROID_H_ diff --git a/media/base/android/media_drm_bridge.cc b/media/base/android/media_drm_bridge.cc index 0aaeef4..79f7d42 100644 --- a/media/base/android/media_drm_bridge.cc +++ b/media/base/android/media_drm_bridge.cc @@ -15,12 +15,13 @@ #include "base/location.h" #include "base/logging.h" #include "base/message_loop/message_loop_proxy.h" -#include "base/numerics/safe_conversions.h" #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/sys_byteorder.h" #include "base/sys_info.h" #include "jni/MediaDrmBridge_jni.h" +#include "media/base/android/media_client_android.h" +#include "media/base/android/media_drm_bridge_delegate.h" #include "media/base/cdm_key_information.h" #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. @@ -40,20 +41,6 @@ namespace { // here to report session expiration info. const char kDummyKeyId[] = "Dummy Key Id"; -uint32 ReadUint32(const uint8_t* data) { - uint32 value = 0; - for (int i = 0; i < 4; ++i) - value = (value << 8) | data[i]; - return value; -} - -uint64 ReadUint64(const uint8_t* data) { - uint64 value = 0; - for (int i = 0; i < 8; ++i) - value = (value << 8) | data[i]; - return value; -} - // Returns string session ID from jbyteArray (byte[] in Java). std::string GetSessionId(JNIEnv* env, jbyteArray j_session_id) { std::vector<uint8> session_id_vector; @@ -61,116 +48,10 @@ std::string GetSessionId(JNIEnv* env, jbyteArray j_session_id) { return std::string(session_id_vector.begin(), session_id_vector.end()); } -// The structure of an ISO CENC Protection System Specific Header (PSSH) box is -// as follows. (See ISO/IEC FDIS 23001-7:2011(E).) -// Note: ISO boxes use big-endian values. -// -// PSSH { -// uint32 Size -// uint32 Type -// uint64 LargeSize # Field is only present if value(Size) == 1. -// uint32 VersionAndFlags -// uint8[16] SystemId -// uint32 DataSize -// uint8[DataSize] Data -// } -const int kBoxHeaderSize = 8; // Box's header contains Size and Type. -const int kBoxLargeSizeSize = 8; -const int kPsshVersionFlagSize = 4; -const int kPsshSystemIdSize = 16; -const int kPsshDataSizeSize = 4; -const uint32 kTencType = 0x74656e63; -const uint32 kPsshType = 0x70737368; const uint8 kWidevineUuid[16] = { 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED }; -typedef std::vector<uint8> UUID; - -// Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the -// "Data" of the box and put it in |pssh_data|. Returns true if such a box is -// found and successfully parsed. Returns false otherwise. -// Notes: -// 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box -// will be set in |pssh_data|. -// 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped. -bool GetPsshData(const std::vector<uint8_t>& data, - const UUID& uuid, - std::vector<uint8>* pssh_data) { - int bytes_left = base::checked_cast<int>(data.size()); - const uint8_t* cur = &data[0]; - const uint8_t* data_end = cur + bytes_left; - - while (bytes_left > 0) { - const uint8* box_head = cur; - - if (bytes_left < kBoxHeaderSize) - return false; - - uint64_t box_size = ReadUint32(cur); - uint32 type = ReadUint32(cur + 4); - cur += kBoxHeaderSize; - bytes_left -= kBoxHeaderSize; - - if (box_size == 1) { // LargeSize is present. - if (bytes_left < kBoxLargeSizeSize) - return false; - - box_size = ReadUint64(cur); - cur += kBoxLargeSizeSize; - bytes_left -= kBoxLargeSizeSize; - } else if (box_size == 0) { - box_size = bytes_left + kBoxHeaderSize; - } - - const uint8* box_end = box_head + box_size; - if (data_end < box_end) - return false; - - if (type == kTencType) { - // Skip 'tenc' box. - cur = box_end; - bytes_left = data_end - cur; - continue; - } else if (type != kPsshType) { - return false; - } - - const int kPsshBoxMinimumSize = - kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize; - if (box_end < cur + kPsshBoxMinimumSize) - return false; - - uint32 version_and_flags = ReadUint32(cur); - cur += kPsshVersionFlagSize; - bytes_left -= kPsshVersionFlagSize; - if (version_and_flags != 0) - return false; - - DCHECK_GE(bytes_left, kPsshSystemIdSize); - if (!std::equal(uuid.begin(), uuid.end(), cur)) { - cur = box_end; - bytes_left = data_end - cur; - continue; - } - - cur += kPsshSystemIdSize; - bytes_left -= kPsshSystemIdSize; - - uint32 data_size = ReadUint32(cur); - cur += kPsshDataSizeSize; - bytes_left -= kPsshDataSizeSize; - - if (box_end < cur + data_size) - return false; - - pssh_data->assign(cur, cur + data_size); - return true; - } - - return false; -} - // Convert |init_data_type| to a string supported by MediaDRM. // "audio"/"video" does not matter, so use "video". std::string ConvertInitDataType(media::EmeInitDataType init_data_type) { @@ -187,45 +68,37 @@ std::string ConvertInitDataType(media::EmeInitDataType init_data_type) { } } -class KeySystemUuidManager { +class KeySystemManager { public: - KeySystemUuidManager(); + KeySystemManager(); UUID GetUUID(const std::string& key_system); - void AddMapping(const std::string& key_system, const UUID& uuid); std::vector<std::string> GetPlatformKeySystemNames(); private: - typedef base::hash_map<std::string, UUID> KeySystemUuidMap; + using KeySystemUuidMap = MediaClientAndroid::KeySystemUuidMap; KeySystemUuidMap key_system_uuid_map_; - DISALLOW_COPY_AND_ASSIGN(KeySystemUuidManager); + DISALLOW_COPY_AND_ASSIGN(KeySystemManager); }; -KeySystemUuidManager::KeySystemUuidManager() { +KeySystemManager::KeySystemManager() { // Widevine is always supported in Android. key_system_uuid_map_[kWidevineKeySystem] = UUID(kWidevineUuid, kWidevineUuid + arraysize(kWidevineUuid)); + MediaClientAndroid* client = GetMediaClientAndroid(); + if (client) + client->AddKeySystemUUIDMappings(&key_system_uuid_map_); } -UUID KeySystemUuidManager::GetUUID(const std::string& key_system) { +UUID KeySystemManager::GetUUID(const std::string& key_system) { KeySystemUuidMap::iterator it = key_system_uuid_map_.find(key_system); if (it == key_system_uuid_map_.end()) return UUID(); return it->second; } -void KeySystemUuidManager::AddMapping(const std::string& key_system, - const UUID& uuid) { - KeySystemUuidMap::iterator it = key_system_uuid_map_.find(key_system); - DCHECK(it == key_system_uuid_map_.end()) - << "Shouldn't overwrite an existing key system."; - if (it != key_system_uuid_map_.end()) - return; - key_system_uuid_map_[key_system] = uuid; -} - -std::vector<std::string> KeySystemUuidManager::GetPlatformKeySystemNames() { +std::vector<std::string> KeySystemManager::GetPlatformKeySystemNames() { std::vector<std::string> key_systems; for (KeySystemUuidMap::iterator it = key_system_uuid_map_.begin(); it != key_system_uuid_map_.end(); ++it) { @@ -236,7 +109,7 @@ std::vector<std::string> KeySystemUuidManager::GetPlatformKeySystemNames() { return key_systems; } -base::LazyInstance<KeySystemUuidManager>::Leaky g_key_system_uuid_manager = +base::LazyInstance<KeySystemManager>::Leaky g_key_system_manager = LAZY_INSTANCE_INITIALIZER; // Checks whether |key_system| is supported with |container_mime_type|. Only @@ -249,7 +122,7 @@ bool IsKeySystemSupportedWithTypeImpl(const std::string& key_system, if (!MediaDrmBridge::IsAvailable()) return false; - UUID scheme_uuid = g_key_system_uuid_manager.Get().GetUUID(key_system); + UUID scheme_uuid = g_key_system_manager.Get().GetUUID(key_system); if (scheme_uuid.empty()) return false; @@ -287,17 +160,6 @@ std::string GetSecurityLevelString( } // namespace -// Called by Java. -static void AddKeySystemUuidMapping(JNIEnv* env, - jclass clazz, - jstring j_key_system, - jobject j_buffer) { - std::string key_system = ConvertJavaStringToUTF8(env, j_key_system); - uint8* buffer = static_cast<uint8*>(env->GetDirectBufferAddress(j_buffer)); - UUID uuid(buffer, buffer + 16); - g_key_system_uuid_manager.Get().AddMapping(key_system, uuid); -} - // static bool MediaDrmBridge::IsAvailable() { if (base::android::BuildInfo::GetInstance()->sdk_int() < 19) @@ -324,7 +186,7 @@ bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) { // static std::vector<std::string> MediaDrmBridge::GetPlatformKeySystemNames() { - return g_key_system_uuid_manager.Get().GetPlatformKeySystemNames(); + return g_key_system_manager.Get().GetPlatformKeySystemNames(); } // static @@ -385,7 +247,7 @@ scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create( if (!IsAvailable()) return media_drm_bridge.Pass(); - UUID scheme_uuid = g_key_system_uuid_manager.Get().GetUUID(key_system); + UUID scheme_uuid = g_key_system_manager.Get().GetUUID(key_system); if (scheme_uuid.empty()) return media_drm_bridge.Pass(); @@ -449,19 +311,31 @@ void MediaDrmBridge::CreateSessionAndGenerateRequest( JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jbyteArray> j_init_data; - - // Widevine MediaDrm plugin only accepts the "data" part of the PSSH box as - // the init data when using MP4 container. - if (std::equal(scheme_uuid_.begin(), scheme_uuid_.end(), kWidevineUuid) && - init_data_type == media::EmeInitDataType::CENC) { - std::vector<uint8> pssh_data; - if (!GetPsshData(init_data, scheme_uuid_, &pssh_data)) { - promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid PSSH data."); - return; + ScopedJavaLocalRef<jobjectArray> j_optional_parameters; + + MediaClientAndroid* client = GetMediaClientAndroid(); + if (client) { + MediaDrmBridgeDelegate* delegate = + client->GetMediaDrmBridgeDelegate(scheme_uuid_); + if (delegate) { + std::vector<uint8> init_data_from_delegate; + std::vector<std::string> optional_parameters_from_delegate; + if (!delegate->OnCreateSession(init_data_type, init_data, + &init_data_from_delegate, + &optional_parameters_from_delegate)) { + promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid init data."); + } + j_init_data = base::android::ToJavaByteArray( + env, vector_as_array(&init_data_from_delegate), + init_data_from_delegate.size()); + if (!optional_parameters_from_delegate.empty()) { + j_optional_parameters = base::android::ToJavaArrayOfStrings( + env, optional_parameters_from_delegate); + } } - j_init_data = base::android::ToJavaByteArray( - env, vector_as_array(&pssh_data), pssh_data.size()); - } else { + } + + if (j_init_data.is_null()) { j_init_data = base::android::ToJavaByteArray( env, vector_as_array(&init_data), init_data.size()); } @@ -469,8 +343,10 @@ void MediaDrmBridge::CreateSessionAndGenerateRequest( ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, ConvertInitDataType(init_data_type)); uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass()); - Java_MediaDrmBridge_createSession(env, j_media_drm_.obj(), j_init_data.obj(), - j_mime.obj(), promise_id); + Java_MediaDrmBridge_createSessionFromNative(env, j_media_drm_.obj(), + j_init_data.obj(), j_mime.obj(), + j_optional_parameters.obj(), + promise_id); } void MediaDrmBridge::LoadSession( diff --git a/media/base/android/media_drm_bridge_delegate.cc b/media/base/android/media_drm_bridge_delegate.cc new file mode 100644 index 0000000..5630d4a --- /dev/null +++ b/media/base/android/media_drm_bridge_delegate.cc @@ -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. + +#include "media/base/android/media_drm_bridge_delegate.h" + +#include "base/logging.h" + +namespace media { + +MediaDrmBridgeDelegate::MediaDrmBridgeDelegate() { +} + +MediaDrmBridgeDelegate::~MediaDrmBridgeDelegate() { +} + +bool MediaDrmBridgeDelegate::OnCreateSession( + const 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) { + DCHECK(init_data_out->empty()); + DCHECK(optional_parameters_out->empty()); + return true; +} + +} // namespace media diff --git a/media/base/android/media_drm_bridge_delegate.h b/media/base/android/media_drm_bridge_delegate.h new file mode 100644 index 0000000..7a18dac --- /dev/null +++ b/media/base/android/media_drm_bridge_delegate.h @@ -0,0 +1,46 @@ +// 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 MEDIA_BASE_ANDROID_MEDIA_DRM_BRIDGE_DELEGATE_H_ +#define MEDIA_BASE_ANDROID_MEDIA_DRM_BRIDGE_DELEGATE_H_ + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "media/base/android/media_client_android.h" +#include "media/base/eme_constants.h" +#include "media/base/media_export.h" + +namespace media { + +// Allows embedders to modify the Android MediaDrm flow. Delegates are +// registered to a specific key system. +class MEDIA_EXPORT MediaDrmBridgeDelegate { + public: + MediaDrmBridgeDelegate(); + virtual ~MediaDrmBridgeDelegate(); + + // Returns the UUID of the DRM scheme that this delegate applies to. + virtual const UUID GetUUID() const = 0; + + // Invoked from CreateSession. + // If |init_data_out| is filled, it replaces |init_data| to send to the + // MediaDrm instance. + // If |optional_parameters_out| is filled, it is expected to be an + // even-length list of (key, value) pairs to send to the MediaDrm instance. + // Returns false if the request should be rejected. + virtual bool OnCreateSession( + const 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); + + private: + DISALLOW_COPY_AND_ASSIGN(MediaDrmBridgeDelegate); +}; + +} // namespace media + +#endif // MEDIA_BASE_ANDROID_MEDIA_DRM_BRIDGE_DELEGATE_H_ diff --git a/media/media.gyp b/media/media.gyp index e530e90..c228c83 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -1739,6 +1739,8 @@ 'base/android/demuxer_android.h', 'base/android/demuxer_stream_player_params.cc', 'base/android/demuxer_stream_player_params.h', + 'base/android/media_client_android.cc', + 'base/android/media_client_android.h', 'base/android/media_codec_bridge.cc', 'base/android/media_codec_bridge.h', 'base/android/media_common_android.h', @@ -1746,6 +1748,8 @@ 'base/android/media_decoder_job.h', 'base/android/media_drm_bridge.cc', 'base/android/media_drm_bridge.h', + 'base/android/media_drm_bridge_delegate.cc', + 'base/android/media_drm_bridge_delegate.h', 'base/android/media_jni_registrar.cc', 'base/android/media_jni_registrar.h', 'base/android/media_player_android.cc', |