Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of Cef.RegisterWidevineCdm #1935

Merged
merged 6 commits into from Feb 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
72 changes: 71 additions & 1 deletion CefSharp.Core/Cef.h
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
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
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
@@ -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
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
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
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
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
@@ -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()">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be some sort of disclaimer here that explains widevine CDM requires additional steps to enable and propritary codes are not supported in the default Nuget packages as they require licensing.

What happens if you open this page with a default build?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Widevine not registered and not using proprietary codecs just says that widevine is "NOT AVAILABLE", and some of the codecs are listed as not supporting playback.

I'll add a note at the end of the page re proprietary codecs and contacting Widevine for access to widevine - and a link to the issue #1934.

<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
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
@@ -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
@@ -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,
}
}