diff --git a/CefSharp.Core/Cef.h b/CefSharp.Core/Cef.h index 473ef99de3..166827eb81 100644 --- a/CefSharp.Core/Cef.h +++ b/CefSharp.Core/Cef.h @@ -17,6 +17,7 @@ #include "Internals/PluginVisitor.h" #include "Internals/CefTaskScheduler.h" #include "Internals/CefGetGeolocationCallbackWrapper.h" +#include "Internals/CefRegisterCdmCallbackAdapter.h" #include "CookieManager.h" #include "CefSettings.h" #include "RequestContext.h" @@ -461,7 +462,7 @@ namespace CefSharp static void UnregisterInternalWebPlugin(String^ path) { CefUnregisterInternalWebPlugin(StringUtils::ToNative(path)); - } + } /// /// Call during process startup to enable High-DPI support on Windows 7 or newer. @@ -607,5 +608,74 @@ namespace CefSharp { CefSetCrashKeyValue(StringUtils::ToNative(key), StringUtils::ToNative(value)); } + + /// + /// Register the Widevine CDM plugin. + /// + /// The client application is responsible for downloading an appropriate + /// platform-specific CDM binary distribution from Google, extracting the + /// contents, and building the required directory structure on the local machine. + /// The IBrowserHost::StartDownload method class can be used + /// to implement this functionality in CefSharp. Contact Google via + /// https://www.widevine.com/contact.html for details on CDM download. + /// + /// + /// |path| is a directory that must contain the following files: + /// 1. manifest.json file from the CDM binary distribution (see below). + /// 2. widevinecdm file from the CDM binary distribution (e.g. + /// widevinecdm.dll on Windows, libwidevinecdm.dylib on OS X, + /// libwidevinecdm.so on Linux). + /// 3. widevidecdmadapter file from the CEF binary distribution (e.g. + /// widevinecdmadapter.dll on Windows, widevinecdmadapter.plugin on OS X, + /// libwidevinecdmadapter.so on Linux). + /// + /// If any of these files are missing or if the manifest file has incorrect + /// contents the registration will fail and |callback| will receive an |ErrorCode| + /// value of CdmRegistrationErrorCode.IncorrectContents. + /// + /// The manifest.json file must contain the following keys: + /// A. "os": Supported OS (e.g. "mac", "win" or "linux"). + /// B. "arch": Supported architecture (e.g. "ia32" or "x64"). + /// C. "x-cdm-module-versions": Module API version (e.g. "4"). + /// D. "x-cdm-interface-versions": Interface API version (e.g. "8"). + /// E. "x-cdm-host-versions": Host API version (e.g. "8"). + /// F. "version": CDM version (e.g. "1.4.8.903"). + /// G. "x-cdm-codecs": List of supported codecs (e.g. "vp8,vp9.0,avc1"). + /// + /// A through E are used to verify compatibility with the current Chromium + /// version. If the CDM is not compatible the registration will fail and + /// |callback| will receive an |ErrorCode| value of + /// CdmRegistrationErrorCode.Incompatible. + /// + /// |callback| will be executed asynchronously once registration is complete. + /// + /// On Linux this function must be called before CefInitialize() and the + /// registration cannot be changed during runtime. If registration is not + /// supported at the time that RegisterWidevineCdm() is called then |callback| + /// will receive an |ErrorCode| value of CdmRegistrationErrorCode.NotSupported. + /// + static void RegisterWidevineCdm(String^ path, [Optional] IRegisterCdmCallback^ callback) + { + CefRegisterCdmCallbackAdapter* adapter = nullptr; + + if (callback != nullptr) + adapter = new CefRegisterCdmCallbackAdapter(callback); + + CefRegisterWidevineCdm(StringUtils::ToNative(path), adapter); + } + + /// + /// Register the Widevine CDM plugin. + /// + /// See RegisterWidevineCdm(String, IRegisterCdmCallback) for more details. + /// + static Task^ RegisterWidevineCdmAsync(String^ path) + { + RegisterCdmCallback^ callback = gcnew RegisterCdmCallback(); + + RegisterWidevineCdm(path, callback); + + return callback->Task; + } }; } diff --git a/CefSharp.Core/CefSharp.Core.vcxproj b/CefSharp.Core/CefSharp.Core.vcxproj index b06b3e7e75..7cc136793f 100644 --- a/CefSharp.Core/CefSharp.Core.vcxproj +++ b/CefSharp.Core/CefSharp.Core.vcxproj @@ -270,6 +270,7 @@ + diff --git a/CefSharp.Core/CefSharp.Core.vcxproj.filters b/CefSharp.Core/CefSharp.Core.vcxproj.filters index 28d36b64a1..5fad10abae 100644 --- a/CefSharp.Core/CefSharp.Core.vcxproj.filters +++ b/CefSharp.Core/CefSharp.Core.vcxproj.filters @@ -262,6 +262,9 @@ Header Files + + Header Files + diff --git a/CefSharp.Core/Internals/CefRegisterCdmCallbackAdapter.h b/CefSharp.Core/Internals/CefRegisterCdmCallbackAdapter.h new file mode 100644 index 0000000000..3971adc80c --- /dev/null +++ b/CefSharp.Core/Internals/CefRegisterCdmCallbackAdapter.h @@ -0,0 +1,51 @@ +// Copyright © 2010-2016 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +#pragma once + +#include "Stdafx.h" +#include "CefWrapper.h" + +namespace CefSharp +{ + namespace Internals + { + private class CefRegisterCdmCallbackAdapter : public CefRegisterCdmCallback + { + private: + gcroot _callback; + + public: + CefRegisterCdmCallbackAdapter(IRegisterCdmCallback^ callback) + { + _callback = callback; + } + + ~CefRegisterCdmCallbackAdapter() + { + delete _callback; + _callback = nullptr; + } + + /// + /// Method that will be called when CDM registration is complete. |result| + /// will be CEF_CDM_REGISTRATION_ERROR_NONE if registration completed + /// successfully. Otherwise, |result| and |error_message| will contain + /// additional information about why registration failed. + /// + virtual void OnCdmRegistrationComplete(cef_cdm_registration_error_t result, + const CefString& error_message) OVERRIDE + { + auto r = gcnew CdmRegistration(); + + r->ErrorCode = (CdmRegistrationErrorCode)result; + r->ErrorMessage = StringUtils::ToClr(error_message); + + _callback->OnRegistrationComplete(r); + } + + IMPLEMENT_REFCOUNTING(CefRegisterCdmCallbackAdapter) + }; + } +} \ No newline at end of file diff --git a/CefSharp.Example/CefExample.cs b/CefSharp.Example/CefExample.cs index 4335533479..be5edfc224 100644 --- a/CefSharp.Example/CefExample.cs +++ b/CefSharp.Example/CefExample.cs @@ -25,6 +25,7 @@ public static class CefExample public const string ResponseFilterTestUrl = "custom://cefsharp/ResponseFilterTest.html"; public const string DraggableRegionTestUrl = "custom://cefsharp/DraggableRegionTest.html"; public const string CssAnimationTestUrl = "custom://cefsharp/CssAnimationTest.html"; + public const string CdmSupportTestUrl = "custom://cefsharp/CdmSupportTest.html"; public const string TestResourceUrl = "http://test/resource/load"; public const string RenderProcessCrashedUrl = "http://processcrashed"; public const string TestUnicodeResourceUrl = "http://test/resource/loadUnicode"; @@ -41,6 +42,10 @@ public static void Init(bool osr, bool multiThreadedMessageLoop, IBrowserProcess // Environment.SetEnvironmentVariable("GOOGLE_DEFAULT_CLIENT_ID", ""); // Environment.SetEnvironmentVariable("GOOGLE_DEFAULT_CLIENT_SECRET", ""); + // Widevine CDM registration - pass in directory where Widevine CDM binaries and manifest.json are located. + // For more information on support for DRM content with Widevine see: https://github.com/cefsharp/CefSharp/issues/1934 + Cef.RegisterWidevineCdm(@".\WidevineCdm"); + //Chromium Command Line args //http://peter.sh/experiments/chromium-command-line-switches/ //NOTE: Not all relevant in relation to `CefSharp`, use for reference purposes only. diff --git a/CefSharp.Example/CefSharp.Example.csproj b/CefSharp.Example/CefSharp.Example.csproj index 4c11d80ae8..f628cbcc6a 100644 --- a/CefSharp.Example/CefSharp.Example.csproj +++ b/CefSharp.Example/CefSharp.Example.csproj @@ -135,6 +135,7 @@ + diff --git a/CefSharp.Example/CefSharpSchemeHandler.cs b/CefSharp.Example/CefSharpSchemeHandler.cs index 193a1c951b..ecf7d64874 100644 --- a/CefSharp.Example/CefSharpSchemeHandler.cs +++ b/CefSharp.Example/CefSharpSchemeHandler.cs @@ -49,7 +49,8 @@ static CefSharpSchemeHandler() { "/ScriptedMethodsTest.html", Resources.ScriptedMethodsTest }, { "/ResponseFilterTest.html", Resources.ResponseFilterTest }, { "/DraggableRegionTest.html", Resources.DraggableRegionTest }, - { "/CssAnimationTest.html", Resources.CssAnimation } + { "/CssAnimationTest.html", Resources.CssAnimation }, + { "/CdmSupportTest.html", Resources.CdmSupportTest } }; } diff --git a/CefSharp.Example/Properties/Resources.Designer.cs b/CefSharp.Example/Properties/Resources.Designer.cs index 555cca6e50..e0475a76c7 100644 --- a/CefSharp.Example/Properties/Resources.Designer.cs +++ b/CefSharp.Example/Properties/Resources.Designer.cs @@ -336,6 +336,32 @@ internal static string bootstrap_theme_min_css { } } + /// + /// Looks up a localized string similar to + ///<html> + ///<head> + /// <script language="JavaScript"> + /// function probeSupport() { + /// var tests = []; + /// var testKeySystems = [ + /// 'org.w3.clearkey', + /// 'com.widevine.alpha', + /// 'com.microsoft.playready', + /// 'com.apple.fps.2_0', + /// 'com.apple.fps.1_0', + /// 'com.apple.fps', + /// 'com.adobe.primetime' + /// ]; + /// + /// var basicVideoCapabilities = [ + /// { contentType: 'video/mp4; c [rest of string was truncated]";. + /// + internal static string CdmSupportTest { + get { + return ResourceManager.GetString("CdmSupportTest", resourceCulture); + } + } + /// /// Looks up a localized string similar to <!DOCTYPE html> ///<html> diff --git a/CefSharp.Example/Properties/Resources.resx b/CefSharp.Example/Properties/Resources.resx index bbf3b6f36e..651518c122 100644 --- a/CefSharp.Example/Properties/Resources.resx +++ b/CefSharp.Example/Properties/Resources.resx @@ -190,4 +190,7 @@ ..\Resources\CssAnimation.html;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + ..\resources\cdmsupporttest.html;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + \ No newline at end of file diff --git a/CefSharp.Example/Resources/CdmSupportTest.html b/CefSharp.Example/Resources/CdmSupportTest.html new file mode 100644 index 0000000000..b278c94fce --- /dev/null +++ b/CefSharp.Example/Resources/CdmSupportTest.html @@ -0,0 +1,88 @@ + + + + + + +

+    

+ Google Shaka Player demo - video play back +

+

+ Note:
+

    +
  • + Support for Widevine CDM requires additional steps as + outlined here. +
  • +
  • + Use of proprietary codecs (such as H.264) requires a custom build of CEF to be used due to + licensing requirements.
    + Details on how to build the CEF project and other resources can be + found here.
  • +
+

+ + \ No newline at end of file diff --git a/CefSharp.Wpf.Example/MainWindow.xaml b/CefSharp.Wpf.Example/MainWindow.xaml index cc5cb979f5..9afc8e8057 100644 --- a/CefSharp.Wpf.Example/MainWindow.xaml +++ b/CefSharp.Wpf.Example/MainWindow.xaml @@ -39,6 +39,7 @@ + + /// Represents the response to an attempt to register the Widevine Content Decryption Module (CDM) + ///
+ public class CdmRegistration + { + /// + /// If CDM registration succeeded then value will be , for other values see the enumeration . + /// + public CdmRegistrationErrorCode ErrorCode { get; set; } + + /// + /// Contains an error message containing additional information if is not . + /// + public string ErrorMessage { get; set; } + } +} diff --git a/CefSharp/CdmRegistrationErrorCode.cs b/CefSharp/CdmRegistrationErrorCode.cs new file mode 100644 index 0000000000..8f61285497 --- /dev/null +++ b/CefSharp/CdmRegistrationErrorCode.cs @@ -0,0 +1,34 @@ +// Copyright © 2010-2016 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; + +namespace CefSharp +{ + /// + /// Lists the errors that can be reported during Widevine Content Decryption Module (CDM) registration. + /// + public enum CdmRegistrationErrorCode + { + /// + /// No error. Registration completed successfully. + /// + None, + + /// + /// Required files or manifest contents are missing. + /// + IncorrectContents, + + /// + /// The CDM is incompatible with the current Chromium version. + /// + Incompatible, + + /// + /// CDM registration is not supported at this time. + /// + NotSupported, + } +} diff --git a/CefSharp/CefSharp.csproj b/CefSharp/CefSharp.csproj index 874ea6b179..b4da25aee0 100644 --- a/CefSharp/CefSharp.csproj +++ b/CefSharp/CefSharp.csproj @@ -71,6 +71,8 @@
+ + @@ -97,6 +99,7 @@ + @@ -213,6 +216,7 @@ + diff --git a/CefSharp/IRegisterCdmCallback.cs b/CefSharp/IRegisterCdmCallback.cs new file mode 100644 index 0000000000..80aaf14aca --- /dev/null +++ b/CefSharp/IRegisterCdmCallback.cs @@ -0,0 +1,19 @@ +// Copyright © 2010-2016 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +namespace CefSharp +{ + /// + /// Content Decryption Module (CDM) registration callback used for asynchronous completion. + /// + public interface IRegisterCdmCallback : IDisposable + { + /// + /// Method that will be called once CDM registration is complete + /// + /// The result of the CDM registration process + void OnRegistrationComplete(CdmRegistration registration); + } +} diff --git a/CefSharp/RegisterCdmCallback.cs b/CefSharp/RegisterCdmCallback.cs new file mode 100644 index 0000000000..dfdd5be522 --- /dev/null +++ b/CefSharp/RegisterCdmCallback.cs @@ -0,0 +1,45 @@ +// Copyright © 2010-2016 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using System.Threading.Tasks; +using CefSharp.Internals; + +namespace CefSharp +{ + /// + /// Provides a callback implementation of for use with asynchronous Widevine CDM registration. + /// + public class RegisterCdmCallback: IRegisterCdmCallback + { + private readonly TaskCompletionSource taskCompletionSource; + + public RegisterCdmCallback() + { + taskCompletionSource = new TaskCompletionSource(); + } + + void IRegisterCdmCallback.OnRegistrationComplete(CdmRegistration registration) + { + taskCompletionSource.TrySetResultAsync(registration); + } + + public Task Task + { + get { return taskCompletionSource.Task; } + } + + void IDisposable.Dispose() + { + var task = taskCompletionSource.Task; + + //If the Task hasn't completed and this is being disposed then + //set the TCS to false + if (task.IsCompleted == false) + { + taskCompletionSource.TrySetResultAsync(null); + } + } + } +}