#include "base/callback_helpers.h"
#include "base/debug/trace_event.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/metrics/sparse_histogram.h"
#include "base/numerics/safe_conversions.h"
+#include "content/renderer/media/crypto/key_systems.h"
#include "content/renderer/pepper/ppb_buffer_impl.h"
#include "media/base/audio_buffer.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/data_buffer.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/base/video_util.h"
+#include "ppapi/shared_impl/array_var.h"
#include "ppapi/shared_impl/scoped_pp_resource.h"
+#include "ppapi/shared_impl/time_conversion.h"
#include "ppapi/shared_impl/var.h"
#include "ppapi/shared_impl/var_tracker.h"
#include "ppapi/thunk/enter.h"
using media::CdmPromise;
using media::Decryptor;
+using media::KeyIdsPromise;
using media::MediaKeys;
using media::NewSessionCdmPromise;
using media::SimpleCdmPromise;
using ppapi::ArrayBufferVar;
+using ppapi::ArrayVar;
using ppapi::PpapiGlobals;
using ppapi::ScopedPPResource;
using ppapi::StringVar;
//
// Returns true if |block_info| is successfully filled. Returns false
// otherwise.
-static bool MakeEncryptedBlockInfo(
+bool MakeEncryptedBlockInfo(
const scoped_refptr<media::DecoderBuffer>& encrypted_buffer,
uint32_t request_id,
PP_EncryptedBlockInfo* block_info) {
}
}
+// TODO(xhwang): Unify EME UMA reporting code when prefixed EME is deprecated.
+// See http://crbug.com/412987 for details.
+void ReportSystemCodeUMA(const std::string& key_system, uint32 system_code) {
+ // Sparse histogram macro does not cache the histogram, so it's safe to use
+ // macro with non-static histogram name here.
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Media.EME." + KeySystemNameForUMA(key_system) + ".SystemCode",
+ system_code);
+}
+
} // namespace
ContentDecryptorDelegate::ContentDecryptorDelegate(
const media::SessionReadyCB& session_ready_cb,
const media::SessionClosedCB& session_closed_cb,
const media::SessionErrorCB& session_error_cb,
+ const media::SessionKeysChangeCB& session_keys_change_cb,
+ const media::SessionExpirationUpdateCB& session_expiration_update_cb,
const base::Closure& fatal_plugin_error_cb) {
DCHECK(!key_system.empty());
DCHECK(key_system_.empty());
session_ready_cb_ = session_ready_cb;
session_closed_cb_ = session_closed_cb;
session_error_cb_ = session_error_cb;
+ session_keys_change_cb_ = session_keys_change_cb;
+ session_expiration_update_cb_ = session_expiration_update_cb;
fatal_plugin_error_cb_ = fatal_plugin_error_cb;
plugin_decryption_interface_->Initialize(
SatisfyAllPendingCallbacksOnError();
}
+void ContentDecryptorDelegate::SetServerCertificate(
+ const uint8_t* certificate,
+ uint32_t certificate_length,
+ scoped_ptr<media::SimpleCdmPromise> promise) {
+ if (!certificate ||
+ certificate_length < media::limits::kMinCertificateLength ||
+ certificate_length > media::limits::kMaxCertificateLength) {
+ promise->reject(
+ media::MediaKeys::INVALID_ACCESS_ERROR, 0, "Incorrect certificate.");
+ return;
+ }
+
+ uint32_t promise_id = SavePromise(promise.PassAs<CdmPromise>());
+ PP_Var certificate_array =
+ PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
+ certificate_length, certificate);
+ plugin_decryption_interface_->SetServerCertificate(
+ pp_instance_, promise_id, certificate_array);
+}
+
void ContentDecryptorDelegate::CreateSession(
const std::string& init_data_type,
const uint8* init_data,
response_array);
}
-void ContentDecryptorDelegate::ReleaseSession(
+void ContentDecryptorDelegate::CloseSession(
+ const std::string& web_session_id,
+ scoped_ptr<SimpleCdmPromise> promise) {
+ if (web_session_id.length() > media::limits::kMaxWebSessionIdLength) {
+ promise->reject(
+ media::MediaKeys::INVALID_ACCESS_ERROR, 0, "Incorrect session.");
+ return;
+ }
+
+ uint32_t promise_id = SavePromise(promise.PassAs<CdmPromise>());
+ plugin_decryption_interface_->CloseSession(
+ pp_instance_, promise_id, StringVar::StringToPPVar(web_session_id));
+}
+
+void ContentDecryptorDelegate::RemoveSession(
const std::string& web_session_id,
scoped_ptr<SimpleCdmPromise> promise) {
+ if (web_session_id.length() > media::limits::kMaxWebSessionIdLength) {
+ promise->reject(
+ media::MediaKeys::INVALID_ACCESS_ERROR, 0, "Incorrect session.");
+ return;
+ }
+
+ uint32_t promise_id = SavePromise(promise.PassAs<CdmPromise>());
+ plugin_decryption_interface_->RemoveSession(
+ pp_instance_, promise_id, StringVar::StringToPPVar(web_session_id));
+}
+
+void ContentDecryptorDelegate::GetUsableKeyIds(
+ const std::string& web_session_id,
+ scoped_ptr<media::KeyIdsPromise> promise) {
+ if (web_session_id.length() > media::limits::kMaxWebSessionIdLength) {
+ promise->reject(
+ media::MediaKeys::INVALID_ACCESS_ERROR, 0, "Incorrect session.");
+ return;
+ }
+
uint32_t promise_id = SavePromise(promise.PassAs<CdmPromise>());
- plugin_decryption_interface_->ReleaseSession(
+ plugin_decryption_interface_->GetUsableKeyIds(
pp_instance_, promise_id, StringVar::StringToPPVar(web_session_id));
}
void ContentDecryptorDelegate::OnPromiseResolved(uint32 promise_id) {
scoped_ptr<CdmPromise> promise = TakePromise(promise_id);
-
- // Special case due to http://crbug.com/408330. CDM is resolving LoadSession()
- // with this method when the session is not found. Instead it should call
- // PromiseResolvedWithSession(""), so emulate that here until 408330 is fixed.
- // TODO(jrummell): Remove this code when the CDM is updated.
- if (promise &&
- promise->GetResolveParameterType() == media::CdmPromise::STRING_TYPE) {
- NewSessionCdmPromise* session_promise =
- static_cast<NewSessionCdmPromise*>(promise.get());
- session_promise->resolve(std::string());
- return;
- }
-
if (!promise ||
promise->GetResolveParameterType() != media::CdmPromise::VOID_TYPE) {
NOTREACHED();
session_promise->resolve(web_session_id_string->value());
}
+void ContentDecryptorDelegate::OnPromiseResolvedWithKeyIds(
+ uint32 promise_id,
+ PP_Var key_ids_array) {
+ scoped_ptr<CdmPromise> promise = TakePromise(promise_id);
+
+ ArrayVar* key_ids = ArrayVar::FromPPVar(key_ids_array);
+ DCHECK(key_ids && key_ids->GetLength() <= media::limits::kMaxKeyIds);
+ media::KeyIdsVector key_ids_vector;
+ if (key_ids && key_ids->GetLength() <= media::limits::kMaxKeyIds) {
+ for (size_t i = 0; i < key_ids->GetLength(); ++i) {
+ ArrayBufferVar* array_buffer = ArrayBufferVar::FromPPVar(key_ids->Get(i));
+
+ if (!array_buffer ||
+ array_buffer->ByteLength() < media::limits::kMinKeyIdLength ||
+ array_buffer->ByteLength() > media::limits::kMaxKeyIdLength) {
+ NOTREACHED();
+ continue;
+ }
+
+ std::vector<uint8> key_id;
+ const uint8* data = static_cast<const uint8*>(array_buffer->Map());
+ key_id.assign(data, data + array_buffer->ByteLength());
+ key_ids_vector.push_back(key_id);
+ }
+ }
+
+ if (!promise ||
+ promise->GetResolveParameterType() !=
+ media::CdmPromise::KEY_IDS_VECTOR_TYPE) {
+ NOTREACHED();
+ return;
+ }
+
+ KeyIdsPromise* key_ids_promise(static_cast<KeyIdsPromise*>(promise.get()));
+ key_ids_promise->resolve(key_ids_vector);
+}
+
void ContentDecryptorDelegate::OnPromiseRejected(
uint32 promise_id,
PP_CdmExceptionCode exception_code,
uint32 system_code,
PP_Var error_description) {
+ ReportSystemCodeUMA(key_system_, system_code);
+
StringVar* error_description_string = StringVar::FromPPVar(error_description);
DCHECK(error_description_string);
scoped_ptr<CdmPromise> promise = TakePromise(promise_id);
+ DCHECK(promise);
if (promise) {
promise->reject(PpExceptionTypeToMediaException(exception_code),
system_code,
web_session_id_string->value(), message_vector, verified_gurl);
}
+void ContentDecryptorDelegate::OnSessionKeysChange(
+ PP_Var web_session_id,
+ PP_Bool has_additional_usable_key) {
+ if (session_keys_change_cb_.is_null())
+ return;
+
+ StringVar* web_session_id_string = StringVar::FromPPVar(web_session_id);
+ DCHECK(web_session_id_string);
+
+ session_keys_change_cb_.Run(web_session_id_string->value(),
+ PP_ToBool(has_additional_usable_key));
+}
+
+void ContentDecryptorDelegate::OnSessionExpirationChange(
+ PP_Var web_session_id,
+ PP_Time new_expiry_time) {
+ if (session_expiration_update_cb_.is_null())
+ return;
+
+ StringVar* web_session_id_string = StringVar::FromPPVar(web_session_id);
+ DCHECK(web_session_id_string);
+
+ session_expiration_update_cb_.Run(web_session_id_string->value(),
+ ppapi::PPTimeToTime(new_expiry_time));
+}
+
void ContentDecryptorDelegate::OnSessionReady(PP_Var web_session_id) {
if (session_ready_cb_.is_null())
return;
PP_CdmExceptionCode exception_code,
uint32 system_code,
PP_Var error_description) {
+ ReportSystemCodeUMA(key_system_, system_code);
+
if (session_error_cb_.is_null())
return;