Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / crypto / proxy_media_keys.cc
1 // Copyright 2014 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_media_keys.h"
6
7 #include <vector>
8
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "content/renderer/media/crypto/key_systems.h"
13 #include "content/renderer/media/crypto/renderer_cdm_manager.h"
14 #include "media/base/cdm_promise.h"
15
16 namespace content {
17
18 scoped_ptr<ProxyMediaKeys> ProxyMediaKeys::Create(
19     const std::string& key_system,
20     const GURL& security_origin,
21     RendererCdmManager* manager,
22     const media::SessionMessageCB& session_message_cb,
23     const media::SessionReadyCB& session_ready_cb,
24     const media::SessionClosedCB& session_closed_cb,
25     const media::SessionErrorCB& session_error_cb) {
26   DCHECK(manager);
27   scoped_ptr<ProxyMediaKeys> proxy_media_keys(
28       new ProxyMediaKeys(manager,
29                          session_message_cb,
30                          session_ready_cb,
31                          session_closed_cb,
32                          session_error_cb));
33   proxy_media_keys->InitializeCdm(key_system, security_origin);
34   return proxy_media_keys.Pass();
35 }
36
37 ProxyMediaKeys::~ProxyMediaKeys() {
38   manager_->DestroyCdm(cdm_id_);
39   manager_->UnregisterMediaKeys(cdm_id_);
40
41   // Reject any outstanding promises.
42   for (PromiseMap::iterator it = session_id_to_promise_map_.begin();
43        it != session_id_to_promise_map_.end();
44        ++it) {
45     it->second->reject(
46         media::MediaKeys::NOT_SUPPORTED_ERROR, 0, "The operation was aborted.");
47   }
48   session_id_to_promise_map_.clear();
49 }
50
51 void ProxyMediaKeys::CreateSession(
52     const std::string& init_data_type,
53     const uint8* init_data,
54     int init_data_length,
55     SessionType session_type,
56     scoped_ptr<media::NewSessionCdmPromise> promise) {
57   // TODO(xhwang): Move these checks up to blink and DCHECK here.
58   // See http://crbug.com/342510
59   CdmHostMsg_CreateSession_ContentType create_session_content_type;
60   if (init_data_type == "cenc") {
61     create_session_content_type = CREATE_SESSION_TYPE_MP4;
62   } else if (init_data_type == "webm") {
63     create_session_content_type = CREATE_SESSION_TYPE_WEBM;
64   } else {
65     DLOG(ERROR) << "Unsupported EME CreateSession content type of "
66                 << init_data_type;
67     promise->reject(
68         NOT_SUPPORTED_ERROR,
69         0,
70         "Unsupported EME CreateSession init data type of " + init_data_type);
71     return;
72   }
73
74   uint32 session_id = CreateSessionId();
75   SavePromise(session_id, promise.PassAs<media::CdmPromise>());
76   manager_->CreateSession(
77       cdm_id_,
78       session_id,
79       create_session_content_type,
80       std::vector<uint8>(init_data, init_data + init_data_length));
81 }
82
83 void ProxyMediaKeys::LoadSession(
84     const std::string& web_session_id,
85     scoped_ptr<media::NewSessionCdmPromise> promise) {
86   // TODO(xhwang): Check key system and platform support for LoadSession in
87   // blink and add NOTREACHED() here.
88   DLOG(ERROR) << "ProxyMediaKeys doesn't support session loading.";
89   promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported.");
90 }
91
92 void ProxyMediaKeys::UpdateSession(
93     const std::string& web_session_id,
94     const uint8* response,
95     int response_length,
96     scoped_ptr<media::SimpleCdmPromise> promise) {
97   uint32 session_id = LookupSessionId(web_session_id);
98   if (!session_id) {
99     promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist.");
100     return;
101   }
102
103   SavePromise(session_id, promise.PassAs<media::CdmPromise>());
104   manager_->UpdateSession(
105       cdm_id_,
106       session_id,
107       std::vector<uint8>(response, response + response_length));
108 }
109
110 void ProxyMediaKeys::ReleaseSession(
111     const std::string& web_session_id,
112     scoped_ptr<media::SimpleCdmPromise> promise) {
113   uint32 session_id = LookupSessionId(web_session_id);
114   if (!session_id) {
115     promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist.");
116     return;
117   }
118
119   SavePromise(session_id, promise.PassAs<media::CdmPromise>());
120   manager_->ReleaseSession(cdm_id_, session_id);
121 }
122
123 void ProxyMediaKeys::OnSessionCreated(uint32 session_id,
124                                       const std::string& web_session_id) {
125   AssignWebSessionId(session_id, web_session_id);
126   scoped_ptr<media::CdmPromise> promise = TakePromise(session_id);
127   if (promise) {
128     media::NewSessionCdmPromise* session_promise(
129         static_cast<media::NewSessionCdmPromise*>(promise.get()));
130     session_promise->resolve(web_session_id);
131   }
132 }
133
134 void ProxyMediaKeys::OnSessionMessage(uint32 session_id,
135                                       const std::vector<uint8>& message,
136                                       const GURL& destination_url) {
137   session_message_cb_.Run(
138       LookupWebSessionId(session_id), message, destination_url);
139 }
140
141 void ProxyMediaKeys::OnSessionReady(uint32 session_id) {
142   scoped_ptr<media::CdmPromise> promise = TakePromise(session_id);
143   if (promise) {
144     media::SimpleCdmPromise* simple_promise(
145         static_cast<media::SimpleCdmPromise*>(promise.get()));
146     simple_promise->resolve();
147   } else {
148     // Still needed for keyadded.
149     const std::string web_session_id = LookupWebSessionId(session_id);
150     session_ready_cb_.Run(web_session_id);
151   }
152 }
153
154 void ProxyMediaKeys::OnSessionClosed(uint32 session_id) {
155   const std::string web_session_id = LookupWebSessionId(session_id);
156   DropWebSessionId(web_session_id);
157   scoped_ptr<media::CdmPromise> promise = TakePromise(session_id);
158   if (promise) {
159     media::SimpleCdmPromise* simple_promise(
160         static_cast<media::SimpleCdmPromise*>(promise.get()));
161     simple_promise->resolve();
162   } else {
163     // It is possible for the CDM to close a session independent of a
164     // Release() request.
165     session_closed_cb_.Run(web_session_id);
166   }
167 }
168
169 void ProxyMediaKeys::OnSessionError(uint32 session_id,
170                                     media::MediaKeys::KeyError error_code,
171                                     uint32 system_code) {
172   const std::string web_session_id = LookupWebSessionId(session_id);
173   media::MediaKeys::Exception exception_code;
174   switch (error_code) {
175     case media::MediaKeys::kClientError:
176       exception_code = media::MediaKeys::CLIENT_ERROR;
177       break;
178     case media::MediaKeys::kOutputError:
179       exception_code = media::MediaKeys::OUTPUT_ERROR;
180       break;
181     case media::MediaKeys::kUnknownError:
182     default:
183       exception_code = media::MediaKeys::UNKNOWN_ERROR;
184       break;
185   }
186
187   scoped_ptr<media::CdmPromise> promise = TakePromise(session_id);
188   if (promise) {
189     promise->reject(exception_code, system_code, std::string());
190     return;
191   }
192
193   // Errors generally happen in response to a request, but it is possible
194   // for something bad to happen in the CDM and it needs to tell the client.
195   session_error_cb_.Run(
196       web_session_id, exception_code, system_code, std::string());
197 }
198
199 int ProxyMediaKeys::GetCdmId() const {
200   return cdm_id_;
201 }
202
203 ProxyMediaKeys::ProxyMediaKeys(
204     RendererCdmManager* manager,
205     const media::SessionMessageCB& session_message_cb,
206     const media::SessionReadyCB& session_ready_cb,
207     const media::SessionClosedCB& session_closed_cb,
208     const media::SessionErrorCB& session_error_cb)
209     : manager_(manager),
210       session_message_cb_(session_message_cb),
211       session_ready_cb_(session_ready_cb),
212       session_closed_cb_(session_closed_cb),
213       session_error_cb_(session_error_cb),
214       next_session_id_(1) {
215   cdm_id_ = manager->RegisterMediaKeys(this);
216 }
217
218 void ProxyMediaKeys::InitializeCdm(const std::string& key_system,
219                                    const GURL& security_origin) {
220   manager_->InitializeCdm(cdm_id_, this, key_system, security_origin);
221 }
222
223 uint32_t ProxyMediaKeys::CreateSessionId() {
224   return next_session_id_++;
225 }
226
227 void ProxyMediaKeys::AssignWebSessionId(uint32_t session_id,
228                                         const std::string& web_session_id) {
229   DCHECK(!ContainsKey(web_session_to_session_id_map_, web_session_id));
230   DCHECK(session_id);
231   web_session_to_session_id_map_.insert(
232       std::make_pair(web_session_id, session_id));
233 }
234
235 uint32_t ProxyMediaKeys::LookupSessionId(
236     const std::string& web_session_id) const {
237   SessionIdMap::const_iterator it =
238       web_session_to_session_id_map_.find(web_session_id);
239   return (it != web_session_to_session_id_map_.end()) ? it->second : 0;
240 }
241
242 std::string ProxyMediaKeys::LookupWebSessionId(uint32_t session_id) const {
243   for (SessionIdMap::const_iterator it = web_session_to_session_id_map_.begin();
244        it != web_session_to_session_id_map_.end();
245        ++it) {
246     if (it->second == session_id)
247       return it->first;
248   }
249   // Possible to get an error creating a session, so no |web_session_id|
250   // available.
251   return std::string();
252 }
253
254 void ProxyMediaKeys::DropWebSessionId(const std::string& web_session_id) {
255   web_session_to_session_id_map_.erase(web_session_id);
256 }
257
258 void ProxyMediaKeys::SavePromise(uint32_t session_id,
259                                  scoped_ptr<media::CdmPromise> promise) {
260   // Should only be one promise outstanding for any |session_id|.
261   DCHECK(!ContainsKey(session_id_to_promise_map_, session_id));
262   session_id_to_promise_map_.add(session_id, promise.Pass());
263 }
264
265 scoped_ptr<media::CdmPromise> ProxyMediaKeys::TakePromise(uint32_t session_id) {
266   PromiseMap::iterator it = session_id_to_promise_map_.find(session_id);
267   // May not be a promise associated with this session for asynchronous events.
268   if (it == session_id_to_promise_map_.end())
269     return scoped_ptr<media::CdmPromise>();
270   return session_id_to_promise_map_.take_and_erase(it);
271 }
272
273 }  // namespace content