diff options
Diffstat (limited to 'content/browser/media/cdm/browser_cdm_manager.cc')
-rw-r--r-- | content/browser/media/cdm/browser_cdm_manager.cc | 294 |
1 files changed, 294 insertions, 0 deletions
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 0000000..d66692e --- /dev/null +++ b/content/browser/media/cdm/browser_cdm_manager.cc @@ -0,0 +1,294 @@ +// 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::BrowserCdm; +using media::MediaKeys; + +// 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() { + STLDeleteValues(&cdm_map_); +} + +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 defined(OS_ANDROID) + if (CommandLine::ForCurrentProcess() + ->HasSwitch(switches::kDisableInfobarForProtectedMediaIdentifier)) { + CreateSessionIfPermitted(cdm_id, session_id, mime_type, init_data, true); + return; + } +#endif + + 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 |