blob: ee4fbbdf11586e04ed490b207b0413c113d46712 [file] [log] [blame]
// Copyright (c) 2013 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 "media/base/android/media_drm_bridge.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/logging.h"
#include "media/base/android/media_player_manager.h"
using base::android::ConvertJavaStringToUTF8;
using base::android::JavaByteArrayToByteVector;
using base::android::ScopedJavaLocalRef;
namespace media {
static uint32 ReadUint32(const uint8_t* data) {
uint32 value = 0;
for (int i = 0; i < 4; ++i)
value = (value << 8) | data[i];
return value;
}
static uint64 ReadUint64(const uint8_t* data) {
uint64 value = 0;
for (int i = 0; i < 8; ++i)
value = (value << 8) | data[i];
return value;
}
// The structure of an ISO CENC Protection System Specific Header (PSSH) box is
// as follows. (See ISO/IEC FDIS 23001-7:2011(E).)
// Note: ISO boxes use big-endian values.
//
// PSSH {
// uint32 Size
// uint32 Type
// uint64 LargeSize # Field is only present if value(Size) == 1.
// uint32 VersionAndFlags
// uint8[16] SystemId
// uint32 DataSize
// uint8[DataSize] Data
// }
static const int kBoxHeaderSize = 8; // Box's header contains Size and Type.
static const int kBoxLargeSizeSize = 8;
static const int kPsshVersionFlagSize = 4;
static const int kPsshSystemIdSize = 16;
static const int kPsshDataSizeSize = 4;
static const uint32 kTencType = 0x74656e63;
static const uint32 kPsshType = 0x70737368;
// Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the
// "Data" of the box and put it in |pssh_data|. Returns true if such a box is
// found and successfully parsed. Returns false otherwise.
// Notes:
// 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box
// will be set in |pssh_data|.
// 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped.
static bool GetPsshData(const uint8* data, int data_size,
const std::vector<uint8>& uuid,
std::vector<uint8>* pssh_data) {
const uint8* cur = data;
const uint8* data_end = data + data_size;
int bytes_left = data_size;
while (bytes_left > 0) {
const uint8* box_head = cur;
if (bytes_left < kBoxHeaderSize)
return false;
uint64_t box_size = ReadUint32(cur);
uint32 type = ReadUint32(cur + 4);
cur += kBoxHeaderSize;
bytes_left -= kBoxHeaderSize;
if (box_size == 1) { // LargeSize is present.
if (bytes_left < kBoxLargeSizeSize)
return false;
box_size = ReadUint64(cur);
cur += kBoxLargeSizeSize;
bytes_left -= kBoxLargeSizeSize;
} else if (box_size == 0) {
box_size = bytes_left + kBoxHeaderSize;
}
const uint8* box_end = box_head + box_size;
if (data_end < box_end)
return false;
if (type == kTencType) {
// Skip 'tenc' box.
cur = box_end;
bytes_left = data_end - cur;
continue;
} else if (type != kPsshType) {
return false;
}
const int kPsshBoxMinimumSize =
kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize;
if (box_end < cur + kPsshBoxMinimumSize)
return false;
uint32 version_and_flags = ReadUint32(cur);
cur += kPsshVersionFlagSize;
bytes_left -= kPsshVersionFlagSize;
if (version_and_flags != 0)
return false;
DCHECK_GE(bytes_left, kPsshSystemIdSize);
if (!std::equal(uuid.begin(), uuid.end(), cur)) {
cur = box_end;
bytes_left = data_end - cur;
continue;
}
cur += kPsshSystemIdSize;
bytes_left -= kPsshSystemIdSize;
uint32 data_size = ReadUint32(cur);
cur += kPsshDataSizeSize;
bytes_left -= kPsshDataSizeSize;
if (box_end < cur + data_size)
return false;
pssh_data->assign(cur, cur + data_size);
return true;
}
return false;
}
// static
bool MediaDrmBridge::IsAvailable() {
return false;
}
MediaDrmBridge* MediaDrmBridge::Create(int media_keys_id,
const std::vector<uint8>& uuid,
MediaPlayerManager* manager) {
if (!IsAvailable())
return NULL;
// TODO(qinmin): check whether the uuid is valid.
return new MediaDrmBridge(media_keys_id, uuid, manager);
}
MediaDrmBridge::MediaDrmBridge(int media_keys_id,
const std::vector<uint8>& uuid,
MediaPlayerManager* manager)
: media_keys_id_(media_keys_id), uuid_(uuid), manager_(manager) {
// TODO(qinmin): pass the uuid to DRM engine.
}
MediaDrmBridge::~MediaDrmBridge() {}
bool MediaDrmBridge::GenerateKeyRequest(const std::string& type,
const uint8* init_data,
int init_data_length) {
std::vector<uint8> pssh_data;
if (!GetPsshData(init_data, init_data_length, uuid_, &pssh_data))
return false;
NOTIMPLEMENTED();
return false;
}
void MediaDrmBridge::CancelKeyRequest(const std::string& session_id) {
NOTIMPLEMENTED();
}
void MediaDrmBridge::AddKey(const uint8* key, int key_length,
const uint8* init_data, int init_data_length,
const std::string& session_id) {
NOTIMPLEMENTED();
}
ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
NOTIMPLEMENTED();
return ScopedJavaLocalRef<jobject>();
}
void MediaDrmBridge::OnKeyMessage(JNIEnv* env,
jobject j_media_drm,
jstring j_session_id,
jbyteArray j_message,
jstring j_destination_url) {
std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
std::vector<uint8> message;
JavaByteArrayToByteVector(env, j_message, &message);
std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url);
manager_->OnKeyMessage(media_keys_id_, session_id, message, destination_url);
}
void MediaDrmBridge::OnDrmEvent(JNIEnv* env,
jobject j_media_drm,
jstring session_id,
jint event,
jint extra,
jstring data) {
NOTIMPLEMENTED();
}
} // namespace media