/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "NuPlayer2Drm"
#include "NuPlayer2Drm.h"
#include <media/NdkWrapper.h>
#include <utils/Log.h>
#include <sstream>
namespace android {
Vector<DrmUUID> NuPlayer2Drm::parsePSSH(const void *pssh, size_t psshsize)
{
Vector<DrmUUID> drmSchemes, empty;
const int DATALEN_SIZE = 4;
// the format of the buffer is 1 or more of:
// {
// 16 byte uuid
// 4 byte data length N
// N bytes of data
// }
// Determine the number of entries in the source data.
// Since we got the data from stagefright, we trust it is valid and properly formatted.
const uint8_t *data = (const uint8_t*)pssh;
size_t len = psshsize;
size_t numentries = 0;
while (len > 0) {
if (len < DrmUUID::UUID_SIZE) {
ALOGE("ParsePSSH: invalid PSSH data");
return empty;
}
const uint8_t *uuidPtr = data;
// skip uuid
data += DrmUUID::UUID_SIZE;
len -= DrmUUID::UUID_SIZE;
// get data length
if (len < DATALEN_SIZE) {
ALOGE("ParsePSSH: invalid PSSH data");
return empty;
}
uint32_t datalen = *((uint32_t*)data);
data += DATALEN_SIZE;
len -= DATALEN_SIZE;
if (len < datalen) {
ALOGE("ParsePSSH: invalid PSSH data");
return empty;
}
// skip the data
data += datalen;
len -= datalen;
DrmUUID _uuid(uuidPtr);
drmSchemes.add(_uuid);
ALOGV("ParsePSSH[%zu]: %s: %s", numentries,
_uuid.toHexString().string(),
DrmUUID::arrayToHex(data, datalen).string()
);
numentries++;
}
return drmSchemes;
}
Vector<DrmUUID> NuPlayer2Drm::getSupportedDrmSchemes(const void *pssh, size_t psshsize)
{
Vector<DrmUUID> psshDRMs = parsePSSH(pssh, psshsize);
Vector<DrmUUID> supportedDRMs;
for (size_t i = 0; i < psshDRMs.size(); i++) {
DrmUUID uuid = psshDRMs[i];
if (AMediaDrmWrapper::isCryptoSchemeSupported(uuid.ptr(), NULL)) {
supportedDRMs.add(uuid);
}
}
ALOGV("getSupportedDrmSchemes: psshDRMs: %zu supportedDRMs: %zu",
psshDRMs.size(), supportedDRMs.size());
return supportedDRMs;
}
sp<ABuffer> NuPlayer2Drm::retrieveDrmInfo(const void *pssh, uint32_t psshsize)
{
std::ostringstream buf;
// 1) PSSH bytes
buf.write(reinterpret_cast<const char *>(&psshsize), sizeof(psshsize));
buf.write(reinterpret_cast<const char *>(pssh), psshsize);
ALOGV("retrieveDrmInfo: MEDIA2_DRM_INFO PSSH: size: %u %s", psshsize,
DrmUUID::arrayToHex((uint8_t*)pssh, psshsize).string());
// 2) supportedDRMs
Vector<DrmUUID> supportedDRMs = getSupportedDrmSchemes(pssh, psshsize);
uint32_t n = supportedDRMs.size();
buf.write(reinterpret_cast<char *>(&n), sizeof(n));
for (size_t i = 0; i < n; i++) {
DrmUUID uuid = supportedDRMs[i];
buf.write(reinterpret_cast<const char *>(&n), sizeof(n));
buf.write(reinterpret_cast<const char *>(uuid.ptr()), DrmUUID::UUID_SIZE);
ALOGV("retrieveDrmInfo: MEDIA2_DRM_INFO supportedScheme[%zu] %s", i,
uuid.toHexString().string());
}
sp<ABuffer> drmInfoBuffer = ABuffer::CreateAsCopy(buf.str().c_str(), buf.tellp());
return drmInfoBuffer;
}
status_t NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo, PlayerMessage *playerMsg)
{
std::ostringstream pssh, drmInfo;
// 0) Generate PSSH bytes
for (size_t i = 0; i < psshInfo->numentries; i++) {
PsshEntry *entry = &psshInfo->entries[i];
uint32_t datalen = entry->datalen;
pssh.write(reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
pssh.write(reinterpret_cast<const char *>(&datalen), sizeof(datalen));
pssh.write(reinterpret_cast<const char *>(entry->data), datalen);
}
uint32_t psshSize = pssh.tellp();
std::string psshBase = pssh.str();
const auto* psshPtr = reinterpret_cast<const uint8_t*>(psshBase.c_str());
ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO PSSH: size: %u %s", psshSize,
DrmUUID::arrayToHex(psshPtr, psshSize).string());
// 1) Write PSSH bytes
playerMsg->add_values()->set_bytes_value(
reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
// 2) Write supportedDRMs
uint32_t numentries = psshInfo->numentries;
playerMsg->add_values()->set_int32_value(numentries);
for (size_t i = 0; i < numentries; i++) {
PsshEntry *entry = &psshInfo->entries[i];
playerMsg->add_values()->set_bytes_value(
reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i,
DrmUUID::arrayToHex((const uint8_t*)&entry->uuid, sizeof(AMediaUUID)).string());
}
return OK;
}
} // namespace android