diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc index 1e22e5b2afb1..6e160181d052 100644 --- a/content/renderer/media/media_factory.cc +++ b/content/renderer/media/media_factory.cc @@ -92,6 +92,8 @@ #include "media/cdm/fuchsia/fuchsia_cdm_factory.h" #include "media/fuchsia/video/fuchsia_decoder_factory.h" #include "media/mojo/clients/mojo_fuchsia_cdm_provider.h" +#elif BUILDFLAG(USE_STARBOARD_MEDIA) +#include "media/starboard/starboard_cdm_factory.h" #elif BUILDFLAG(ENABLE_MOJO_CDM) #include "media/mojo/clients/mojo_cdm_factory.h" // nogncheck #else @@ -855,6 +857,8 @@ media::CdmFactory* MediaFactory::GetCdmFactory() { DCHECK(interface_broker_); cdm_factory_ = std::make_unique( std::make_unique(interface_broker_)); +#elif BUILDFLAG(USE_STARBOARD_MEDIA) + cdm_factory_ = std::make_unique(); #elif BUILDFLAG(ENABLE_MOJO_CDM) cdm_factory_ = std::make_unique(GetMediaInterfaceFactory()); diff --git a/content/renderer/media/media_permission_dispatcher.cc b/content/renderer/media/media_permission_dispatcher.cc index 5bf380abf477..e79a4f13b1f3 100644 --- a/content/renderer/media/media_permission_dispatcher.cc +++ b/content/renderer/media/media_permission_dispatcher.cc @@ -149,8 +149,12 @@ void MediaPermissionDispatcher::OnPermissionStatus( PermissionStatusCB permission_status_cb = std::move(iter->second); requests_.erase(iter); +#if BUILDFLAG(USE_STARBOARD_MEDIA) + std::move(permission_status_cb).Run(true); +#else // BUILDFLAG(USE_STARBOARD_MEDIA) std::move(permission_status_cb) .Run(status == blink::mojom::PermissionStatus::GRANTED); +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) } void MediaPermissionDispatcher::OnConnectionError() { diff --git a/content/shell/renderer/shell_content_renderer_client.cc b/content/shell/renderer/shell_content_renderer_client.cc index ccc81de3d2ba..2f7d6c25e69c 100644 --- a/content/shell/renderer/shell_content_renderer_client.cc +++ b/content/shell/renderer/shell_content_renderer_client.cc @@ -40,6 +40,11 @@ #include "third_party/blink/public/web/web_view.h" #include "v8/include/v8.h" +#if BUILDFLAG(USE_STARBOARD_MEDIA) +#include "starboard/media.h" +#include "components/cdm/renderer/widevine_key_system_info.h" +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + #if BUILDFLAG(ENABLE_PLUGINS) #include "ppapi/shared_impl/ppapi_switches.h" // nogncheck #endif @@ -49,10 +54,6 @@ #include "media/base/media_switches.h" #endif -#if BUILDFLAG(USE_STARBOARD_MEDIA) -#include "starboard/media.h" -#endif // BUILDFLAG(USE_STARBOARD_MEDIA) - namespace content { namespace { @@ -270,6 +271,60 @@ ShellContentRendererClient::CreateURLLoaderThrottleProvider( return std::make_unique(); } +#if BUILDFLAG(USE_STARBOARD_MEDIA) +media::SupportedCodecs GetStarboardEmeSupportedCodecs() { + media::SupportedCodecs codecs = ::media::EME_CODEC_AAC | + ::media::EME_CODEC_AVC1 | + ::media::EME_CODEC_VP9_PROFILE0 | + ::media::EME_CODEC_VP9_PROFILE2 | + ::media::EME_CODEC_VP8 | + ::media::EME_CODEC_OPUS | + ::media::EME_CODEC_VORBIS | + ::media::EME_CODEC_MPEG_H_AUDIO | + ::media::EME_CODEC_FLAC | + ::media::EME_CODEC_HEVC_PROFILE_MAIN | + ::media::EME_CODEC_HEVC_PROFILE_MAIN10 | + ::media::EME_CODEC_AV1 | + ::media::EME_CODEC_AC3 | + ::media::EME_CODEC_EAC3; + return codecs; +} + +void AddStarboardCmaKeySystems(::media::KeySystemInfos* key_system_infos) { + media::SupportedCodecs codecs = GetStarboardEmeSupportedCodecs(); + +#if BUILDFLAG(IS_ANDROID) + using Robustness = cdm::WidevineKeySystemInfo::Robustness; + + const base::flat_set kEncryptionSchemes = { + media::EncryptionScheme::kCenc, media::EncryptionScheme::kCbcs}; + + const base::flat_set kSessionTypes = { + media::CdmSessionType::kTemporary}; + + key_system_infos->emplace_back(new cdm::WidevineKeySystemInfo( + codecs, // Regular codecs. + kEncryptionSchemes, // Encryption schemes. + kSessionTypes, // Session types. + codecs, // Hardware secure codecs. + kEncryptionSchemes, // Hardware secure encryption schemes. + kSessionTypes, // Hardware secure session types. + Robustness::HW_SECURE_CRYPTO, // Max audio robustness. + Robustness::HW_SECURE_ALL, // Max video robustness. + media::EmeFeatureSupport::ALWAYS_ENABLED, // Persistent state. + media::EmeFeatureSupport::ALWAYS_ENABLED)); // Distinctive identifier. +#endif +} + +void ShellContentRendererClient::GetSupportedKeySystems( + media::GetSupportedKeySystemsCB cb) { + media::KeySystemInfos key_systems; + AddStarboardCmaKeySystems(&key_systems); + std::move(cb).Run(std::move(key_systems)); +} + +#else // BUILDFLAG(USE_STARBOARD_MEDIA) + #if BUILDFLAG(ENABLE_MOJO_CDM) void ShellContentRendererClient::GetSupportedKeySystems( media::GetSupportedKeySystemsCB cb) { @@ -281,6 +336,8 @@ void ShellContentRendererClient::GetSupportedKeySystems( } #endif +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + #if BUILDFLAG(USE_STARBOARD_MEDIA) // TODO(b/376542844): Eliminate the usage of hardcoded MIME string once we // support to query codec capabilities with configs. diff --git a/media/base/cdm_context.cc b/media/base/cdm_context.cc index 50d95a78575b..dee7e9e407aa 100644 --- a/media/base/cdm_context.cc +++ b/media/base/cdm_context.cc @@ -47,6 +47,13 @@ MediaCryptoContext* CdmContext::GetMediaCryptoContext() { } #endif +#if BUILDFLAG(USE_STARBOARD_MEDIA) +SbDrmSystem CdmContext::GetSbDrmSystem() { + return kSbDrmSystemInvalid; +} +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + + #if BUILDFLAG(IS_FUCHSIA) FuchsiaCdmContext* CdmContext::GetFuchsiaCdmContext() { return nullptr; diff --git a/media/base/cdm_context.h b/media/base/cdm_context.h index 260251b42fc6..84b9f1baa52c 100644 --- a/media/base/cdm_context.h +++ b/media/base/cdm_context.h @@ -23,6 +23,9 @@ class ChromeOsCdmContext; } #endif +#if BUILDFLAG(USE_STARBOARD_MEDIA) +#include "starboard/drm.h" +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) namespace media { class CallbackRegistration; @@ -112,6 +115,10 @@ class MEDIA_EXPORT CdmContext { virtual MediaCryptoContext* GetMediaCryptoContext(); #endif +#if BUILDFLAG(USE_STARBOARD_MEDIA) + virtual SbDrmSystem GetSbDrmSystem(); +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + #if BUILDFLAG(IS_FUCHSIA) // Returns FuchsiaCdmContext interface when the context is backed by Fuchsia // CDM. Otherwise returns nullptr. diff --git a/media/starboard/BUILD.gn b/media/starboard/BUILD.gn index a85aadd9c72d..356cd5d2846b 100644 --- a/media/starboard/BUILD.gn +++ b/media/starboard/BUILD.gn @@ -77,6 +77,12 @@ source_set("starboard") { "decoder_buffer_allocator.cc", "decoder_buffer_allocator.h", "decoder_buffer_memory_info.h", + "starboard_cdm.cc", + "starboard_cdm.h", + "starboard_cdm_factory.cc", + "starboard_cdm_factory.h", + "starboard_cdm_session.cc", + "starboard_cdm_session.h", "starboard_memory_allocator.h", ] } diff --git a/media/starboard/starboard_cdm.cc b/media/starboard/starboard_cdm.cc index 8f3ccd8f62b1..47d8fe98d238 100644 --- a/media/starboard/starboard_cdm.cc +++ b/media/starboard/starboard_cdm.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Cobalt Authors. All Rights Reserved. +// Copyright 2024 The Cobalt Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,371 +12,324 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "cobalt/media/base/drm_system.h" +#include "starboard_cdm.h" +#include + +#include #include #include +#include -#include "base/bind.h" -#include "base/compiler_specific.h" #include "base/logging.h" -#include "cobalt/base/instance_counter.h" +#include "base/strings/string_number_conversions.h" +#include "base/time/time.h" +#include "crypto/symmetric_key.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/cdm_promise.h" +#include "media/base/decoder_buffer.h" +#include "media/base/decrypt_config.h" +#include "media/base/limits.h" +#include "media/base/video_decoder_config.h" +#include "media/base/video_frame.h" +#include "media/cdm/cbcs_decryptor.h" +#include "media/cdm/cenc_decryptor.h" +#include "media/cdm/cenc_utils.h" -namespace cobalt { namespace media { -SbDrmSystem CreateSbDrmSystemWithHistogram( - const char* key_system, void* context, - SbDrmSessionUpdateRequestFunc update_request_callback, - SbDrmSessionUpdatedFunc session_updated_callback, - SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback, - SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback, - SbDrmSessionClosedFunc session_closed_callback, - MediaMetricsProvider& media_metrics_provider) { - media_metrics_provider.StartTrackingAction(MediaAction::SBDRM_CREATE); - auto drm_system = SbDrmCreateSystem( - key_system, context, update_request_callback, session_updated_callback, - key_statuses_changed_callback, server_certificate_updated_callback, - session_closed_callback); - media_metrics_provider.EndTrackingAction(MediaAction::SBDRM_CREATE); - return drm_system; -} - -DECLARE_INSTANCE_COUNTER(DrmSystem); - -DrmSystem::Session::Session( - DrmSystem* drm_system, - SessionUpdateKeyStatusesCallback update_key_statuses_callback, - SessionClosedCallback session_closed_callback) - : drm_system_(drm_system), - update_key_statuses_callback_(update_key_statuses_callback), - session_closed_callback_(session_closed_callback), - closed_(false), - weak_factory_(this) { - DCHECK(!update_key_statuses_callback_.is_null()); - DCHECK(!session_closed_callback_.is_null()); -} - -DrmSystem::Session::~Session() { - if (id_ && !closed_) { - // Auto-closing semantics is derived from EME spec. - // - // If a MediaKeySession object is not closed when it becomes inaccessible - // to the page, the CDM shall close the key session associated with - // the object. - // https://www.w3.org/TR/encrypted-media/#mediakeysession-interface - Close(); +namespace { + +std::string GetInitDataTypeName(EmeInitDataType type) { + switch (type) { + case EmeInitDataType::WEBM: + return "webm"; + case EmeInitDataType::CENC: + return "cenc"; + case EmeInitDataType::KEYIDS: + return "keyids"; + case EmeInitDataType::UNKNOWN: + return "unknown"; } + NOTREACHED() << "Unexpected EmeInitDataType"; } -void DrmSystem::Session::GenerateUpdateRequest( - const std::string& type, const uint8* init_data, int init_data_length, - const SessionUpdateRequestGeneratedCallback& - session_update_request_generated_callback, - const SessionUpdateRequestDidNotGenerateCallback& - session_update_request_did_not_generate_callback) { - update_request_generated_callback_ = - session_update_request_generated_callback; - drm_system_->GenerateSessionUpdateRequest( - weak_factory_.GetWeakPtr(), type, init_data, init_data_length, - session_update_request_generated_callback, - session_update_request_did_not_generate_callback); -} +CdmMessageType SbDrmSessionRequestTypeToMediaMessageType( + SbDrmSessionRequestType type) { + switch (type) { + case kSbDrmSessionRequestTypeLicenseRequest: + return CdmMessageType::LICENSE_REQUEST; + case kSbDrmSessionRequestTypeLicenseRenewal: + return CdmMessageType::LICENSE_RENEWAL; + case kSbDrmSessionRequestTypeLicenseRelease: + return CdmMessageType::LICENSE_RELEASE; + case kSbDrmSessionRequestTypeIndividualizationRequest: + return CdmMessageType::INDIVIDUALIZATION_REQUEST; + } -void DrmSystem::Session::Update( - const uint8* key, int key_length, - const SessionUpdatedCallback& session_updated_callback, - const SessionDidNotUpdateCallback& session_did_not_update_callback) { - drm_system_->UpdateSession(*id_, key, key_length, session_updated_callback, - session_did_not_update_callback); + NOTREACHED() << "Unexpected SbDrmSessionRequestType " << type; } -void DrmSystem::Session::Close() { - drm_system_->CloseSession(*id_); - closed_ = true; +CdmKeyInformation::KeyStatus ToCdmKeyStatus(SbDrmKeyStatus status) { + switch (status) { + case kSbDrmKeyStatusUsable: + return CdmKeyInformation::KeyStatus::USABLE; + case kSbDrmKeyStatusExpired: + return CdmKeyInformation::KeyStatus::EXPIRED; + case kSbDrmKeyStatusReleased: + return CdmKeyInformation::KeyStatus::RELEASED; + case kSbDrmKeyStatusRestricted: + return CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED; + case kSbDrmKeyStatusDownscaled: + return CdmKeyInformation::KeyStatus::OUTPUT_DOWNSCALED; + case kSbDrmKeyStatusPending: + return CdmKeyInformation::KeyStatus::KEY_STATUS_PENDING; + case kSbDrmKeyStatusError: + return CdmKeyInformation::KeyStatus::INTERNAL_ERROR; + } + NOTREACHED() << "Unexpected SbDrmKeyStatus " << status; } -DrmSystem::DrmSystem(const char* key_system) - : wrapped_drm_system_(CreateSbDrmSystemWithHistogram( - key_system, this, OnSessionUpdateRequestGeneratedFunc, - OnSessionUpdatedFunc, OnSessionKeyStatusesChangedFunc, - OnServerCertificateUpdatedFunc, OnSessionClosedFunc, - media_metrics_provider_)), +} // namespace + +StarboardCdm::StarboardCdm( + const CdmConfig& cdm_config, + const SessionMessageCB& message_cb, + const SessionClosedCB& closed_cb, + const SessionKeysChangeCB& keys_change_cb, + const SessionExpirationUpdateCB& expiration_update_cb) + : message_cb_{message_cb}, + closed_cb_{closed_cb}, + keys_change_cb_{keys_change_cb}, + expiration_update_cb_{expiration_update_cb}, task_runner_(base::SequencedTaskRunner::GetCurrentDefault()), - ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), - weak_this_(weak_ptr_factory_.GetWeakPtr()) { - ON_INSTANCE_CREATED(DrmSystem); - - if (is_valid()) { - LOG(INFO) << "Successfully created SbDrmSystem (" << wrapped_drm_system_ - << "), key system: " << key_system << "."; - } else { - LOG(INFO) << "Failed to create SbDrmSystem, key system: " << key_system; - } + sb_drm_(SbDrmCreateSystem(cdm_config.key_system.c_str(), + this, + OnSessionUpdateRequestGeneratedFunc, + OnSessionUpdatedFunc, + OnSessionKeyStatusesChangedFunc, + OnServerCertificateUpdatedFunc, + OnSessionClosedFunc)) { + DCHECK(message_cb_); + DCHECK(closed_cb_); + DCHECK(keys_change_cb_); + DCHECK(expiration_update_cb_); + + DCHECK(SbDrmSystemIsValid(sb_drm_)); + + LOG(INFO) << "Starboard CDM created successfully with SbDrm."; } -DrmSystem::~DrmSystem() { - ON_INSTANCE_RELEASED(DrmSystem); - - if (is_valid()) { - media_metrics_provider_.StartTrackingAction(MediaAction::SBDRM_DESTROY); - SbDrmDestroySystem(wrapped_drm_system_); - media_metrics_provider_.EndTrackingAction(MediaAction::SBDRM_DESTROY); +StarboardCdm::~StarboardCdm() { + if (SbDrmSystemIsValid(sb_drm_)) { + SbDrmDestroySystem(sb_drm_); } else { LOG(WARNING) << "Attempting to close invalid SbDrmSystem"; } } -std::unique_ptr DrmSystem::CreateSession( - SessionUpdateKeyStatusesCallback session_update_key_statuses_callback, - SessionClosedCallback session_closed_callback) { - DCHECK(task_runner_->RunsTasksInCurrentSequence()); - return std::unique_ptr(new Session( - this, session_update_key_statuses_callback, session_closed_callback)); -} - -bool DrmSystem::IsServerCertificateUpdatable() { - DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK(is_valid()); - - if (SbDrmIsServerCertificateUpdatable(wrapped_drm_system_)) { - LOG(INFO) << "SbDrmSystem (" << wrapped_drm_system_ - << ") supports server certificate update"; - return true; - } - LOG(INFO) << "SbDrmSystem (" << wrapped_drm_system_ - << ") doesn't support server certificate update"; - return false; -} - -void DrmSystem::UpdateServerCertificate( - const uint8_t* certificate, int certificate_size, - ServerCertificateUpdatedCallback server_certificate_updated_callback) { +void StarboardCdm::SetServerCertificate( + const std::vector& certificate, + std::unique_ptr promise) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK(IsServerCertificateUpdatable()); - DCHECK(is_valid()); - - if (!certificate) { - LOG(ERROR) << "Updating server with NULL certificate"; - return; - } - - LOG(INFO) << "Updating server certificate of drm system (" - << wrapped_drm_system_ - << "), certificate size: " << certificate_size; + LOG(INFO) << "StarboardCdm - Set server cert - size:" << certificate.size(); int ticket = next_ticket_++; if (!SbDrmTicketIsValid(ticket)) { LOG(ERROR) << "Updating server with invalid ticket"; return; } + + uint32_t promise_id = promises_.SavePromise(std::move(promise)); + ticket_to_server_certificate_updated_map_.insert( - std::make_pair(ticket, server_certificate_updated_callback)); - SbDrmUpdateServerCertificate(wrapped_drm_system_, ticket, certificate, - certificate_size); -} + std::make_pair(ticket, promise_id)); -bool DrmSystem::GetMetrics(std::vector* metrics) { - DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK(metrics); - int size = 0; - const uint8_t* raw_metrics = - static_cast(SbDrmGetMetrics(wrapped_drm_system_, &size)); - if (!raw_metrics) { - return false; - } - SB_DCHECK(size >= 0); - if (size < 0) { - return false; - } - metrics->assign(raw_metrics, raw_metrics + size); - return true; + SbDrmUpdateServerCertificate(sb_drm_, ticket, certificate.data(), + certificate.size()); } -void DrmSystem::GenerateSessionUpdateRequest( - const base::WeakPtr& session, const std::string& type, - const uint8_t* init_data, int init_data_length, - const SessionUpdateRequestGeneratedCallback& - session_update_request_generated_callback, - const SessionUpdateRequestDidNotGenerateCallback& - session_update_request_did_not_generate_callback) { +void StarboardCdm::CreateSessionAndGenerateRequest( + CdmSessionType session_type, + EmeInitDataType init_data_type, + const std::vector& init_data, + std::unique_ptr promise) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK(is_valid()); - if (!init_data) { - LOG(ERROR) << "Generate session update request with invalid init_data"; - return; - } + uint32_t promise_id = promises_.SavePromise(std::move(promise)); + + auto session = std::make_unique( + message_cb_, keys_change_cb_, expiration_update_cb_); - // Store the context of the call. SessionUpdateRequest session_update_request; - session_update_request.session = session; - session_update_request.generated_callback = - session_update_request_generated_callback; - session_update_request.did_not_generate_callback = - session_update_request_did_not_generate_callback; - int ticket = next_ticket_++; + session_update_request.session = std::move(session); + session_update_request.promise_id = promise_id; + session_update_request.session_type = session_type; + int ticket = next_ticket_++; if (!SbDrmTicketIsValid(ticket)) { LOG(ERROR) << "Generate session update request with invalid ticket"; return; } ticket_to_session_update_request_map_.insert( - std::make_pair(ticket, session_update_request)); + std::make_pair(ticket, std::move(session_update_request))); - LOG(INFO) << "Generate session update request of drm system (" - << wrapped_drm_system_ << "), type: " << type - << ", init data size: " << init_data_length + LOG(INFO) << "Generate session update request of drm system (" << sb_drm_ + << "), type: " << GetInitDataTypeName(init_data_type) + << ", init data size: " << init_data.size() << ", ticket: " << ticket; - media_metrics_provider_.StartTrackingAction( - MediaAction::SBDRM_GENERATE_SESSION_UPDATE_REQUEST); - SbDrmGenerateSessionUpdateRequest(wrapped_drm_system_, ticket, type.c_str(), - init_data, init_data_length); - media_metrics_provider_.EndTrackingAction( - MediaAction::SBDRM_GENERATE_SESSION_UPDATE_REQUEST); + SbDrmGenerateSessionUpdateRequest(sb_drm_, ticket, + GetInitDataTypeName(init_data_type).c_str(), + init_data.data(), init_data.size()); } -void DrmSystem::UpdateSession( - const std::string& session_id, const uint8_t* key, int key_length, - const SessionUpdatedCallback& session_updated_callback, - const SessionDidNotUpdateCallback& session_did_not_update_callback) { +void StarboardCdm::LoadSession(CdmSessionType session_type, + const std::string& session_id, + std::unique_ptr promise) { + LOG(INFO) << "Starboard CDM - Load Session - NOT IMPL"; + promise->reject(CdmPromise::Exception::INVALID_STATE_ERROR, 0, + "SbDrm doesn't implement Load Session."); + return; +} + +void StarboardCdm::UpdateSession(const std::string& session_id, + const std::vector& response, + std::unique_ptr promise) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK(is_valid()); + LOG(INFO) << "StarboardCdm - update session"; + auto it = session_map_.find(session_id); + if (it == session_map_.end()) { + LOG(WARNING) << "Update session - session doesn't exist"; + promise->reject(CdmPromise::Exception::INVALID_STATE_ERROR, 0, + "Session doesn't exist."); + return; + } + + // Caller should NOT pass in an empty response. + DCHECK(!response.empty()); + + uint32_t promise_id = promises_.SavePromise(std::move(promise)); + + StarboardCdmSession* session = it->second.get(); + DCHECK(session); // Store the context of the call. SessionUpdate session_update; - session_update.updated_callback = session_updated_callback; - session_update.did_not_update_callback = session_did_not_update_callback; + session_update.promise_id = promise_id; + session_update.session = session; int ticket = next_ticket_++; if (!SbDrmTicketIsValid(ticket)) { - LOG(ERROR) << "Update session with invalid ticket"; + LOG(WARNING) << "Update session with invalid ticket"; return; } ticket_to_session_update_map_.insert(std::make_pair(ticket, session_update)); - LOG(INFO) << "Update session of drm system (" << wrapped_drm_system_ - << "), key length: " << key_length << ", ticket: " << ticket + LOG(INFO) << "Update session of drm system (" << sb_drm_ + << "), key length: " << response.size() << ", ticket: " << ticket << ", session id: " << session_id; - media_metrics_provider_.StartTrackingAction( - MediaAction::SBDRM_UPDATE_SESSION); - SbDrmUpdateSession(wrapped_drm_system_, ticket, key, key_length, + SbDrmUpdateSession(sb_drm_, ticket, response.data(), response.size(), session_id.c_str(), session_id.size()); - media_metrics_provider_.EndTrackingAction(MediaAction::SBDRM_UPDATE_SESSION); } -void DrmSystem::CloseSession(const std::string& session_id) { +// Runs the parallel steps from https://w3c.github.io/encrypted-media/#close. +void StarboardCdm::CloseSession(const std::string& session_id, + std::unique_ptr promise) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK(is_valid()); + LOG(INFO) << "StarboardCdm - Close session"; + auto it = session_map_.find(session_id); + if (it == session_map_.end()) { + promise->reject(CdmPromise::Exception::INVALID_STATE_ERROR, 0, + "Session doesn't exist."); + return; + } - LOG(INFO) << "Close session of drm system (" << wrapped_drm_system_ - << "), session id: " << session_id; + StarboardCdmSession* session = it->second.get(); + DCHECK(session); - media_metrics_provider_.StartTrackingAction(MediaAction::SBDRM_CLOSE_SESSION); - SbDrmCloseSession(wrapped_drm_system_, session_id.c_str(), session_id.size()); - media_metrics_provider_.EndTrackingAction(MediaAction::SBDRM_CLOSE_SESSION); + SbDrmCloseSession(sb_drm_, session_id.c_str(), session_id.size()); + + session_map_.erase(session_id); + closed_cb_.Run(session_id, media::CdmSessionClosedReason::kClose); + promise->resolve(); } -void DrmSystem::OnSessionUpdateRequestGenerated( - SessionTicketAndOptionalId ticket_and_optional_id, SbDrmStatus status, - SbDrmSessionRequestType type, const std::string& error_message, - std::unique_ptr message, int message_size) { +// Runs the parallel steps from https://w3c.github.io/encrypted-media/#remove. +void StarboardCdm::RemoveSession(const std::string& session_id, + std::unique_ptr promise) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); + LOG(INFO) << "Starboard CDM - Remove Session - NOT IMPL"; + promise->reject(CdmPromise::Exception::INVALID_STATE_ERROR, 0, + "SbDrm doesn't implement License Release."); + return; +} - int ticket = ticket_and_optional_id.ticket; - const base::Optional& session_id = ticket_and_optional_id.id; +CdmContext* StarboardCdm::GetCdmContext() { + return this; +} - LOG(INFO) << "Receiving session update request notification from drm system (" - << wrapped_drm_system_ << "), status: " << status - << ", type: " << type << ", ticket: " << ticket - << ", session id: " << session_id.value_or("n/a"); +SbDrmSystem StarboardCdm::GetSbDrmSystem() { + return sb_drm_; +} - if (SbDrmTicketIsValid(ticket)) { - // Called back as a result of |SbDrmGenerateSessionUpdateRequest|. +std::unique_ptr StarboardCdm::RegisterEventCB( + EventCB event_cb) { + return event_callbacks_.Register(std::move(event_cb)); +} + +void StarboardCdm::OnSessionUpdateRequestGenerated( + SessionTicketAndOptionalId ticket_and_optional_id, + SbDrmStatus status, + SbDrmSessionRequestType type, + const std::string& error_message, + std::vector message, + int message_size) { + int ticket = ticket_and_optional_id.ticket; + const std::optional& session_id = ticket_and_optional_id.id; - // Restore the context of |GenerateSessionUpdateRequest|. - TicketToSessionUpdateRequestMap::iterator session_update_request_iterator = + if (SbDrmTicketIsValid(ticket)) { + auto session_update_request_iterator = ticket_to_session_update_request_map_.find(ticket); if (session_update_request_iterator == ticket_to_session_update_request_map_.end()) { - LOG(ERROR) << "Unknown session update request ticket: " << ticket << "."; + LOG(INFO) << "Unknown session update request ticket: " << ticket << "."; return; } - const SessionUpdateRequest& session_update_request = - session_update_request_iterator->second; - - // As DrmSystem::Session may be released, need to check it before using it. - if (session_update_request.session && - !session_update_request.session->is_closed()) { - // Interpret the result. - if (session_id) { - // Successful request generation. - - // Enable session lookup by id which is used by spontaneous callbacks. - session_update_request.session->set_id(*session_id); - id_to_session_map_.insert( - std::make_pair(*session_id, session_update_request.session)); - - LOG(INFO) << "Calling session update request callback on drm system (" - << wrapped_drm_system_ << ") with type: " << type - << ", message size: " << message_size; - - session_update_request.generated_callback.Run(type, std::move(message), - message_size); - } else { - // Failure during request generation. - LOG(INFO) << "Calling session update request callback on drm system (" - << wrapped_drm_system_ << "), status: " << status - << ", error message: " << error_message; - session_update_request.did_not_generate_callback.Run(status, - error_message); - } - } - // Sweep the context of |GenerateSessionUpdateRequest| once license updated. + auto session_request = std::move(session_update_request_iterator->second); + session_request.session->SetSessionId(session_id.value()); + session_map_[session_id.value()] = std::move(session_request.session); + ticket_to_session_update_request_map_.erase( session_update_request_iterator); - } else { - // Called back spontaneously by the underlying DRM system. - // Spontaneous calls must refer to a valid session. - if (!session_id) { - LOG(ERROR) << "SbDrmSessionUpdateRequestFunc() should not be called " - "with both invalid ticket and null session id."; - NOTREACHED(); - return; - } + promises_.ResolvePromise(session_request.promise_id, session_id.value()); + } - // Find the session by ID. - IdToSessionMap::iterator session_iterator = - id_to_session_map_.find(*session_id); - if (session_iterator == id_to_session_map_.end()) { - LOG(ERROR) << "Unknown session id: " << *session_id << "."; - return; - } + LOG(INFO) << "Calling session update request callback on drm system (" + << sb_drm_ << ") with type: " << type + << ", message size: " << message.size(); - // As DrmSystem::Session may be released, need to check it before using it. - if (session_iterator->second) { - LOG(INFO) << "Calling session update request callback on drm system (" - << wrapped_drm_system_ << "), type: " << type - << ", message size " << message_size; - session_iterator->second->update_request_generated_callback().Run( - type, std::move(message), message_size); - } + auto session_iterator = session_map_.find(session_id.value()); + if (session_iterator == session_map_.end()) { + LOG(ERROR) << "Unknown session id: " << session_id.value() << "."; + return; } + StarboardCdmSession* session = session_iterator->second.get(); + session->InvokeOnSessionMessage( + SbDrmSessionRequestTypeToMediaMessageType(type), message); } -void DrmSystem::OnSessionUpdated(int ticket, SbDrmStatus status, - const std::string& error_message) { - DCHECK(task_runner_->RunsTasksInCurrentSequence()); - +void StarboardCdm::OnSessionUpdated(int ticket, + SbDrmStatus status, + const std::string& error_message) { LOG(INFO) << "Receiving session updated notification from drm system (" - << wrapped_drm_system_ << "), status: " << status - << ", ticket: " << ticket << ", error message: " << error_message; + << sb_drm_ << "), status: " << status << ", ticket: " << ticket + << ", error message: " << error_message; // Restore the context of |UpdateSession|. TicketToSessionUpdateMap::iterator session_update_iterator = @@ -389,67 +342,40 @@ void DrmSystem::OnSessionUpdated(int ticket, SbDrmStatus status, // Interpret the result. if (status == kSbDrmStatusSuccess) { - session_update.updated_callback.Run(); - } else { - session_update.did_not_update_callback.Run(status, error_message); + promises_.ResolvePromise(session_update.promise_id); } // Sweep the context of |UpdateSession|. ticket_to_session_update_map_.erase(session_update_iterator); } -void DrmSystem::OnSessionKeyStatusChanged( - const std::string& session_id, const std::vector& key_ids, - const std::vector& key_statuses) { - DCHECK(task_runner_->RunsTasksInCurrentSequence()); - +void StarboardCdm::OnSessionKeyStatusChanged(const std::string& session_id, + CdmKeysInfo keys_info, + bool has_additional_usable_key) { LOG(INFO) << "Receiving session key status changed notification from drm" - << " system (" << wrapped_drm_system_ - << "), session id: " << session_id - << ", number of key ids: " << key_ids.size(); - - // Find the session by ID. - IdToSessionMap::iterator session_iterator = - id_to_session_map_.find(session_id); - if (session_iterator == id_to_session_map_.end()) { - LOG(ERROR) << "Unknown session id: " << session_id << "."; - return; - } - - // As DrmSystem::Session may be released, need to check it before using it. - if (session_iterator->second) { - session_iterator->second->update_key_statuses_callback().Run(key_ids, - key_statuses); - } -} - -void DrmSystem::OnSessionClosed(const std::string& session_id) { - DCHECK(task_runner_->RunsTasksInCurrentSequence()); - - LOG(INFO) << "Receiving session closed notification from drm system (" - << wrapped_drm_system_ << "), session id: " << session_id; + << " system (" << sb_drm_ << "), session id: " << session_id + << ", number of key ids: " << keys_info.size() + << ", has_additional_usable_key?: " + << (has_additional_usable_key ? "true" : "false"); // Find the session by ID. - IdToSessionMap::iterator session_iterator = - id_to_session_map_.find(session_id); - if (session_iterator == id_to_session_map_.end()) { + auto session_iterator = session_map_.find(session_id); + if (session_iterator == session_map_.end()) { LOG(ERROR) << "Unknown session id: " << session_id << "."; return; } - // As DrmSystem::Session may be released, need to check it before using it. - if (session_iterator->second) { - session_iterator->second->session_closed_callback().Run(); - } - id_to_session_map_.erase(session_iterator); + StarboardCdmSession* session = session_iterator->second.get(); + session->InvokeOnSessionKeysChange(has_additional_usable_key, + std::move(keys_info)); } -void DrmSystem::OnServerCertificateUpdated(int ticket, SbDrmStatus status, - const std::string& error_message) { - DCHECK(task_runner_->RunsTasksInCurrentSequence()); - +void StarboardCdm::OnServerCertificateUpdated( + int ticket, + SbDrmStatus status, + const std::string& error_message) { LOG(INFO) << "Receiving server certificate updated notification from drm" - << " system (" << wrapped_drm_system_ << "), ticket: " << ticket + << " system (" << sb_drm_ << "), ticket: " << ticket << ", status: " << status << ", error message: " << error_message; auto iter = ticket_to_server_certificate_updated_map_.find(ticket); @@ -457,65 +383,101 @@ void DrmSystem::OnServerCertificateUpdated(int ticket, SbDrmStatus status, LOG(ERROR) << "Unknown ticket: " << ticket << "."; return; } - iter->second.Run(status, error_message); + + if (status == kSbDrmStatusSuccess) { + promises_.ResolvePromise(iter->second); + } + ticket_to_server_certificate_updated_map_.erase(iter); } +void StarboardCdm::OnSessionClosed(const std::string& session_id) { + // Find the session by ID. + auto session_iterator = session_map_.find(session_id); + if (session_iterator == session_map_.end()) { + LOG(ERROR) << "Unknown session id: " << session_id << "."; + return; + } + + closed_cb_.Run(session_id, media::CdmSessionClosedReason::kClose); + session_map_.erase(session_iterator); +} + // static -void DrmSystem::OnSessionUpdateRequestGeneratedFunc( - SbDrmSystem wrapped_drm_system, void* context, int ticket, - SbDrmStatus status, SbDrmSessionRequestType type, const char* error_message, - const void* session_id, int session_id_size, const void* content, - int content_size, const char* url) { +void StarboardCdm::OnSessionUpdateRequestGeneratedFunc( + SbDrmSystem sb_drm, + void* context, + int ticket, + SbDrmStatus status, + SbDrmSessionRequestType type, + const char* error_message, + const void* session_id, + int session_id_size, + const void* content, + int content_size, + const char* url) { DCHECK(context); - DrmSystem* drm_system = static_cast(context); - DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_); + StarboardCdm* cdm = static_cast(context); + DCHECK_EQ(sb_drm, cdm->sb_drm_); - base::Optional session_id_copy; - std::unique_ptr content_copy; + std::optional session_id_copy; if (session_id) { session_id_copy = std::string(static_cast(session_id), static_cast(session_id) + session_id_size); - - content_copy.reset(new uint8[content_size]); - memcpy(content_copy.get(), content, content_size); } - drm_system->task_runner_->PostTask( + LOG(INFO) << "Receiving session update request notification from drm " + "system (" + << sb_drm << "), status: " << status << ", type: " << type + << ", ticket: " << ticket + << ", session id: " << session_id_copy.value_or("n/a"); + + const uint8_t* begin = static_cast(content); + const uint8_t* end = begin + content_size; + const std::vector content_copy(begin, end); + + cdm->task_runner_->PostTask( FROM_HERE, - base::Bind(&DrmSystem::OnSessionUpdateRequestGenerated, - drm_system->weak_this_, - SessionTicketAndOptionalId{ticket, session_id_copy}, status, - type, error_message ? std::string(error_message) : "", - base::Passed(&content_copy), content_size)); + base::BindOnce(&StarboardCdm::OnSessionUpdateRequestGenerated, + cdm->weak_factory_.GetWeakPtr(), + SessionTicketAndOptionalId{ticket, session_id_copy}, + status, type, + error_message ? std::string(error_message) : "", + std::move(content_copy), content_size)); } // static -void DrmSystem::OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system, - void* context, int ticket, - SbDrmStatus status, - const char* error_message, - const void* session_id, - int session_id_size) { +void StarboardCdm::OnSessionUpdatedFunc(SbDrmSystem sb_drm, + void* context, + int ticket, + SbDrmStatus status, + const char* error_message, + const void* session_id, + int session_id_size) { DCHECK(context); - DrmSystem* drm_system = static_cast(context); - DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_); + StarboardCdm* cdm = static_cast(context); + DCHECK_EQ(sb_drm, cdm->sb_drm_); - drm_system->task_runner_->PostTask( + cdm->task_runner_->PostTask( FROM_HERE, - base::Bind(&DrmSystem::OnSessionUpdated, drm_system->weak_this_, ticket, - status, error_message ? std::string(error_message) : "")); + base::BindOnce(&StarboardCdm::OnSessionUpdated, + cdm->weak_factory_.GetWeakPtr(), ticket, status, + error_message ? std::string(error_message) : "")); } // static -void DrmSystem::OnSessionKeyStatusesChangedFunc( - SbDrmSystem wrapped_drm_system, void* context, const void* session_id, - int session_id_size, int number_of_keys, const SbDrmKeyId* key_ids, +void StarboardCdm::OnSessionKeyStatusesChangedFunc( + SbDrmSystem sb_drm, + void* context, + const void* session_id, + int session_id_size, + int number_of_keys, + const SbDrmKeyId* key_ids, const SbDrmKeyStatus* key_statuses) { DCHECK(context); - DrmSystem* drm_system = static_cast(context); - DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_); + StarboardCdm* cdm = static_cast(context); + DCHECK_EQ(sb_drm, cdm->sb_drm_); DCHECK(session_id != NULL); @@ -523,45 +485,51 @@ void DrmSystem::OnSessionKeyStatusesChangedFunc( std::string(static_cast(session_id), static_cast(session_id) + session_id_size); - std::vector key_ids_copy(number_of_keys); - std::vector key_statuses_copy(number_of_keys); + bool has_additional_usable_key = false; + CdmKeysInfo keys_info; for (int i = 0; i < number_of_keys; ++i) { const char* identifier = reinterpret_cast(key_ids[i].identifier); std::string key_id(identifier, identifier + key_ids[i].identifier_size); - key_ids_copy[i] = key_id; - key_statuses_copy[i] = key_statuses[i]; + CdmKeyInformation::KeyStatus status = ToCdmKeyStatus(key_statuses[i]); + has_additional_usable_key |= (status == CdmKeyInformation::USABLE); + + keys_info.emplace_back(new CdmKeyInformation(key_id, status, 0)); } - drm_system->task_runner_->PostTask( + cdm->task_runner_->PostTask( FROM_HERE, - base::Bind(&DrmSystem::OnSessionKeyStatusChanged, drm_system->weak_this_, - session_id_copy, key_ids_copy, key_statuses_copy)); + base::BindOnce(&StarboardCdm::OnSessionKeyStatusChanged, + cdm->weak_factory_.GetWeakPtr(), session_id_copy, + std::move(keys_info), has_additional_usable_key)); } // static -void DrmSystem::OnServerCertificateUpdatedFunc(SbDrmSystem wrapped_drm_system, - void* context, int ticket, - SbDrmStatus status, - const char* error_message) { +void StarboardCdm::OnServerCertificateUpdatedFunc(SbDrmSystem sb_drm, + void* context, + int ticket, + SbDrmStatus status, + const char* error_message) { DCHECK(context); - DrmSystem* drm_system = static_cast(context); - DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_); + StarboardCdm* cdm = static_cast(context); + DCHECK_EQ(sb_drm, cdm->sb_drm_); - drm_system->task_runner_->PostTask( - FROM_HERE, base::Bind(&DrmSystem::OnServerCertificateUpdated, - drm_system->weak_this_, ticket, status, - error_message ? std::string(error_message) : "")); + cdm->task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&StarboardCdm::OnServerCertificateUpdated, + cdm->weak_factory_.GetWeakPtr(), ticket, status, + error_message ? std::string(error_message) : "")); } // static -void DrmSystem::OnSessionClosedFunc(SbDrmSystem wrapped_drm_system, - void* context, const void* session_id, - int session_id_size) { +void StarboardCdm::OnSessionClosedFunc(SbDrmSystem sb_drm, + void* context, + const void* session_id, + int session_id_size) { DCHECK(context); - DrmSystem* drm_system = static_cast(context); - DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_); + StarboardCdm* cdm = static_cast(context); + DCHECK_EQ(sb_drm, cdm->sb_drm_); DCHECK(session_id != NULL); @@ -569,10 +537,10 @@ void DrmSystem::OnSessionClosedFunc(SbDrmSystem wrapped_drm_system, std::string(static_cast(session_id), static_cast(session_id) + session_id_size); - drm_system->task_runner_->PostTask( - FROM_HERE, base::Bind(&DrmSystem::OnSessionClosed, drm_system->weak_this_, - session_id_copy)); + cdm->task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&StarboardCdm::OnSessionClosed, + cdm->weak_factory_.GetWeakPtr(), session_id_copy)); } } // namespace media -} // namespace cobalt diff --git a/media/starboard/starboard_cdm.h b/media/starboard/starboard_cdm.h index 446735b8919c..d969e76f896c 100644 --- a/media/starboard/starboard_cdm.h +++ b/media/starboard/starboard_cdm.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Cobalt Authors. All Rights Reserved. +// Copyright 2024 The Cobalt Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,252 +12,186 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef COBALT_MEDIA_BASE_DRM_SYSTEM_H_ -#define COBALT_MEDIA_BASE_DRM_SYSTEM_H_ +#ifndef MEDIA_STARBOARD_STARBOARD_CDM_H +#define MEDIA_STARBOARD_STARBOARD_CDM_H +#include + +#include #include #include +#include #include -#include "base/basictypes.h" -#include "base/containers/hash_tables.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/optional.h" -#include "base/single_thread_task_runner.h" -#include "base/task/sequenced_task_runner.h" -#include "cobalt/media/base/metrics_provider.h" -#include "starboard/atomic.h" #include "starboard/drm.h" +#include "starboard_cdm_session.h" + +#include "base/containers/flat_map.h" +#include "base/memory/scoped_refptr.h" +#include "base/synchronization/lock.h" +#include "base/thread_annotations.h" +#include "media/base/callback_registry.h" +#include "media/base/cdm_config.h" +#include "media/base/cdm_context.h" +#include "media/base/cdm_key_information.h" +#include "media/base/cdm_promise.h" +#include "media/base/cdm_promise_adapter.h" +#include "media/base/content_decryption_module.h" +#include "media/base/decryptor.h" +#include "media/base/media_export.h" -namespace cobalt { namespace media { -// A C++ wrapper around |SbDrmSystem|. -// -// Ensures that callbacks are always asynchronous and performed -// from the same thread where |DrmSystem| was instantiated. -class DrmSystem : public base::RefCounted { +class MEDIA_EXPORT StarboardCdm : public ContentDecryptionModule, + public CdmContext { public: - typedef base::Callback message, - int message_size)> - SessionUpdateRequestGeneratedCallback; - typedef base::Callback - SessionUpdateRequestDidNotGenerateCallback; - typedef base::Callback SessionUpdatedCallback; - typedef base::Callback - SessionDidNotUpdateCallback; - typedef base::Callback& key_ids, - const std::vector& key_statuses)> - SessionUpdateKeyStatusesCallback; - typedef base::Callback SessionClosedCallback; - typedef base::Callback - ServerCertificateUpdatedCallback; - - // Flyweight that provides RAII semantics for sessions. - // Most of logic is implemented by |DrmSystem| and thus sessions must be - // destroyed before |DrmSystem|. - class Session { - public: - ~Session(); - - const base::Optional& id() const { return id_; } - - // Wraps |SbDrmGenerateSessionUpdateRequest|. - // - // |session_update_request_generated_callback| is called upon a successful - // request generation. IMPORTANT: It may be called multiple times after - // a single call to |CreateSessionAndGenerateUpdateRequest|, for example - // when the underlying DRM system needs to update a license. - // - // |session_update_request_did_not_generate_callback| is called upon a - // failure during request generation. Unlike its successful counterpart, - // never called spontaneously. - void GenerateUpdateRequest( - const std::string& type, const uint8* init_data, int init_data_length, - const SessionUpdateRequestGeneratedCallback& - session_update_request_generated_callback, - const SessionUpdateRequestDidNotGenerateCallback& - session_update_request_did_not_generate_callback); - - // Wraps |SbDrmUpdateSession|. - // - // |session_updated_callback| is called upon a successful session update. - // |session_did_not_update_callback| is called upon a failure during session - // update. - void Update( - const uint8* key, int key_length, - const SessionUpdatedCallback& session_updated_callback, - const SessionDidNotUpdateCallback& session_did_not_update_callback); - - // Wraps |SbDrmCloseSession|. - void Close(); - bool is_closed() const { return closed_; } - - private: - // Private API for |DrmSystem|. - Session(DrmSystem* drm_system, - SessionUpdateKeyStatusesCallback update_key_statuses_callback, - SessionClosedCallback session_closed_callback); - void set_id(const std::string& id) { id_ = id; } - const SessionUpdateRequestGeneratedCallback& - update_request_generated_callback() const { - return update_request_generated_callback_; - } - const SessionUpdateKeyStatusesCallback& update_key_statuses_callback() - const { - return update_key_statuses_callback_; - } - const SessionClosedCallback& session_closed_callback() const { - return session_closed_callback_; - } - - DrmSystem* const drm_system_; - SessionUpdateKeyStatusesCallback update_key_statuses_callback_; - SessionClosedCallback session_closed_callback_; - bool closed_; - base::Optional id_; - // Supports spontaneous invocations of |SbDrmSessionUpdateRequestFunc|. - SessionUpdateRequestGeneratedCallback update_request_generated_callback_; - - base::WeakPtrFactory weak_factory_; - - friend class DrmSystem; - - DISALLOW_COPY_AND_ASSIGN(Session); - }; - - explicit DrmSystem(const char* key_system); - ~DrmSystem(); - - SbDrmSystem wrapped_drm_system() { return wrapped_drm_system_; } - - bool is_valid() const { return SbDrmSystemIsValid(wrapped_drm_system_); } - - std::unique_ptr CreateSession( - SessionUpdateKeyStatusesCallback session_update_key_statuses_callback, - SessionClosedCallback session_closed_callback); - - bool IsServerCertificateUpdatable(); - void UpdateServerCertificate( - const uint8_t* certificate, int certificate_size, - ServerCertificateUpdatedCallback server_certificate_updated_callback); - bool GetMetrics(std::vector* metrics); + StarboardCdm(const CdmConfig& cdm_config, + const SessionMessageCB& session_message_cb, + const SessionClosedCB& session_closed_cb, + const SessionKeysChangeCB& session_keys_change_cb, + const SessionExpirationUpdateCB& session_expiration_update_cb); + ~StarboardCdm(); + + StarboardCdm(const StarboardCdm&) = delete; + StarboardCdm& operator=(const StarboardCdm&) = delete; + + // ContentDecryptionModule implementation. + void SetServerCertificate(const std::vector& certificate, + std::unique_ptr promise) override; + void CreateSessionAndGenerateRequest( + CdmSessionType session_type, + EmeInitDataType init_data_type, + const std::vector& init_data, + std::unique_ptr promise) override; + void LoadSession(CdmSessionType session_type, + const std::string& session_id, + std::unique_ptr promise) override; + void UpdateSession(const std::string& session_id, + const std::vector& response, + std::unique_ptr promise) override; + void CloseSession(const std::string& session_id, + std::unique_ptr promise) override; + void RemoveSession(const std::string& session_id, + std::unique_ptr promise) override; + CdmContext* GetCdmContext() override; + + // CdmContext implementation. + std::unique_ptr RegisterEventCB( + EventCB event_cb) override; + + SbDrmSystem GetSbDrmSystem() override; private: // Stores context of |GenerateSessionUpdateRequest|. struct SessionUpdateRequest { - base::WeakPtr session; - SessionUpdateRequestGeneratedCallback generated_callback; - SessionUpdateRequestDidNotGenerateCallback did_not_generate_callback; + std::unique_ptr session; + uint32_t promise_id; + CdmSessionType session_type; }; - typedef base::hash_map + typedef std::unordered_map TicketToSessionUpdateRequestMap; - typedef base::hash_map> IdToSessionMap; - - typedef base::hash_map - TicketToServerCertificateUpdatedMap; + typedef std::unordered_map TicketToServerCertificateUpdatedMap; // Stores context of |Session::Update|. struct SessionUpdate { - SessionUpdatedCallback updated_callback; - SessionDidNotUpdateCallback did_not_update_callback; + StarboardCdmSession* session; + uint32_t promise_id; }; - typedef base::hash_map TicketToSessionUpdateMap; + typedef std::unordered_map TicketToSessionUpdateMap; - // Defined to work around the limitation on number of parameters of - // base::Bind(). struct SessionTicketAndOptionalId { int ticket; - base::Optional id; + std::optional id; }; - // Private API for |Session|. - void GenerateSessionUpdateRequest( - const base::WeakPtr& session, const std::string& type, - const uint8_t* init_data, int init_data_length, - const SessionUpdateRequestGeneratedCallback& - session_update_request_generated_callback, - const SessionUpdateRequestDidNotGenerateCallback& - session_update_request_did_not_generate_callback); - void UpdateSession( - const std::string& session_id, const uint8_t* key, int key_length, - const SessionUpdatedCallback& session_updated_callback, - const SessionDidNotUpdateCallback& session_did_not_update_callback); - void CloseSession(const std::string& session_id); + // Default task runner. + scoped_refptr const task_runner_; + + const SbDrmSystem sb_drm_; + + int next_ticket_ = 0; + TicketToSessionUpdateRequestMap ticket_to_session_update_request_map_; + TicketToSessionUpdateMap ticket_to_session_update_map_; + TicketToServerCertificateUpdatedMap ticket_to_server_certificate_updated_map_; + + SessionMessageCB message_cb_; + SessionClosedCB closed_cb_; + SessionKeysChangeCB keys_change_cb_; + SessionExpirationUpdateCB expiration_update_cb_; + + CdmPromiseAdapter promises_; + base::flat_map> + session_map_; + + CallbackRegistry event_callbacks_; + + base::WeakPtrFactory weak_factory_{this}; // Called on the constructor thread, parameters are copied and owned by these // methods. void OnSessionUpdateRequestGenerated( - SessionTicketAndOptionalId ticket_and_optional_id, SbDrmStatus status, - SbDrmSessionRequestType type, const std::string& error_message, - std::unique_ptr message, int message_size); - void OnSessionUpdated(int ticket, SbDrmStatus status, + SessionTicketAndOptionalId ticket_and_optional_id, + SbDrmStatus status, + SbDrmSessionRequestType type, + const std::string& error_message, + std::vector message, + int message_size); + void OnSessionUpdated(int ticket, + SbDrmStatus status, const std::string& error_message); - void OnSessionKeyStatusChanged( - const std::string& session_id, const std::vector& key_ids, - const std::vector& key_statuses); - void OnServerCertificateUpdated(int ticket, SbDrmStatus status, + void OnSessionKeyStatusChanged(const std::string& session_id, + CdmKeysInfo keys_info, + bool has_additional_usable_keys); + + void OnServerCertificateUpdated(int ticket, + SbDrmStatus status, const std::string& error_message); void OnSessionClosed(const std::string& session_id); - // Called on any thread, parameters need to be copied immediately. + // SbDrm functions static void OnSessionUpdateRequestGeneratedFunc( - SbDrmSystem wrapped_drm_system, void* context, int ticket, - SbDrmStatus status, SbDrmSessionRequestType type, - const char* error_message, const void* session_id, int session_id_size, - const void* content, int content_size, const char* url); + SbDrmSystem wrapped_drm_system, + void* context, + int ticket, + SbDrmStatus status, + SbDrmSessionRequestType type, + const char* error_message, + const void* session_id, + int session_id_size, + const void* content, + int content_size, + const char* url); + static void OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system, - void* context, int ticket, + void* context, + int ticket, SbDrmStatus status, const char* error_message, const void* session_id, int session_id_length); static void OnSessionKeyStatusesChangedFunc( - SbDrmSystem wrapped_drm_system, void* context, const void* session_id, - int session_id_size, int number_of_keys, const SbDrmKeyId* key_ids, + SbDrmSystem wrapped_drm_system, + void* context, + const void* session_id, + int session_id_size, + int number_of_keys, + const SbDrmKeyId* key_ids, const SbDrmKeyStatus* key_statuses); - static void OnSessionClosedFunc(SbDrmSystem wrapped_drm_system, void* context, - const void* session_id, int session_id_size); + static void OnSessionClosedFunc(SbDrmSystem wrapped_drm_system, + void* context, + const void* session_id, + int session_id_size); static void OnServerCertificateUpdatedFunc(SbDrmSystem wrapped_drm_system, - void* context, int ticket, + void* context, + int ticket, SbDrmStatus status, const char* error_message); - - MediaMetricsProvider media_metrics_provider_; - const SbDrmSystem wrapped_drm_system_; - scoped_refptr const task_runner_; - - // Factory should only be used to create the initial weak pointer. All - // subsequent weak pointers are created by copying the initial one. This is - // required to keep weak pointers bound to the constructor thread. - base::WeakPtrFactory weak_ptr_factory_; - base::WeakPtr weak_this_; - - int next_ticket_ = 0; - // Supports concurrent calls to |GenerateSessionUpdateRequest|. - TicketToSessionUpdateRequestMap ticket_to_session_update_request_map_; - - // Supports spontaneous invocations of |SbDrmSessionUpdateRequestFunc|. - IdToSessionMap id_to_session_map_; - - TicketToServerCertificateUpdatedMap ticket_to_server_certificate_updated_map_; - - // Supports concurrent calls to |Session::Update|. - TicketToSessionUpdateMap ticket_to_session_update_map_; - - DISALLOW_COPY_AND_ASSIGN(DrmSystem); }; } // namespace media -} // namespace cobalt -#endif // COBALT_MEDIA_BASE_DRM_SYSTEM_H_ +#endif // MEDIA_STARBOARD_STARBOARD_CDM_H diff --git a/media/starboard/starboard_cdm_factory.cc b/media/starboard/starboard_cdm_factory.cc new file mode 100644 index 000000000000..a5d989ca1487 --- /dev/null +++ b/media/starboard/starboard_cdm_factory.cc @@ -0,0 +1,46 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard_cdm_factory.h" +#include "starboard_cdm.h" + +#include "base/functional/bind.h" +#include "base/task/bind_post_task.h" +#include "media/base/cdm_config.h" +#include "media/base/key_systems.h" + +namespace media { + +StarboardCdmFactory::StarboardCdmFactory() {} + +StarboardCdmFactory::~StarboardCdmFactory() = default; + +void StarboardCdmFactory::Create( + const CdmConfig& cdm_config, + const SessionMessageCB& session_message_cb, + const SessionClosedCB& session_closed_cb, + const SessionKeysChangeCB& session_keys_change_cb, + const SessionExpirationUpdateCB& session_expiration_update_cb, + CdmCreatedCB cdm_created_cb) { + auto cdm = base::MakeRefCounted( + cdm_config, session_message_cb, session_closed_cb, session_keys_change_cb, + session_expiration_update_cb); + + auto bound_cdm_created_cb = + base::BindPostTaskToCurrentDefault(std::move(cdm_created_cb)); + + std::move(bound_cdm_created_cb).Run(std::move(cdm), ""); +} + +} // namespace media diff --git a/media/starboard/starboard_cdm_factory.h b/media/starboard/starboard_cdm_factory.h new file mode 100644 index 000000000000..c421a665876f --- /dev/null +++ b/media/starboard/starboard_cdm_factory.h @@ -0,0 +1,45 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MEDIA_STARBOARD_STARBOARD_CDM_FACTORY_H_ +#define MEDIA_STARBOARD_STARBOARD_CDM_FACTORY_H_ + +#include + +#include "media/base/cdm_factory.h" +#include "url/origin.h" + +namespace media { + +class MEDIA_EXPORT StarboardCdmFactory final : public CdmFactory { + public: + StarboardCdmFactory(); + + StarboardCdmFactory(const StarboardCdmFactory&) = delete; + StarboardCdmFactory& operator=(const StarboardCdmFactory&) = delete; + + ~StarboardCdmFactory() override; + + // CdmFactory implementation. + void Create(const CdmConfig& cdm_config, + const SessionMessageCB& session_message_cb, + const SessionClosedCB& session_closed_cb, + const SessionKeysChangeCB& session_keys_change_cb, + const SessionExpirationUpdateCB& session_expiration_update_cb, + CdmCreatedCB cdm_created_cb) override; +}; + +} // namespace media + +#endif // MEDIA_STARBOARD_STARBOARD_CDM_FACTORY_H_ diff --git a/media/starboard/starboard_cdm_session.cc b/media/starboard/starboard_cdm_session.cc new file mode 100644 index 000000000000..43824838b8fe --- /dev/null +++ b/media/starboard/starboard_cdm_session.cc @@ -0,0 +1,70 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "build/build_config.h" + +#include "starboard_cdm_session.h" + +#include + +#include "base/logging.h" +#include "media/base/cdm_key_information.h" + +namespace media { + +StarboardCdmSession::StarboardCdmSession( + const SessionMessageCB& session_message_cb, + const SessionKeysChangeCB& session_keys_change_cb, + const SessionExpirationUpdateCB& session_expiration_update_cb) + : session_message_cb_(session_message_cb), + session_keys_change_cb_(session_keys_change_cb), + session_expiration_update_cb_(session_expiration_update_cb) { + auto weak_this = weak_factory_.GetWeakPtr(); + message_cb_ = base::BindPostTaskToCurrentDefault( + base::BindRepeating(&StarboardCdmSession::OnSessionMessage, weak_this)); + keys_change_cb_ = base::BindPostTaskToCurrentDefault(base::BindRepeating( + &StarboardCdmSession::OnSessionKeysChange, weak_this)); +} + +bool StarboardCdmSession::SetSessionId(std::string session_id) { + session_id_ = session_id; + return true; +} + +void StarboardCdmSession::InvokeOnSessionMessage( + CdmMessageType message_type, + const std::vector& message) { + message_cb_.Run(message_type, message); +} + +void StarboardCdmSession::InvokeOnSessionKeysChange(bool has_new_usable_keys, + CdmKeysInfo keys_info) { + keys_change_cb_.Run(has_new_usable_keys, std::move(keys_info)); +} + +void StarboardCdmSession::OnSessionMessage( + CdmMessageType message_type, + const std::vector& message) { + DCHECK(!session_id_.empty()); + session_message_cb_.Run(session_id_, message_type, message); +} + +void StarboardCdmSession::OnSessionKeysChange(bool has_new_usable_keys, + CdmKeysInfo keys_info) { + DCHECK(!session_id_.empty()); + session_keys_change_cb_.Run(session_id_, has_new_usable_keys, + std::move(keys_info)); +} + +} // namespace media diff --git a/media/starboard/starboard_cdm_session.h b/media/starboard/starboard_cdm_session.h new file mode 100644 index 000000000000..235cdf51fd49 --- /dev/null +++ b/media/starboard/starboard_cdm_session.h @@ -0,0 +1,86 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MEDIA_STARBOARD_STARBOARD_CDM_SESSION_H +#define MEDIA_STARBOARD_STARBOARD_CDM_SESSION_H + +#include + +#include +#include +#include +#include +#include + +#include "base/memory/scoped_refptr.h" +#include "base/synchronization/lock.h" +#include "base/thread_annotations.h" +#include "media/base/callback_registry.h" +#include "media/base/cdm_config.h" +#include "media/base/cdm_context.h" +#include "media/base/cdm_key_information.h" +#include "media/base/cdm_promise.h" +#include "media/base/content_decryption_module.h" +#include "media/base/decryptor.h" +#include "media/base/media_export.h" +#include "media/cdm/json_web_key.h" +#include "starboard/drm.h" + +namespace media { + +class MEDIA_EXPORT StarboardCdmSession { + public: + StarboardCdmSession( + const SessionMessageCB& session_message_cb, + const SessionKeysChangeCB& session_keys_change_cb, + const SessionExpirationUpdateCB& session_expiration_update_cb); + StarboardCdmSession(const StarboardCdmSession&) = delete; + StarboardCdmSession& operator=(const StarboardCdmSession&) = delete; + ~StarboardCdmSession() = default; + + using MessageCB = + base::RepeatingCallback& message)>; + using KeysChangeCB = base::RepeatingCallback; + + bool SetSessionId(std::string session_id); + + void InvokeOnSessionMessage(CdmMessageType message_type, + const std::vector& message); + void InvokeOnSessionKeysChange(bool has_new_usable_keys, + CdmKeysInfo keys_info); + + MessageCB message_cb_; + KeysChangeCB keys_change_cb_; + + private: + //// Callbacks for firing session events. + SessionMessageCB session_message_cb_; + SessionKeysChangeCB session_keys_change_cb_; + SessionExpirationUpdateCB session_expiration_update_cb_; + + // Callbacks for forwarding session events. + void OnSessionMessage(CdmMessageType message_type, + const std::vector& message); + void OnSessionKeysChange(bool has_new_usable_keys, CdmKeysInfo keys_info); + + std::string session_id_; + + base::WeakPtrFactory weak_factory_{this}; +}; + +} // namespace media + +#endif // MEDIA_STARBOARD_STARBOARD_CDM_SESSION_H diff --git a/media/starboard/starboard_renderer.cc b/media/starboard/starboard_renderer.cc index 7296ca04f84f..fe21a2794515 100644 --- a/media/starboard/starboard_renderer.cc +++ b/media/starboard/starboard_renderer.cc @@ -127,25 +127,51 @@ void StarboardRenderer::Initialize(MediaResource* media_resource, return; } -#if COBALT_MEDIA_ENABLE_ENCRYPTED_PLAYBACKS - base::AutoLock auto_lock(lock_); + // TODO(sideboard) ensure SetCdm has been called before |CreatePlayerBridge()| + // and the init_cb. + // + // base::AutoLock auto_lock(lock_); + // + // bool is_encrypted = + // audio_stream_ && audio_stream_->audio_decoder_config().is_encrypted(); + // is_encrypted |= + // video_stream_ && video_stream_->video_decoder_config().is_encrypted(); + // if (is_encrypted) { + // // TODO(b/328305808): Shall we call client_->OnVideoNaturalSizeChange() + // to + // // provide an initial size before the license exchange finishes? + + // RunSetDrmSystemReadyCB(BindPostTaskToCurrentDefault( + // base::Bind(&SbPlayerPipeline::CreatePlayer, this))); + // return; + // } - bool is_encrypted = - audio_stream_ && audio_stream_->audio_decoder_config().is_encrypted(); - is_encrypted |= - video_stream_ && video_stream_->video_decoder_config().is_encrypted(); - if (is_encrypted) { - // TODO(b/328305808): Shall we call client_->OnVideoNaturalSizeChange() to - // provide an initial size before the license exchange finishes? + // |init_cb| will be called inside |CreatePlayerBridge()|. + CreatePlayerBridge(std::move(init_cb)); +} + +void StarboardRenderer::SetCdm(CdmContext* cdm_context, + CdmAttachedCB cdm_attached_cb) { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + DCHECK(cdm_context); + TRACE_EVENT0("media", "StarboardRenderer::SetCdm"); - RunSetDrmSystemReadyCB(BindPostTaskToCurrentDefault( - base::Bind(&SbPlayerPipeline::CreatePlayer, this))); + if (SbDrmSystemIsValid(drm_system_)) { + LOG(WARNING) << "Switching CDM not supported."; + std::move(cdm_attached_cb).Run(false); return; } -#endif // COBALT_MEDIA_ENABLE_ENCRYPTED_PLAYBACKS - // |init_cb| will be called inside |CreatePlayerBridge()|. - CreatePlayerBridge(std::move(init_cb)); +// TODO(b/378908083) We should only build StarboardRenderer +// if use_starboard_media=true +#if BUILDFLAG(USE_STARBOARD_MEDIA) + drm_system_ = cdm_context->GetSbDrmSystem(); + std::move(cdm_attached_cb).Run(true); + LOG(INFO) << "CDM set successfully."; +#else // BUILDFLAG(USE_STARBOARD_MEDIA) + std::move(cdm_attached_cb).Run(false); + LOG(INFO) << "CDM failure - USE_STARBOARD_MEDIA not set."; +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) } void StarboardRenderer::Flush(base::OnceClosure flush_cb) { @@ -345,9 +371,7 @@ void StarboardRenderer::CreatePlayerBridge(PipelineStatusCallback init_cb) { video_mime_type, // TODO(b/326497953): Support suspend/resume. // TODO(b/326508279): Support background mode. - kSbWindowInvalid, - // TODO(b/328305808): Implement SbDrm support. - kSbDrmSystemInvalid, this, + kSbWindowInvalid, drm_system_, this, // TODO(b/376320224); Verify set bounds works nullptr, // TODO(b/326497953): Support suspend/resume. diff --git a/media/starboard/starboard_renderer.h b/media/starboard/starboard_renderer.h index d0e5584c3ecb..b762c4ed1c76 100644 --- a/media/starboard/starboard_renderer.h +++ b/media/starboard/starboard_renderer.h @@ -51,10 +51,7 @@ class MEDIA_EXPORT StarboardRenderer final : public Renderer, void Initialize(MediaResource* media_resource, RendererClient* client, PipelineStatusCallback init_cb) final; - void SetCdm(CdmContext* cdm_context, CdmAttachedCB cdm_attached_cb) final { - // TODO(b/328305808): Implement encrypted playback. - NOTIMPLEMENTED(); - } + void SetCdm(CdmContext* cdm_context, CdmAttachedCB cdm_attached_cb) final; void SetLatencyHint(absl::optional latency_hint) final { // TODO(b/375271848): Address NOTIMPLEMENTED(). NOTIMPLEMENTED(); @@ -127,6 +124,8 @@ class MEDIA_EXPORT StarboardRenderer final : public Renderer, // TODO(b/375674101): Support batched samples write. const int max_audio_samples_per_write_ = 1; + SbDrmSystem drm_system_{kSbDrmSystemInvalid}; + base::Lock lock_; std::unique_ptr player_bridge_;