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);
+ }
+ }
+ }
+}