diff --git a/media/starboard/starboard_cdm.cc b/media/starboard/starboard_cdm.cc new file mode 100644 index 000000000000..8f3ccd8f62b1 --- /dev/null +++ b/media/starboard/starboard_cdm.cc @@ -0,0 +1,578 @@ +// Copyright 2017 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 "cobalt/media/base/drm_system.h" + +#include +#include + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "cobalt/base/instance_counter.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(); + } +} + +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); +} + +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); +} + +void DrmSystem::Session::Close() { + drm_system_->CloseSession(*id_); + closed_ = true; +} + +DrmSystem::DrmSystem(const char* key_system) + : wrapped_drm_system_(CreateSbDrmSystemWithHistogram( + key_system, this, OnSessionUpdateRequestGeneratedFunc, + OnSessionUpdatedFunc, OnSessionKeyStatusesChangedFunc, + OnServerCertificateUpdatedFunc, OnSessionClosedFunc, + media_metrics_provider_)), + 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; + } +} + +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); + } 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) { + 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; + + int ticket = next_ticket_++; + if (!SbDrmTicketIsValid(ticket)) { + LOG(ERROR) << "Updating server with invalid ticket"; + return; + } + ticket_to_server_certificate_updated_map_.insert( + std::make_pair(ticket, server_certificate_updated_callback)); + SbDrmUpdateServerCertificate(wrapped_drm_system_, ticket, certificate, + certificate_size); +} + +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; +} + +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) { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + DCHECK(is_valid()); + + if (!init_data) { + LOG(ERROR) << "Generate session update request with invalid init_data"; + return; + } + + // 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_++; + + 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)); + + LOG(INFO) << "Generate session update request of drm system (" + << wrapped_drm_system_ << "), type: " << type + << ", init data size: " << init_data_length + << ", 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); +} + +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) { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + DCHECK(is_valid()); + + // 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; + int ticket = next_ticket_++; + + if (!SbDrmTicketIsValid(ticket)) { + LOG(ERROR) << "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 + << ", session id: " << session_id; + + media_metrics_provider_.StartTrackingAction( + MediaAction::SBDRM_UPDATE_SESSION); + SbDrmUpdateSession(wrapped_drm_system_, ticket, key, key_length, + session_id.c_str(), session_id.size()); + media_metrics_provider_.EndTrackingAction(MediaAction::SBDRM_UPDATE_SESSION); +} + +void DrmSystem::CloseSession(const std::string& session_id) { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + DCHECK(is_valid()); + + LOG(INFO) << "Close session of drm system (" << wrapped_drm_system_ + << "), session id: " << session_id; + + 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); +} + +void DrmSystem::OnSessionUpdateRequestGenerated( + SessionTicketAndOptionalId ticket_and_optional_id, SbDrmStatus status, + SbDrmSessionRequestType type, const std::string& error_message, + std::unique_ptr message, int message_size) { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + + int ticket = ticket_and_optional_id.ticket; + const base::Optional& session_id = ticket_and_optional_id.id; + + 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"); + + if (SbDrmTicketIsValid(ticket)) { + // Called back as a result of |SbDrmGenerateSessionUpdateRequest|. + + // Restore the context of |GenerateSessionUpdateRequest|. + TicketToSessionUpdateRequestMap::iterator 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 << "."; + 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. + 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; + } + + // 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) { + 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); + } + } +} + +void DrmSystem::OnSessionUpdated(int ticket, SbDrmStatus status, + const std::string& error_message) { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + + LOG(INFO) << "Receiving session updated notification from drm system (" + << wrapped_drm_system_ << "), status: " << status + << ", ticket: " << ticket << ", error message: " << error_message; + + // Restore the context of |UpdateSession|. + TicketToSessionUpdateMap::iterator session_update_iterator = + ticket_to_session_update_map_.find(ticket); + if (session_update_iterator == ticket_to_session_update_map_.end()) { + LOG(ERROR) << "Unknown session update ticket: " << ticket << "."; + return; + } + const SessionUpdate& session_update = session_update_iterator->second; + + // Interpret the result. + if (status == kSbDrmStatusSuccess) { + session_update.updated_callback.Run(); + } else { + session_update.did_not_update_callback.Run(status, error_message); + } + + // 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()); + + 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; + + // 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->session_closed_callback().Run(); + } + id_to_session_map_.erase(session_iterator); +} + +void DrmSystem::OnServerCertificateUpdated(int ticket, SbDrmStatus status, + const std::string& error_message) { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + + LOG(INFO) << "Receiving server certificate updated notification from drm" + << " system (" << wrapped_drm_system_ << "), ticket: " << ticket + << ", status: " << status << ", error message: " << error_message; + + auto iter = ticket_to_server_certificate_updated_map_.find(ticket); + if (iter == ticket_to_server_certificate_updated_map_.end()) { + LOG(ERROR) << "Unknown ticket: " << ticket << "."; + return; + } + iter->second.Run(status, error_message); + ticket_to_server_certificate_updated_map_.erase(iter); +} + +// 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) { + DCHECK(context); + DrmSystem* drm_system = static_cast(context); + DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_); + + base::Optional session_id_copy; + std::unique_ptr content_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( + 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)); +} + +// 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) { + DCHECK(context); + DrmSystem* drm_system = static_cast(context); + DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_); + + drm_system->task_runner_->PostTask( + FROM_HERE, + base::Bind(&DrmSystem::OnSessionUpdated, drm_system->weak_this_, 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, + const SbDrmKeyStatus* key_statuses) { + DCHECK(context); + DrmSystem* drm_system = static_cast(context); + DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_); + + DCHECK(session_id != NULL); + + std::string session_id_copy = + 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); + + 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]; + } + + drm_system->task_runner_->PostTask( + FROM_HERE, + base::Bind(&DrmSystem::OnSessionKeyStatusChanged, drm_system->weak_this_, + session_id_copy, key_ids_copy, key_statuses_copy)); +} + +// static +void DrmSystem::OnServerCertificateUpdatedFunc(SbDrmSystem wrapped_drm_system, + 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_); + + drm_system->task_runner_->PostTask( + FROM_HERE, base::Bind(&DrmSystem::OnServerCertificateUpdated, + drm_system->weak_this_, 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) { + DCHECK(context); + DrmSystem* drm_system = static_cast(context); + DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_); + + DCHECK(session_id != NULL); + + std::string session_id_copy = + 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)); +} + +} // namespace media +} // namespace cobalt diff --git a/media/starboard/starboard_cdm.h b/media/starboard/starboard_cdm.h new file mode 100644 index 000000000000..446735b8919c --- /dev/null +++ b/media/starboard/starboard_cdm.h @@ -0,0 +1,263 @@ +// Copyright 2017 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 COBALT_MEDIA_BASE_DRM_SYSTEM_H_ +#define COBALT_MEDIA_BASE_DRM_SYSTEM_H_ + +#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" + +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 { + 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); + + private: + // Stores context of |GenerateSessionUpdateRequest|. + struct SessionUpdateRequest { + base::WeakPtr session; + SessionUpdateRequestGeneratedCallback generated_callback; + SessionUpdateRequestDidNotGenerateCallback did_not_generate_callback; + }; + typedef base::hash_map + TicketToSessionUpdateRequestMap; + + typedef base::hash_map> IdToSessionMap; + + typedef base::hash_map + TicketToServerCertificateUpdatedMap; + + // Stores context of |Session::Update|. + struct SessionUpdate { + SessionUpdatedCallback updated_callback; + SessionDidNotUpdateCallback did_not_update_callback; + }; + typedef base::hash_map TicketToSessionUpdateMap; + + // Defined to work around the limitation on number of parameters of + // base::Bind(). + struct SessionTicketAndOptionalId { + int ticket; + base::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); + + // 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, + 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, + const std::string& error_message); + void OnSessionClosed(const std::string& session_id); + + // Called on any thread, parameters need to be copied immediately. + 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); + static void OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system, + 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, + const SbDrmKeyStatus* key_statuses); + + 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, + 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_