Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / media / base / android / media_drm_bridge.cc
blobdbcd1871b81b542ade6750b9b5247366dba2758a
1 // Copyright (c) 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.
5 #include "media/base/android/media_drm_bridge.h"
7 #include <algorithm>
9 #include "base/android/build_info.h"
10 #include "base/android/jni_array.h"
11 #include "base/android/jni_string.h"
12 #include "base/callback_helpers.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/strings/string_util.h"
17 #include "jni/MediaDrmBridge_jni.h"
18 #include "media/base/android/media_player_manager.h"
20 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
22 using base::android::AttachCurrentThread;
23 using base::android::ConvertUTF8ToJavaString;
24 using base::android::ConvertJavaStringToUTF8;
25 using base::android::JavaByteArrayToByteVector;
26 using base::android::ScopedJavaLocalRef;
28 namespace media {
30 static uint32 ReadUint32(const uint8_t* data) {
31 uint32 value = 0;
32 for (int i = 0; i < 4; ++i)
33 value = (value << 8) | data[i];
34 return value;
37 static uint64 ReadUint64(const uint8_t* data) {
38 uint64 value = 0;
39 for (int i = 0; i < 8; ++i)
40 value = (value << 8) | data[i];
41 return value;
44 // The structure of an ISO CENC Protection System Specific Header (PSSH) box is
45 // as follows. (See ISO/IEC FDIS 23001-7:2011(E).)
46 // Note: ISO boxes use big-endian values.
48 // PSSH {
49 // uint32 Size
50 // uint32 Type
51 // uint64 LargeSize # Field is only present if value(Size) == 1.
52 // uint32 VersionAndFlags
53 // uint8[16] SystemId
54 // uint32 DataSize
55 // uint8[DataSize] Data
56 // }
57 const int kBoxHeaderSize = 8; // Box's header contains Size and Type.
58 const int kBoxLargeSizeSize = 8;
59 const int kPsshVersionFlagSize = 4;
60 const int kPsshSystemIdSize = 16;
61 const int kPsshDataSizeSize = 4;
62 const uint32 kTencType = 0x74656e63;
63 const uint32 kPsshType = 0x70737368;
64 const uint8 kWidevineUuid[16] = {
65 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
66 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED };
68 static std::vector<uint8> GetUUID(const std::string& key_system) {
69 // For security reasons, we only do exact string comparisons here - we don't
70 // try to parse the |key_system| in any way.
71 if (key_system == kWidevineKeySystem) {
72 return std::vector<uint8>(kWidevineUuid,
73 kWidevineUuid + arraysize(kWidevineUuid));
75 return std::vector<uint8>();
78 // Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the
79 // "Data" of the box and put it in |pssh_data|. Returns true if such a box is
80 // found and successfully parsed. Returns false otherwise.
81 // Notes:
82 // 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box
83 // will be set in |pssh_data|.
84 // 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped.
85 static bool GetPsshData(const uint8* data, int data_size,
86 const std::vector<uint8>& uuid,
87 std::vector<uint8>* pssh_data) {
88 const uint8* cur = data;
89 const uint8* data_end = data + data_size;
90 int bytes_left = data_size;
92 while (bytes_left > 0) {
93 const uint8* box_head = cur;
95 if (bytes_left < kBoxHeaderSize)
96 return false;
98 uint64_t box_size = ReadUint32(cur);
99 uint32 type = ReadUint32(cur + 4);
100 cur += kBoxHeaderSize;
101 bytes_left -= kBoxHeaderSize;
103 if (box_size == 1) { // LargeSize is present.
104 if (bytes_left < kBoxLargeSizeSize)
105 return false;
107 box_size = ReadUint64(cur);
108 cur += kBoxLargeSizeSize;
109 bytes_left -= kBoxLargeSizeSize;
110 } else if (box_size == 0) {
111 box_size = bytes_left + kBoxHeaderSize;
114 const uint8* box_end = box_head + box_size;
115 if (data_end < box_end)
116 return false;
118 if (type == kTencType) {
119 // Skip 'tenc' box.
120 cur = box_end;
121 bytes_left = data_end - cur;
122 continue;
123 } else if (type != kPsshType) {
124 return false;
127 const int kPsshBoxMinimumSize =
128 kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize;
129 if (box_end < cur + kPsshBoxMinimumSize)
130 return false;
132 uint32 version_and_flags = ReadUint32(cur);
133 cur += kPsshVersionFlagSize;
134 bytes_left -= kPsshVersionFlagSize;
135 if (version_and_flags != 0)
136 return false;
138 DCHECK_GE(bytes_left, kPsshSystemIdSize);
139 if (!std::equal(uuid.begin(), uuid.end(), cur)) {
140 cur = box_end;
141 bytes_left = data_end - cur;
142 continue;
145 cur += kPsshSystemIdSize;
146 bytes_left -= kPsshSystemIdSize;
148 uint32 data_size = ReadUint32(cur);
149 cur += kPsshDataSizeSize;
150 bytes_left -= kPsshDataSizeSize;
152 if (box_end < cur + data_size)
153 return false;
155 pssh_data->assign(cur, cur + data_size);
156 return true;
159 return false;
162 static MediaDrmBridge::SecurityLevel GetSecurityLevelFromString(
163 const std::string& security_level_str) {
164 if (0 == security_level_str.compare("L1"))
165 return MediaDrmBridge::SECURITY_LEVEL_1;
166 if (0 == security_level_str.compare("L3"))
167 return MediaDrmBridge::SECURITY_LEVEL_3;
168 DCHECK(security_level_str.empty());
169 return MediaDrmBridge::SECURITY_LEVEL_NONE;
172 static std::string GetSecurityLevelString(
173 MediaDrmBridge::SecurityLevel security_level) {
174 switch (security_level) {
175 case MediaDrmBridge::SECURITY_LEVEL_NONE:
176 return "";
177 case MediaDrmBridge::SECURITY_LEVEL_1:
178 return "L1";
179 case MediaDrmBridge::SECURITY_LEVEL_3:
180 return "L3";
182 return "";
185 // Checks whether |key_system| is supported with |container_mime_type|. Only
186 // checks |key_system| support if |container_mime_type| is empty.
187 // TODO(xhwang): The |container_mime_type| is not the same as contentType in
188 // the EME spec. Revisit this once the spec issue with initData type is
189 // resolved.
190 static bool IsKeySystemSupportedWithTypeImpl(
191 const std::string& key_system,
192 const std::string& container_mime_type) {
193 if (!MediaDrmBridge::IsAvailable())
194 return false;
196 std::vector<uint8> scheme_uuid = GetUUID(key_system);
197 if (scheme_uuid.empty())
198 return false;
200 JNIEnv* env = AttachCurrentThread();
201 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
202 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
203 ScopedJavaLocalRef<jstring> j_container_mime_type =
204 ConvertUTF8ToJavaString(env, container_mime_type);
205 return Java_MediaDrmBridge_isCryptoSchemeSupported(
206 env, j_scheme_uuid.obj(), j_container_mime_type.obj());
209 // static
210 bool MediaDrmBridge::IsAvailable() {
211 return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
214 // static
215 bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) {
216 DCHECK(IsAvailable());
217 return SECURITY_LEVEL_1 == security_level;
220 // static
221 bool MediaDrmBridge::IsSecurityLevelSupported(const std::string& key_system,
222 SecurityLevel security_level) {
223 if (!IsAvailable())
224 return false;
226 // Pass 0 as |cdm_id| and NULL as |manager| as they are not used in
227 // creation time of MediaDrmBridge.
228 scoped_ptr<MediaDrmBridge> media_drm_bridge =
229 MediaDrmBridge::Create(0, key_system, GURL(), NULL);
230 if (!media_drm_bridge)
231 return false;
233 return media_drm_bridge->SetSecurityLevel(security_level);
236 // static
237 bool MediaDrmBridge::IsKeySystemSupported(const std::string& key_system) {
238 DCHECK(!key_system.empty());
239 return IsKeySystemSupportedWithTypeImpl(key_system, "");
242 // static
243 bool MediaDrmBridge::IsKeySystemSupportedWithType(
244 const std::string& key_system,
245 const std::string& container_mime_type) {
246 DCHECK(!key_system.empty() && !container_mime_type.empty());
247 return IsKeySystemSupportedWithTypeImpl(key_system, container_mime_type);
250 bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
251 return RegisterNativesImpl(env);
254 MediaDrmBridge::MediaDrmBridge(int cdm_id,
255 const std::vector<uint8>& scheme_uuid,
256 const GURL& security_origin,
257 MediaPlayerManager* manager)
258 : cdm_id_(cdm_id),
259 scheme_uuid_(scheme_uuid),
260 security_origin_(security_origin),
261 manager_(manager) {
262 JNIEnv* env = AttachCurrentThread();
263 CHECK(env);
265 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
266 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
267 j_media_drm_.Reset(Java_MediaDrmBridge_create(
268 env, j_scheme_uuid.obj(), reinterpret_cast<intptr_t>(this)));
271 MediaDrmBridge::~MediaDrmBridge() {
272 JNIEnv* env = AttachCurrentThread();
273 if (!j_media_drm_.is_null())
274 Java_MediaDrmBridge_release(env, j_media_drm_.obj());
277 // static
278 scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create(int cdm_id,
279 const std::string& key_system,
280 const GURL& security_origin,
281 MediaPlayerManager* manager) {
282 scoped_ptr<MediaDrmBridge> media_drm_bridge;
283 if (!IsAvailable())
284 return media_drm_bridge.Pass();
286 std::vector<uint8> scheme_uuid = GetUUID(key_system);
287 if (scheme_uuid.empty())
288 return media_drm_bridge.Pass();
290 media_drm_bridge.reset(
291 new MediaDrmBridge(cdm_id, scheme_uuid, security_origin, manager));
292 if (media_drm_bridge->j_media_drm_.is_null())
293 media_drm_bridge.reset();
295 return media_drm_bridge.Pass();
298 bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level) {
299 JNIEnv* env = AttachCurrentThread();
301 std::string security_level_str = GetSecurityLevelString(security_level);
302 if (security_level_str.empty())
303 return false;
305 ScopedJavaLocalRef<jstring> j_security_level =
306 ConvertUTF8ToJavaString(env, security_level_str);
307 return Java_MediaDrmBridge_setSecurityLevel(
308 env, j_media_drm_.obj(), j_security_level.obj());
311 bool MediaDrmBridge::CreateSession(uint32 session_id,
312 const std::string& content_type,
313 const uint8* init_data,
314 int init_data_length) {
315 JNIEnv* env = AttachCurrentThread();
316 ScopedJavaLocalRef<jbyteArray> j_init_data;
317 // Caller should always use "video/*" content types.
318 DCHECK_EQ(0u, content_type.find("video/"));
320 // Widevine MediaDrm plugin only accepts the "data" part of the PSSH box as
321 // the init data when using MP4 container.
322 if (std::equal(scheme_uuid_.begin(), scheme_uuid_.end(), kWidevineUuid) &&
323 content_type == "video/mp4") {
324 std::vector<uint8> pssh_data;
325 if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data))
326 return false;
327 j_init_data =
328 base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size());
329 } else {
330 j_init_data =
331 base::android::ToJavaByteArray(env, init_data, init_data_length);
334 ScopedJavaLocalRef<jstring> j_mime =
335 ConvertUTF8ToJavaString(env, content_type);
336 Java_MediaDrmBridge_createSession(
337 env, j_media_drm_.obj(), session_id, j_init_data.obj(), j_mime.obj());
338 return true;
341 void MediaDrmBridge::LoadSession(uint32 session_id,
342 const std::string& web_session_id) {
343 // MediaDrmBridge doesn't support loading sessions.
344 NOTREACHED();
347 void MediaDrmBridge::UpdateSession(uint32 session_id,
348 const uint8* response,
349 int response_length) {
350 DVLOG(1) << __FUNCTION__;
351 JNIEnv* env = AttachCurrentThread();
352 ScopedJavaLocalRef<jbyteArray> j_response =
353 base::android::ToJavaByteArray(env, response, response_length);
354 Java_MediaDrmBridge_updateSession(
355 env, j_media_drm_.obj(), session_id, j_response.obj());
358 void MediaDrmBridge::ReleaseSession(uint32 session_id) {
359 DVLOG(1) << __FUNCTION__;
360 JNIEnv* env = AttachCurrentThread();
361 Java_MediaDrmBridge_releaseSession(env, j_media_drm_.obj(), session_id);
364 void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) {
365 if (closure.is_null()) {
366 media_crypto_ready_cb_.Reset();
367 return;
370 DCHECK(media_crypto_ready_cb_.is_null());
372 if (!GetMediaCrypto().is_null()) {
373 base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure);
374 return;
377 media_crypto_ready_cb_ = closure;
380 void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) {
381 DCHECK(!GetMediaCrypto().is_null());
382 if (!media_crypto_ready_cb_.is_null())
383 base::ResetAndReturn(&media_crypto_ready_cb_).Run();
386 void MediaDrmBridge::OnSessionCreated(JNIEnv* env,
387 jobject j_media_drm,
388 jint j_session_id,
389 jstring j_web_session_id) {
390 uint32 session_id = j_session_id;
391 std::string web_session_id = ConvertJavaStringToUTF8(env, j_web_session_id);
392 manager_->OnSessionCreated(cdm_id_, session_id, web_session_id);
395 void MediaDrmBridge::OnSessionMessage(JNIEnv* env,
396 jobject j_media_drm,
397 jint j_session_id,
398 jbyteArray j_message,
399 jstring j_destination_url) {
400 uint32 session_id = j_session_id;
401 std::vector<uint8> message;
402 JavaByteArrayToByteVector(env, j_message, &message);
403 std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url);
404 GURL destination_gurl(destination_url);
405 if (!destination_gurl.is_valid() && !destination_gurl.is_empty()) {
406 DLOG(WARNING) << "SessionMessage destination_url is invalid : "
407 << destination_gurl.possibly_invalid_spec();
408 destination_gurl = GURL::EmptyGURL(); // Replace invalid destination_url.
411 manager_->OnSessionMessage(cdm_id_, session_id, message, destination_gurl);
414 void MediaDrmBridge::OnSessionReady(JNIEnv* env,
415 jobject j_media_drm,
416 jint j_session_id) {
417 uint32 session_id = j_session_id;
418 manager_->OnSessionReady(cdm_id_, session_id);
421 void MediaDrmBridge::OnSessionClosed(JNIEnv* env,
422 jobject j_media_drm,
423 jint j_session_id) {
424 uint32 session_id = j_session_id;
425 manager_->OnSessionClosed(cdm_id_, session_id);
428 void MediaDrmBridge::OnSessionError(JNIEnv* env,
429 jobject j_media_drm,
430 jint j_session_id) {
431 uint32 session_id = j_session_id;
432 manager_->OnSessionError(cdm_id_, session_id, MediaKeys::kUnknownError, 0);
435 ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
436 JNIEnv* env = AttachCurrentThread();
437 return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
440 MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() {
441 JNIEnv* env = AttachCurrentThread();
442 ScopedJavaLocalRef<jstring> j_security_level =
443 Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj());
444 std::string security_level_str =
445 ConvertJavaStringToUTF8(env, j_security_level.obj());
446 return GetSecurityLevelFromString(security_level_str);
449 bool MediaDrmBridge::IsProtectedSurfaceRequired() {
450 return IsSecureDecoderRequired(GetSecurityLevel());
453 void MediaDrmBridge::ResetDeviceCredentials(
454 const ResetCredentialsCB& callback) {
455 DCHECK(reset_credentials_cb_.is_null());
456 reset_credentials_cb_ = callback;
457 JNIEnv* env = AttachCurrentThread();
458 Java_MediaDrmBridge_resetDeviceCredentials(env, j_media_drm_.obj());
461 void MediaDrmBridge::OnResetDeviceCredentialsCompleted(
462 JNIEnv* env, jobject, bool success) {
463 base::ResetAndReturn(&reset_credentials_cb_).Run(success);
466 } // namespace media