Skip to content

Commit

Permalink
Implementation of Cef.RegisterWidevineCdm (#1935)
Browse files Browse the repository at this point in the history
* Added Cef.RegisterWidevineCdm for CEF Widevine CDM registration #1934
* Added test page for WidevineCdm registration and example registration #1934
* Noted additional steps for Widevine and proprietary codecs to CdmSupportTest.html
* Link for more information changed to issue #1934 in CefExample.cs
* replaced Cef.RegisterCdmCallback's async result with IRegisterCdmCallback parameter
* RegisterCdmCallback now uses taskCompletionSource
  • Loading branch information
justinstenning authored and amaitland committed Feb 13, 2017
1 parent a3c2fd4 commit 2298ea0
Show file tree
Hide file tree
Showing 16 changed files with 378 additions and 2 deletions.
72 changes: 71 additions & 1 deletion CefSharp.Core/Cef.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -461,7 +462,7 @@ namespace CefSharp
static void UnregisterInternalWebPlugin(String^ path)
{
CefUnregisterInternalWebPlugin(StringUtils::ToNative(path));
}
}

/// <summary>
/// Call during process startup to enable High-DPI support on Windows 7 or newer.
Expand Down Expand Up @@ -607,5 +608,74 @@ namespace CefSharp
{
CefSetCrashKeyValue(StringUtils::ToNative(key), StringUtils::ToNative(value));
}

/// <summary>
/// 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.
/// </summary>
static void RegisterWidevineCdm(String^ path, [Optional] IRegisterCdmCallback^ callback)
{
CefRegisterCdmCallbackAdapter* adapter = nullptr;

if (callback != nullptr)
adapter = new CefRegisterCdmCallbackAdapter(callback);

CefRegisterWidevineCdm(StringUtils::ToNative(path), adapter);
}

/// <summary>
/// Register the Widevine CDM plugin.
///
/// See RegisterWidevineCdm(String, IRegisterCdmCallback) for more details.
/// </summary>
static Task<CdmRegistration^>^ RegisterWidevineCdmAsync(String^ path)
{
RegisterCdmCallback^ callback = gcnew RegisterCdmCallback();

RegisterWidevineCdm(path, callback);

return callback->Task;
}
};
}
1 change: 1 addition & 0 deletions CefSharp.Core/CefSharp.Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@
<ClInclude Include="Internals\CefPdfPrintCallbackWrapper.h" />
<ClInclude Include="Internals\CefPostDataElementWrapper.h" />
<ClInclude Include="Internals\CefPostDataWrapper.h" />
<ClInclude Include="Internals\CefRegisterCdmCallbackAdapter.h" />
<ClInclude Include="Internals\CefResolveCallbackAdapter.h" />
<ClInclude Include="Internals\CefResponseFilterAdapter.h" />
<ClInclude Include="Internals\CefResponseWrapper.h" />
Expand Down
3 changes: 3 additions & 0 deletions CefSharp.Core/CefSharp.Core.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@
<ClInclude Include="Internals\CefCertificateCallbackWrapper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Internals\CefRegisterCdmCallbackAdapter.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Internals\CefSharpBrowserWrapper.h">
Expand Down
51 changes: 51 additions & 0 deletions CefSharp.Core/Internals/CefRegisterCdmCallbackAdapter.h
Original file line number Diff line number Diff line change
@@ -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<IRegisterCdmCallback^> _callback;

public:
CefRegisterCdmCallbackAdapter(IRegisterCdmCallback^ callback)
{
_callback = callback;
}

~CefRegisterCdmCallbackAdapter()
{
delete _callback;
_callback = nullptr;
}

/// <summary>
/// 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.
/// </summary>
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)
};
}
}
5 changes: 5 additions & 0 deletions CefSharp.Example/CefExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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.
Expand Down
1 change: 1 addition & 0 deletions CefSharp.Example/CefSharp.Example.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
<Content Include="Resources\assets\js\shBrushCSharp.js" />
<None Include="Resources\assets\js\shBrushJScript.js" />
<Content Include="Resources\assets\js\shCore.js" />
<Content Include="Resources\CdmSupportTest.html" />
<Content Include="Resources\CssAnimation.html" />
<Content Include="Resources\ExceptionTest.html" />
<Content Include="Resources\BindingTest.html" />
Expand Down
3 changes: 2 additions & 1 deletion CefSharp.Example/CefSharpSchemeHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
};
}

Expand Down
26 changes: 26 additions & 0 deletions CefSharp.Example/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions CefSharp.Example/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,7 @@
<data name="CssAnimation" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\CssAnimation.html;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="CdmSupportTest" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\cdmsupporttest.html;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
</root>
88 changes: 88 additions & 0 deletions CefSharp.Example/Resources/CdmSupportTest.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@

<html>
<head>
<script language="JavaScript">
function probeSupport() {
var tests = [];
var testKeySystems = [
'com.widevine.alpha',
'org.w3.clearkey'
];

var testCodecs = [
{ type: 'H.264', contentType: 'video/mp4; codecs="avc1.42E01E"' },
{ type: 'H.264/MPEG-4 AVC', contentType: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"' },
{ type: 'ogg', contentType: 'video/ogg; codecs="theora"' },
{ type: 'webm-vp8', contentType: 'video/webm; codecs="vp8"' },
{ type: 'webm-vp9-', contentType: 'video/webm; codecs="vp9"' }
];

var basicVideoCapabilities = [
{ contentType: 'video/mp4; codecs="avc1.42E01E"' },
{ contentType: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"' },
{ contentType: 'video/webm; codecs="vp8"' },
{ contentType: 'video/webm; codecs="vp9"' }
];

var basicConfig = {
videoCapabilities: basicVideoCapabilities
};
var configs = [basicConfig];
var support = { contentDecryptionModules: {}, playbackSupport: {} };

testKeySystems.forEach(function (keySystem) {
var p = navigator.requestMediaKeySystemAccess(keySystem, configs)
.then(function (access) {
var config = access.getConfiguration();
support.contentDecryptionModules[keySystem] = "AVAILABLE";
}, function () {
support.contentDecryptionModules[keySystem] = "NOT AVAILABLE";
});
tests.push(p);
});

return Promise.all(tests).then(function () {
var testEl = document.createElement("video"),
mpeg4, h264, ogg, webm;
testCodecs.forEach(function (testCodec) {
var canPlay = testEl.canPlayType(testCodec.contentType);
canPlay = canPlay !== "" ? canPlay : "no";
support.playbackSupport[testCodec.type] = { "contentType": testCodec.contentType, "supported": canPlay };
});
return support;
});
}

function printSupport(support) {
var output = document.getElementById('output');
output.textContent = support;
}

function doTest() {
probeSupport().then(function (support) {
printSupport(JSON.stringify(support, null, ' '));
});
}
</script>
</head>
<body onload="doTest()">
<pre id="output"></pre>
<p>
<a href="https://shaka-player-demo.appspot.com/demo/">Google Shaka Player demo - video play back</a>
</p>
<p>
<strong>Note:</strong><br />
<ul>
<li>
Support for Widevine CDM requires additional steps as
<a href="https://github.com/cefsharp/CefSharp/issues/1934">outlined here</a>.
</li>
<li>
Use of proprietary codecs (such as H.264) requires a custom build of CEF to be used due to
licensing requirements.<br />
Details on how to build the CEF project and other resources can be
<a href="https://bitbucket.org/chromiumembedded/cef">found here</a>.</li>
</ul>
</p>
</body>
</html>
1 change: 1 addition & 0 deletions CefSharp.Wpf.Example/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<MenuItem Header="_Adobe Flash Demo" Command="controls:CefSharpCommands.OpenTabCommand" CommandParameter="http://www.adobe.com/software/flash/about/"/>
<MenuItem Header="_Print Current tab to PDF" Command="controls:CefSharpCommands.PrintTabToPdfCommand" />
<MenuItem Header="_Load Custom Request" Command="controls:CefSharpCommands.CustomCommand" CommandParameter="CustomRequest" />
<MenuItem Header="_CDM/DRM Support Test" Command="controls:CefSharpCommands.OpenTabCommand" CommandParameter="{Binding Source={x:Static ex:CefExample.CdmSupportTestUrl}}"/>
</MenuItem>
</Menu>
<controls:NonReloadingTabControl x:Name="TabControl"
Expand Down
24 changes: 24 additions & 0 deletions CefSharp/CdmRegistration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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
{
/// <summary>
/// Represents the response to an attempt to register the Widevine Content Decryption Module (CDM)
/// </summary>
public class CdmRegistration
{
/// <summary>
/// If CDM registration succeeded then value will be <see cref="CdmRegistrationErrorCode.None"/>, for other values see the enumeration <see cref="CdmRegistrationErrorCode" />.
/// </summary>
public CdmRegistrationErrorCode ErrorCode { get; set; }

/// <summary>
/// Contains an error message containing additional information if <see cref="ErrorCode"/> is not <see cref="CdmRegistrationErrorCode.None"/>.
/// </summary>
public string ErrorMessage { get; set; }
}
}
34 changes: 34 additions & 0 deletions CefSharp/CdmRegistrationErrorCode.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Lists the errors that can be reported during Widevine Content Decryption Module (CDM) registration.
/// </summary>
public enum CdmRegistrationErrorCode
{
/// <summary>
/// No error. Registration completed successfully.
/// </summary>
None,

/// <summary>
/// Required files or manifest contents are missing.
/// </summary>
IncorrectContents,

/// <summary>
/// The CDM is incompatible with the current Chromium version.
/// </summary>
Incompatible,

/// <summary>
/// CDM registration is not supported at this time.
/// </summary>
NotSupported,
}
}
Loading

0 comments on commit 2298ea0

Please sign in to comment.