blob: eeadabcb05bd68bc2783887f6fd5682199db0dca [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/media/cdm/cast_cdm_proxy.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "media/base/cdm_key_information.h"
#include "media/base/cdm_promise.h"
namespace chromecast {
namespace media {
namespace {
// media::CdmPromiseTemplate implementation that wraps a promise so as to
// allow passing to other threads.
template <typename... T>
class CdmPromiseInternal : public ::media::CdmPromiseTemplate<T...> {
public:
CdmPromiseInternal(std::unique_ptr<::media::CdmPromiseTemplate<T...>> promise)
: task_runner_(base::ThreadTaskRunnerHandle::Get()),
promise_(std::move(promise)) {}
~CdmPromiseInternal() final {
if (IsPromiseSettled())
return;
DCHECK(promise_);
RejectPromiseOnDestruction();
}
// CdmPromiseTemplate<> implementation.
void resolve(const T&... result) final;
void reject(::media::MediaKeys::Exception exception,
uint32_t system_code,
const std::string& error_message) final {
MarkPromiseSettled();
task_runner_->PostTask(
FROM_HERE, base::Bind(&::media::CdmPromiseTemplate<T...>::reject,
base::Owned(promise_.release()), exception,
system_code, error_message));
}
private:
using ::media::CdmPromiseTemplate<T...>::IsPromiseSettled;
using ::media::CdmPromiseTemplate<T...>::MarkPromiseSettled;
using ::media::CdmPromiseTemplate<T...>::RejectPromiseOnDestruction;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
std::unique_ptr<::media::CdmPromiseTemplate<T...>> promise_;
};
template <typename... T>
void CdmPromiseInternal<T...>::resolve(const T&... result) {
MarkPromiseSettled();
task_runner_->PostTask(
FROM_HERE, base::Bind(&::media::CdmPromiseTemplate<T...>::resolve,
base::Owned(promise_.release()), result...));
}
template <typename... T>
std::unique_ptr<CdmPromiseInternal<T...>> BindPromiseToCurrentLoop(
std::unique_ptr<::media::CdmPromiseTemplate<T...>> promise) {
return base::WrapUnique(new CdmPromiseInternal<T...>(std::move(promise)));
}
} // namespace
// A macro runs current member function on |task_runner_| thread.
#define FORWARD_ON_CDM_THREAD(param_fn, ...) \
task_runner_->PostTask( \
FROM_HERE, base::Bind(&CastCdm::param_fn, \
base::Unretained(cast_cdm_.get()), ##__VA_ARGS__))
CastCdmProxy::CastCdmProxy(
const scoped_refptr<CastCdm>& cast_cdm,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
: cast_cdm_(cast_cdm), task_runner_(task_runner) {}
CastCdmProxy::~CastCdmProxy() {
DCHECK(thread_checker_.CalledOnValidThread());
cast_cdm_->AddRef();
CastCdm* raw_cdm = cast_cdm_.get();
cast_cdm_ = nullptr;
task_runner_->ReleaseSoon(FROM_HERE, raw_cdm);
}
CastCdm* CastCdmProxy::cast_cdm() const {
DCHECK(thread_checker_.CalledOnValidThread());
return cast_cdm_.get();
}
void CastCdmProxy::SetServerCertificate(
const std::vector<uint8_t>& certificate,
std::unique_ptr<::media::SimpleCdmPromise> promise) {
DCHECK(thread_checker_.CalledOnValidThread());
FORWARD_ON_CDM_THREAD(
SetServerCertificate, certificate,
base::Passed(BindPromiseToCurrentLoop(std::move(promise))));
}
void CastCdmProxy::CreateSessionAndGenerateRequest(
::media::MediaKeys::SessionType session_type,
::media::EmeInitDataType init_data_type,
const std::vector<uint8_t>& init_data,
std::unique_ptr<::media::NewSessionCdmPromise> promise) {
DCHECK(thread_checker_.CalledOnValidThread());
FORWARD_ON_CDM_THREAD(
CreateSessionAndGenerateRequest, session_type, init_data_type, init_data,
base::Passed(BindPromiseToCurrentLoop(std::move(promise))));
}
void CastCdmProxy::LoadSession(
::media::MediaKeys::SessionType session_type,
const std::string& session_id,
std::unique_ptr<::media::NewSessionCdmPromise> promise) {
DCHECK(thread_checker_.CalledOnValidThread());
FORWARD_ON_CDM_THREAD(
LoadSession, session_type, session_id,
base::Passed(BindPromiseToCurrentLoop(std::move(promise))));
}
void CastCdmProxy::UpdateSession(
const std::string& session_id,
const std::vector<uint8_t>& response,
std::unique_ptr<::media::SimpleCdmPromise> promise) {
DCHECK(thread_checker_.CalledOnValidThread());
FORWARD_ON_CDM_THREAD(
UpdateSession, session_id, response,
base::Passed(BindPromiseToCurrentLoop(std::move(promise))));
}
void CastCdmProxy::CloseSession(
const std::string& session_id,
std::unique_ptr<::media::SimpleCdmPromise> promise) {
DCHECK(thread_checker_.CalledOnValidThread());
FORWARD_ON_CDM_THREAD(
CloseSession, session_id,
base::Passed(BindPromiseToCurrentLoop(std::move(promise))));
}
void CastCdmProxy::RemoveSession(
const std::string& session_id,
std::unique_ptr<::media::SimpleCdmPromise> promise) {
DCHECK(thread_checker_.CalledOnValidThread());
FORWARD_ON_CDM_THREAD(
RemoveSession, session_id,
base::Passed(BindPromiseToCurrentLoop(std::move(promise))));
}
::media::CdmContext* CastCdmProxy::GetCdmContext() {
// This will be recast as a CastCdmService pointer before being passed to the
// media pipeline. The returned object should only be called on the CMA
// renderer thread.
return cast_cdm_->GetCdmContext();
}
} // namespace media
} // namespace chromecast