blob: e2bc89c5443a75b4c65eb9ac7738a46e86d9ab7d [file] [log] [blame]
/*
* Copyright (C) 2010 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.
*/
#include "include/DRMExtractor.h"
#include <arpa/inet.h>
#include <utils/String8.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/Utils.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaBuffer.h>
#include <drm/drm_framework_common.h>
#include <utils/Errors.h>
namespace android {
class DRMSource : public MediaSource {
public:
DRMSource(const sp<MediaSource> &mediaSource,
const sp<DecryptHandle> &decryptHandle,
DrmManagerClient *managerClient,
int32_t trackId, DrmBuffer *ipmpBox);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
protected:
virtual ~DRMSource();
private:
sp<MediaSource> mOriginalMediaSource;
sp<DecryptHandle> mDecryptHandle;
DrmManagerClient* mDrmManagerClient;
size_t mTrackId;
mutable Mutex mDRMLock;
size_t mNALLengthSize;
bool mWantsNALFragments;
DRMSource(const DRMSource &);
DRMSource &operator=(const DRMSource &);
};
////////////////////////////////////////////////////////////////////////////////
DRMSource::DRMSource(const sp<MediaSource> &mediaSource,
const sp<DecryptHandle> &decryptHandle,
DrmManagerClient *managerClient,
int32_t trackId, DrmBuffer *ipmpBox)
: mOriginalMediaSource(mediaSource),
mDecryptHandle(decryptHandle),
mDrmManagerClient(managerClient),
mTrackId(trackId),
mNALLengthSize(0),
mWantsNALFragments(false) {
CHECK(mDrmManagerClient);
mDrmManagerClient->initializeDecryptUnit(
mDecryptHandle, trackId, ipmpBox);
const char *mime;
bool success = getFormat()->findCString(kKeyMIMEType, &mime);
CHECK(success);
if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
uint32_t type;
const void *data;
size_t size;
CHECK(getFormat()->findData(kKeyAVCC, &type, &data, &size));
const uint8_t *ptr = (const uint8_t *)data;
CHECK(size >= 7);
CHECK_EQ(ptr[0], 1); // configurationVersion == 1
// The number of bytes used to encode the length of a NAL unit.
mNALLengthSize = 1 + (ptr[4] & 3);
}
}
DRMSource::~DRMSource() {
Mutex::Autolock autoLock(mDRMLock);
mDrmManagerClient->finalizeDecryptUnit(mDecryptHandle, mTrackId);
}
status_t DRMSource::start(MetaData *params) {
int32_t val;
if (params && params->findInt32(kKeyWantsNALFragments, &val)
&& val != 0) {
mWantsNALFragments = true;
} else {
mWantsNALFragments = false;
}
return mOriginalMediaSource->start(params);
}
status_t DRMSource::stop() {
return mOriginalMediaSource->stop();
}
sp<MetaData> DRMSource::getFormat() {
return mOriginalMediaSource->getFormat();
}
status_t DRMSource::read(MediaBuffer **buffer, const ReadOptions *options) {
Mutex::Autolock autoLock(mDRMLock);
status_t err;
if ((err = mOriginalMediaSource->read(buffer, options)) != OK) {
return err;
}
size_t len = (*buffer)->range_length();
char *src = (char *)(*buffer)->data() + (*buffer)->range_offset();
DrmBuffer encryptedDrmBuffer(src, len);
DrmBuffer decryptedDrmBuffer;
decryptedDrmBuffer.length = len;
decryptedDrmBuffer.data = new char[len];
DrmBuffer *pDecryptedDrmBuffer = &decryptedDrmBuffer;
if ((err = mDrmManagerClient->decrypt(mDecryptHandle, mTrackId,
&encryptedDrmBuffer, &pDecryptedDrmBuffer)) != NO_ERROR) {
if (decryptedDrmBuffer.data) {
delete [] decryptedDrmBuffer.data;
decryptedDrmBuffer.data = NULL;
}
return err;
}
CHECK(pDecryptedDrmBuffer == &decryptedDrmBuffer);
const char *mime;
CHECK(getFormat()->findCString(kKeyMIMEType, &mime));
if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) && !mWantsNALFragments) {
uint8_t *dstData = (uint8_t*)src;
size_t srcOffset = 0;
size_t dstOffset = 0;
len = decryptedDrmBuffer.length;
while (srcOffset < len) {
CHECK(srcOffset + mNALLengthSize <= len);
size_t nalLength = 0;
const uint8_t* data = (const uint8_t*)(&decryptedDrmBuffer.data[srcOffset]);
switch (mNALLengthSize) {
case 1:
nalLength = *data;
break;
case 2:
nalLength = U16_AT(data);
break;
case 3:
nalLength = ((size_t)data[0] << 16) | U16_AT(&data[1]);
break;
case 4:
nalLength = U32_AT(data);
break;
default:
CHECK(!"Should not be here.");
break;
}
srcOffset += mNALLengthSize;
size_t end = srcOffset + nalLength;
if (end > len || end < srcOffset) {
if (decryptedDrmBuffer.data) {
delete [] decryptedDrmBuffer.data;
decryptedDrmBuffer.data = NULL;
}
return ERROR_MALFORMED;
}
if (nalLength == 0) {
continue;
}
if (dstOffset > SIZE_MAX - 4 ||
dstOffset + 4 > SIZE_MAX - nalLength ||
dstOffset + 4 + nalLength > (*buffer)->size()) {
(*buffer)->release();
(*buffer) = NULL;
if (decryptedDrmBuffer.data) {
delete [] decryptedDrmBuffer.data;
decryptedDrmBuffer.data = NULL;
}
return ERROR_MALFORMED;
}
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 1;
memcpy(&dstData[dstOffset], &decryptedDrmBuffer.data[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
}
CHECK_EQ(srcOffset, len);
(*buffer)->set_range((*buffer)->range_offset(), dstOffset);
} else {
memcpy(src, decryptedDrmBuffer.data, decryptedDrmBuffer.length);
(*buffer)->set_range((*buffer)->range_offset(), decryptedDrmBuffer.length);
}
if (decryptedDrmBuffer.data) {
delete [] decryptedDrmBuffer.data;
decryptedDrmBuffer.data = NULL;
}
return OK;
}
////////////////////////////////////////////////////////////////////////////////
DRMExtractor::DRMExtractor(const sp<DataSource> &source, const char* mime)
: mDataSource(source),
mDecryptHandle(NULL),
mDrmManagerClient(NULL) {
mOriginalExtractor = MediaExtractor::Create(source, mime);
mOriginalExtractor->setDrmFlag(true);
mOriginalExtractor->getMetaData()->setInt32(kKeyIsDRM, 1);
source->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
}
DRMExtractor::~DRMExtractor() {
}
size_t DRMExtractor::countTracks() {
return mOriginalExtractor->countTracks();
}
sp<MediaSource> DRMExtractor::getTrack(size_t index) {
sp<MediaSource> originalMediaSource = mOriginalExtractor->getTrack(index);
originalMediaSource->getFormat()->setInt32(kKeyIsDRM, 1);
int32_t trackID;
CHECK(getTrackMetaData(index, 0)->findInt32(kKeyTrackID, &trackID));
DrmBuffer ipmpBox;
ipmpBox.data = mOriginalExtractor->getDrmTrackInfo(trackID, &(ipmpBox.length));
CHECK(ipmpBox.length > 0);
return new DRMSource(originalMediaSource, mDecryptHandle, mDrmManagerClient,
trackID, &ipmpBox);
}
sp<MetaData> DRMExtractor::getTrackMetaData(size_t index, uint32_t flags) {
return mOriginalExtractor->getTrackMetaData(index, flags);
}
sp<MetaData> DRMExtractor::getMetaData() {
return mOriginalExtractor->getMetaData();
}
bool SniffDRM(
const sp<DataSource> &source, String8 *mimeType, float *confidence,
sp<AMessage> *) {
sp<DecryptHandle> decryptHandle = source->DrmInitialization();
if (decryptHandle != NULL) {
if (decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED) {
*mimeType = String8("drm+container_based+") + decryptHandle->mimeType;
*confidence = 10.0f;
} else if (decryptHandle->decryptApiType == DecryptApiType::ELEMENTARY_STREAM_BASED) {
*mimeType = String8("drm+es_based+") + decryptHandle->mimeType;
*confidence = 10.0f;
} else {
return false;
}
return true;
}
return false;
}
} //namespace android