17d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
27d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
37d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// found in the LICENSE file.
47d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
57d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "media/base/android/media_drm_bridge.h"
67d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
7c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include <algorithm>
8c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
93551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include "base/android/build_info.h"
10eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/android/jni_array.h"
11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/android/jni_string.h"
12424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "base/callback_helpers.h"
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/containers/hash_tables.h"
14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/lazy_instance.h"
15424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "base/location.h"
167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/logging.h"
17424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "base/message_loop/message_loop_proxy.h"
18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/strings/string_util.h"
196d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "base/sys_byteorder.h"
203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include "jni/MediaDrmBridge_jni.h"
217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)using base::android::AttachCurrentThread;
253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)using base::android::ConvertUTF8ToJavaString;
26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochusing base::android::ConvertJavaStringToUTF8;
27eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochusing base::android::JavaByteArrayToByteVector;
287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)using base::android::ScopedJavaLocalRef;
297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)namespace media {
317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochstatic uint32 ReadUint32(const uint8_t* data) {
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  uint32 value = 0;
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  for (int i = 0; i < 4; ++i)
35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    value = (value << 8) | data[i];
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return value;
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochstatic uint64 ReadUint64(const uint8_t* data) {
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  uint64 value = 0;
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  for (int i = 0; i < 8; ++i)
42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    value = (value << 8) | data[i];
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return value;
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
45eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
46eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// The structure of an ISO CENC Protection System Specific Header (PSSH) box is
47eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// as follows. (See ISO/IEC FDIS 23001-7:2011(E).)
48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Note: ISO boxes use big-endian values.
49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch//
50eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// PSSH {
51eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch//   uint32 Size
52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch//   uint32 Type
53eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch//   uint64 LargeSize  # Field is only present if value(Size) == 1.
54eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch//   uint32 VersionAndFlags
55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch//   uint8[16] SystemId
56eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch//   uint32 DataSize
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch//   uint8[DataSize] Data
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// }
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kBoxHeaderSize = 8;  // Box's header contains Size and Type.
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kBoxLargeSizeSize = 8;
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kPsshVersionFlagSize = 4;
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kPsshSystemIdSize = 16;
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kPsshDataSizeSize = 4;
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const uint32 kTencType = 0x74656e63;
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const uint32 kPsshType = 0x70737368;
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const uint8 kWidevineUuid[16] = {
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED };
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)typedef std::vector<uint8> UUID;
71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)class KeySystemUuidManager {
73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) public:
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  KeySystemUuidManager();
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  UUID GetUUID(const std::string& key_system);
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  void AddMapping(const std::string& key_system, const UUID& uuid);
77f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  std::vector<std::string> GetPlatformKeySystemNames();
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) private:
80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  typedef base::hash_map<std::string, UUID> KeySystemUuidMap;
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  KeySystemUuidMap key_system_uuid_map_;
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(KeySystemUuidManager);
85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)KeySystemUuidManager::KeySystemUuidManager() {
88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Widevine is always supported in Android.
89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  key_system_uuid_map_[kWidevineKeySystem] =
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      UUID(kWidevineUuid, kWidevineUuid + arraysize(kWidevineUuid));
91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)UUID KeySystemUuidManager::GetUUID(const std::string& key_system) {
94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  KeySystemUuidMap::iterator it = key_system_uuid_map_.find(key_system);
95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (it == key_system_uuid_map_.end())
96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return UUID();
97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return it->second;
98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void KeySystemUuidManager::AddMapping(const std::string& key_system,
101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                      const UUID& uuid) {
102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  KeySystemUuidMap::iterator it = key_system_uuid_map_.find(key_system);
103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(it == key_system_uuid_map_.end())
104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      << "Shouldn't overwrite an existing key system.";
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (it != key_system_uuid_map_.end())
106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  key_system_uuid_map_[key_system] = uuid;
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
110f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)std::vector<std::string> KeySystemUuidManager::GetPlatformKeySystemNames() {
111f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  std::vector<std::string> key_systems;
112f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (KeySystemUuidMap::iterator it = key_system_uuid_map_.begin();
113f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)       it != key_system_uuid_map_.end(); ++it) {
114f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Rule out the key system handled by Chrome explicitly.
115f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (it->first != kWidevineKeySystem)
116f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      key_systems.push_back(it->first);
117f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
118f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return key_systems;
119f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
120f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)base::LazyInstance<KeySystemUuidManager>::Leaky g_key_system_uuid_manager =
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    LAZY_INSTANCE_INITIALIZER;
123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the
125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// "Data" of the box and put it in |pssh_data|. Returns true if such a box is
126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// found and successfully parsed. Returns false otherwise.
127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Notes:
128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box
129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// will be set in |pssh_data|.
130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped.
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochstatic bool GetPsshData(const uint8* data, int data_size,
132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                        const UUID& uuid,
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                        std::vector<uint8>* pssh_data) {
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const uint8* cur = data;
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const uint8* data_end = data + data_size;
136eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int bytes_left = data_size;
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  while (bytes_left > 0) {
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const uint8* box_head = cur;
140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
141eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (bytes_left < kBoxHeaderSize)
142eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return false;
143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    uint64_t box_size = ReadUint32(cur);
145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    uint32 type = ReadUint32(cur + 4);
146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    cur += kBoxHeaderSize;
147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    bytes_left -= kBoxHeaderSize;
148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
149eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (box_size == 1) {  // LargeSize is present.
150eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if (bytes_left < kBoxLargeSizeSize)
151eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        return false;
152eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
153eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      box_size = ReadUint64(cur);
154eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      cur += kBoxLargeSizeSize;
155eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      bytes_left -= kBoxLargeSizeSize;
156eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    } else if (box_size == 0) {
157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      box_size = bytes_left + kBoxHeaderSize;
158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const uint8* box_end = box_head + box_size;
161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (data_end < box_end)
162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return false;
163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
164eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (type == kTencType) {
165eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // Skip 'tenc' box.
166eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      cur = box_end;
167eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      bytes_left = data_end - cur;
168eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      continue;
169eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    } else if (type != kPsshType) {
170eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return false;
171eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
172eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
173eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const int kPsshBoxMinimumSize =
174eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize;
175eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (box_end < cur + kPsshBoxMinimumSize)
176eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return false;
177eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
178eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    uint32 version_and_flags = ReadUint32(cur);
179eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    cur += kPsshVersionFlagSize;
180eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    bytes_left -= kPsshVersionFlagSize;
181eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (version_and_flags != 0)
182eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return false;
183eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
184eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    DCHECK_GE(bytes_left, kPsshSystemIdSize);
185eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (!std::equal(uuid.begin(), uuid.end(), cur)) {
186eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      cur = box_end;
187eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      bytes_left = data_end - cur;
188eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      continue;
189eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
190eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
191eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    cur += kPsshSystemIdSize;
192eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    bytes_left -= kPsshSystemIdSize;
193eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
194eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    uint32 data_size = ReadUint32(cur);
195eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    cur += kPsshDataSizeSize;
196eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    bytes_left -= kPsshDataSizeSize;
197eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
198eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (box_end < cur + data_size)
199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return false;
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
201eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    pssh_data->assign(cur, cur + data_size);
202eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return true;
203eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return false;
206eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
207eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
20858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)static MediaDrmBridge::SecurityLevel GetSecurityLevelFromString(
20958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const std::string& security_level_str) {
21058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (0 == security_level_str.compare("L1"))
21158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return MediaDrmBridge::SECURITY_LEVEL_1;
21258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (0 == security_level_str.compare("L3"))
21358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return MediaDrmBridge::SECURITY_LEVEL_3;
21458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DCHECK(security_level_str.empty());
21558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return MediaDrmBridge::SECURITY_LEVEL_NONE;
21658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
21758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)static std::string GetSecurityLevelString(
2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    MediaDrmBridge::SecurityLevel security_level) {
2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  switch (security_level) {
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case MediaDrmBridge::SECURITY_LEVEL_NONE:
2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return "";
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case MediaDrmBridge::SECURITY_LEVEL_1:
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return "L1";
2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case MediaDrmBridge::SECURITY_LEVEL_3:
2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return "L3";
22758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return "";
2293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
2303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
231c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// Checks whether |key_system| is supported with |container_mime_type|. Only
232c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// checks |key_system| support if |container_mime_type| is empty.
233c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// TODO(xhwang): The |container_mime_type| is not the same as contentType in
234c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// the EME spec. Revisit this once the spec issue with initData type is
235c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// resolved.
236c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochstatic bool IsKeySystemSupportedWithTypeImpl(
237c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    const std::string& key_system,
238c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    const std::string& container_mime_type) {
239c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (!MediaDrmBridge::IsAvailable())
240c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return false;
241c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  UUID scheme_uuid = g_key_system_uuid_manager.Get().GetUUID(key_system);
243c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (scheme_uuid.empty())
244c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return false;
245c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
246c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  JNIEnv* env = AttachCurrentThread();
247c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
248c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
249c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  ScopedJavaLocalRef<jstring> j_container_mime_type =
250c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      ConvertUTF8ToJavaString(env, container_mime_type);
251c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return Java_MediaDrmBridge_isCryptoSchemeSupported(
252c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      env, j_scheme_uuid.obj(), j_container_mime_type.obj());
253c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
254c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
25558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// static
2563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool MediaDrmBridge::IsAvailable() {
2570f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
2583551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
2593551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
26058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// static
2615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) {
262a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(IsAvailable());
2635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return SECURITY_LEVEL_1 == security_level;
26458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
26558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
266a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static
267a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool MediaDrmBridge::IsSecurityLevelSupported(const std::string& key_system,
268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                              SecurityLevel security_level) {
269a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!IsAvailable())
270a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
271a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
2725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<MediaDrmBridge> media_drm_bridge =
273cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      MediaDrmBridge::CreateSessionless(key_system);
2745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!media_drm_bridge)
2755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
2765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return media_drm_bridge->SetSecurityLevel(security_level);
27858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
27958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2806d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)static void AddKeySystemUuidMapping(JNIEnv* env, jclass clazz,
2816d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                                    jstring j_key_system,
2826d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                                    jobject j_buffer) {
2836d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  std::string key_system = ConvertJavaStringToUTF8(env, j_key_system);
2846d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  uint8* buffer = static_cast<uint8*>(env->GetDirectBufferAddress(j_buffer));
2856d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  UUID uuid(buffer, buffer + 16);
286cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  g_key_system_uuid_manager.Get().AddMapping(key_system, uuid);
287cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
288cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
289a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static
290f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)std::vector<std::string> MediaDrmBridge::GetPlatformKeySystemNames() {
291f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return g_key_system_uuid_manager.Get().GetPlatformKeySystemNames();
292f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
293f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
294f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// static
295c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochbool MediaDrmBridge::IsKeySystemSupported(const std::string& key_system) {
296c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  DCHECK(!key_system.empty());
297c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return IsKeySystemSupportedWithTypeImpl(key_system, "");
298c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
299c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
300c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// static
301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool MediaDrmBridge::IsKeySystemSupportedWithType(
302a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& key_system,
30358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const std::string& container_mime_type) {
304c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  DCHECK(!key_system.empty() && !container_mime_type.empty());
305c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return IsKeySystemSupportedWithTypeImpl(key_system, container_mime_type);
30658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
30758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
3083551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
3093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return RegisterNativesImpl(env);
310eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
311eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
312cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)MediaDrmBridge::MediaDrmBridge(const std::vector<uint8>& scheme_uuid,
313cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                               const SessionCreatedCB& session_created_cb,
314cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                               const SessionMessageCB& session_message_cb,
315cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                               const SessionReadyCB& session_ready_cb,
316cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                               const SessionClosedCB& session_closed_cb,
317cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                               const SessionErrorCB& session_error_cb)
318cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    : scheme_uuid_(scheme_uuid),
319cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      session_created_cb_(session_created_cb),
320cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      session_message_cb_(session_message_cb),
321cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      session_ready_cb_(session_ready_cb),
322cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      session_closed_cb_(session_closed_cb),
323cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      session_error_cb_(session_error_cb) {
3243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  JNIEnv* env = AttachCurrentThread();
3253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  CHECK(env);
3263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
3273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
3283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
3293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  j_media_drm_.Reset(Java_MediaDrmBridge_create(
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      env, j_scheme_uuid.obj(), reinterpret_cast<intptr_t>(this)));
331eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
3327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
3333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)MediaDrmBridge::~MediaDrmBridge() {
3343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  JNIEnv* env = AttachCurrentThread();
33546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  player_tracker_.NotifyCdmUnset();
33658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!j_media_drm_.is_null())
33758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    Java_MediaDrmBridge_release(env, j_media_drm_.obj());
3383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
3397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static
341cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create(
342cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const std::string& key_system,
343cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const SessionCreatedCB& session_created_cb,
344cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const SessionMessageCB& session_message_cb,
345cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const SessionReadyCB& session_ready_cb,
346cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const SessionClosedCB& session_closed_cb,
347cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const SessionErrorCB& session_error_cb) {
3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<MediaDrmBridge> media_drm_bridge;
349a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!IsAvailable())
350a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return media_drm_bridge.Pass();
3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
352cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  UUID scheme_uuid = g_key_system_uuid_manager.Get().GetUUID(key_system);
353a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (scheme_uuid.empty())
354a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return media_drm_bridge.Pass();
355a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
356cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  media_drm_bridge.reset(new MediaDrmBridge(scheme_uuid,
357cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                            session_created_cb,
358cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                            session_message_cb,
359cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                            session_ready_cb,
360cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                            session_closed_cb,
361cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                            session_error_cb));
362cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
363a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (media_drm_bridge->j_media_drm_.is_null())
364a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    media_drm_bridge.reset();
3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return media_drm_bridge.Pass();
3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
369cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// static
370cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)scoped_ptr<MediaDrmBridge> MediaDrmBridge::CreateSessionless(
371cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const std::string& key_system) {
372cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return MediaDrmBridge::Create(key_system,
373cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                SessionCreatedCB(),
374cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                SessionMessageCB(),
375cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                SessionReadyCB(),
376cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                SessionClosedCB(),
377cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                SessionErrorCB());
378cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
379cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
3805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level) {
3815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  JNIEnv* env = AttachCurrentThread();
3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string security_level_str = GetSecurityLevelString(security_level);
3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (security_level_str.empty())
3855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
3865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ScopedJavaLocalRef<jstring> j_security_level =
3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ConvertUTF8ToJavaString(env, security_level_str);
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return Java_MediaDrmBridge_setSecurityLevel(
3905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      env, j_media_drm_.obj(), j_security_level.obj());
3915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
393a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool MediaDrmBridge::CreateSession(uint32 session_id,
3945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                   const std::string& content_type,
395a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   const uint8* init_data,
396a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   int init_data_length) {
397cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DVLOG(1) << __FUNCTION__;
398cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
399cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!session_created_cb_.is_null())
400cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      << "CreateSession called on a sessionless MediaDrmBridge object.";
401cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
4023551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  JNIEnv* env = AttachCurrentThread();
403c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  ScopedJavaLocalRef<jbyteArray> j_init_data;
404c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // Caller should always use "video/*" content types.
405c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  DCHECK_EQ(0u, content_type.find("video/"));
406c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
407c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // Widevine MediaDrm plugin only accepts the "data" part of the PSSH box as
408c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // the init data when using MP4 container.
409c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (std::equal(scheme_uuid_.begin(), scheme_uuid_.end(), kWidevineUuid) &&
410c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      content_type == "video/mp4") {
411c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    std::vector<uint8> pssh_data;
412c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data))
413c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return false;
414c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    j_init_data =
415c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size());
416c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  } else {
417c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    j_init_data =
418c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        base::android::ToJavaByteArray(env, init_data, init_data_length);
419c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
420c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
4215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ScopedJavaLocalRef<jstring> j_mime =
4225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ConvertUTF8ToJavaString(env, content_type);
423a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  Java_MediaDrmBridge_createSession(
424c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      env, j_media_drm_.obj(), session_id, j_init_data.obj(), j_mime.obj());
4253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return true;
4267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
4277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
4285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MediaDrmBridge::LoadSession(uint32 session_id,
4295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 const std::string& web_session_id) {
4305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // MediaDrmBridge doesn't support loading sessions.
4315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  NOTREACHED();
4325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
434a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void MediaDrmBridge::UpdateSession(uint32 session_id,
435a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   const uint8* response,
436a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   int response_length) {
43758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DVLOG(1) << __FUNCTION__;
438cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
439cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!session_ready_cb_.is_null())
440cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      << __FUNCTION__ << " called on a sessionless MediaDrmBridge object.";
441cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
4423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  JNIEnv* env = AttachCurrentThread();
443a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ScopedJavaLocalRef<jbyteArray> j_response =
444a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::android::ToJavaByteArray(env, response, response_length);
445a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  Java_MediaDrmBridge_updateSession(
446a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      env, j_media_drm_.obj(), session_id, j_response.obj());
4477d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
4487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
449a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void MediaDrmBridge::ReleaseSession(uint32 session_id) {
450a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DVLOG(1) << __FUNCTION__;
451cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
452cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!session_closed_cb_.is_null())
453cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      << __FUNCTION__ << " called on a sessionless MediaDrmBridge object.";
454cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
4553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  JNIEnv* env = AttachCurrentThread();
456a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  Java_MediaDrmBridge_releaseSession(env, j_media_drm_.obj(), session_id);
4577d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
4587d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
45946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)int MediaDrmBridge::RegisterPlayer(const base::Closure& new_key_cb,
46046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                                   const base::Closure& cdm_unset_cb) {
46146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return player_tracker_.RegisterPlayer(new_key_cb, cdm_unset_cb);
46246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
46346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
46446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void MediaDrmBridge::UnregisterPlayer(int registration_id) {
46546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  player_tracker_.UnregisterPlayer(registration_id);
46646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
46746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
468424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) {
469424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  if (closure.is_null()) {
470424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    media_crypto_ready_cb_.Reset();
471424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return;
472424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  }
473424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
474424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  DCHECK(media_crypto_ready_cb_.is_null());
475424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
476424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  if (!GetMediaCrypto().is_null()) {
477424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure);
478424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return;
479424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  }
480424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
481424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  media_crypto_ready_cb_ = closure;
482424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
483424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
484424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) {
485424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  DCHECK(!GetMediaCrypto().is_null());
486424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  if (!media_crypto_ready_cb_.is_null())
487424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    base::ResetAndReturn(&media_crypto_ready_cb_).Run();
488424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
489424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
490a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void MediaDrmBridge::OnSessionCreated(JNIEnv* env,
491a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                      jobject j_media_drm,
492a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                      jint j_session_id,
493a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                      jstring j_web_session_id) {
494a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  uint32 session_id = j_session_id;
495a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::string web_session_id = ConvertJavaStringToUTF8(env, j_web_session_id);
496cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  session_created_cb_.Run(session_id, web_session_id);
497a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
498a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
499a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void MediaDrmBridge::OnSessionMessage(JNIEnv* env,
500a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                      jobject j_media_drm,
501a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                      jint j_session_id,
502a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                      jbyteArray j_message,
503a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                      jstring j_destination_url) {
504a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  uint32 session_id = j_session_id;
505eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  std::vector<uint8> message;
506eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  JavaByteArrayToByteVector(env, j_message, &message);
507cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  GURL destination_gurl = GURL(ConvertJavaStringToUTF8(env, j_destination_url));
5085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!destination_gurl.is_valid() && !destination_gurl.is_empty()) {
5095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DLOG(WARNING) << "SessionMessage destination_url is invalid : "
5105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                  << destination_gurl.possibly_invalid_spec();
5115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    destination_gurl = GURL::EmptyGURL();  // Replace invalid destination_url.
5125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
513cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  session_message_cb_.Run(session_id, message, destination_gurl);
514a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
5157d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
516a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void MediaDrmBridge::OnSessionReady(JNIEnv* env,
517a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    jobject j_media_drm,
518a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    jint j_session_id) {
519a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  uint32 session_id = j_session_id;
520cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  session_ready_cb_.Run(session_id);
521116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // TODO(xhwang/jrummell): Move this when usableKeyIds/keyschange are
522116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // implemented.
523116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  player_tracker_.NotifyNewKey();
5247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
5257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
526a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void MediaDrmBridge::OnSessionClosed(JNIEnv* env,
527a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     jobject j_media_drm,
528a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     jint j_session_id) {
529a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  uint32 session_id = j_session_id;
530cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  session_closed_cb_.Run(session_id);
5313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
5323551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
533a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void MediaDrmBridge::OnSessionError(JNIEnv* env,
534a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    jobject j_media_drm,
535a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    jint j_session_id) {
536a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  uint32 session_id = j_session_id;
537cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  session_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0);
5387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
5397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
540424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
541424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  JNIEnv* env = AttachCurrentThread();
542424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
543424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
544424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
545424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() {
546424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  JNIEnv* env = AttachCurrentThread();
547424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  ScopedJavaLocalRef<jstring> j_security_level =
548424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj());
54958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::string security_level_str =
550424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      ConvertJavaStringToUTF8(env, j_security_level.obj());
55158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return GetSecurityLevelFromString(security_level_str);
552424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
553424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
554424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)bool MediaDrmBridge::IsProtectedSurfaceRequired() {
55558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return IsSecureDecoderRequired(GetSecurityLevel());
556424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
557424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
55868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void MediaDrmBridge::ResetDeviceCredentials(
55968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    const ResetCredentialsCB& callback) {
56068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  DCHECK(reset_credentials_cb_.is_null());
56168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  reset_credentials_cb_ = callback;
56268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  JNIEnv* env = AttachCurrentThread();
56368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  Java_MediaDrmBridge_resetDeviceCredentials(env, j_media_drm_.obj());
56468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
56568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
56668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void MediaDrmBridge::OnResetDeviceCredentialsCompleted(
56768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    JNIEnv* env, jobject, bool success) {
56868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  base::ResetAndReturn(&reset_credentials_cb_).Run(success);
56968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
57068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
5717d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}  // namespace media
572