blob: d9c7b087745ff07e30eb0b411cdf1c28d3d22da0 [file] [log] [blame]
// Copyright 2020 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 "media/renderers/win/media_foundation_protection_manager.h"
#include <mferror.h>
#include <windows.foundation.h>
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/win/core_winrt_util.h"
#include "base/win/scoped_hstring.h"
#include "base/win/windows_types.h"
#include "media/base/win/mf_helpers.h"
namespace media {
using Microsoft::WRL::ComPtr;
MediaFoundationProtectionManager::MediaFoundationProtectionManager() {
DVLOG_FUNC(1);
}
MediaFoundationProtectionManager::~MediaFoundationProtectionManager() {
DVLOG_FUNC(1);
}
HRESULT MediaFoundationProtectionManager::RuntimeClassInitialize(
scoped_refptr<base::SequencedTaskRunner> task_runner,
WaitingCB waiting_cb) {
DVLOG_FUNC(1);
task_runner_ = std::move(task_runner);
waiting_cb_ = std::move(waiting_cb);
if (!base::win::ScopedHString::ResolveCoreWinRTStringDelayload())
return E_FAIL;
// Init an empty |property_set_| as MFMediaEngine could access it via
// |get_Properties| before we populate it within SetPMPServer.
base::win::ScopedHString property_set_id = base::win::ScopedHString::Create(
RuntimeClass_Windows_Foundation_Collections_PropertySet);
RETURN_IF_FAILED(
base::win::RoActivateInstance(property_set_id.get(), &property_set_));
return S_OK;
}
HRESULT MediaFoundationProtectionManager::SetCdmProxy(
scoped_refptr<MediaFoundationCdmProxy> cdm_proxy) {
DVLOG_FUNC(1);
DCHECK(cdm_proxy);
cdm_proxy_ = std::move(cdm_proxy);
ComPtr<ABI::Windows::Media::Protection::IMediaProtectionPMPServer> pmp_server;
RETURN_IF_FAILED(cdm_proxy_->GetPMPServer(IID_PPV_ARGS(&pmp_server)));
RETURN_IF_FAILED(SetPMPServer(pmp_server.Get()));
return S_OK;
}
HRESULT MediaFoundationProtectionManager::SetPMPServer(
ABI::Windows::Media::Protection::IMediaProtectionPMPServer* pmp_server) {
DVLOG_FUNC(1);
DCHECK(pmp_server);
ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>>
property_map;
RETURN_IF_FAILED(property_set_.As(&property_map));
// MFMediaEngine uses |pmp_server_key| to get the Protected Media Path (PMP)
// server used for playing protected content. This is not currently documented
// in MSDN.
boolean replaced = false;
base::win::ScopedHString pmp_server_key = base::win::ScopedHString::Create(
L"Windows.Media.Protection.MediaProtectionPMPServer");
RETURN_IF_FAILED(
property_map->Insert(pmp_server_key.get(), pmp_server, &replaced));
return S_OK;
}
HRESULT MediaFoundationProtectionManager::BeginEnableContent(
IMFActivate* enabler_activate,
IMFTopology* topology,
IMFAsyncCallback* callback,
IUnknown* state) {
DVLOG_FUNC(1);
ComPtr<IUnknown> unknown_object;
ComPtr<IMFAsyncResult> async_result;
RETURN_IF_FAILED(
MFCreateAsyncResult(nullptr, callback, state, &async_result));
RETURN_IF_FAILED(
enabler_activate->ActivateObject(IID_PPV_ARGS(&unknown_object)));
// |enabler_type| can be obtained from IMFContentEnabler
// (https://docs.microsoft.com/en-us/windows/win32/api/mfidl/nn-mfidl-imfcontentenabler).
// If not, try IMediaProtectionServiceRequest
// (https://docs.microsoft.com/en-us/uwp/api/windows.media.protection.imediaprotectionservicerequest).
GUID enabler_type = GUID_NULL;
ComPtr<IMFContentEnabler> content_enabler;
if (SUCCEEDED(unknown_object.As(&content_enabler))) {
RETURN_IF_FAILED(content_enabler->GetEnableType(&enabler_type));
} else {
ComPtr<ABI::Windows::Media::Protection::IMediaProtectionServiceRequest>
service_request;
RETURN_IF_FAILED(unknown_object.As(&service_request));
RETURN_IF_FAILED(service_request->get_Type(&enabler_type));
}
if (enabler_type == MFENABLETYPE_MF_RebootRequired) {
DLOG(ERROR) << __func__ << ": MF_E_REBOOT_REQUIRED";
return MF_E_REBOOT_REQUIRED;
} else if (enabler_type == MFENABLETYPE_MF_UpdateRevocationInformation) {
DLOG(ERROR) << __func__ << ": MF_E_GRL_VERSION_TOO_LOW";
return MF_E_GRL_VERSION_TOO_LOW;
} else if (enabler_type == MFENABLETYPE_MF_UpdateUntrustedComponent) {
auto hr = HRESULT_FROM_WIN32(ERROR_INVALID_IMAGE_HASH);
DLOG(ERROR) << __func__ << ": hr=" << hr;
return hr;
} else {
RETURN_IF_FAILED(cdm_proxy_->ProcessContentEnabler(unknown_object.Get(),
async_result.Get()));
// Force post task so `OnBeginEnableContent()` and `OnEndEnableContent()`
// are always in sequence.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MediaFoundationProtectionManager::OnBeginEnableContent,
weak_factory_.GetWeakPtr()));
}
return S_OK;
}
HRESULT MediaFoundationProtectionManager::EndEnableContent(
IMFAsyncResult* async_result) {
DVLOG_FUNC(1);
// Force post task so `OnBeginEnableContent()` and `OnEndEnableContent()` are
// always in sequence.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MediaFoundationProtectionManager::OnEndEnableContent,
weak_factory_.GetWeakPtr()));
// Get status from the given |async_result| for the purpose of logging.
// Returns S_OK as there is no additional work being done here.
HRESULT async_status = async_result->GetStatus();
if (FAILED(async_status)) {
DLOG(ERROR) << "Content enabling failed. hr=" << async_status;
} else {
DVLOG(2) << "Content enabling succeeded";
}
return S_OK;
}
// IMediaProtectionManager implementation
HRESULT MediaFoundationProtectionManager::add_ServiceRequested(
ABI::Windows::Media::Protection::IServiceRequestedEventHandler* handler,
EventRegistrationToken* cookie) {
return E_NOTIMPL;
}
HRESULT MediaFoundationProtectionManager::remove_ServiceRequested(
EventRegistrationToken cookie) {
return E_NOTIMPL;
}
HRESULT MediaFoundationProtectionManager::add_RebootNeeded(
ABI::Windows::Media::Protection::IRebootNeededEventHandler* handler,
EventRegistrationToken* cookie) {
return E_NOTIMPL;
}
HRESULT MediaFoundationProtectionManager::remove_RebootNeeded(
EventRegistrationToken cookie) {
return E_NOTIMPL;
}
HRESULT MediaFoundationProtectionManager::add_ComponentLoadFailed(
ABI::Windows::Media::Protection::IComponentLoadFailedEventHandler* handler,
EventRegistrationToken* cookie) {
return E_NOTIMPL;
}
HRESULT MediaFoundationProtectionManager::remove_ComponentLoadFailed(
EventRegistrationToken cookie) {
return E_NOTIMPL;
}
HRESULT MediaFoundationProtectionManager::get_Properties(
ABI::Windows::Foundation::Collections::IPropertySet** properties) {
DVLOG_FUNC(2);
if (!properties)
return E_POINTER;
return property_set_.CopyTo(properties);
}
void MediaFoundationProtectionManager::OnBeginEnableContent() {
DVLOG_FUNC(2);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
// If EnableContent takes too long, report waiting for key status. Choose a
// timeout of 500ms to be on the safe side, e.g. on slower machines.
const auto kWaitingForKeyTimeOut = base::Milliseconds(500);
waiting_for_key_time_out_cb_.Reset(
base::BindOnce(&MediaFoundationProtectionManager::OnWaitingForKeyTimeOut,
weak_factory_.GetWeakPtr()));
task_runner_->PostDelayedTask(FROM_HERE,
waiting_for_key_time_out_cb_.callback(),
kWaitingForKeyTimeOut);
}
void MediaFoundationProtectionManager::OnEndEnableContent() {
DVLOG_FUNC(2);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
waiting_for_key_time_out_cb_.Cancel();
}
void MediaFoundationProtectionManager::OnWaitingForKeyTimeOut() {
DVLOG_FUNC(2);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
waiting_for_key_time_out_cb_.Cancel();
waiting_cb_.Run(WaitingReason::kNoDecryptionKey);
}
} // namespace media