123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672 |
- // 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/cdm/win/media_foundation_cdm.h"
- #include <mferror.h>
- #include <stdlib.h>
- #include <vector>
- #include "base/bind.h"
- #include "base/logging.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/strings/stringprintf.h"
- #include "base/win/scoped_co_mem.h"
- #include "base/win/scoped_propvariant.h"
- #include "base/win/win_util.h"
- #include "base/win/windows_version.h"
- #include "media/base/cdm_promise.h"
- #include "media/base/win/media_foundation_cdm_proxy.h"
- #include "media/base/win/mf_helpers.h"
- #include "media/cdm/win/media_foundation_cdm_module.h"
- #include "media/cdm/win/media_foundation_cdm_session.h"
- namespace media {
- namespace {
- using Microsoft::WRL::ClassicCom;
- using Microsoft::WRL::ComPtr;
- using Microsoft::WRL::Make;
- using Microsoft::WRL::RuntimeClass;
- using Microsoft::WRL::RuntimeClassFlags;
- using Exception = CdmPromise::Exception;
- HRESULT CreatePolicySetEvent(ComPtr<IMFMediaEvent>& policy_set_event) {
- base::win::ScopedPropVariant policy_set_prop;
- PROPVARIANT* var_to_set = policy_set_prop.Receive();
- var_to_set->vt = VT_UI4;
- var_to_set->ulVal = 0;
- RETURN_IF_FAILED(MFCreateMediaEvent(
- MEPolicySet, GUID_NULL, S_OK, policy_set_prop.ptr(), &policy_set_event));
- return S_OK;
- }
- // Notifies the Decryptor about the last key ID so the decryptor can prefetch
- // the corresponding key to reduce start-to-play time when resuming playback.
- // This is done by sending a MEContentProtectionMetadata event.
- HRESULT RefreshDecryptor(IMFTransform* decryptor,
- const GUID& protection_system_id,
- const GUID& last_key_id) {
- // The MFT_MESSAGE_NOTIFY_START_OF_STREAM message is usually sent by the MF
- // pipeline when starting playback. Here we send it out-of-band as it is a
- // pre-requisite for getting the decryptor to process the
- // MEContentProtectionMetadata event.
- RETURN_IF_FAILED(
- decryptor->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0));
- // After receiving a MEContentProtectionMetadata event, the Decryptor
- // requires that it is notified of a MEPolicySet event to continue decryption.
- ComPtr<IMFMediaEvent> policy_set_event;
- RETURN_IF_FAILED(CreatePolicySetEvent(policy_set_event));
- RETURN_IF_FAILED(decryptor->ProcessMessage(
- MFT_MESSAGE_NOTIFY_EVENT,
- reinterpret_cast<ULONG_PTR>(policy_set_event.Get())));
- // Prepare the MEContentProtectionMetadata event.
- ComPtr<IMFMediaEvent> key_rotation_event;
- RETURN_IF_FAILED(MFCreateMediaEvent(MEContentProtectionMetadata, GUID_NULL,
- S_OK, nullptr, &key_rotation_event));
- // MF_EVENT_STREAM_METADATA_SYSTEMID expects the system ID (GUID) to be in
- // little endian order. So no need to call `ByteArrayFromGUID()`.
- RETURN_IF_FAILED(key_rotation_event->SetBlob(
- MF_EVENT_STREAM_METADATA_SYSTEMID,
- reinterpret_cast<const uint8_t*>(&protection_system_id), sizeof(GUID)));
- std::vector<uint8_t> last_key_id_byte_array = ByteArrayFromGUID(last_key_id);
- RETURN_IF_FAILED(key_rotation_event->SetBlob(
- MF_EVENT_STREAM_METADATA_CONTENT_KEYIDS, last_key_id_byte_array.data(),
- last_key_id_byte_array.size()));
- // The `dwInputStreamID` refers to a local stream ID of the Decryptor. Since
- // Decryptors typically only support a single stream, always pass 0 here.
- RETURN_IF_FAILED(
- decryptor->ProcessEvent(/*dwInputStreamID=*/0, key_rotation_event.Get()));
- return S_OK;
- }
- // The HDCP value follows the feature value in
- // https://docs.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilities.istypesupported?view=winrt-19041
- // - 0 (off)
- // - 1 (on without HDCP 2.2 Type 1 restriction)
- // - 2 (on with HDCP 2.2 Type 1 restriction)
- int GetHdcpValue(HdcpVersion hdcp_version) {
- switch (hdcp_version) {
- case HdcpVersion::kHdcpVersionNone:
- return 0;
- case HdcpVersion::kHdcpVersion1_0:
- case HdcpVersion::kHdcpVersion1_1:
- case HdcpVersion::kHdcpVersion1_2:
- case HdcpVersion::kHdcpVersion1_3:
- case HdcpVersion::kHdcpVersion1_4:
- case HdcpVersion::kHdcpVersion2_0:
- case HdcpVersion::kHdcpVersion2_1:
- return 1;
- case HdcpVersion::kHdcpVersion2_2:
- case HdcpVersion::kHdcpVersion2_3:
- return 2;
- }
- }
- class CdmProxyImpl : public MediaFoundationCdmProxy {
- public:
- CdmProxyImpl(ComPtr<IMFContentDecryptionModule> mf_cdm,
- base::RepeatingClosure hardware_context_reset_cb,
- MediaFoundationCdm::CdmEventCB cdm_event_cb)
- : mf_cdm_(mf_cdm),
- hardware_context_reset_cb_(std::move(hardware_context_reset_cb)),
- cdm_event_cb_(std::move(cdm_event_cb)) {}
- // MediaFoundationCdmProxy implementation
- HRESULT GetPMPServer(REFIID riid, LPVOID* object_result) override {
- DVLOG_FUNC(1);
- ComPtr<IMFGetService> cdm_services;
- RETURN_IF_FAILED(mf_cdm_.As(&cdm_services));
- RETURN_IF_FAILED(cdm_services->GetService(
- MF_CONTENTDECRYPTIONMODULE_SERVICE, riid, object_result));
- return S_OK;
- }
- HRESULT GetInputTrustAuthority(uint32_t stream_id,
- uint32_t /*stream_count*/,
- const uint8_t* content_init_data,
- uint32_t content_init_data_size,
- REFIID riid,
- IUnknown** object_out) override {
- DVLOG_FUNC(1);
- if (input_trust_authorities_.count(stream_id)) {
- RETURN_IF_FAILED(input_trust_authorities_[stream_id].CopyTo(object_out));
- return S_OK;
- }
- if (!trusted_input_) {
- RETURN_IF_FAILED(mf_cdm_->CreateTrustedInput(
- content_init_data, content_init_data_size, &trusted_input_));
- }
- // GetInputTrustAuthority takes IUnknown* as the output. Using other COM
- // interface will have a v-table mismatch issue.
- ComPtr<IUnknown> unknown;
- RETURN_IF_FAILED(
- trusted_input_->GetInputTrustAuthority(stream_id, riid, &unknown));
- ComPtr<IMFInputTrustAuthority> input_trust_authority;
- RETURN_IF_FAILED(unknown.As(&input_trust_authority));
- RETURN_IF_FAILED(unknown.CopyTo(object_out));
- // Success! Store ITA in the map.
- input_trust_authorities_[stream_id] = input_trust_authority;
- return S_OK;
- }
- HRESULT SetLastKeyId(uint32_t stream_id, REFGUID key_id) override {
- DVLOG_FUNC(1);
- last_key_ids_[stream_id] = key_id;
- return S_OK;
- }
- HRESULT RefreshTrustedInput() override {
- DVLOG_FUNC(1);
- // Refresh all decryptors of the last key IDs.
- for (const auto& entry : input_trust_authorities_) {
- const auto& stream_id = entry.first;
- const auto& input_trust_authority = entry.second;
- const auto& last_key_id = last_key_ids_[stream_id];
- if (last_key_id == GUID_NULL)
- continue;
- ComPtr<IMFTransform> decryptor;
- RETURN_IF_FAILED(
- input_trust_authority->GetDecrypter(IID_PPV_ARGS(&decryptor)));
- GUID protection_system_id;
- RETURN_IF_FAILED(GetProtectionSystemId(&protection_system_id));
- RETURN_IF_FAILED(
- RefreshDecryptor(decryptor.Get(), protection_system_id, last_key_id));
- }
- input_trust_authorities_.clear();
- last_key_ids_.clear();
- return S_OK;
- }
- HRESULT
- ProcessContentEnabler(IUnknown* request, IMFAsyncResult* result) override {
- DVLOG_FUNC(1);
- ComPtr<IMFContentEnabler> content_enabler;
- RETURN_IF_FAILED(request->QueryInterface(IID_PPV_ARGS(&content_enabler)));
- return mf_cdm_->SetContentEnabler(content_enabler.Get(), result);
- }
- void OnHardwareContextReset() override {
- // Hardware context reset happens, all the crypto sessions are in invalid
- // states. So drop everything here.
- // TODO(xhwang): Keep the `last_key_ids_` here for faster resume.
- trusted_input_.Reset();
- input_trust_authorities_.clear();
- last_key_ids_.clear();
- // Must be the last call because `this` could be destructed when running
- // the callback. We are not certain because `this` is ref-counted.
- hardware_context_reset_cb_.Run();
- }
- void OnSignificantPlayback() override {
- cdm_event_cb_.Run(CdmEvent::kSignificantPlayback);
- }
- void OnPlaybackError() override {
- cdm_event_cb_.Run(CdmEvent::kPlaybackError);
- }
- private:
- ~CdmProxyImpl() override = default;
- HRESULT GetProtectionSystemId(GUID* protection_system_id) {
- // Typically the CDM should only return one protection system ID. So just
- // use the first one if available.
- base::win::ScopedCoMem<GUID> protection_system_ids;
- DWORD count = 0;
- RETURN_IF_FAILED(
- mf_cdm_->GetProtectionSystemIds(&protection_system_ids, &count));
- if (count == 0)
- return E_FAIL;
- *protection_system_id = *protection_system_ids;
- DVLOG(2) << __func__ << " protection_system_id="
- << base::win::WStringFromGUID(*protection_system_id);
- return S_OK;
- }
- ComPtr<IMFContentDecryptionModule> mf_cdm_;
- // Callbacks to notify hardware context reset and playback error.
- base::RepeatingClosure hardware_context_reset_cb_;
- MediaFoundationCdm::CdmEventCB cdm_event_cb_;
- // Store IMFTrustedInput to avoid potential performance cost.
- ComPtr<IMFTrustedInput> trusted_input_;
- // |stream_id| to IMFInputTrustAuthority (ITA) mapping. Serves two purposes:
- // 1. The same ITA should always be returned in GetInputTrustAuthority() for
- // the same |stream_id|.
- // 2. The ITA must keep alive for decryptors to work.
- std::map<uint32_t, ComPtr<IMFInputTrustAuthority>> input_trust_authorities_;
- // |stream_id| to last used key ID mapping.
- std::map<uint32_t, GUID> last_key_ids_;
- };
- } // namespace
- // static
- bool MediaFoundationCdm::IsAvailable() {
- return base::win::GetVersion() >= base::win::Version::WIN10_20H1;
- }
- MediaFoundationCdm::MediaFoundationCdm(
- const std::string& uma_prefix,
- const CreateMFCdmCB& create_mf_cdm_cb,
- const IsTypeSupportedCB& is_type_supported_cb,
- const StoreClientTokenCB& store_client_token_cb,
- const CdmEventCB& cdm_event_cb,
- const SessionMessageCB& session_message_cb,
- const SessionClosedCB& session_closed_cb,
- const SessionKeysChangeCB& session_keys_change_cb,
- const SessionExpirationUpdateCB& session_expiration_update_cb)
- : uma_prefix_(uma_prefix),
- create_mf_cdm_cb_(create_mf_cdm_cb),
- is_type_supported_cb_(is_type_supported_cb),
- store_client_token_cb_(store_client_token_cb),
- cdm_event_cb_(cdm_event_cb),
- session_message_cb_(session_message_cb),
- session_closed_cb_(session_closed_cb),
- session_keys_change_cb_(session_keys_change_cb),
- session_expiration_update_cb_(session_expiration_update_cb) {
- DVLOG_FUNC(1);
- DCHECK(!uma_prefix_.empty());
- DCHECK(create_mf_cdm_cb_);
- DCHECK(is_type_supported_cb_);
- DCHECK(session_message_cb_);
- DCHECK(session_closed_cb_);
- DCHECK(session_keys_change_cb_);
- DCHECK(session_expiration_update_cb_);
- }
- MediaFoundationCdm::~MediaFoundationCdm() {
- DVLOG_FUNC(1);
- }
- HRESULT MediaFoundationCdm::Initialize() {
- HRESULT hresult = E_FAIL;
- ComPtr<IMFContentDecryptionModule> mf_cdm;
- create_mf_cdm_cb_.Run(hresult, mf_cdm);
- if (!mf_cdm) {
- DCHECK(FAILED(hresult));
- // Only report CdmEvent::kCdmError here as this is where most failures
- // happen, and other errors can be easily triggered by sites, e.g. a bad
- // server certificate or a bad license.
- OnCdmEvent(CdmEvent::kCdmError);
- return hresult;
- }
- mf_cdm_.Swap(mf_cdm);
- return S_OK;
- }
- void MediaFoundationCdm::SetServerCertificate(
- const std::vector<uint8_t>& certificate,
- std::unique_ptr<SimpleCdmPromise> promise) {
- DVLOG_FUNC(1);
- if (!mf_cdm_) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "CDM Unavailable");
- return;
- }
- auto hr =
- mf_cdm_->SetServerCertificate(certificate.data(), certificate.size());
- base::UmaHistogramSparse(uma_prefix_ + "SetServerCertificate", hr);
- if (FAILED(hr)) {
- promise->reject(Exception::NOT_SUPPORTED_ERROR, 0, "Failed to set cert");
- return;
- }
- promise->resolve();
- }
- void MediaFoundationCdm::GetStatusForPolicy(
- HdcpVersion min_hdcp_version,
- std::unique_ptr<KeyStatusCdmPromise> promise) {
- if (!mf_cdm_) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "CDM Unavailable");
- return;
- }
- // Keys should be always usable when there is no HDCP requirement.
- if (min_hdcp_version == HdcpVersion::kHdcpVersionNone) {
- promise->resolve(CdmKeyInformation::KeyStatus::USABLE);
- return;
- }
- // HDCP is independent to the codec. So query H.264, which is always supported
- // by MFCDM.
- const std::string content_type =
- base::StringPrintf("video/mp4;codecs=\"avc1\";features=\"hdcp=%d\"",
- GetHdcpValue(min_hdcp_version));
- is_type_supported_cb_.Run(
- content_type,
- base::BindOnce(&MediaFoundationCdm::OnIsTypeSupportedResult,
- weak_factory_.GetWeakPtr(), std::move(promise)));
- }
- void MediaFoundationCdm::CreateSessionAndGenerateRequest(
- CdmSessionType session_type,
- EmeInitDataType init_data_type,
- const std::vector<uint8_t>& init_data,
- std::unique_ptr<NewSessionCdmPromise> promise) {
- DVLOG_FUNC(1);
- if (!mf_cdm_) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "CDM Unavailable");
- return;
- }
- // TODO(xhwang): Implement session expiration update.
- auto session = std::make_unique<MediaFoundationCdmSession>(
- uma_prefix_, session_message_cb_, session_keys_change_cb_,
- session_expiration_update_cb_);
- if (FAILED(session->Initialize(mf_cdm_.Get(), session_type))) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0,
- "Failed to create session");
- return;
- }
- int session_token = next_session_token_++;
- // Keep a raw pointer since the |promise| will be moved to the callback.
- // Use base::Unretained() is safe because |session| is owned by |this|.
- auto* raw_promise = promise.get();
- auto session_id_cb =
- base::BindOnce(&MediaFoundationCdm::OnSessionId, base::Unretained(this),
- session_token, std::move(promise));
- if (FAILED(session->GenerateRequest(init_data_type, init_data,
- std::move(session_id_cb)))) {
- raw_promise->reject(Exception::INVALID_STATE_ERROR, 0, "Init failure");
- return;
- }
- pending_sessions_.emplace(session_token, std::move(session));
- }
- void MediaFoundationCdm::LoadSession(
- CdmSessionType session_type,
- const std::string& session_id,
- std::unique_ptr<NewSessionCdmPromise> promise) {
- DVLOG_FUNC(1);
- if (!mf_cdm_) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "CDM Unavailable");
- return;
- }
- NOTIMPLEMENTED();
- promise->reject(Exception::NOT_SUPPORTED_ERROR, 0, "Load not supported");
- }
- void MediaFoundationCdm::UpdateSession(
- const std::string& session_id,
- const std::vector<uint8_t>& response,
- std::unique_ptr<SimpleCdmPromise> promise) {
- DVLOG_FUNC(1);
- if (!mf_cdm_) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "CDM Unavailable");
- return;
- }
- auto* session = GetSession(session_id);
- if (!session) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "Session not found");
- return;
- }
- if (FAILED(session->Update(response))) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "Update failed");
- return;
- }
- // Failure to store the client token will not prevent the CDM from correctly
- // functioning.
- StoreClientTokenIfNeeded();
- promise->resolve();
- }
- void MediaFoundationCdm::CloseSession(
- const std::string& session_id,
- std::unique_ptr<SimpleCdmPromise> promise) {
- DVLOG_FUNC(1);
- CloseSessionInternal(session_id, CdmSessionClosedReason::kClose,
- std::move(promise));
- }
- void MediaFoundationCdm::RemoveSession(
- const std::string& session_id,
- std::unique_ptr<SimpleCdmPromise> promise) {
- DVLOG_FUNC(1);
- if (!mf_cdm_) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "CDM Unavailable");
- return;
- }
- auto* session = GetSession(session_id);
- if (!session) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "Session not found");
- return;
- }
- if (FAILED(session->Remove())) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "Remove failed");
- return;
- }
- promise->resolve();
- }
- CdmContext* MediaFoundationCdm::GetCdmContext() {
- return this;
- }
- bool MediaFoundationCdm::RequiresMediaFoundationRenderer() {
- return true;
- }
- scoped_refptr<MediaFoundationCdmProxy>
- MediaFoundationCdm::GetMediaFoundationCdmProxy() {
- DVLOG_FUNC(1);
- if (!mf_cdm_) {
- DLOG(ERROR) << __func__ << ": Invalid state with null `mf_cdm_`";
- return nullptr;
- }
- if (!cdm_proxy_) {
- cdm_proxy_ = base::MakeRefCounted<CdmProxyImpl>(
- mf_cdm_,
- base::BindRepeating(&MediaFoundationCdm::OnHardwareContextReset,
- weak_factory_.GetWeakPtr()),
- base::BindRepeating(&MediaFoundationCdm::OnCdmEvent,
- weak_factory_.GetWeakPtr()));
- }
- return cdm_proxy_;
- }
- bool MediaFoundationCdm::OnSessionId(
- int session_token,
- std::unique_ptr<NewSessionCdmPromise> promise,
- const std::string& session_id) {
- DVLOG_FUNC(1) << "session_token=" << session_token
- << ", session_id=" << session_id;
- auto itr = pending_sessions_.find(session_token);
- DCHECK(itr != pending_sessions_.end());
- auto session = std::move(itr->second);
- DCHECK(session);
- pending_sessions_.erase(itr);
- if (session_id.empty() || sessions_.count(session_id)) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0,
- "Empty or duplicate session ID");
- return false;
- }
- sessions_.emplace(session_id, std::move(session));
- promise->resolve(session_id);
- return true;
- }
- MediaFoundationCdmSession* MediaFoundationCdm::GetSession(
- const std::string& session_id) {
- auto itr = sessions_.find(session_id);
- if (itr == sessions_.end())
- return nullptr;
- return itr->second.get();
- }
- void MediaFoundationCdm::CloseSessionInternal(
- const std::string& session_id,
- CdmSessionClosedReason reason,
- std::unique_ptr<SimpleCdmPromise> promise) {
- DVLOG_FUNC(1);
- if (!mf_cdm_) {
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "CDM Unavailable");
- return;
- }
- // Validate that this is a reference to an open session. close() shouldn't
- // be called if the session is already closed. However, the operation is
- // asynchronous, so there is a window where close() was called a second time
- // just before the closed event arrives. As a result it is possible that the
- // session is already closed, so assume that the session is closed if it
- // doesn't exist. https://github.com/w3c/encrypted-media/issues/365.
- //
- // close() is called from a MediaKeySession object, so it is unlikely that
- // this method will be called with a previously unseen |session_id|.
- auto* session = GetSession(session_id);
- if (!session) {
- promise->resolve();
- return;
- }
- if (FAILED(session->Close())) {
- sessions_.erase(session_id);
- promise->reject(Exception::INVALID_STATE_ERROR, 0, "Close failed");
- return;
- }
- // EME requires running session closed algorithm before resolving the
- // promise.
- sessions_.erase(session_id);
- session_closed_cb_.Run(session_id, reason);
- promise->resolve();
- }
- // When hardware context is reset, all sessions are in a bad state. Close all
- // the sessions and hopefully the player will create new sessions to resume.
- void MediaFoundationCdm::OnHardwareContextReset() {
- DVLOG_FUNC(1);
- // Collect all the session IDs to avoid iterating the map while we delete
- // entries in the map (in `CloseSession()`).
- std::vector<std::string> session_ids;
- for (const auto& s : sessions_)
- session_ids.push_back(s.first);
- for (const auto& session_id : session_ids) {
- CloseSessionInternal(session_id,
- CdmSessionClosedReason::kHardwareContextReset,
- std::make_unique<DoNothingCdmPromise<>>());
- }
- cdm_proxy_.reset();
- // Reset IMFContentDecryptionModule which also holds the old ITA.
- mf_cdm_.Reset();
- // Recreates IMFContentDecryptionModule so we can create new sessions.
- if (FAILED(Initialize())) {
- DLOG(ERROR) << __func__ << ": Re-initialization failed";
- DCHECK(!mf_cdm_);
- }
- }
- void MediaFoundationCdm::OnCdmEvent(CdmEvent event) {
- DVLOG_FUNC(1);
- cdm_event_cb_.Run(event);
- }
- void MediaFoundationCdm::OnIsTypeSupportedResult(
- std::unique_ptr<KeyStatusCdmPromise> promise,
- bool is_supported) {
- if (is_supported) {
- promise->resolve(CdmKeyInformation::KeyStatus::USABLE);
- } else {
- promise->resolve(CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED);
- }
- }
- void MediaFoundationCdm::StoreClientTokenIfNeeded() {
- DVLOG_FUNC(1);
- ComPtr<IMFAttributes> attributes;
- if (FAILED(mf_cdm_.As(&attributes))) {
- DLOG(ERROR) << "Failed to access the CDM's IMFAttribute store";
- return;
- }
- base::win::ScopedCoMem<uint8_t> client_token;
- uint32_t client_token_size;
- HRESULT hr = attributes->GetAllocatedBlob(
- EME_CONTENTDECRYPTIONMODULE_CLIENT_TOKEN.fmtid, &client_token,
- &client_token_size);
- if (FAILED(hr)) {
- if (hr != MF_E_ATTRIBUTENOTFOUND)
- DLOG(ERROR) << "Failed to get the client token blob. hr=" << hr;
- return;
- }
- DVLOG(2) << "Got client token of size " << client_token_size;
- std::vector<uint8_t> client_token_vector;
- client_token_vector.assign(client_token.get(),
- client_token.get() + client_token_size);
- // The store operation is cross-process so only run it if we have a new
- // client token.
- if (client_token_vector == cached_client_token_)
- return;
- cached_client_token_ = client_token_vector;
- store_client_token_cb_.Run(cached_client_token_);
- }
- } // namespace media
|