proxy_decryptor.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/renderer/media/crypto/proxy_decryptor.h" 6 7#include "base/bind.h" 8#include "base/callback_helpers.h" 9#include "base/logging.h" 10#include "base/strings/string_util.h" 11#include "content/renderer/media/crypto/content_decryption_module_factory.h" 12#if defined(OS_ANDROID) 13#include "content/renderer/media/android/renderer_media_player_manager.h" 14#endif // defined(OS_ANDROID) 15#include "media/cdm/json_web_key.h" 16#include "media/cdm/key_system_names.h" 17 18namespace content { 19 20// Since these reference IDs may conflict with the ones generated in 21// WebContentDecryptionModuleSessionImpl for the short time both paths are 22// active, start with 100000 and generate the IDs from there. 23// TODO(jrummell): Only allow one path http://crbug.com/306680. 24uint32 ProxyDecryptor::next_reference_id_ = 100000; 25 26const uint32 INVALID_REFERENCE_ID = 0; 27 28#if defined(ENABLE_PEPPER_CDMS) 29void ProxyDecryptor::DestroyHelperPlugin() { 30 ContentDecryptionModuleFactory::DestroyHelperPlugin( 31 web_media_player_client_, web_frame_); 32} 33#endif // defined(ENABLE_PEPPER_CDMS) 34 35ProxyDecryptor::ProxyDecryptor( 36#if defined(ENABLE_PEPPER_CDMS) 37 blink::WebMediaPlayerClient* web_media_player_client, 38 blink::WebFrame* web_frame, 39#elif defined(OS_ANDROID) 40 RendererMediaPlayerManager* manager, 41 int media_keys_id, 42#endif // defined(ENABLE_PEPPER_CDMS) 43 const KeyAddedCB& key_added_cb, 44 const KeyErrorCB& key_error_cb, 45 const KeyMessageCB& key_message_cb) 46 : weak_ptr_factory_(this), 47#if defined(ENABLE_PEPPER_CDMS) 48 web_media_player_client_(web_media_player_client), 49 web_frame_(web_frame), 50#elif defined(OS_ANDROID) 51 manager_(manager), 52 media_keys_id_(media_keys_id), 53#endif // defined(ENABLE_PEPPER_CDMS) 54 key_added_cb_(key_added_cb), 55 key_error_cb_(key_error_cb), 56 key_message_cb_(key_message_cb), 57 is_clear_key_(false) { 58 DCHECK(!key_added_cb_.is_null()); 59 DCHECK(!key_error_cb_.is_null()); 60 DCHECK(!key_message_cb_.is_null()); 61} 62 63ProxyDecryptor::~ProxyDecryptor() { 64 // Destroy the decryptor explicitly before destroying the plugin. 65 { 66 base::AutoLock auto_lock(lock_); 67 media_keys_.reset(); 68 } 69} 70 71// TODO(xhwang): Support multiple decryptor notification request (e.g. from 72// video and audio decoders). The current implementation is okay for the current 73// media pipeline since we initialize audio and video decoders in sequence. 74// But ProxyDecryptor should not depend on media pipeline's implementation 75// detail. 76void ProxyDecryptor::SetDecryptorReadyCB( 77 const media::DecryptorReadyCB& decryptor_ready_cb) { 78 base::AutoLock auto_lock(lock_); 79 80 // Cancels the previous decryptor request. 81 if (decryptor_ready_cb.is_null()) { 82 if (!decryptor_ready_cb_.is_null()) 83 base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL); 84 return; 85 } 86 87 // Normal decryptor request. 88 DCHECK(decryptor_ready_cb_.is_null()); 89 if (media_keys_) { 90 decryptor_ready_cb.Run(media_keys_->GetDecryptor()); 91 return; 92 } 93 decryptor_ready_cb_ = decryptor_ready_cb; 94} 95 96bool ProxyDecryptor::InitializeCDM(const std::string& key_system, 97 const GURL& frame_url) { 98 DVLOG(1) << "InitializeCDM: key_system = " << key_system; 99 100 base::AutoLock auto_lock(lock_); 101 102 DCHECK(!media_keys_); 103 media_keys_ = CreateMediaKeys(key_system, frame_url); 104 if (!media_keys_) 105 return false; 106 107 if (!decryptor_ready_cb_.is_null()) 108 base::ResetAndReturn(&decryptor_ready_cb_).Run(media_keys_->GetDecryptor()); 109 110 is_clear_key_ = 111 media::IsClearKey(key_system) || media::IsExternalClearKey(key_system); 112 return true; 113} 114 115bool ProxyDecryptor::GenerateKeyRequest(const std::string& type, 116 const uint8* init_data, 117 int init_data_length) { 118 // Use a unique reference id for this request. 119 uint32 reference_id = next_reference_id_++; 120 if (!media_keys_->GenerateKeyRequest( 121 reference_id, type, init_data, init_data_length)) { 122 media_keys_.reset(); 123 return false; 124 } 125 126 return true; 127} 128 129void ProxyDecryptor::AddKey(const uint8* key, 130 int key_length, 131 const uint8* init_data, 132 int init_data_length, 133 const std::string& session_id) { 134 DVLOG(1) << "AddKey()"; 135 136 // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called. 137 uint32 reference_id = LookupReferenceId(session_id); 138 if (reference_id == INVALID_REFERENCE_ID) { 139 // Session hasn't been referenced before, so it is an error. 140 // Note that the specification says "If sessionId is not null and is 141 // unrecognized, throw an INVALID_ACCESS_ERR." However, for backwards 142 // compatibility the error is not thrown, but rather reported as a 143 // KeyError. 144 key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0); 145 return; 146 } 147 148 // EME WD spec only supports a single array passed to the CDM. For 149 // Clear Key using v0.1b, both arrays are used (|init_data| is key_id). 150 // Since the EME WD spec supports the key as a JSON Web Key, 151 // convert the 2 arrays to a JWK and pass it as the single array. 152 if (is_clear_key_) { 153 // Decryptor doesn't support empty key ID (see http://crbug.com/123265). 154 // So ensure a non-empty value is passed. 155 if (!init_data) { 156 static const uint8 kDummyInitData[1] = {0}; 157 init_data = kDummyInitData; 158 init_data_length = arraysize(kDummyInitData); 159 } 160 161 std::string jwk = 162 media::GenerateJWKSet(key, key_length, init_data, init_data_length); 163 DCHECK(!jwk.empty()); 164 media_keys_->AddKey(reference_id, 165 reinterpret_cast<const uint8*>(jwk.data()), jwk.size(), 166 NULL, 0); 167 return; 168 } 169 170 media_keys_->AddKey(reference_id, key, key_length, NULL, 0); 171} 172 173void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) { 174 DVLOG(1) << "CancelKeyRequest()"; 175 176 // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called. 177 uint32 reference_id = LookupReferenceId(session_id); 178 if (reference_id == INVALID_REFERENCE_ID) { 179 // Session hasn't been created, so it is an error. 180 key_error_cb_.Run( 181 std::string(), media::MediaKeys::kUnknownError, 0); 182 } 183 else { 184 media_keys_->CancelKeyRequest(reference_id); 185 } 186} 187 188scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys( 189 const std::string& key_system, 190 const GURL& frame_url) { 191 return ContentDecryptionModuleFactory::Create( 192 key_system, 193#if defined(ENABLE_PEPPER_CDMS) 194 web_media_player_client_, 195 web_frame_, 196 base::Bind(&ProxyDecryptor::DestroyHelperPlugin, 197 weak_ptr_factory_.GetWeakPtr()), 198#elif defined(OS_ANDROID) 199 manager_, 200 media_keys_id_, 201 frame_url, 202#endif // defined(ENABLE_PEPPER_CDMS) 203 base::Bind(&ProxyDecryptor::KeyAdded, weak_ptr_factory_.GetWeakPtr()), 204 base::Bind(&ProxyDecryptor::KeyError, weak_ptr_factory_.GetWeakPtr()), 205 base::Bind(&ProxyDecryptor::KeyMessage, weak_ptr_factory_.GetWeakPtr()), 206 base::Bind(&ProxyDecryptor::SetSessionId, 207 weak_ptr_factory_.GetWeakPtr())); 208} 209 210void ProxyDecryptor::KeyAdded(uint32 reference_id) { 211 // Assumes that SetSessionId() has been called before this. 212 key_added_cb_.Run(LookupSessionId(reference_id)); 213} 214 215void ProxyDecryptor::KeyError(uint32 reference_id, 216 media::MediaKeys::KeyError error_code, 217 int system_code) { 218 // Assumes that SetSessionId() has been called before this. 219 key_error_cb_.Run(LookupSessionId(reference_id), error_code, system_code); 220} 221 222void ProxyDecryptor::KeyMessage(uint32 reference_id, 223 const std::vector<uint8>& message, 224 const std::string& default_url) { 225 // Assumes that SetSessionId() has been called before this. 226 key_message_cb_.Run(LookupSessionId(reference_id), message, default_url); 227} 228 229void ProxyDecryptor::SetSessionId(uint32 reference_id, 230 const std::string& session_id) { 231 // Due to heartbeat messages, SetSessionId() can get called multiple times. 232 SessionIdMap::iterator it = sessions_.find(reference_id); 233 DCHECK(it == sessions_.end() || it->second == session_id); 234 if (it == sessions_.end()) 235 sessions_[reference_id] = session_id; 236} 237 238uint32 ProxyDecryptor::LookupReferenceId(const std::string& session_id) { 239 for (SessionIdMap::iterator it = sessions_.begin(); 240 it != sessions_.end(); 241 ++it) { 242 if (it->second == session_id) 243 return it->first; 244 } 245 246 // If |session_id| is null, then use the single reference id. 247 if (session_id.empty() && sessions_.size() == 1) 248 return sessions_.begin()->first; 249 250 return INVALID_REFERENCE_ID; 251} 252 253const std::string& ProxyDecryptor::LookupSessionId(uint32 reference_id) { 254 DCHECK_NE(reference_id, INVALID_REFERENCE_ID); 255 256 // Session may not exist if error happens during GenerateKeyRequest(). 257 SessionIdMap::iterator it = sessions_.find(reference_id); 258 return (it != sessions_.end()) ? it->second : EmptyString(); 259} 260 261} // namespace content 262