blob: 9e3fbd1c56835be7305edbbbc91ce1d058833657 [file] [log] [blame]
// Copyright 2018 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/media/widevine_hardware_caps_win.h"
#include <comdef.h>
#include <d3d11_1.h>
#include <initguid.h>
#include <stdint.h>
#include <wrl/client.h>
#include <bitset>
#include "base/macros.h"
#include "base/stl_util.h"
#include "media/base/decrypt_config.h"
#include "media/media_buildflags.h"
namespace {
// Alias for printing HRESULT.
const auto PrintHr = logging::SystemErrorCodeToString;
// TODO(xhwang): Deduplicate this file and widevine_cdm_proxy_factory.cc.
// clang-format off
DEFINE_GUID(kD3DCryptoTypeIntelWidevine,
0x586e681, 0x4e14, 0x4133, 0x85, 0xe5, 0xa1, 0x4, 0x1f, 0x59, 0x9e, 0x26);
// clang-format on
// Bit indices for Intel Widevine hardware secure decryption capabilities.
//
// Encryption schemes are defined in ISO/IEC 23001-7, "Common encryption in ISO
// base media file format files". Version 1 refers to ISO/IEC 23001-7:2012.
// Version 3 refers to ISO/IEC 23001-7:2016. The difference that matters in this
// context is as follows:
// - In Version 1, section 9.5, "In full sample encryption, the entire sample is
// encrypted".
// - In Version 3, section 9.4.1, "Full sample encryption MAY be used for all
// encrypted media types other than NAL Structured video, which SHALL use
// Subsample encryption."
// Therefore with kCencVersion1, it is possible that an entire sample of NAL
// Structured video is encrypted. This is not allowed with kCencVersion3.
enum IntelWidevineCaps {
kSupported = 0,
kAesCtr = 8,
kAesCbc = 9,
kCencVersion1 = 10,
kCencVersion3 = 11,
kCbcs = 17,
};
struct CodecToD3D11DecoderProfile {
media::VideoCodec video_codec;
GUID d3d11_decoder_profile;
};
const CodecToD3D11DecoderProfile kCodecsToQuery[] = {
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
{media::VideoCodec::kCodecH264, D3D11_DECODER_PROFILE_H264_VLD_NOFGT},
#endif
{media::VideoCodec::kCodecVP9, D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0},
};
// Use |video_device| to help check whether |d3d11_decoder_profile| is supported
// with CENC.
bool IsD3D11DecoderProfileSupportedWithCenc(ID3D11VideoDevice* video_device,
const GUID& d3d11_decoder_profile) {
D3D11_VIDEO_CONTENT_PROTECTION_CAPS caps = {};
// Check whether kD3DCryptoTypeIntelWidevine is supported with |codec|.
auto hresult = video_device->GetContentProtectionCaps(
&kD3DCryptoTypeIntelWidevine, &d3d11_decoder_profile, &caps);
if (FAILED(hresult)) {
DVLOG(1) << "Failed to GetContentProtectionCaps: " << PrintHr(hresult);
return false;
}
// For kD3DCryptoTypeIntelWidevine, this is a bitmask of IntelWidevineCaps.
auto capability = std::bitset<64>(caps.ProtectedMemorySize);
DVLOG(1) << "Content protection caps: " << capability;
if (!capability.test(IntelWidevineCaps::kSupported)) {
DVLOG(1) << "Hardware secure decryption not supported";
return false;
}
if (!capability.test(IntelWidevineCaps::kAesCtr)) {
DVLOG(1) << "AES-CTR decryption not supported";
return false;
}
// Query for CENC.
// TODO(crbug.com/899984): There are contents encrypted with kCencVersion1 out
// there, so this check is not sufficient. Update this to check kCencVersion1.
if (!capability.test(IntelWidevineCaps::kCencVersion3)) {
DVLOG(1) << "CENC version 3 not supported";
return false;
}
DVLOG(1) << "Widevine hardware secure CENC-v3 decryption supported. CENC-v1 "
"playback may fail!";
return true;
}
} // namespace
void GetWidevineHardwareCaps(
const base::flat_set<media::CdmProxy::Protocol>& cdm_proxy_protocols,
base::flat_set<media::VideoCodec>* video_codecs,
base::flat_set<media::EncryptionMode>* encryption_schemes) {
DCHECK(!cdm_proxy_protocols.empty());
DCHECK(video_codecs->empty());
DCHECK(encryption_schemes->empty());
// We only support kD3DCryptoTypeIntelWidevine.
if (!cdm_proxy_protocols.count(media::CdmProxy::Protocol::kIntel)) {
DVLOG(1) << "CDM supported CdmProxy protocol not supported by the system";
return;
}
Microsoft::WRL::ComPtr<ID3D11Device> device;
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device;
// D3D11CdmProxy requires D3D_FEATURE_LEVEL_11_1.
const D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_1};
// Create device and populate |device|.
HRESULT hresult = D3D11CreateDevice(
nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, feature_levels,
base::size(feature_levels), D3D11_SDK_VERSION, device.GetAddressOf(),
nullptr, nullptr);
if (FAILED(hresult)) {
DVLOG(1) << "Failed to create the D3D11Device: " << PrintHr(hresult);
return;
}
hresult = device.CopyTo(video_device.GetAddressOf());
if (FAILED(hresult)) {
DVLOG(1) << "Failed to get ID3D11VideoDevice: " << PrintHr(hresult);
return;
}
// TODO(xhwang): Support query for CBCS. Maybe return all encryption schemes
// supported by a codec.
for (const auto& entry : kCodecsToQuery) {
if (IsD3D11DecoderProfileSupportedWithCenc(video_device.Get(),
entry.d3d11_decoder_profile)) {
video_codecs->insert(entry.video_codec);
}
}
if (!video_codecs->empty())
encryption_schemes->insert(media::EncryptionMode::kCenc);
}