Index: content/browser/media/cdm/browser_cdm_manager.cc |
diff --git a/content/browser/media/cdm/browser_cdm_manager.cc b/content/browser/media/cdm/browser_cdm_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f8f7129d651e575b25bd9b8925140c646d83f14c |
--- /dev/null |
+++ b/content/browser/media/cdm/browser_cdm_manager.cc |
@@ -0,0 +1,293 @@ |
+// 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/browser/media/cdm/browser_cdm_manager.h" |
+ |
+#include "base/command_line.h" |
+#include "base/stl_util.h" |
+#include "content/common/media/cdm_messages.h" |
+#include "content/public/browser/browser_context.h" |
+#include "content/public/browser/render_frame_host.h" |
+#include "content/public/browser/render_process_host.h" |
+#include "content/public/browser/render_view_host.h" |
+#include "content/public/browser/web_contents.h" |
+#include "content/public/common/content_client.h" |
+#include "content/public/common/content_switches.h" |
+#include "media/base/browser_cdm.h" |
+#include "media/base/browser_cdm_factory.h" |
+#include "media/base/media_switches.h" |
+ |
+namespace content { |
+ |
+using media::MediaKeys; |
ddorwin
2014/06/04 01:02:30
order
xhwang
2014/06/04 18:54:07
Done.
|
+using media::BrowserCdm; |
+ |
+// Maximum lengths for various EME API parameters. These are checks to |
+// prevent unnecessarily large parameters from being passed around, and the |
+// lengths are somewhat arbitrary as the EME spec doesn't specify any limits. |
+const size_t kMaxInitDataLength = 64 * 1024; // 64 KB |
+const size_t kMaxSessionResponseLength = 64 * 1024; // 64 KB |
+const size_t kMaxKeySystemLength = 256; |
+ |
+// static |
+BrowserCdmManager* BrowserCdmManager::Create(RenderFrameHost* rfh) { |
+ return new BrowserCdmManager(rfh); |
+} |
+ |
+BrowserCdmManager::BrowserCdmManager(RenderFrameHost* render_frame_host) |
+ : render_frame_host_(render_frame_host), |
+ web_contents_(WebContents::FromRenderFrameHost(render_frame_host)), |
+ weak_ptr_factory_(this) { |
+} |
+ |
+BrowserCdmManager::~BrowserCdmManager() { |
xhwang
2014/06/03 22:43:08
There could be some destruction order issues here.
|
+ for (CdmMap::iterator it = cdm_map_.begin(); it != cdm_map_.end(); ++it) |
ddorwin
2014/06/04 01:02:30
Does stl_utils handle this?
xhwang
2014/06/04 18:54:07
Good point. Done.
|
+ delete it->second; |
+} |
+ |
+BrowserCdm* BrowserCdmManager::GetCdm(int cdm_id) { |
+ CdmMap::const_iterator iter = cdm_map_.find(cdm_id); |
+ return (iter == cdm_map_.end()) ? NULL : iter->second; |
+} |
+ |
+void BrowserCdmManager::OnSessionCreated( |
+ int cdm_id, |
+ uint32 session_id, |
+ const std::string& web_session_id) { |
+ Send(new CdmMsg_SessionCreated( |
+ RoutingID(), cdm_id, session_id, web_session_id)); |
+} |
+ |
+void BrowserCdmManager::OnSessionMessage( |
+ int cdm_id, |
+ uint32 session_id, |
+ const std::vector<uint8>& message, |
+ const GURL& destination_url) { |
+ GURL verified_gurl = destination_url; |
+ if (!verified_gurl.is_valid() && !verified_gurl.is_empty()) { |
+ DLOG(WARNING) << "SessionMessage destination_url is invalid : " |
+ << destination_url.possibly_invalid_spec(); |
+ verified_gurl = GURL::EmptyGURL(); // Replace invalid destination_url. |
+ } |
+ |
+ Send(new CdmMsg_SessionMessage( |
+ RoutingID(), cdm_id, session_id, message, verified_gurl)); |
+} |
+ |
+void BrowserCdmManager::OnSessionReady(int cdm_id, uint32 session_id) { |
+ Send(new CdmMsg_SessionReady(RoutingID(), cdm_id, session_id)); |
+} |
+ |
+void BrowserCdmManager::OnSessionClosed(int cdm_id, uint32 session_id) { |
+ Send(new CdmMsg_SessionClosed(RoutingID(), cdm_id, session_id)); |
+} |
+ |
+void BrowserCdmManager::OnSessionError(int cdm_id, |
+ uint32 session_id, |
+ MediaKeys::KeyError error_code, |
+ uint32 system_code) { |
+ Send(new CdmMsg_SessionError( |
+ RoutingID(), cdm_id, session_id, error_code, system_code)); |
+} |
+ |
+void BrowserCdmManager::OnInitializeCdm(int cdm_id, |
+ const std::string& key_system, |
+ const GURL& security_origin) { |
+ if (key_system.size() > kMaxKeySystemLength) { |
+ // This failure will be discovered and reported by OnCreateSession() |
+ // as GetCdm() will return null. |
+ NOTREACHED() << "Invalid key system: " << key_system; |
+ return; |
+ } |
+ |
+ AddCdm(cdm_id, key_system, security_origin); |
+} |
+ |
+void BrowserCdmManager::OnCreateSession( |
+ int cdm_id, |
+ uint32 session_id, |
+ CdmHostMsg_CreateSession_ContentType content_type, |
+ const std::vector<uint8>& init_data) { |
+ if (init_data.size() > kMaxInitDataLength) { |
+ LOG(WARNING) << "InitData for ID: " << cdm_id |
+ << " too long: " << init_data.size(); |
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); |
+ return; |
+ } |
+ |
+ // Convert the session content type into a MIME type. "audio" and "video" |
+ // don't matter, so using "video" for the MIME type. |
+ // Ref: |
+ // https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-createsession |
+ std::string mime_type; |
+ switch (content_type) { |
+ case CREATE_SESSION_TYPE_WEBM: |
+ mime_type = "video/webm"; |
+ break; |
+ case CREATE_SESSION_TYPE_MP4: |
+ mime_type = "video/mp4"; |
+ break; |
+ default: |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ if (CommandLine::ForCurrentProcess() |
+ ->HasSwitch(switches::kDisableInfobarForProtectedMediaIdentifier)) { |
+ CreateSessionIfPermitted(cdm_id, session_id, mime_type, init_data, true); |
+ return; |
+ } |
+ |
+ BrowserCdm* cdm = GetCdm(cdm_id); |
+ if (!cdm) { |
+ DLOG(WARNING) << "No CDM for ID " << cdm_id << " found"; |
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); |
+ return; |
+ } |
+ |
+ BrowserContext* context = |
+ web_contents_->GetRenderProcessHost()->GetBrowserContext(); |
+ |
+ std::map<int, GURL>::const_iterator iter = |
+ cdm_security_origin_map_.find(cdm_id); |
+ if (iter == cdm_security_origin_map_.end()) { |
+ NOTREACHED(); |
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); |
+ return; |
+ } |
+ |
+ context->RequestProtectedMediaIdentifierPermission( |
+ web_contents_->GetRenderProcessHost()->GetID(), |
+ web_contents_->GetRenderViewHost()->GetRoutingID(), |
+ iter->second, |
+ base::Bind(&BrowserCdmManager::CreateSessionIfPermitted, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ cdm_id, |
+ session_id, |
+ mime_type, |
+ init_data)); |
+} |
+ |
+void BrowserCdmManager::OnUpdateSession( |
+ int cdm_id, |
+ uint32 session_id, |
+ const std::vector<uint8>& response) { |
+ BrowserCdm* cdm = GetCdm(cdm_id); |
+ if (!cdm) { |
+ DLOG(WARNING) << "No CDM for ID " << cdm_id << " found"; |
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); |
+ return; |
+ } |
+ |
+ if (response.size() > kMaxSessionResponseLength) { |
+ LOG(WARNING) << "Response for ID " << cdm_id |
+ << " is too long: " << response.size(); |
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); |
+ return; |
+ } |
+ |
+ cdm->UpdateSession(session_id, &response[0], response.size()); |
+} |
+ |
+void BrowserCdmManager::OnReleaseSession(int cdm_id, uint32 session_id) { |
+ BrowserCdm* cdm = GetCdm(cdm_id); |
+ if (!cdm) { |
+ DLOG(WARNING) << "No CDM for ID " << cdm_id << " found"; |
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); |
+ return; |
+ } |
+ |
+ cdm->ReleaseSession(session_id); |
+} |
+ |
+void BrowserCdmManager::OnDestroyCdm(int cdm_id) { |
+ BrowserCdm* cdm = GetCdm(cdm_id); |
+ if (!cdm) |
+ return; |
+ |
+ CancelAllPendingSessionCreations(cdm_id); |
+ RemoveCdm(cdm_id); |
+} |
+ |
+void BrowserCdmManager::CancelAllPendingSessionCreations(int cdm_id) { |
+ BrowserContext* context = |
+ web_contents_->GetRenderProcessHost()->GetBrowserContext(); |
+ std::map<int, GURL>::const_iterator iter = |
+ cdm_security_origin_map_.find(cdm_id); |
+ if (iter == cdm_security_origin_map_.end()) |
+ return; |
+ context->CancelProtectedMediaIdentifierPermissionRequests( |
+ web_contents_->GetRenderProcessHost()->GetID(), |
+ web_contents_->GetRenderViewHost()->GetRoutingID(), |
+ iter->second); |
+} |
+ |
+void BrowserCdmManager::AddCdm(int cdm_id, |
+ const std::string& key_system, |
+ const GURL& security_origin) { |
+ DCHECK(!GetCdm(cdm_id)); |
+ base::WeakPtr<BrowserCdmManager> weak_this = weak_ptr_factory_.GetWeakPtr(); |
+ scoped_ptr<BrowserCdm> cdm(media::CreateBrowserCdm( |
+ key_system, |
+ base::Bind(&BrowserCdmManager::OnSessionCreated, weak_this, cdm_id), |
+ base::Bind(&BrowserCdmManager::OnSessionMessage, weak_this, cdm_id), |
+ base::Bind(&BrowserCdmManager::OnSessionReady, weak_this, cdm_id), |
+ base::Bind(&BrowserCdmManager::OnSessionClosed, weak_this, cdm_id), |
+ base::Bind(&BrowserCdmManager::OnSessionError, weak_this, cdm_id))); |
+ |
+ if (!cdm) { |
+ // This failure will be discovered and reported by OnCreateSession() |
+ // as GetCdm() will return null. |
+ DVLOG(1) << "failed to create CDM."; |
+ return; |
+ } |
+ |
+ cdm_map_[cdm_id] = cdm.release(); |
+ cdm_security_origin_map_[cdm_id] = security_origin; |
+} |
+ |
+void BrowserCdmManager::RemoveCdm(int cdm_id) { |
+ // TODO(xhwang): Detach CDM from the player it's set to. In prefixed |
+ // EME implementation the current code is fine because we always destroy the |
+ // player before we destroy the DrmBridge. This will not always be the case |
+ // in unprefixed EME implementation. |
+ CdmMap::iterator iter = cdm_map_.find(cdm_id); |
+ if (iter != cdm_map_.end()) { |
+ delete iter->second; |
+ cdm_map_.erase(iter); |
+ } |
+ cdm_security_origin_map_.erase(cdm_id); |
+} |
+ |
+int BrowserCdmManager::RoutingID() { |
+ return render_frame_host_->GetRoutingID(); |
+} |
+ |
+bool BrowserCdmManager::Send(IPC::Message* msg) { |
+ return render_frame_host_->Send(msg); |
+} |
+ |
+void BrowserCdmManager::CreateSessionIfPermitted( |
+ int cdm_id, |
+ uint32 session_id, |
+ const std::string& content_type, |
+ const std::vector<uint8>& init_data, |
+ bool permitted) { |
+ if (!permitted) { |
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); |
+ return; |
+ } |
+ |
+ BrowserCdm* cdm = GetCdm(cdm_id); |
+ if (!cdm) { |
+ DLOG(WARNING) << "No CDM for ID: " << cdm_id << " found"; |
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); |
+ return; |
+ } |
+ |
+ // This could fail, in which case a SessionError will be fired. |
+ cdm->CreateSession(session_id, content_type, &init_data[0], init_data.size()); |
+} |
+ |
+} // namespace content |