Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1338)

Unified Diff: content/renderer/media/crypto/encrypted_media_player_support_impl.cc

Issue 501473003: Move EME code out of WebMediaPlayerImpl. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address CR comments Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/media/crypto/encrypted_media_player_support_impl.cc
diff --git a/content/renderer/media/crypto/encrypted_media_player_support_impl.cc b/content/renderer/media/crypto/encrypted_media_player_support_impl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c2aae64b081fa67af4d729d733181ef13d00454c
--- /dev/null
+++ b/content/renderer/media/crypto/encrypted_media_player_support_impl.cc
@@ -0,0 +1,471 @@
+// 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 "content/renderer/media/crypto/encrypted_media_player_support_impl.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/renderer/media/crypto/key_systems.h"
+#include "content/renderer/media/webcontentdecryptionmodule_impl.h"
+#include "content/renderer/pepper/pepper_webplugin_impl.h"
+#include "media/base/bind_to_current_loop.h"
+#include "third_party/WebKit/public/platform/WebContentDecryptionModule.h"
+#include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h"
+#include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
+
+#if defined(ENABLE_PEPPER_CDMS)
+#include "content/renderer/media/crypto/pepper_cdm_wrapper_impl.h"
+#endif
+
+using blink::WebMediaPlayer;
+using blink::WebMediaPlayerClient;
+using blink::WebString;
+
+namespace content {
+
+#define BIND_TO_RENDER_LOOP(function) \
+ (media::BindToCurrentLoop(base::Bind(function, AsWeakPtr())))
+
+#define BIND_TO_RENDER_LOOP1(function, arg1) \
+ (media::BindToCurrentLoop(base::Bind(function, AsWeakPtr(), arg1)))
+
+
+// Prefix for histograms related to Encrypted Media Extensions.
+static const char* kMediaEme = "Media.EME.";
+
+// Used for calls to decryptor_ready_cb where the result can be ignored.
+static void DoNothing(bool success) {
+}
+
+// Convert a WebString to ASCII, falling back on an empty string in the case
+// of a non-ASCII string.
+static std::string ToASCIIOrEmpty(const WebString& string) {
+ return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
+ : std::string();
+}
+
+// Helper functions to report media EME related stats to UMA. They follow the
+// convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
+// UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
+// that UMA_* macros require the names to be constant throughout the process'
+// lifetime.
+static void EmeUMAHistogramEnumeration(const std::string& key_system,
+ const std::string& method,
+ int sample,
+ int boundary_value) {
+ base::LinearHistogram::FactoryGet(
+ kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
+ 1, boundary_value, boundary_value + 1,
+ base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
+}
+
+static void EmeUMAHistogramCounts(const std::string& key_system,
+ const std::string& method,
+ int sample) {
+ // Use the same parameters as UMA_HISTOGRAM_COUNTS.
+ base::Histogram::FactoryGet(
+ kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
+ 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
+}
+
+// Helper enum for reporting generateKeyRequest/addKey histograms.
+enum MediaKeyException {
+ kUnknownResultId,
+ kSuccess,
+ kKeySystemNotSupported,
+ kInvalidPlayerState,
+ kMaxMediaKeyException
+};
+
+static MediaKeyException MediaKeyExceptionForUMA(
+ WebMediaPlayer::MediaKeyException e) {
+ switch (e) {
+ case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
+ return kKeySystemNotSupported;
+ case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
+ return kInvalidPlayerState;
+ case WebMediaPlayer::MediaKeyExceptionNoError:
+ return kSuccess;
+ default:
+ return kUnknownResultId;
+ }
+}
+
+// Helper for converting |key_system| name and exception |e| to a pair of enum
+// values from above, for reporting to UMA.
+static void ReportMediaKeyExceptionToUMA(const std::string& method,
+ const std::string& key_system,
+ WebMediaPlayer::MediaKeyException e) {
+ MediaKeyException result_id = MediaKeyExceptionForUMA(e);
+ DCHECK_NE(result_id, kUnknownResultId) << e;
+ EmeUMAHistogramEnumeration(
+ key_system, method, result_id, kMaxMediaKeyException);
+}
+
+// Guess the type of |init_data|. This is only used to handle some corner cases
+// so we keep it as simple as possible without breaking major use cases.
+static std::string GuessInitDataType(const unsigned char* init_data,
+ unsigned init_data_length) {
+ // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes.
+ if (init_data_length == 16)
+ return "video/webm";
+
+ return "video/mp4";
+}
+
+scoped_ptr<EncryptedMediaPlayerSupport> EncryptedMediaPlayerSupport::create(
+ blink::WebMediaPlayerClient* client) {
+ return scoped_ptr<EncryptedMediaPlayerSupport>(
+ new EncryptedMediaPlayerSupportImpl(client));
+}
+
+EncryptedMediaPlayerSupportImpl::EncryptedMediaPlayerSupportImpl(
+ blink::WebMediaPlayerClient* client)
+ : client_(client),
+ web_cdm_(NULL) {
+}
+
+EncryptedMediaPlayerSupportImpl::~EncryptedMediaPlayerSupportImpl() {
+}
+
+WebMediaPlayer::MediaKeyException
+EncryptedMediaPlayerSupportImpl::GenerateKeyRequest(
+ blink::WebLocalFrame* frame,
+ const WebString& key_system,
+ const unsigned char* init_data,
+ unsigned init_data_length) {
+ DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
+ << std::string(reinterpret_cast<const char*>(init_data),
+ static_cast<size_t>(init_data_length));
+
+ std::string ascii_key_system =
+ GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
+
+ WebMediaPlayer::MediaKeyException e =
+ GenerateKeyRequestInternal(frame, ascii_key_system, init_data,
+ init_data_length);
+ ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
+ return e;
+}
+
+
+WebMediaPlayer::MediaKeyException
+EncryptedMediaPlayerSupportImpl::GenerateKeyRequestInternal(
+ blink::WebLocalFrame* frame,
+ const std::string& key_system,
+ const unsigned char* init_data,
+ unsigned init_data_length) {
+ if (!IsConcreteSupportedKeySystem(key_system))
+ return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
+
+ // We do not support run-time switching between key systems for now.
+ if (current_key_system_.empty()) {
+ if (!proxy_decryptor_) {
+ proxy_decryptor_.reset(new ProxyDecryptor(
+#if defined(ENABLE_PEPPER_CDMS)
+ // Create() must be called synchronously as |frame| may not be
+ // valid afterwards.
+ base::Bind(&PepperCdmWrapperImpl::Create, frame),
+#elif defined(ENABLE_BROWSER_CDMS)
+#error Browser side CDM in WMPI for prefixed EME API not supported yet.
+#endif
+ BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupportImpl::OnKeyAdded),
+ BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupportImpl::OnKeyError),
+ BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupportImpl::OnKeyMessage)));
+ }
+
+ GURL security_origin(frame->document().securityOrigin().toString());
+ if (!proxy_decryptor_->InitializeCDM(key_system, security_origin))
+ return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
+
+ if (proxy_decryptor_ && !decryptor_ready_cb_.is_null()) {
+ base::ResetAndReturn(&decryptor_ready_cb_)
+ .Run(proxy_decryptor_->GetDecryptor(), base::Bind(DoNothing));
+ }
+
+ current_key_system_ = key_system;
+ } else if (key_system != current_key_system_) {
+ return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
+ }
+
+ std::string init_data_type = init_data_type_;
+ if (init_data_type.empty())
+ init_data_type = GuessInitDataType(init_data, init_data_length);
+
+ // TODO(xhwang): We assume all streams are from the same container (thus have
+ // the same "type") for now. In the future, the "type" should be passed down
+ // from the application.
+ if (!proxy_decryptor_->GenerateKeyRequest(
+ init_data_type, init_data, init_data_length)) {
+ current_key_system_.clear();
+ return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
+ }
+
+ return WebMediaPlayer::MediaKeyExceptionNoError;
+}
+
+WebMediaPlayer::MediaKeyException EncryptedMediaPlayerSupportImpl::AddKey(
+ const WebString& key_system,
+ const unsigned char* key,
+ unsigned key_length,
+ const unsigned char* init_data,
+ unsigned init_data_length,
+ const WebString& session_id) {
+ DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
+ << std::string(reinterpret_cast<const char*>(key),
+ static_cast<size_t>(key_length)) << ", "
+ << std::string(reinterpret_cast<const char*>(init_data),
+ static_cast<size_t>(init_data_length)) << " ["
+ << base::string16(session_id) << "]";
+
+ std::string ascii_key_system =
+ GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
+ std::string ascii_session_id = ToASCIIOrEmpty(session_id);
+
+ WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
+ key,
+ key_length,
+ init_data,
+ init_data_length,
+ ascii_session_id);
+ ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
+ return e;
+}
+
+WebMediaPlayer::MediaKeyException
+EncryptedMediaPlayerSupportImpl::AddKeyInternal(
+ const std::string& key_system,
+ const unsigned char* key,
+ unsigned key_length,
+ const unsigned char* init_data,
+ unsigned init_data_length,
+ const std::string& session_id) {
+ DCHECK(key);
+ DCHECK_GT(key_length, 0u);
+
+ if (!IsConcreteSupportedKeySystem(key_system))
+ return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
+
+ if (current_key_system_.empty() || key_system != current_key_system_)
+ return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
+
+ proxy_decryptor_->AddKey(
+ key, key_length, init_data, init_data_length, session_id);
+ return WebMediaPlayer::MediaKeyExceptionNoError;
+}
+
+WebMediaPlayer::MediaKeyException
+EncryptedMediaPlayerSupportImpl::CancelKeyRequest(
+ const WebString& key_system,
+ const WebString& session_id) {
+ DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
+ << " [" << base::string16(session_id) << "]";
+
+ std::string ascii_key_system =
+ GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
+ std::string ascii_session_id = ToASCIIOrEmpty(session_id);
+
+ WebMediaPlayer::MediaKeyException e =
+ CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
+ ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
+ return e;
+}
+
+WebMediaPlayer::MediaKeyException
+EncryptedMediaPlayerSupportImpl::CancelKeyRequestInternal(
+ const std::string& key_system,
+ const std::string& session_id) {
+ if (!IsConcreteSupportedKeySystem(key_system))
+ return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
+
+ if (current_key_system_.empty() || key_system != current_key_system_)
+ return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
+
+ proxy_decryptor_->CancelKeyRequest(session_id);
+ return WebMediaPlayer::MediaKeyExceptionNoError;
+}
+
+void EncryptedMediaPlayerSupportImpl::SetContentDecryptionModule(
+ blink::WebContentDecryptionModule* cdm) {
+ // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
+ if (!cdm)
+ return;
+
+ web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
+
+ if (web_cdm_ && !decryptor_ready_cb_.is_null())
+ base::ResetAndReturn(&decryptor_ready_cb_)
+ .Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing));
+}
+
+void EncryptedMediaPlayerSupportImpl::SetContentDecryptionModule(
+ blink::WebContentDecryptionModule* cdm,
+ blink::WebContentDecryptionModuleResult result) {
+ // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
+ if (!cdm) {
+ result.completeWithError(
+ blink::WebContentDecryptionModuleExceptionNotSupportedError,
+ 0,
+ "Null MediaKeys object is not supported.");
+ return;
+ }
+
+ web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
+
+ if (web_cdm_ && !decryptor_ready_cb_.is_null()) {
+ base::ResetAndReturn(&decryptor_ready_cb_)
+ .Run(web_cdm_->GetDecryptor(), BIND_TO_RENDER_LOOP1(
+ &EncryptedMediaPlayerSupportImpl::ContentDecryptionModuleAttached,
+ result));
+ } else {
+ // No pipeline/decoder connected, so resolve the promise. When something
+ // is connected, setting the CDM will happen in SetDecryptorReadyCB().
+ ContentDecryptionModuleAttached(result, true);
+ }
+}
+
+void EncryptedMediaPlayerSupportImpl::SetContentDecryptionModuleSync(
+ blink::WebContentDecryptionModule* cdm) {
+ // Used when loading media and no pipeline/decoder attached yet.
+ DCHECK(decryptor_ready_cb_.is_null());
+
+ web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
+}
+
+void EncryptedMediaPlayerSupportImpl::ContentDecryptionModuleAttached(
+ blink::WebContentDecryptionModuleResult result,
+ bool success) {
+ if (success) {
+ result.complete();
+ return;
+ }
+
+ result.completeWithError(
+ blink::WebContentDecryptionModuleExceptionNotSupportedError,
+ 0,
+ "Unable to set MediaKeys object");
+}
+
+media::SetDecryptorReadyCB
+EncryptedMediaPlayerSupportImpl::CreateSetDecryptorReadyCB() {
+ return BIND_TO_RENDER_LOOP(
+ &EncryptedMediaPlayerSupportImpl::SetDecryptorReadyCB);
+}
+
+media::Demuxer::NeedKeyCB
+EncryptedMediaPlayerSupportImpl::CreateNeedKeyCB() {
+ return BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupportImpl::OnNeedKey);
+}
+
+void EncryptedMediaPlayerSupportImpl::OnPipelineDecryptError() {
+ EmeUMAHistogramCounts(current_key_system_, "DecryptError", 1);
+}
+
+void EncryptedMediaPlayerSupportImpl::OnNeedKey(const std::string& type,
+ const std::vector<uint8>& init_data) {
+ // Do not fire NeedKey event if encrypted media is not enabled.
+ if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
+ !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
+ return;
+ }
+
+ UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
+
+ DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
+ if (init_data_type_.empty())
+ init_data_type_ = type;
+
+ const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
+ client_->keyNeeded(
+ WebString::fromUTF8(type), init_data_ptr, init_data.size());
+}
+
+void EncryptedMediaPlayerSupportImpl::OnKeyAdded(
+ const std::string& session_id) {
+ EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
+ client_->keyAdded(
+ WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
+ WebString::fromUTF8(session_id));
+}
+
+void EncryptedMediaPlayerSupportImpl::OnKeyError(const std::string& session_id,
+ media::MediaKeys::KeyError error_code,
+ uint32 system_code) {
+ EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
+ error_code, media::MediaKeys::kMaxKeyError);
+
+ uint16 short_system_code = 0;
+ if (system_code > std::numeric_limits<uint16>::max()) {
+ LOG(WARNING) << "system_code exceeds unsigned short limit.";
+ short_system_code = std::numeric_limits<uint16>::max();
+ } else {
+ short_system_code = static_cast<uint16>(system_code);
+ }
+
+ client_->keyError(
+ WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
+ WebString::fromUTF8(session_id),
+ static_cast<WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
+ short_system_code);
+}
+
+void EncryptedMediaPlayerSupportImpl::OnKeyMessage(
+ const std::string& session_id,
+ const std::vector<uint8>& message,
+ const GURL& destination_url) {
+ DCHECK(destination_url.is_empty() || destination_url.is_valid());
+
+ client_->keyMessage(
+ WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
+ WebString::fromUTF8(session_id),
+ message.empty() ? NULL : &message[0],
+ message.size(),
+ destination_url);
+}
+
+void EncryptedMediaPlayerSupportImpl::SetDecryptorReadyCB(
+ const media::DecryptorReadyCB& decryptor_ready_cb) {
+ // Cancels the previous decryptor request.
+ if (decryptor_ready_cb.is_null()) {
+ if (!decryptor_ready_cb_.is_null()) {
+ base::ResetAndReturn(&decryptor_ready_cb_)
+ .Run(NULL, base::Bind(DoNothing));
+ }
+ return;
+ }
+
+ // TODO(xhwang): Support multiple decryptor notification request (e.g. from
+ // video and audio). The current implementation is okay for the current
+ // media pipeline since we initialize audio and video decoders in sequence.
+ // But WebMediaPlayerImpl should not depend on media pipeline's implementation
+ // detail.
+ DCHECK(decryptor_ready_cb_.is_null());
+
+ // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink.
+ DCHECK(!proxy_decryptor_ || !web_cdm_);
+
+ if (proxy_decryptor_) {
+ decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor(),
+ base::Bind(DoNothing));
+ return;
+ }
+
+ if (web_cdm_) {
+ decryptor_ready_cb.Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing));
+ return;
+ }
+
+ decryptor_ready_cb_ = decryptor_ready_cb;
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698