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 #ifndef MEDIA_CDM_PPAPI_CDM_WRAPPER_H_ 6 #define MEDIA_CDM_PPAPI_CDM_WRAPPER_H_ 7 8 #include <map> 9 #include <queue> 10 #include <string> 11 12 #include "base/basictypes.h" 13 #include "media/cdm/ppapi/api/content_decryption_module.h" 14 #include "media/cdm/ppapi/cdm_helpers.h" 15 #include "media/cdm/ppapi/supported_cdm_versions.h" 16 #include "ppapi/cpp/logging.h" 17 18 namespace media { 19 20 // CdmWrapper wraps different versions of ContentDecryptionModule interfaces and 21 // exposes a common interface to the caller. 22 // 23 // The caller should call CdmWrapper::Create() to create a CDM instance. 24 // CdmWrapper will first try to create a CDM instance that supports the latest 25 // CDM interface (ContentDecryptionModule). If such an instance cannot be 26 // created (e.g. an older CDM was loaded), CdmWrapper will try to create a CDM 27 // that supports an older version of CDM interface (e.g. 28 // ContentDecryptionModule_*). Internally CdmWrapper converts the CdmWrapper 29 // calls to corresponding ContentDecryptionModule calls. 30 // 31 // Note that CdmWrapper interface always reflects the latest state of content 32 // decryption related PPAPI APIs (e.g. pp::ContentDecryptor_Private). 33 // 34 // Since this file is highly templated and default implementations are short 35 // (just a shim layer in most cases), everything is done in this header file. 36 class CdmWrapper { 37 public: 38 // CDM_1 and CDM_2 methods AddKey() and CancelKeyRequest() may require 39 // callbacks to fire. Use this enum to indicate the additional calls required. 40 // TODO(jrummell): Remove return value once CDM_1 and CDM_2 are no longer 41 // supported. 42 enum Result { 43 NO_ACTION, 44 CALL_KEY_ADDED, 45 CALL_KEY_ERROR 46 }; 47 48 static CdmWrapper* Create(const char* key_system, 49 uint32_t key_system_size, 50 GetCdmHostFunc get_cdm_host_func, 51 void* user_data); 52 53 virtual ~CdmWrapper() {}; 54 55 virtual void CreateSession(uint32_t session_id, 56 const char* type, 57 uint32_t type_size, 58 const uint8_t* init_data, 59 uint32_t init_data_size) = 0; 60 virtual Result UpdateSession(uint32_t session_id, 61 const uint8_t* response, 62 uint32_t response_size) = 0; 63 virtual Result ReleaseSession(uint32_t session_id) = 0; 64 virtual void TimerExpired(void* context) = 0; 65 virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer, 66 cdm::DecryptedBlock* decrypted_buffer) = 0; 67 virtual cdm::Status InitializeAudioDecoder( 68 const cdm::AudioDecoderConfig& audio_decoder_config) = 0; 69 virtual cdm::Status InitializeVideoDecoder( 70 const cdm::VideoDecoderConfig& video_decoder_config) = 0; 71 virtual void DeinitializeDecoder(cdm::StreamType decoder_type) = 0; 72 virtual void ResetDecoder(cdm::StreamType decoder_type) = 0; 73 virtual cdm::Status DecryptAndDecodeFrame( 74 const cdm::InputBuffer& encrypted_buffer, 75 cdm::VideoFrame* video_frame) = 0; 76 virtual cdm::Status DecryptAndDecodeSamples( 77 const cdm::InputBuffer& encrypted_buffer, 78 cdm::AudioFrames* audio_frames) = 0; 79 virtual void OnPlatformChallengeResponse( 80 const cdm::PlatformChallengeResponse& response) = 0; 81 virtual void OnQueryOutputProtectionStatus( 82 uint32_t link_mask, 83 uint32_t output_protection_mask) = 0; 84 85 // ContentDecryptionModule_1 and ContentDecryptionModule_2 interface methods 86 // AddKey() and CancelKeyRequest() (older versions of UpdateSession() and 87 // ReleaseSession(), respectively) pass in the web_session_id rather than the 88 // session_id. As well, Host_1 and Host_2 callbacks SendKeyMessage() and 89 // SendKeyError() include the web_session_id, but the actual callbacks need 90 // session_id. 91 // 92 // The following functions maintain the session_id <-> web_session_id mapping. 93 // These can be removed once _1 and _2 interfaces are no longer supported. 94 95 // Determine the corresponding session_id for |web_session_id|. 96 virtual uint32_t LookupSessionId(const std::string& web_session_id) = 0; 97 98 // Determine the corresponding session_id for |session_id|. 99 virtual const std::string LookupWebSessionId(uint32_t session_id) = 0; 100 101 // Map between session_id and web_session_id. 102 // TODO(jrummell): The following can be removed once CDM_1 and CDM_2 are 103 // no longer supported. 104 typedef std::map<uint32_t, std::string> SessionMap; 105 SessionMap session_map_; 106 107 static const uint32_t kInvalidSessionId = 0; 108 109 // As the response from PrefixedGenerateKeyRequest() may be synchronous or 110 // asynchronous, keep track of the current request during the call to handle 111 // synchronous responses or errors. If no response received, add this request 112 // to a queue and assume that the subsequent responses come back in the order 113 // issued. 114 // TODO(jrummell): Remove once all supported CDM host interfaces support 115 // session_id. 116 uint32_t current_key_request_session_id_; 117 std::queue<uint32_t> pending_key_request_session_ids_; 118 119 protected: 120 CdmWrapper() : current_key_request_session_id_(kInvalidSessionId) {} 121 122 private: 123 DISALLOW_COPY_AND_ASSIGN(CdmWrapper); 124 }; 125 126 // Template class that does the CdmWrapper -> CdmInterface conversion. Default 127 // implementations are provided. Any methods that need special treatment should 128 // be specialized. 129 template <class CdmInterface> 130 class CdmWrapperImpl : public CdmWrapper { 131 public: 132 static CdmWrapper* Create(const char* key_system, 133 uint32_t key_system_size, 134 GetCdmHostFunc get_cdm_host_func, 135 void* user_data) { 136 void* cdm_instance = ::CreateCdmInstance( 137 CdmInterface::kVersion, key_system, key_system_size, get_cdm_host_func, 138 user_data); 139 if (!cdm_instance) 140 return NULL; 141 142 return new CdmWrapperImpl<CdmInterface>( 143 static_cast<CdmInterface*>(cdm_instance)); 144 } 145 146 virtual ~CdmWrapperImpl() { 147 cdm_->Destroy(); 148 } 149 150 virtual void CreateSession(uint32_t session_id, 151 const char* type, 152 uint32_t type_size, 153 const uint8_t* init_data, 154 uint32_t init_data_size) OVERRIDE { 155 cdm_->CreateSession(session_id, type, type_size, init_data, init_data_size); 156 } 157 158 virtual Result UpdateSession(uint32_t session_id, 159 const uint8_t* response, 160 uint32_t response_size) OVERRIDE { 161 cdm_->UpdateSession(session_id, response, response_size); 162 return NO_ACTION; 163 } 164 165 virtual Result ReleaseSession(uint32_t session_id) OVERRIDE { 166 cdm_->ReleaseSession(session_id); 167 return NO_ACTION; 168 } 169 170 virtual void TimerExpired(void* context) OVERRIDE { 171 cdm_->TimerExpired(context); 172 } 173 174 virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer, 175 cdm::DecryptedBlock* decrypted_buffer) OVERRIDE { 176 return cdm_->Decrypt(encrypted_buffer, decrypted_buffer); 177 } 178 179 virtual cdm::Status InitializeAudioDecoder( 180 const cdm::AudioDecoderConfig& audio_decoder_config) OVERRIDE { 181 return cdm_->InitializeAudioDecoder(audio_decoder_config); 182 } 183 184 virtual cdm::Status InitializeVideoDecoder( 185 const cdm::VideoDecoderConfig& video_decoder_config) OVERRIDE { 186 return cdm_->InitializeVideoDecoder(video_decoder_config); 187 } 188 189 virtual void DeinitializeDecoder(cdm::StreamType decoder_type) OVERRIDE { 190 cdm_->DeinitializeDecoder(decoder_type); 191 } 192 193 virtual void ResetDecoder(cdm::StreamType decoder_type) OVERRIDE { 194 cdm_->ResetDecoder(decoder_type); 195 } 196 197 virtual cdm::Status DecryptAndDecodeFrame( 198 const cdm::InputBuffer& encrypted_buffer, 199 cdm::VideoFrame* video_frame) OVERRIDE { 200 return cdm_->DecryptAndDecodeFrame(encrypted_buffer, video_frame); 201 } 202 203 virtual cdm::Status DecryptAndDecodeSamples( 204 const cdm::InputBuffer& encrypted_buffer, 205 cdm::AudioFrames* audio_frames) OVERRIDE { 206 return cdm_->DecryptAndDecodeSamples(encrypted_buffer, audio_frames); 207 } 208 209 virtual void OnPlatformChallengeResponse( 210 const cdm::PlatformChallengeResponse& response) OVERRIDE { 211 cdm_->OnPlatformChallengeResponse(response); 212 } 213 214 virtual void OnQueryOutputProtectionStatus( 215 uint32_t link_mask, 216 uint32_t output_protection_mask) OVERRIDE { 217 cdm_->OnQueryOutputProtectionStatus(link_mask, output_protection_mask); 218 } 219 220 uint32_t LookupSessionId(const std::string& web_session_id) { 221 for (SessionMap::iterator it = session_map_.begin(); 222 it != session_map_.end(); 223 ++it) { 224 if (it->second == web_session_id) 225 return it->first; 226 } 227 228 // There is no entry in the map; assume it came from the current 229 // PrefixedGenerateKeyRequest() call (if possible). If no current request, 230 // assume it came from the oldest PrefixedGenerateKeyRequest() call. 231 uint32_t session_id = current_key_request_session_id_; 232 if (current_key_request_session_id_) { 233 // Only 1 response is allowed for the current 234 // PrefixedGenerateKeyRequest(). 235 current_key_request_session_id_ = kInvalidSessionId; 236 } else { 237 PP_DCHECK(!pending_key_request_session_ids_.empty()); 238 session_id = pending_key_request_session_ids_.front(); 239 pending_key_request_session_ids_.pop(); 240 } 241 242 // If this is a valid |session_id|, add it to the list. Otherwise, avoid 243 // adding empty string as a mapping to prevent future calls with an empty 244 // string from using the wrong session_id. 245 if (!web_session_id.empty()) { 246 PP_DCHECK(session_map_.find(session_id) == session_map_.end()); 247 session_map_[session_id] = web_session_id; 248 } 249 250 return session_id; 251 } 252 253 const std::string LookupWebSessionId(uint32_t session_id) { 254 // Session may not exist if error happens during CreateSession(). 255 SessionMap::iterator it = session_map_.find(session_id); 256 return (it != session_map_.end()) ? it->second : std::string(); 257 } 258 259 private: 260 CdmWrapperImpl(CdmInterface* cdm) : cdm_(cdm) { 261 PP_DCHECK(cdm_); 262 } 263 264 CdmInterface* cdm_; 265 266 DISALLOW_COPY_AND_ASSIGN(CdmWrapperImpl); 267 }; 268 269 // For ContentDecryptionModule_1 and ContentDecryptionModule_2, 270 // CreateSession(), UpdateSession(), and ReleaseSession() call methods 271 // are incompatible with ContentDecryptionModule_3. Use the following 272 // templated functions to handle this. 273 274 template <class CdmInterface> 275 void PrefixedGenerateKeyRequest(CdmWrapper* wrapper, 276 CdmInterface* cdm, 277 uint32_t session_id, 278 const char* type, 279 uint32_t type_size, 280 const uint8_t* init_data, 281 uint32_t init_data_size) { 282 // As it is possible for CDMs to reply synchronously during the call to 283 // GenerateKeyRequest(), keep track of |session_id|. 284 wrapper->current_key_request_session_id_ = session_id; 285 286 cdm::Status status = 287 cdm->GenerateKeyRequest(type, type_size, init_data, init_data_size); 288 PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError); 289 if (status != cdm::kSuccess) { 290 // If GenerateKeyRequest() failed, no subsequent asynchronous replies 291 // will be sent. Verify that a response was sent synchronously. 292 PP_DCHECK(wrapper->current_key_request_session_id_ == 293 CdmWrapper::kInvalidSessionId); 294 wrapper->current_key_request_session_id_ = CdmWrapper::kInvalidSessionId; 295 return; 296 } 297 298 if (wrapper->current_key_request_session_id_) { 299 // If this request is still pending (SendKeyMessage() or SendKeyError() 300 // not called synchronously), add |session_id| to the end of the queue. 301 // Without CDM support, it is impossible to match SendKeyMessage() 302 // (or SendKeyError()) responses to the |session_id|. Doing the best 303 // we can by keeping track of this in a queue, and assuming the responses 304 // come back in order. 305 wrapper->pending_key_request_session_ids_.push(session_id); 306 wrapper->current_key_request_session_id_ = CdmWrapper::kInvalidSessionId; 307 } 308 } 309 310 template <class CdmInterface> 311 CdmWrapper::Result PrefixedAddKey(CdmWrapper* wrapper, 312 CdmInterface* cdm, 313 uint32_t session_id, 314 const uint8_t* response, 315 uint32_t response_size) { 316 const std::string web_session_id = wrapper->LookupWebSessionId(session_id); 317 if (web_session_id.empty()) { 318 // Possible if UpdateSession() called before CreateSession(). 319 return CdmWrapper::CALL_KEY_ERROR; 320 } 321 322 // CDM_1 and CDM_2 accept initdata, which is no longer needed. 323 // In it's place pass in NULL. 324 cdm::Status status = cdm->AddKey(web_session_id.data(), web_session_id.size(), 325 response, response_size, 326 NULL, 0); 327 PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError); 328 if (status != cdm::kSuccess) { 329 // Some CDMs using Host_1/2 don't call keyerror, so send one. 330 return CdmWrapper::CALL_KEY_ERROR; 331 } 332 333 return CdmWrapper::CALL_KEY_ADDED; 334 } 335 336 template <class CdmInterface> 337 CdmWrapper::Result PrefixedCancelKeyRequest(CdmWrapper* wrapper, 338 CdmInterface* cdm, 339 uint32_t session_id) { 340 const std::string web_session_id = wrapper->LookupWebSessionId(session_id); 341 if (web_session_id.empty()) { 342 // Possible if ReleaseSession() called before CreateSession(). 343 return CdmWrapper::CALL_KEY_ERROR; 344 } 345 346 wrapper->session_map_.erase(session_id); 347 cdm::Status status = 348 cdm->CancelKeyRequest(web_session_id.data(), web_session_id.size()); 349 350 PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError); 351 if (status != cdm::kSuccess) { 352 // Some CDMs using Host_1/2 don't call keyerror, so send one. 353 return CdmWrapper::CALL_KEY_ERROR; 354 } 355 356 return CdmWrapper::NO_ACTION; 357 } 358 359 // Specializations for ContentDecryptionModule_1. 360 361 template <> 362 void CdmWrapperImpl<cdm::ContentDecryptionModule_1>::CreateSession( 363 uint32_t session_id, 364 const char* type, 365 uint32_t type_size, 366 const uint8_t* init_data, 367 uint32_t init_data_size) { 368 PrefixedGenerateKeyRequest( 369 this, cdm_, session_id, type, type_size, init_data, init_data_size); 370 } 371 372 template <> 373 CdmWrapper::Result CdmWrapperImpl< 374 cdm::ContentDecryptionModule_1>::UpdateSession(uint32_t session_id, 375 const uint8_t* response, 376 uint32_t response_size) { 377 return PrefixedAddKey(this, cdm_, session_id, response, response_size); 378 } 379 380 template <> 381 CdmWrapper::Result CdmWrapperImpl< 382 cdm::ContentDecryptionModule_1>::ReleaseSession(uint32_t session_id) { 383 return PrefixedCancelKeyRequest(this, cdm_, session_id); 384 } 385 386 template <> void CdmWrapperImpl<cdm::ContentDecryptionModule_1>:: 387 OnPlatformChallengeResponse( 388 const cdm::PlatformChallengeResponse& response) { 389 PP_NOTREACHED(); 390 } 391 392 template <> void CdmWrapperImpl<cdm::ContentDecryptionModule_1>:: 393 OnQueryOutputProtectionStatus(uint32_t link_mask, 394 uint32_t output_protection_mask) { 395 PP_NOTREACHED(); 396 } 397 398 template <> cdm::Status CdmWrapperImpl<cdm::ContentDecryptionModule_1>:: 399 DecryptAndDecodeSamples(const cdm::InputBuffer& encrypted_buffer, 400 cdm::AudioFrames* audio_frames) { 401 AudioFramesImpl audio_frames_1; 402 cdm::Status status = 403 cdm_->DecryptAndDecodeSamples(encrypted_buffer, &audio_frames_1); 404 if (status != cdm::kSuccess) 405 return status; 406 407 audio_frames->SetFrameBuffer(audio_frames_1.PassFrameBuffer()); 408 audio_frames->SetFormat(cdm::kAudioFormatS16); 409 return cdm::kSuccess; 410 } 411 412 // Specializations for ContentDecryptionModule_2. 413 414 template <> 415 void CdmWrapperImpl<cdm::ContentDecryptionModule_2>::CreateSession( 416 uint32_t session_id, 417 const char* type, 418 uint32_t type_size, 419 const uint8_t* init_data, 420 uint32_t init_data_size) { 421 PrefixedGenerateKeyRequest( 422 this, cdm_, session_id, type, type_size, init_data, init_data_size); 423 } 424 425 template <> 426 CdmWrapper::Result CdmWrapperImpl< 427 cdm::ContentDecryptionModule_2>::UpdateSession(uint32_t session_id, 428 const uint8_t* response, 429 uint32_t response_size) { 430 return PrefixedAddKey(this, cdm_, session_id, response, response_size); 431 } 432 433 template <> 434 CdmWrapper::Result CdmWrapperImpl< 435 cdm::ContentDecryptionModule_2>::ReleaseSession(uint32_t session_id) { 436 return PrefixedCancelKeyRequest(this, cdm_, session_id); 437 } 438 439 CdmWrapper* CdmWrapper::Create(const char* key_system, 440 uint32_t key_system_size, 441 GetCdmHostFunc get_cdm_host_func, 442 void* user_data) { 443 COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion == 444 cdm::ContentDecryptionModule_3::kVersion, 445 update_code_below); 446 447 // Ensure IsSupportedCdmInterfaceVersion matches this implementation. 448 // Always update this DCHECK when updating this function. 449 // If this check fails, update this function and DCHECK or update 450 // IsSupportedCdmInterfaceVersion. 451 PP_DCHECK( 452 !IsSupportedCdmInterfaceVersion( 453 cdm::ContentDecryptionModule::kVersion + 1) && 454 IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule::kVersion) && 455 IsSupportedCdmInterfaceVersion( 456 cdm::ContentDecryptionModule_2::kVersion) && 457 IsSupportedCdmInterfaceVersion( 458 cdm::ContentDecryptionModule_1::kVersion) && 459 !IsSupportedCdmInterfaceVersion( 460 cdm::ContentDecryptionModule_1::kVersion - 1)); 461 462 // Try to create the CDM using the latest CDM interface version. 463 CdmWrapper* cdm_wrapper = 464 CdmWrapperImpl<cdm::ContentDecryptionModule>::Create( 465 key_system, key_system_size, get_cdm_host_func, user_data); 466 if (cdm_wrapper) 467 return cdm_wrapper; 468 469 // Try to see if the CDM supports older version(s) of the CDM interface. 470 cdm_wrapper = CdmWrapperImpl<cdm::ContentDecryptionModule_2>::Create( 471 key_system, key_system_size, get_cdm_host_func, user_data); 472 if (cdm_wrapper) 473 return cdm_wrapper; 474 475 cdm_wrapper = CdmWrapperImpl<cdm::ContentDecryptionModule_1>::Create( 476 key_system, key_system_size, get_cdm_host_func, user_data); 477 return cdm_wrapper; 478 } 479 480 // When updating the CdmAdapter, ensure you've updated the CdmWrapper to contain 481 // stub implementations for new or modified methods that the older CDM interface 482 // does not have. 483 // Also update supported_cdm_versions.h. 484 COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion == 485 cdm::ContentDecryptionModule_3::kVersion, 486 ensure_cdm_wrapper_templates_have_old_version_support); 487 488 } // namespace media 489 490 #endif // MEDIA_CDM_PPAPI_CDM_WRAPPER_H_ 491