blob: f33c6526753f2a49d8b54c2c49450ec7068bda3d [file]
//
// Copyright 2020 Comcast Cable Communications Management, LLC
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0
#if defined(HAS_OCDM)
#include "third_party/starboard/rdk/shared/drm/drm_system_ocdm.h"
#include <dlfcn.h>
#include <mutex>
#include <cstring>
#include <gst/gst.h>
#include "starboard/memory.h"
#include "starboard/common/mutex.h"
#include "starboard/shared/starboard/thread_checker.h"
#include <websocket/URL.h>
#include <opencdm/open_cdm.h>
#include <opencdm/open_cdm_adapter.h>
#include "third_party/starboard/rdk/shared/log_override.h"
namespace third_party {
namespace starboard {
namespace rdk {
namespace shared {
namespace drm {
namespace {
static SbMutex g_session_dtor_mutex_ = SB_MUTEX_INITIALIZER;
struct OcdmSessionDeleter {
void operator()(OpenCDMSession* session) {
if (session) {
SbMutexAcquire(&g_session_dtor_mutex_);
opencdm_destruct_session(session);
SbMutexRelease(&g_session_dtor_mutex_);
}
}
};
using ScopedOcdmSession = std::unique_ptr<OpenCDMSession, OcdmSessionDeleter>;
using OcdmGstSessionDecryptExFn =
OpenCDMError(*)(struct OpenCDMSession*, GstBuffer*, GstBuffer*, const uint32_t, GstBuffer*, GstBuffer*, uint32_t, GstCaps*);
using OcdmGstSessionDecryptBufferFn =
OpenCDMError(*)(struct OpenCDMSession* session, GstBuffer* buffer, GstCaps* caps);
using OcdmGetMetricSystemDataFn =
OpenCDMError(*)(struct OpenCDMSystem* system, uint32_t* bufferLength, uint8_t* buffer);
static OcdmGstSessionDecryptExFn g_ocdmGstSessionDecryptEx { nullptr };
static OcdmGstSessionDecryptBufferFn g_ocdmGstSessionDecryptBuffer { nullptr };
static OcdmGetMetricSystemDataFn g_ocdmGetMetricSystemData { nullptr };
} // namespace
namespace session {
SbDrmKeyStatus KeyStatus2DrmKeyStatus(KeyStatus status) {
switch (status) {
case Usable:
return kSbDrmKeyStatusUsable;
case Expired:
return kSbDrmKeyStatusExpired;
case Released:
return kSbDrmKeyStatusReleased;
case OutputRestricted:
return kSbDrmKeyStatusRestricted;
case OutputDownscaled:
return kSbDrmKeyStatusDownscaled;
case StatusPending:
return kSbDrmKeyStatusPending;
default:
case InternalError:
return kSbDrmKeyStatusError;
}
}
class Session {
public:
Session(Session&) = delete;
Session& operator=(Session&) = delete;
Session(
DrmSystemOcdm* drm_system,
OpenCDMSystem* ocdm_system,
void* context,
const SbDrmSessionUpdateRequestFunc& session_update_request_callback,
const SbDrmSessionUpdatedFunc& session_updated_callback,
const SbDrmSessionKeyStatusesChangedFunc& key_statuses_changed_callback,
const SbDrmSessionClosedFunc session_closed_callback);
~Session();
void Close();
void GenerateChallenge(const std::string& type,
const void* initialization_data,
int initialization_data_size,
int ticket);
void Update(const void* key, int key_size, int ticket);
std::string Id() const { return id_; }
int Decrypt( _GstBuffer* buffer,
_GstBuffer* sub_sample, uint32_t sub_sample_count,
_GstBuffer* iv, _GstBuffer* key, _GstCaps* caps);
void DispatchPendingKeyUpdates();
private:
static void OnProcessChallenge(OpenCDMSession* session,
void* user_data,
const char url[],
const uint8_t challenge[],
const uint16_t challenge_length);
static void ProcessChallenge(Session* session,
int ticket,
std::string&& id,
std::string&& url,
std::string&& challenge);
static void OnKeyUpdated(struct OpenCDMSession* session,
void* user_data,
const uint8_t key_id[],
const uint8_t length);
static void OnAllKeysUpdated(const struct OpenCDMSession* session,
void* user_data);
static void OnError(struct OpenCDMSession* session,
void* user_data,
const char message[]);
OpenCDMSessionCallbacks session_callbacks_ = {
&Session::OnProcessChallenge, &Session::OnKeyUpdated, &Session::OnError,
&Session::OnAllKeysUpdated};
enum class Operation {
kNone,
kGenrateChallenge,
kUpdate,
};
::starboard::shared::starboard::ThreadChecker thread_checker_;
Operation operation_{Operation::kNone};
int ticket_{0};
DrmSystemOcdm* drm_system_;
OpenCDMSystem* ocdm_system_;
ScopedOcdmSession session_;
void* context_;
const SbDrmSessionUpdateRequestFunc session_update_request_callback_;
const SbDrmSessionUpdatedFunc session_updated_callback_;
const SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback_;
const SbDrmSessionClosedFunc session_closed_callback_;
::starboard::Mutex mutex_;
::starboard::Mutex close_mutex_;
std::string last_challenge_;
std::string last_challenge_url_;
std::string id_;
std::vector<SbDrmKeyId> pending_key_updates_;
bool all_keys_updated_ { false };
};
Session::Session(
DrmSystemOcdm* drm_system,
OpenCDMSystem* ocdm_system,
void* context,
const SbDrmSessionUpdateRequestFunc& session_update_request_callback,
const SbDrmSessionUpdatedFunc& session_updated_callback,
const SbDrmSessionKeyStatusesChangedFunc& key_statuses_changed_callback,
const SbDrmSessionClosedFunc session_closed_callback)
: drm_system_(drm_system),
ocdm_system_(ocdm_system),
context_(context),
session_update_request_callback_(session_update_request_callback),
session_updated_callback_(session_updated_callback),
key_statuses_changed_callback_(key_statuses_changed_callback),
session_closed_callback_(session_closed_callback) {}
Session::~Session() {
Close();
}
void Session::Close() {
SB_DCHECK(thread_checker_.CalledOnValidThread());
if (session_) {
::starboard::ScopedLock lock(close_mutex_);
ScopedOcdmSession tmp;
{
::starboard::ScopedLock lock(mutex_);
tmp = std::move (session_);
SB_CHECK( session_ == nullptr );
}
opencdm_session_close(tmp.get());
}
auto id = Id();
if (!id.empty())
session_closed_callback_(drm_system_, context_, id.c_str(), id.size());
else
SB_LOG(WARNING) << "Closing ivalid session ?";
{
::starboard::ScopedLock lock(mutex_);
ticket_ = kSbDrmTicketInvalid;
operation_ = Operation::kNone;
id_.clear();
}
}
void Session::GenerateChallenge(const std::string& type,
const void* initialization_data,
int initialization_data_size,
int ticket) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
SB_LOG(INFO) << "Generating challenge";
{
::starboard::ScopedLock lock(mutex_);
ticket_ = ticket;
operation_ = Operation::kGenrateChallenge;
all_keys_updated_ = false;
}
OpenCDMSession* session = nullptr;
if (opencdm_construct_session(
ocdm_system_, Temporary, type.c_str(),
reinterpret_cast<const uint8_t*>(initialization_data),
initialization_data_size, nullptr, 0, &session_callbacks_, this,
&session) != ERROR_NONE ||
!session) {
session_update_request_callback_(drm_system_, context_, ticket,
kSbDrmStatusUnknownError,
kSbDrmSessionRequestTypeLicenseRequest,
nullptr, nullptr, 0, nullptr, 0, nullptr);
return;
}
session_.reset(session);
std::string challenge;
std::string url;
std::string id;
{
::starboard::ScopedLock lock(mutex_);
id_ = opencdm_session_id(session_.get());
id = id_;
challenge.swap(last_challenge_);
url.swap(last_challenge_url_);
}
if (!challenge.empty()) {
Session::ProcessChallenge(this, ticket, std::move(id), std::move(url),
std::move(challenge));
}
}
void Session::DispatchPendingKeyUpdates() {
bool all_keys_updated = false;
std::vector<SbDrmKeyId> pending_key_updates;
{
::starboard::ScopedLock lock(mutex_);
pending_key_updates.swap(pending_key_updates_);
all_keys_updated = all_keys_updated_;
}
if (!pending_key_updates.empty()) {
for (const auto& drm_key_id : pending_key_updates) {
Session::OnKeyUpdated(
session_.get(), this,
drm_key_id.identifier,
drm_key_id.identifier_size);
}
if (all_keys_updated) {
Session::OnAllKeysUpdated(session_.get(), this);
}
}
}
void Session::Update(const void* key, int key_size, int ticket) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
auto id = Id();
SB_DCHECK(!id.empty());
{
::starboard::ScopedLock lock(mutex_);
SB_LOG(INFO) << "Updating session " << id;
ticket_ = ticket;
operation_ = Operation::kUpdate;
}
if (opencdm_session_update(session_.get(), static_cast<const uint8_t*>(key),
key_size) != ERROR_NONE) {
session_updated_callback_(drm_system_, context_, ticket,
kSbDrmStatusUnknownError, nullptr, id.c_str(),
id.size());
}
}
int Session::Decrypt(
_GstBuffer* buffer,
_GstBuffer* sub_sample,
uint32_t sub_sample_count,
_GstBuffer* iv,
_GstBuffer* key,
_GstCaps* caps) {
::starboard::ScopedLock lock(close_mutex_);
OpenCDMSession* session = session_.get();
if (!session)
return ERROR_INVALID_SESSION;
if (g_ocdmGstSessionDecryptEx != nullptr) {
return g_ocdmGstSessionDecryptEx(session, buffer,
sub_sample, sub_sample_count, iv,
key, 0, caps);
}
if (g_ocdmGstSessionDecryptBuffer != nullptr) {
return g_ocdmGstSessionDecryptBuffer(session, buffer, caps);
}
return opencdm_gstreamer_session_decrypt(session, buffer,
sub_sample, sub_sample_count, iv,
key, 0);
}
// static
void Session::OnProcessChallenge(OpenCDMSession* ocdm_session,
void* user_data,
const char url[],
const uint8_t challenge[],
const uint16_t challenge_length) {
if (challenge_length == 0) {
Session::OnError(ocdm_session, user_data, "Empty challenge");
return;
}
Session* session = static_cast<Session*>(user_data);
std::string id;
int ticket;
{
::starboard::ScopedLock lock(session->mutex_);
id = session->Id();
if (id.empty()) {
session->last_challenge_url_ = url;
session->last_challenge_ = {reinterpret_cast<char const*>(challenge),
challenge_length};
return;
}
session->operation_ = Operation::kNone;
ticket = session->ticket_;
session->ticket_ = kSbDrmTicketInvalid;
}
std::string challenge_str = {reinterpret_cast<const char*>(challenge),
challenge_length};
Session::ProcessChallenge(session, ticket, std::move(id), {url},
std::move(challenge_str));
}
// static
void Session::ProcessChallenge(Session* session,
int ticket,
std::string&& id,
std::string&& url,
std::string&& challenge) {
SB_DCHECK(!id.empty() && !challenge.empty());
size_t type_position = challenge.find(":Type:");
std::string request_type = {
challenge.c_str(),
type_position != std::string::npos ? type_position : 0};
unsigned offset = 0u;
if (!request_type.empty() && request_type.length() != challenge.length())
offset = type_position + 6;
SbDrmSessionRequestType message_type = kSbDrmSessionRequestTypeLicenseRequest;
if (request_type.length() == 1)
message_type =
static_cast<SbDrmSessionRequestType>(std::stoi(request_type));
challenge = {challenge.c_str() + offset, challenge.size() - offset};
SB_LOG(INFO) << "Process challenge for " << id << " type " << request_type;
session->session_update_request_callback_(
session->drm_system_, session->context_, ticket, kSbDrmStatusSuccess,
message_type, "", id.c_str(), id.size(), challenge.c_str(),
challenge.size(), url.c_str());
}
// static
void Session::OnKeyUpdated(struct OpenCDMSession* /*ocdm_session*/,
void* user_data,
const uint8_t key_id[],
const uint8_t length) {
Session* session = static_cast<Session*>(user_data);
std::string id;
{
::starboard::ScopedLock lock(session->mutex_);
id = session->Id();
}
SbDrmKeyId drm_key_id;
drm_key_id.identifier_size = std::min(static_cast<int>(length), SB_ARRAY_SIZE_INT(drm_key_id.identifier));
memcpy(drm_key_id.identifier, key_id, drm_key_id.identifier_size);
if (id.empty()) {
::starboard::ScopedLock lock(session->mutex_);
if (SbDrmTicketIsValid(session->ticket_)) {
session->pending_key_updates_.push_back(std::move(drm_key_id));
} else {
SB_LOG(WARNING) << "Updating closed session ?";
}
return;
}
auto status = opencdm_session_status(session->session_.get(), key_id, length);
gchar *md5sum = g_compute_checksum_for_data(G_CHECKSUM_MD5, key_id, length);
SB_LOG(INFO) << "OnKeyUpdated SessionId: '" << id << "', key_id: '" << md5sum << "' status: " << status;
g_free(md5sum);
session->drm_system_->OnKeyUpdated(id, std::move(drm_key_id),
KeyStatus2DrmKeyStatus(status));
}
// static
void Session::OnAllKeysUpdated(const struct OpenCDMSession* /*ocdm_session*/,
void* user_data) {
Session* session = static_cast<Session*>(user_data);
int ticket;
std::string id;
{
::starboard::ScopedLock lock(session->mutex_);
id = session->Id();
session->all_keys_updated_ = true;
if (id.empty()) {
if (SbDrmTicketIsValid(session->ticket_) &&
session->operation_ == Operation::kGenrateChallenge) {
return;
}
}
session->operation_ = Operation::kNone;
ticket = session->ticket_;
session->ticket_ = kSbDrmTicketInvalid;
}
if (id.empty()) {
SB_LOG(WARNING) << "Updating closed session ?";
return;
}
session->session_updated_callback_(session->drm_system_, session->context_,
ticket, kSbDrmStatusSuccess, nullptr,
id.c_str(), id.size());
session->drm_system_->OnAllKeysUpdated();
auto session_keys = session->drm_system_->GetSessionKeys(id);
std::vector<SbDrmKeyId> keys;
std::vector<SbDrmKeyStatus> statuses;
for (auto& session_key : session_keys) {
keys.push_back(session_key.key);
statuses.push_back(session_key.status);
}
session->key_statuses_changed_callback_(
session->drm_system_, session->context_, id.c_str(), id.size(),
session_keys.size(), keys.data(), statuses.data());
SB_LOG(INFO) << "Session update ended " << id;
}
// static
void Session::OnError(struct OpenCDMSession* /*ocdm_session*/,
void* user_data,
const char message[]) {
Session* session = static_cast<Session*>(user_data);
int ticket;
std::string id;
Operation operation;
{
::starboard::ScopedLock lock(session->mutex_);
operation = session->operation_;
session->operation_ = Operation::kNone;
ticket = session->ticket_;
session->ticket_ = kSbDrmTicketInvalid;
id = session->Id();
}
SB_LOG(ERROR) << "DRM error: " << message << ", session " << id;
switch (operation) {
case Operation::kGenrateChallenge:
session->session_update_request_callback_(
session->drm_system_, session->context_, ticket,
kSbDrmStatusUnknownError, kSbDrmSessionRequestTypeLicenseRequest,
nullptr, nullptr, 0, nullptr, 0, nullptr);
break;
case Operation::kUpdate:
session->session_updated_callback_(
session->drm_system_, session->context_, ticket,
kSbDrmStatusUnknownError, nullptr, id.c_str(), id.size());
break;
case Operation::kNone:
break;
default:
SB_NOTREACHED();
break;
}
}
} // namespace session
using session::Session;
DrmSystemOcdm::DrmSystemOcdm(
const char* key_system,
void* context,
SbDrmSessionUpdateRequestFunc session_update_request_callback,
SbDrmSessionUpdatedFunc session_updated_callback,
SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback,
SbDrmSessionClosedFunc session_closed_callback)
: key_system_(key_system),
context_(context),
session_update_request_callback_(session_update_request_callback),
session_updated_callback_(session_updated_callback),
key_statuses_changed_callback_(key_statuses_changed_callback),
server_certificate_updated_callback_(server_certificate_updated_callback),
session_closed_callback_(session_closed_callback) {
SB_LOG(INFO) << "Create DRM system ";
ocdm_system_ = opencdm_create_system(key_system_.c_str());
static std::once_flag flag;
std::call_once(flag, [](){
g_ocdmGstSessionDecryptBuffer = (OcdmGstSessionDecryptBufferFn)dlsym(RTLD_DEFAULT, "opencdm_gstreamer_session_decrypt_buffer");
if (g_ocdmGstSessionDecryptBuffer) {
SB_LOG(INFO) << "Has opencdm_gstreamer_session_decrypt_buffer";
} else {
SB_LOG(INFO) << "No opencdm_gstreamer_session_decrypt_buffer. Will try opencdm_gstreamer_session_decrypt_ex.";
g_ocdmGstSessionDecryptEx = (OcdmGstSessionDecryptExFn)dlsym(RTLD_DEFAULT, "opencdm_gstreamer_session_decrypt_ex");
if (g_ocdmGstSessionDecryptEx) {
SB_LOG(INFO) << "Has opencdm_gstreamer_session_decrypt_ex";
} else {
SB_LOG(INFO) << "No opencdm_gstreamer_session_decrypt_ex. Fallback to opencdm_gstreamer_session_decrypt.";
}
}
g_ocdmGetMetricSystemData = (OcdmGetMetricSystemDataFn)dlsym(RTLD_DEFAULT, "opencdm_get_metric_system_data");
if (g_ocdmGetMetricSystemData) {
SB_LOG(INFO) << "Has opencdm_get_metric_system_data";
} else {
SB_LOG(INFO) << "No opencdm_get_metric_system_data.";
}
});
}
DrmSystemOcdm::~DrmSystemOcdm() {
{
::starboard::ScopedLock lock(mutex_);
if (event_id_ != kSbEventIdInvalid)
SbEventCancel(event_id_);
}
opencdm_destruct_system(ocdm_system_);
}
// static
bool DrmSystemOcdm::IsKeySystemSupported(const char* key_system,
const char* mime_type) {
return opencdm_is_type_supported(key_system, mime_type) == ERROR_NONE;
}
void DrmSystemOcdm::GenerateSessionUpdateRequest(
int ticket,
const char* type,
const void* initialization_data,
int initialization_data_size) {
SB_LOG(INFO) << "Generate challenge type: " << type;
std::unique_ptr<Session> session(
new Session(this, ocdm_system_, context_,
session_update_request_callback_, session_updated_callback_,
key_statuses_changed_callback_, session_closed_callback_));
session->GenerateChallenge(type, initialization_data,
initialization_data_size, ticket);
Session *session_ptr = session.get();
mutex_.Acquire();
sessions_.push_back(std::move(session));
mutex_.Release();
session_ptr->DispatchPendingKeyUpdates();
}
void DrmSystemOcdm::UpdateSession(int ticket,
const void* key,
int key_size,
const void* session_id,
int session_id_size) {
std::string id = {static_cast<const char*>(session_id), static_cast<std::string::size_type>(session_id_size)};
SB_LOG(INFO) << "Update: " << id;
auto* session = GetSessionById(id);
if (session)
session->Update(key, key_size, ticket);
}
void DrmSystemOcdm::CloseSession(const void* session_id, int session_id_size) {
std::string id = {static_cast<const char*>(session_id), static_cast<std::string::size_type>(session_id_size)};
SB_LOG(INFO) << "Close: " << id;
auto* session = GetSessionById(id);
if (session)
session->Close();
}
void DrmSystemOcdm::UpdateServerCertificate(int ticket,
const void* certificate,
int certificate_size) {
auto status = opencdm_system_set_server_certificate(
ocdm_system_, static_cast<const uint8_t*>(certificate), certificate_size);
server_certificate_updated_callback_(
this, context_, ticket,
status == ERROR_NONE ? kSbDrmStatusSuccess : kSbDrmStatusUnknownError,
"Error");
}
SbDrmSystemPrivate::DecryptStatus DrmSystemOcdm::Decrypt(InputBuffer* buffer) {
SB_NOTREACHED();
return kFailure;
}
Session* DrmSystemOcdm::GetSessionById(const std::string& id) const {
::starboard::ScopedLock lock(mutex_);
auto iter = std::find_if(
sessions_.begin(), sessions_.end(),
[&id](const std::unique_ptr<Session>& s) { return id == s->Id(); });
if (iter != sessions_.end())
return iter->get();
return nullptr;
}
void DrmSystemOcdm::AddObserver(DrmSystemOcdm::Observer* obs) {
::starboard::ScopedLock lock(mutex_);
observers_.push_back(obs);
}
void DrmSystemOcdm::RemoveObserver(DrmSystemOcdm::Observer* obs) {
::starboard::ScopedLock lock(mutex_);
auto found = std::find(observers_.begin(), observers_.end(), obs);
SB_DCHECK(found != observers_.end());
observers_.erase(found);
}
void DrmSystemOcdm::OnKeyUpdated(const std::string& session_id,
SbDrmKeyId&& key_id,
SbDrmKeyStatus status) {
::starboard::ScopedLock lock(mutex_);
auto session_key = session_keys_.find(session_id);
KeyWithStatus key_with_status;
key_with_status.key = key_id;
key_with_status.status = status;
if (session_key == session_keys_.end()) {
session_keys_[session_id].emplace_back(std::move(key_with_status));
} else {
auto key_entry = std::find_if(
session_key->second.begin(), session_key->second.end(),
[&key_id](const KeyWithStatus& key_with_status) {
return memcmp(key_id.identifier, key_with_status.key.identifier,
std::min(key_with_status.key.identifier_size,
key_id.identifier_size)) == 0;
});
if (key_entry != session_key->second.end()) {
key_entry->status = status;
} else {
session_key->second.emplace_back(std::move(key_with_status));
}
}
}
void DrmSystemOcdm::OnAllKeysUpdated() {
::starboard::ScopedLock lock(mutex_);
cached_ready_keys_.clear();
if (event_id_ != kSbEventIdInvalid)
SbEventCancel(event_id_);
event_id_ = SbEventSchedule(
[](void* data) {
DrmSystemOcdm* self = reinterpret_cast<DrmSystemOcdm*>(data);
self->AnnounceKeys();
},
this, 0);
}
std::set<std::string> DrmSystemOcdm::GetReadyKeysUnlocked() const {
if (cached_ready_keys_.empty()) {
for (auto& session_key : session_keys_) {
for (auto& key_with_status : session_key.second) {
cached_ready_keys_.emplace(std::string{
reinterpret_cast<const char*>(key_with_status.key.identifier),
static_cast<std::string::size_type>(key_with_status.key.identifier_size)});
}
}
}
return cached_ready_keys_;
}
std::set<std::string> DrmSystemOcdm::GetReadyKeys() const {
::starboard::ScopedLock lock(mutex_);
return GetReadyKeysUnlocked();
}
DrmSystemOcdm::KeysWithStatus DrmSystemOcdm::GetSessionKeys(
const std::string& session_id) const {
auto session_key = session_keys_.find(session_id);
return session_key != session_keys_.end() ? session_key->second
: DrmSystemOcdm::KeysWithStatus{};
}
void DrmSystemOcdm::AnnounceKeys() {
::starboard::ScopedLock lock(mutex_);
auto ready_keys = GetReadyKeysUnlocked();
for (auto* observer : observers_) {
for (auto& key : ready_keys) {
observer->OnKeyReady(reinterpret_cast<const uint8_t*>(key.c_str()),
key.size());
}
}
event_id_ = kSbEventIdInvalid;
}
std::string DrmSystemOcdm::SessionIdByKeyId(const uint8_t* key,
uint8_t key_len) {
SbMutexAcquire(&g_session_dtor_mutex_);
ScopedOcdmSession session{
opencdm_get_system_session(ocdm_system_, key, key_len, 0)};
SbMutexRelease(&g_session_dtor_mutex_);
return session ? opencdm_session_id(session.get()) : std::string{};
}
int DrmSystemOcdm::Decrypt(const std::string& id,
_GstBuffer* buffer,
_GstBuffer* sub_sample,
uint32_t sub_sample_count,
_GstBuffer* iv,
_GstBuffer* key,
_GstCaps* caps) {
session::Session* session = GetSessionById(id);
if (!session)
return ERROR_INVALID_SESSION;
return session->Decrypt(buffer, sub_sample, sub_sample_count, iv, key, caps);
}
const void* DrmSystemOcdm::GetMetrics(int* size) {
if ( !g_ocdmGetMetricSystemData )
return nullptr;
const int kMaxRetry = 5;
for (int i = 0; i < kMaxRetry; ++i) {
uint32_t buffer_length = ( 1 << i ) * 4 * 1024;
std::vector<uint8_t> tmp;
tmp.resize(buffer_length);
const uint32_t kBufferTooSmallErrorCode = 4; // ERROR_BUFFER_TOO_SMALL
auto rc = g_ocdmGetMetricSystemData(ocdm_system_, &buffer_length, tmp.data());
if ( rc == ERROR_NONE ) {
uint16_t out_length = (((buffer_length * 8) / 6) + 4) * sizeof(TCHAR);
metrics_.resize(out_length, '\0');
out_length = WPEFramework::Core::URL::Base64Encode(tmp.data(), buffer_length, reinterpret_cast<char*>(metrics_.data()), out_length, false);
metrics_.resize(out_length);
} else if ( rc == kBufferTooSmallErrorCode && i < (kMaxRetry - 1) ) {
SB_LOG(INFO) << "GetMetrics: buffer is too small, rc = " << rc << ", i = " << i;
continue;
} else {
metrics_.resize(0);
SB_LOG(ERROR) << "GetMetrics: failed, rc = " << rc;
}
break;
}
*size = static_cast<int>(metrics_.size());
return metrics_.data();
}
} // namespace drm
} // namespace shared
} // namespace rdk
} // namespace starboard
} // namespace third_party
#endif // defined(HAS_OCDM)