OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/media/crypto/ppapi_decryptor.h" | 5 #include "content/renderer/media/crypto/ppapi_decryptor.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback.h" | |
11 #include "base/callback_helpers.h" | |
12 #include "base/location.h" | |
13 #include "base/logging.h" | 10 #include "base/logging.h" |
14 #include "base/message_loop/message_loop.h" | 11 #include "base/message_loop/message_loop.h" |
15 #include "base/message_loop/message_loop_proxy.h" | 12 #include "base/message_loop/message_loop_proxy.h" |
16 #include "content/renderer/media/crypto/key_systems.h" | 13 #include "content/renderer/media/crypto/key_systems.h" |
17 #include "content/renderer/pepper/content_decryptor_delegate.h" | 14 #include "content/renderer/pepper/content_decryptor_delegate.h" |
18 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" | 15 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" |
19 #include "media/base/audio_decoder_config.h" | 16 #include "media/base/audio_decoder_config.h" |
20 #include "media/base/cdm_promise.h" | |
21 #include "media/base/data_buffer.h" | 17 #include "media/base/data_buffer.h" |
22 #include "media/base/decoder_buffer.h" | 18 #include "media/base/decoder_buffer.h" |
23 #include "media/base/video_decoder_config.h" | 19 #include "media/base/video_decoder_config.h" |
24 #include "media/base/video_frame.h" | 20 #include "media/base/video_frame.h" |
25 | 21 |
26 namespace content { | 22 namespace content { |
27 | 23 |
28 // This class is needed so that resolving an Update() promise triggers playback | |
29 // of the stream. It intercepts the resolve() call to invoke an additional | |
30 // callback. | |
31 class SessionUpdatedPromise : public media::CdmPromiseTemplate<> { | |
32 public: | |
33 SessionUpdatedPromise(scoped_ptr<media::SimpleCdmPromise> caller_promise, | |
34 const base::Closure& additional_resolve_cb) | |
35 : caller_promise_(caller_promise.Pass()), | |
36 additional_resolve_cb_(additional_resolve_cb) {} | |
37 | |
38 void resolve() override { | |
39 MarkPromiseSettled(); | |
40 additional_resolve_cb_.Run(); | |
41 caller_promise_->resolve(); | |
42 } | |
43 | |
44 void reject(media::MediaKeys::Exception exception_code, | |
45 uint32 system_code, | |
46 const std::string& error_message) override { | |
47 MarkPromiseSettled(); | |
48 caller_promise_->reject(exception_code, system_code, error_message); | |
49 } | |
50 | |
51 protected: | |
52 scoped_ptr<media::SimpleCdmPromise> caller_promise_; | |
53 base::Closure additional_resolve_cb_; | |
54 }; | |
55 | |
56 // This class is needed so that resolving a SessionLoaded() promise triggers | |
57 // playback of the stream. It intercepts the resolve() call to invoke an | |
58 // additional callback. This is only needed until KeysChange event gets passed | |
59 // through Pepper. | |
60 class SessionLoadedPromise : public media::CdmPromiseTemplate<std::string> { | |
61 public: | |
62 SessionLoadedPromise(scoped_ptr<media::NewSessionCdmPromise> caller_promise, | |
63 const base::Closure& additional_resolve_cb) | |
64 : caller_promise_(caller_promise.Pass()), | |
65 additional_resolve_cb_(additional_resolve_cb) {} | |
66 | |
67 void resolve(const std::string& web_session_id) override { | |
68 MarkPromiseSettled(); | |
69 additional_resolve_cb_.Run(); | |
70 caller_promise_->resolve(web_session_id); | |
71 } | |
72 | |
73 void reject(media::MediaKeys::Exception exception_code, | |
74 uint32 system_code, | |
75 const std::string& error_message) override { | |
76 MarkPromiseSettled(); | |
77 caller_promise_->reject(exception_code, system_code, error_message); | |
78 } | |
79 | |
80 protected: | |
81 scoped_ptr<media::NewSessionCdmPromise> caller_promise_; | |
82 base::Closure additional_resolve_cb_; | |
83 }; | |
84 | |
85 scoped_ptr<PpapiDecryptor> PpapiDecryptor::Create( | 24 scoped_ptr<PpapiDecryptor> PpapiDecryptor::Create( |
86 const std::string& key_system, | 25 const std::string& key_system, |
87 const GURL& security_origin, | 26 const GURL& security_origin, |
88 const CreatePepperCdmCB& create_pepper_cdm_cb, | 27 const CreatePepperCdmCB& create_pepper_cdm_cb, |
89 const media::SessionMessageCB& session_message_cb, | 28 const media::SessionMessageCB& session_message_cb, |
90 const media::SessionReadyCB& session_ready_cb, | 29 const media::SessionReadyCB& session_ready_cb, |
91 const media::SessionClosedCB& session_closed_cb, | 30 const media::SessionClosedCB& session_closed_cb, |
92 const media::SessionErrorCB& session_error_cb, | 31 const media::SessionErrorCB& session_error_cb, |
93 const media::SessionKeysChangeCB& session_keys_change_cb, | 32 const media::SessionKeysChangeCB& session_keys_change_cb, |
94 const media::SessionExpirationUpdateCB& session_expiration_update_cb) { | 33 const media::SessionExpirationUpdateCB& session_expiration_update_cb) { |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
194 void PpapiDecryptor::LoadSession( | 133 void PpapiDecryptor::LoadSession( |
195 const std::string& web_session_id, | 134 const std::string& web_session_id, |
196 scoped_ptr<media::NewSessionCdmPromise> promise) { | 135 scoped_ptr<media::NewSessionCdmPromise> promise) { |
197 DVLOG(2) << __FUNCTION__; | 136 DVLOG(2) << __FUNCTION__; |
198 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); | 137 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); |
199 | 138 |
200 if (!CdmDelegate()) { | 139 if (!CdmDelegate()) { |
201 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist."); | 140 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist."); |
202 return; | 141 return; |
203 } | 142 } |
204 | 143 CdmDelegate()->LoadSession(web_session_id, promise.Pass()); |
205 // TODO(jrummell): Intercepting the promise should not be necessary once | |
206 // OnSessionKeysChange() is called in all cases. http://crbug.com/413413. | |
207 scoped_ptr<SessionLoadedPromise> session_loaded_promise( | |
208 new SessionLoadedPromise(promise.Pass(), | |
209 base::Bind(&PpapiDecryptor::ResumePlayback, | |
210 weak_ptr_factory_.GetWeakPtr()))); | |
211 | |
212 CdmDelegate()->LoadSession(web_session_id, session_loaded_promise.Pass()); | |
213 } | 144 } |
214 | 145 |
215 void PpapiDecryptor::UpdateSession( | 146 void PpapiDecryptor::UpdateSession( |
216 const std::string& web_session_id, | 147 const std::string& web_session_id, |
217 const uint8* response, | 148 const uint8* response, |
218 int response_length, | 149 int response_length, |
219 scoped_ptr<media::SimpleCdmPromise> promise) { | 150 scoped_ptr<media::SimpleCdmPromise> promise) { |
220 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); | 151 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); |
221 | 152 |
222 if (!CdmDelegate()) { | 153 if (!CdmDelegate()) { |
223 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist."); | 154 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist."); |
224 return; | 155 return; |
225 } | 156 } |
226 | 157 CdmDelegate()->UpdateSession(web_session_id, response, response_length, |
227 // TODO(jrummell): Intercepting the promise should not be necessary once | 158 promise.Pass()); |
228 // OnSessionKeysChange() is called in all cases. http://crbug.com/413413. | |
229 scoped_ptr<SessionUpdatedPromise> session_updated_promise( | |
230 new SessionUpdatedPromise(promise.Pass(), | |
231 base::Bind(&PpapiDecryptor::ResumePlayback, | |
232 weak_ptr_factory_.GetWeakPtr()))); | |
233 CdmDelegate()->UpdateSession(web_session_id, | |
234 response, | |
235 response_length, | |
236 session_updated_promise.Pass()); | |
237 } | 159 } |
238 | 160 |
239 void PpapiDecryptor::CloseSession(const std::string& web_session_id, | 161 void PpapiDecryptor::CloseSession(const std::string& web_session_id, |
240 scoped_ptr<media::SimpleCdmPromise> promise) { | 162 scoped_ptr<media::SimpleCdmPromise> promise) { |
241 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); | 163 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); |
242 | 164 |
243 if (!CdmDelegate()) { | 165 if (!CdmDelegate()) { |
244 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist."); | 166 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist."); |
245 return; | 167 return; |
246 } | 168 } |
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
485 session_message_cb_.Run(web_session_id, message, destination_url); | 407 session_message_cb_.Run(web_session_id, message, destination_url); |
486 } | 408 } |
487 | 409 |
488 void PpapiDecryptor::OnSessionKeysChange(const std::string& web_session_id, | 410 void PpapiDecryptor::OnSessionKeysChange(const std::string& web_session_id, |
489 bool has_additional_usable_key) { | 411 bool has_additional_usable_key) { |
490 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); | 412 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); |
491 | 413 |
492 // TODO(jrummell): Handling resume playback should be done in the media | 414 // TODO(jrummell): Handling resume playback should be done in the media |
493 // player, not in the Decryptors. http://crbug.com/413413. | 415 // player, not in the Decryptors. http://crbug.com/413413. |
494 if (has_additional_usable_key) | 416 if (has_additional_usable_key) |
495 ResumePlayback(); | 417 AttemptToResumePlayback(); |
496 | 418 |
497 session_keys_change_cb_.Run(web_session_id, has_additional_usable_key); | 419 session_keys_change_cb_.Run(web_session_id, has_additional_usable_key); |
498 } | 420 } |
499 | 421 |
500 void PpapiDecryptor::OnSessionExpirationUpdate( | 422 void PpapiDecryptor::OnSessionExpirationUpdate( |
501 const std::string& web_session_id, | 423 const std::string& web_session_id, |
502 const base::Time& new_expiry_time) { | 424 const base::Time& new_expiry_time) { |
503 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); | 425 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); |
504 session_expiration_update_cb_.Run(web_session_id, new_expiry_time); | 426 session_expiration_update_cb_.Run(web_session_id, new_expiry_time); |
505 } | 427 } |
506 | 428 |
507 void PpapiDecryptor::OnSessionReady(const std::string& web_session_id) { | 429 void PpapiDecryptor::OnSessionReady(const std::string& web_session_id) { |
508 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); | 430 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); |
509 | |
510 // TODO(jrummell): Calling ResumePlayback() here should not be necessary once | |
511 // OnSessionKeysChange() is called in all cases. http://crbug.com/413413. | |
512 ResumePlayback(); | |
513 session_ready_cb_.Run(web_session_id); | 431 session_ready_cb_.Run(web_session_id); |
514 } | 432 } |
515 | 433 |
516 void PpapiDecryptor::OnSessionClosed(const std::string& web_session_id) { | 434 void PpapiDecryptor::OnSessionClosed(const std::string& web_session_id) { |
517 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); | 435 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); |
518 session_closed_cb_.Run(web_session_id); | 436 session_closed_cb_.Run(web_session_id); |
519 } | 437 } |
520 | 438 |
521 void PpapiDecryptor::OnSessionError(const std::string& web_session_id, | 439 void PpapiDecryptor::OnSessionError(const std::string& web_session_id, |
522 MediaKeys::Exception exception_code, | 440 MediaKeys::Exception exception_code, |
523 uint32 system_code, | 441 uint32 system_code, |
524 const std::string& error_description) { | 442 const std::string& error_description) { |
525 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); | 443 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); |
526 session_error_cb_.Run( | 444 session_error_cb_.Run( |
527 web_session_id, exception_code, system_code, error_description); | 445 web_session_id, exception_code, system_code, error_description); |
528 } | 446 } |
529 | 447 |
530 void PpapiDecryptor::ResumePlayback() { | 448 void PpapiDecryptor::AttemptToResumePlayback() { |
531 // Based on the spec, we need to resume playback when update() completes | |
532 // successfully, or when a session is successfully loaded (triggered by | |
533 // OnSessionReady()). So we choose to call the NewKeyCBs here. | |
534 if (!new_audio_key_cb_.is_null()) | 449 if (!new_audio_key_cb_.is_null()) |
535 new_audio_key_cb_.Run(); | 450 new_audio_key_cb_.Run(); |
536 | 451 |
537 if (!new_video_key_cb_.is_null()) | 452 if (!new_video_key_cb_.is_null()) |
538 new_video_key_cb_.Run(); | 453 new_video_key_cb_.Run(); |
539 } | 454 } |
540 | 455 |
541 void PpapiDecryptor::OnFatalPluginError() { | 456 void PpapiDecryptor::OnFatalPluginError() { |
542 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); | 457 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); |
543 pepper_cdm_wrapper_.reset(); | 458 pepper_cdm_wrapper_.reset(); |
544 } | 459 } |
545 | 460 |
546 ContentDecryptorDelegate* PpapiDecryptor::CdmDelegate() { | 461 ContentDecryptorDelegate* PpapiDecryptor::CdmDelegate() { |
547 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); | 462 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); |
548 return (pepper_cdm_wrapper_) ? pepper_cdm_wrapper_->GetCdmDelegate() : NULL; | 463 return (pepper_cdm_wrapper_) ? pepper_cdm_wrapper_->GetCdmDelegate() : NULL; |
549 } | 464 } |
550 | 465 |
551 } // namespace content | 466 } // namespace content |
OLD | NEW |