Merge "DRM framework support: - add a sniffer for DRM files - add DRMSource and DRMExtractor for es_based DRM - add pread in FileSource.cpp for container_based DRM - add native DRM framework API calls in the player for DRM audio/video playback"

This commit is contained in:
Gloria Wang
2010-10-25 12:23:35 -07:00
committed by Android Code Review
15 changed files with 828 additions and 43 deletions

View File

@@ -25,6 +25,7 @@
#include <utils/List.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
#include <drm/DrmManagerClient.h>
namespace android {
@@ -67,6 +68,13 @@ public:
static void RegisterSniffer(SnifferFunc func);
static void RegisterDefaultSniffers();
// for DRM
virtual DecryptHandle* DrmInitialization(DrmManagerClient *client) {
return NULL;
}
virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {};
protected:
virtual ~DataSource() {}

View File

@@ -23,6 +23,7 @@
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/threads.h>
#include <drm/DrmManagerClient.h>
namespace android {
@@ -37,15 +38,29 @@ public:
virtual status_t getSize(off_t *size);
virtual DecryptHandle* DrmInitialization(DrmManagerClient *client);
virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client);
protected:
virtual ~FileSource();
private:
FILE *mFile;
int mFd;
int64_t mOffset;
int64_t mLength;
Mutex mLock;
/*for DRM*/
DecryptHandle *mDecryptHandle;
DrmManagerClient *mDrmManagerClient;
int64_t mDrmBufOffset;
int64_t mDrmBufSize;
unsigned char *mDrmBuf;
ssize_t readAtDRM(off_t offset, void *data, size_t size);
FileSource(const FileSource &);
FileSource &operator=(const FileSource &);
};

View File

@@ -39,6 +39,8 @@ enum {
// Not technically an error.
INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12,
ERROR_NO_LICENSE = MEDIA_ERROR_BASE - 13,
};
} // namespace android

View File

@@ -54,6 +54,12 @@ public:
// CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE
virtual uint32_t flags() const;
// for DRM
virtual void setDrmFlag(bool flag) {};
virtual char* getDrmTrackInfo(size_t trackID, int *len) {
return NULL;
}
protected:
MediaExtractor() {}
virtual ~MediaExtractor() {}

View File

@@ -50,6 +50,8 @@ enum {
kKeyBufferID = 'bfID',
kKeyMaxInputSize = 'inpS',
kKeyThumbnailTime = 'thbT', // int64_t (usecs)
kKeyTrackID = 'trID',
kKeyIsDRM = 'idrm', // int32_t (bool)
kKeyAlbum = 'albu', // cstring
kKeyArtist = 'arti', // cstring

View File

@@ -23,6 +23,7 @@ LOCAL_SRC_FILES += \
CachingDataSource.cpp \
CameraSource.cpp \
DataSource.cpp \
DRMExtractor.cpp \
FileSource.cpp \
HTTPDataSource.cpp \
HTTPStream.cpp \
@@ -61,7 +62,8 @@ LOCAL_SHARED_LIBRARIES := \
libsonivox \
libvorbisidec \
libsurfaceflinger_client \
libcamera_client
libcamera_client \
libdrmframework
LOCAL_STATIC_LIBRARIES := \
libstagefright_aacdec \

View File

@@ -187,7 +187,8 @@ AwesomePlayer::AwesomePlayer()
mExtractorFlags(0),
mLastVideoBuffer(NULL),
mVideoBuffer(NULL),
mSuspensionState(NULL) {
mSuspensionState(NULL),
mDecryptHandle(NULL) {
CHECK_EQ(mClient.connect(), OK);
DataSource::RegisterDefaultSniffers();
@@ -286,6 +287,17 @@ status_t AwesomePlayer::setDataSource_l(
return UNKNOWN_ERROR;
}
dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
if (mDecryptHandle != NULL) {
if (RightsStatus::RIGHTS_VALID == mDecryptHandle->status) {
if (DecryptApiType::CONTAINER_BASED == mDecryptHandle->decryptApiType) {
mDrmManagerClient->consumeRights(mDecryptHandle, Action::PLAY, true);
}
} else {
notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
}
}
return setDataSource_l(extractor);
}
@@ -316,6 +328,11 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
}
mExtractorFlags = extractor->flags();
if (mDecryptHandle != NULL) {
if (DecryptApiType::ELEMENTARY_STREAM_BASED == mDecryptHandle->decryptApiType) {
mDrmManagerClient->consumeRights(mDecryptHandle, Action::PLAY, true);
}
}
return OK;
}
@@ -326,6 +343,15 @@ void AwesomePlayer::reset() {
}
void AwesomePlayer::reset_l() {
if (mDecryptHandle != NULL) {
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
Playback::STOP, 0);
mDrmManagerClient->consumeRights(mDecryptHandle,
Action::PLAY, false);
mDecryptHandle = NULL;
mDrmManagerClient = NULL;
}
if (mFlags & PREPARING) {
mFlags |= PREPARE_CANCELLED;
if (mConnectingDataSource != NULL) {
@@ -568,6 +594,13 @@ status_t AwesomePlayer::play_l() {
seekTo_l(0);
}
if (mDecryptHandle != NULL) {
int64_t position;
getPosition(&position);
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
Playback::START, position / 1000);
}
return OK;
}
@@ -631,6 +664,11 @@ status_t AwesomePlayer::pause_l() {
mFlags &= ~PLAYING;
if (mDecryptHandle != NULL) {
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
Playback::PAUSE, 0);
}
return OK;
}
@@ -727,6 +765,13 @@ void AwesomePlayer::seekAudioIfNecessary_l() {
mWatchForAudioSeekComplete = true;
mWatchForAudioEOS = true;
mSeekNotificationSent = false;
if (mDecryptHandle != NULL) {
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
Playback::PAUSE, 0);
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
Playback::START, mSeekTimeUs / 1000);
}
}
}
@@ -919,6 +964,13 @@ void AwesomePlayer::onVideoEvent() {
mFlags |= FIRST_FRAME;
mSeeking = false;
mSeekNotificationSent = false;
if (mDecryptHandle != NULL) {
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
Playback::PAUSE, 0);
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
Playback::START, timeUs / 1000);
}
}
if (mFlags & FIRST_FRAME) {
@@ -1137,6 +1189,17 @@ status_t AwesomePlayer::finishSetDataSource_l() {
return UNKNOWN_ERROR;
}
dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
if (mDecryptHandle != NULL) {
if (RightsStatus::RIGHTS_VALID == mDecryptHandle->status) {
if (DecryptApiType::CONTAINER_BASED == mDecryptHandle->decryptApiType) {
mDrmManagerClient->consumeRights(mDecryptHandle, Action::PLAY, true);
}
} else {
notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
}
}
if (dataSource->flags() & DataSource::kWantsPrefetching) {
mPrefetcher = new Prefetcher;
}

View File

@@ -0,0 +1,302 @@
/*
* 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 "include/AMRExtractor.h"
#include "include/MP3Extractor.h"
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
#include <arpa/inet.h>
#include <utils/String8.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 <media/stagefright/MediaDebug.h>
#include <drm/drm_framework_common.h>
#include <utils/Errors.h>
namespace android {
DrmManagerClient* gDrmManagerClient = NULL;
class DRMSource : public MediaSource {
public:
DRMSource(const sp<MediaSource> &mediaSource,
DecryptHandle* decryptHandle, 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;
DecryptHandle* mDecryptHandle;
size_t mTrackId;
mutable Mutex mDRMLock;
size_t mNALLengthSize;
bool mWantsNALFragments;
DRMSource(const DRMSource &);
DRMSource &operator=(const DRMSource &);
};
////////////////////////////////////////////////////////////////////////////////
DRMSource::DRMSource(const sp<MediaSource> &mediaSource,
DecryptHandle* decryptHandle, int32_t trackId, DrmBuffer* ipmpBox)
: mOriginalMediaSource(mediaSource),
mDecryptHandle(decryptHandle),
mTrackId(trackId),
mNALLengthSize(0),
mWantsNALFragments(false) {
gDrmManagerClient->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);
gDrmManagerClient->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 = gDrmManagerClient->decrypt(mDecryptHandle, mTrackId,
&encryptedDrmBuffer, &pDecryptedDrmBuffer)) != DRM_NO_ERROR) {
if (decryptedDrmBuffer.data) {
delete [] decryptedDrmBuffer.data;
decryptedDrmBuffer.data = NULL;
}
if (err == DRM_ERROR_LICENSE_EXPIRED) {
return ERROR_NO_LICENSE;
} else {
return ERROR_IO;
}
}
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;
if (srcOffset + nalLength > len) {
if (decryptedDrmBuffer.data) {
delete [] decryptedDrmBuffer.data;
decryptedDrmBuffer.data = NULL;
}
return ERROR_MALFORMED;
}
if (nalLength == 0) {
continue;
}
CHECK(dstOffset + 4 <= (*buffer)->size());
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) {
mOriginalExtractor = MediaExtractor::Create(source, mime);
mOriginalExtractor->setDrmFlag(true);
DrmManagerClient *client;
source->getDrmInfo(&mDecryptHandle, &client);
}
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, trackID, &ipmpBox);
}
sp<MetaData> DRMExtractor::getTrackMetaData(size_t index, uint32_t flags) {
return mOriginalExtractor->getTrackMetaData(index, flags);
}
sp<MetaData> DRMExtractor::getMetaData() {
return mOriginalExtractor->getMetaData();
}
static Mutex gDRMSnifferMutex;
bool SniffDRM(
const sp<DataSource> &source, String8 *mimeType, float *confidence) {
{
Mutex::Autolock autoLock(gDRMSnifferMutex);
if (gDrmManagerClient == NULL) {
gDrmManagerClient = new DrmManagerClient();
}
}
DecryptHandle *decryptHandle = source->DrmInitialization(gDrmManagerClient);
if (decryptHandle != NULL) {
if (decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED) {
*mimeType = String8("drm+container_based+");
} else if (decryptHandle->decryptApiType == DecryptApiType::ELEMENTARY_STREAM_BASED) {
*mimeType = String8("drm+es_based+");
}
*mimeType += decryptHandle->mimeType;
*confidence = 10.0f;
return true;
}
return false;
}
} //namespace android

View File

@@ -19,6 +19,7 @@
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
#include "include/DRMExtractor.h"
#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/DataSource.h>
@@ -94,6 +95,7 @@ void DataSource::RegisterDefaultSniffers() {
RegisterSniffer(SniffAMR);
RegisterSniffer(SniffWAV);
RegisterSniffer(SniffOgg);
RegisterSniffer(SniffDRM);
}
// static

View File

@@ -21,14 +21,26 @@ namespace android {
FileSource::FileSource(const char *filename)
: mFile(fopen(filename, "rb")),
mFd(fileno(mFile)),
mOffset(0),
mLength(-1) {
mLength(-1),
mDecryptHandle(NULL),
mDrmManagerClient(NULL),
mDrmBufOffset(0),
mDrmBufSize(0),
mDrmBuf(NULL){
}
FileSource::FileSource(int fd, int64_t offset, int64_t length)
: mFile(fdopen(fd, "rb")),
mFd(fd),
mOffset(offset),
mLength(length) {
mLength(length),
mDecryptHandle(NULL),
mDrmManagerClient(NULL),
mDrmBufOffset(0),
mDrmBufSize(0),
mDrmBuf(NULL){
CHECK(offset >= 0);
CHECK(length >= 0);
}
@@ -38,6 +50,14 @@ FileSource::~FileSource() {
fclose(mFile);
mFile = NULL;
}
if (mDrmBuf != NULL) {
delete[] mDrmBuf;
mDrmBuf = NULL;
}
if (mDecryptHandle != NULL) {
mDrmManagerClient->closeDecryptSession(mDecryptHandle);
}
}
status_t FileSource::initCheck() const {
@@ -57,13 +77,18 @@ ssize_t FileSource::readAt(off_t offset, void *data, size_t size) {
}
}
int err = fseeko(mFile, offset + mOffset, SEEK_SET);
if (err < 0) {
LOGE("seek to %lld failed", offset + mOffset);
return UNKNOWN_ERROR;
}
if (mDecryptHandle != NULL && DecryptApiType::CONTAINER_BASED
== mDecryptHandle->decryptApiType) {
return readAtDRM(offset, data, size);
} else {
int err = fseeko(mFile, offset + mOffset, SEEK_SET);
if (err < 0) {
LOGE("seek to %lld failed", offset + mOffset);
return UNKNOWN_ERROR;
}
return fread(data, 1, size, mFile);
return fread(data, 1, size, mFile);
}
}
status_t FileSource::getSize(off_t *size) {
@@ -79,4 +104,53 @@ status_t FileSource::getSize(off_t *size) {
return OK;
}
DecryptHandle* FileSource::DrmInitialization(DrmManagerClient* client) {
mDrmManagerClient = client;
if (mDecryptHandle == NULL) {
mDecryptHandle = mDrmManagerClient->openDecryptSession(
mFd, mOffset, mLength);
}
if (mDecryptHandle == NULL) {
mDrmManagerClient = NULL;
}
return mDecryptHandle;
}
void FileSource::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {
*handle = mDecryptHandle;
*client = mDrmManagerClient;
}
ssize_t FileSource::readAtDRM(off_t offset, void *data, size_t size) {
size_t DRM_CACHE_SIZE = 1024;
if (mDrmBuf == NULL) {
mDrmBuf = new unsigned char[DRM_CACHE_SIZE];
}
if (mDrmBuf != NULL && mDrmBufSize > 0 && (offset + mOffset) >= mDrmBufOffset
&& (offset + mOffset + size) <= (mDrmBufOffset + mDrmBufSize)) {
/* Use buffered data */
memcpy(data, (void*)(mDrmBuf+(offset+mOffset-mDrmBufOffset)), size);
return size;
} else if (size <= DRM_CACHE_SIZE) {
/* Buffer new data */
mDrmBufOffset = offset + mOffset;
mDrmBufSize = mDrmManagerClient->pread(mDecryptHandle, mDrmBuf,
DRM_CACHE_SIZE, offset + mOffset);
if (mDrmBufSize > 0) {
int64_t dataRead = 0;
dataRead = size > mDrmBufSize ? mDrmBufSize : size;
memcpy(data, (void*)mDrmBuf, dataRead);
return dataRead;
} else {
return mDrmBufSize;
}
} else {
/* Too big chunk to cache. Call DRM directly */
return mDrmManagerClient->pread(mDecryptHandle, data, size, offset + mOffset);
}
}
} // namespace android

View File

@@ -264,7 +264,9 @@ MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
mHasVideo(false),
mFirstTrack(NULL),
mLastTrack(NULL),
mFileMetaData(new MetaData) {
mFileMetaData(new MetaData),
mFirstSINF(NULL),
mIsDrm(false) {
}
MPEG4Extractor::~MPEG4Extractor() {
@@ -276,6 +278,15 @@ MPEG4Extractor::~MPEG4Extractor() {
track = next;
}
mFirstTrack = mLastTrack = NULL;
SINF *sinf = mFirstSINF;
while (sinf) {
SINF *next = sinf->next;
delete sinf->IPMPData;
delete sinf;
sinf = next;
}
mFirstSINF = NULL;
}
sp<MetaData> MPEG4Extractor::getMetaData() {
@@ -370,6 +381,178 @@ status_t MPEG4Extractor::readMetaData() {
return err;
}
void MPEG4Extractor::setDrmFlag(bool flag) {
mIsDrm = flag;
}
char* MPEG4Extractor::getDrmTrackInfo(size_t trackID, int *len) {
if (mFirstSINF == NULL) {
return NULL;
}
SINF *sinf = mFirstSINF;
while (sinf && (trackID != sinf->trackID)) {
sinf = sinf->next;
}
if (sinf == NULL) {
return NULL;
}
*len = sinf->len;
return sinf->IPMPData;
}
// Reads an encoded integer 7 bits at a time until it encounters the high bit clear.
int32_t readSize(off_t offset,
const sp<DataSource> DataSource, uint8_t *numOfBytes) {
uint32_t size = 0;
uint8_t data;
bool moreData = true;
*numOfBytes = 0;
while (moreData) {
if (DataSource->readAt(offset, &data, 1) < 1) {
return -1;
}
offset ++;
moreData = (data >= 128) ? true : false;
size = (size << 7) | (data & 0x7f); // Take last 7 bits
(*numOfBytes) ++;
}
return size;
}
status_t MPEG4Extractor::parseDrmSINF(off_t *offset, off_t data_offset) {
uint8_t updateIdTag;
if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
return ERROR_IO;
}
data_offset ++;
if (0x01/*OBJECT_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
return ERROR_MALFORMED;
}
uint8_t numOfBytes;
int32_t size = readSize(data_offset, mDataSource, &numOfBytes);
if (size < 0) {
return ERROR_IO;
}
int32_t classSize = size;
data_offset += numOfBytes;
while(size >= 11 ) {
uint8_t descriptorTag;
if (mDataSource->readAt(data_offset, &descriptorTag, 1) < 1) {
return ERROR_IO;
}
data_offset ++;
if (0x11/*OBJECT_DESCRIPTOR_ID_TAG*/ != descriptorTag) {
return ERROR_MALFORMED;
}
uint8_t buffer[8];
//ObjectDescriptorID and ObjectDescriptor url flag
if (mDataSource->readAt(data_offset, buffer, 2) < 2) {
return ERROR_IO;
}
data_offset += 2;
if ((buffer[1] >> 5) & 0x0001) { //url flag is set
return ERROR_MALFORMED;
}
if (mDataSource->readAt(data_offset, buffer, 8) < 8) {
return ERROR_IO;
}
data_offset += 8;
if ((0x0F/*ES_ID_REF_TAG*/ != buffer[1])
|| ( 0x0A/*IPMP_DESCRIPTOR_POINTER_ID_TAG*/ != buffer[5])) {
return ERROR_MALFORMED;
}
SINF *sinf = new SINF;
sinf->trackID = U16_AT(&buffer[3]);
sinf->IPMPDescriptorID = buffer[7];
sinf->next = mFirstSINF;
mFirstSINF = sinf;
size -= (8 + 2 + 1);
}
if (size != 0) {
return ERROR_MALFORMED;
}
if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
return ERROR_IO;
}
data_offset ++;
if(0x05/*IPMP_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
return ERROR_MALFORMED;
}
size = readSize(data_offset, mDataSource, &numOfBytes);
if (size < 0) {
return ERROR_IO;
}
classSize = size;
data_offset += numOfBytes;
while (size > 0) {
uint8_t tag;
int32_t dataLen;
if (mDataSource->readAt(data_offset, &tag, 1) < 1) {
return ERROR_IO;
}
data_offset ++;
if (0x0B/*IPMP_DESCRIPTOR_ID_TAG*/ == tag) {
uint8_t id;
dataLen = readSize(data_offset, mDataSource, &numOfBytes);
if (dataLen < 0) {
return ERROR_IO;
} else if (dataLen < 4) {
return ERROR_MALFORMED;
}
data_offset += numOfBytes;
if (mDataSource->readAt(data_offset, &id, 1) < 1) {
return ERROR_IO;
}
data_offset ++;
SINF *sinf = mFirstSINF;
while (sinf && (sinf->IPMPDescriptorID != id)) {
sinf = sinf->next;
}
if (sinf == NULL) {
return ERROR_MALFORMED;
}
sinf->len = dataLen - 3;
sinf->IPMPData = new char[sinf->len];
if (mDataSource->readAt(data_offset + 2, sinf->IPMPData, sinf->len) < sinf->len) {
return ERROR_IO;
}
data_offset += sinf->len;
size -= (dataLen + numOfBytes + 1);
}
}
if (size != 0) {
return ERROR_MALFORMED;
}
return UNKNOWN_ERROR; // Return a dummy error.
}
static void MakeFourCCString(uint32_t x, char *s) {
s[0] = x >> 24;
s[1] = (x >> 16) & 0xff;
@@ -563,7 +746,11 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
} else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
mHaveMetadata = true;
return UNKNOWN_ERROR; // Return a dummy error.
if (!mIsDrm) {
return UNKNOWN_ERROR; // Return a dummy error.
} else {
return OK;
}
}
break;
}
@@ -618,6 +805,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
height = U32_AT(&buffer[80]);
}
mLastTrack->meta->setInt32(kKeyTrackID, id);
*offset += chunk_size;
break;
}
@@ -1050,6 +1238,20 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
break;
}
case FOURCC('m', 'd', 'a', 't'):
{
if (!mIsDrm) {
*offset += chunk_size;
break;
}
if (chunk_size < 8) {
return ERROR_MALFORMED;
}
return parseDrmSINF(offset, data_offset);
}
default:
{
*offset += chunk_size;
@@ -1576,9 +1778,15 @@ status_t MPEG4Source::read(
} else {
// Whole NAL units are returned but each fragment is prefixed by
// the start code (0x00 00 00 01).
ssize_t num_bytes_read =
mDataSource->readAt(offset, mSrcBuffer, size);
ssize_t num_bytes_read = 0;
int32_t drm = 0;
bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
if (usesDRM) {
num_bytes_read =
mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
} else {
num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
}
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
@@ -1587,40 +1795,46 @@ status_t MPEG4Source::read(
return ERROR_IO;
}
uint8_t *dstData = (uint8_t *)mBuffer->data();
size_t srcOffset = 0;
size_t dstOffset = 0;
if (usesDRM) {
CHECK(mBuffer != NULL);
mBuffer->set_range(0, size);
while (srcOffset < size) {
CHECK(srcOffset + mNALLengthSize <= size);
size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
srcOffset += mNALLengthSize;
} else {
uint8_t *dstData = (uint8_t *)mBuffer->data();
size_t srcOffset = 0;
size_t dstOffset = 0;
if (srcOffset + nalLength > size) {
mBuffer->release();
mBuffer = NULL;
while (srcOffset < size) {
CHECK(srcOffset + mNALLengthSize <= size);
size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
srcOffset += mNALLengthSize;
return ERROR_MALFORMED;
if (srcOffset + nalLength > size) {
mBuffer->release();
mBuffer = NULL;
return ERROR_MALFORMED;
}
if (nalLength == 0) {
continue;
}
CHECK(dstOffset + 4 <= mBuffer->size());
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 1;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
}
if (nalLength == 0) {
continue;
}
CHECK(dstOffset + 4 <= mBuffer->size());
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 0;
dstData[dstOffset++] = 1;
memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
srcOffset += nalLength;
dstOffset += nalLength;
CHECK_EQ(srcOffset, size);
CHECK(mBuffer != NULL);
mBuffer->set_range(0, dstOffset);
}
CHECK_EQ(srcOffset, size);
CHECK(mBuffer != NULL);
mBuffer->set_range(0, dstOffset);
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)dts * 1000000) / mTimescale);

View File

@@ -23,6 +23,7 @@
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
#include "include/DRMExtractor.h"
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDefs.h>
@@ -57,6 +58,18 @@ sp<MediaExtractor> MediaExtractor::Create(
mime, confidence);
}
if (!strncmp(mime, "drm", 3)) {
char *originalMime = strrchr(mime, '+') + 1;
if (!strncmp(mime, "drm+es_based", 12)) {
return new DRMExtractor(source, originalMime);
} else if (!strncmp(mime, "drm+container_based", 19)) {
mime = originalMime;
} else {
return NULL;
}
}
if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
|| !strcasecmp(mime, "audio/mp4")) {
return new MPEG4Extractor(source);

View File

@@ -25,6 +25,7 @@
#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/OMXClient.h>
#include <utils/threads.h>
#include <drm/DrmManagerClient.h>
namespace android {
@@ -35,6 +36,8 @@ struct MediaExtractor;
struct MediaSource;
struct Prefetcher;
struct TimeSource;
class DrmManagerClinet;
class DecryptHandle;
struct AwesomeRenderer : public RefBase {
AwesomeRenderer() {}
@@ -195,6 +198,9 @@ private:
}
} *mSuspensionState;
DrmManagerClient *mDrmManagerClient;
DecryptHandle *mDecryptHandle;
status_t setDataSource_l(
const char *uri,
const KeyedVector<String8, String8> *headers = NULL);

View File

@@ -0,0 +1,59 @@
/*
* 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.
*/
#ifndef DRM_EXTRACTOR_H_
#define DRM_EXTRACTOR_H_
#include <media/stagefright/MediaExtractor.h>
#include <drm/DrmManagerClient.h>
namespace android {
class DataSource;
class SampleTable;
class String8;
class DecryptHandle;
class DRMExtractor : public MediaExtractor {
public:
DRMExtractor(const sp<DataSource> &source, const char *mime);
virtual size_t countTracks();
virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
protected:
virtual ~DRMExtractor();
private:
sp<DataSource> mDataSource;
sp<MediaExtractor> mOriginalExtractor;
DecryptHandle* mDecryptHandle;
DRMExtractor(const DRMExtractor &);
DRMExtractor &operator=(const DRMExtractor &);
};
bool SniffDRM(
const sp<DataSource> &source, String8 *mimeType, float *confidence);
} // namespace android
#endif // DRM_EXTRACTOR_H_

View File

@@ -38,6 +38,10 @@ public:
virtual sp<MetaData> getMetaData();
// for DRM
virtual void setDrmFlag(bool flag);
virtual char* getDrmTrackInfo(size_t trackID, int *len);
protected:
virtual ~MPEG4Extractor();
@@ -70,6 +74,19 @@ private:
static status_t verifyTrack(Track *track);
struct SINF {
SINF *next;
uint16_t trackID;
uint8_t IPMPDescriptorID;
ssize_t len;
char *IPMPData;
};
SINF *mFirstSINF;
bool mIsDrm;
status_t parseDrmSINF(off_t *offset, off_t data_offset);
MPEG4Extractor(const MPEG4Extractor &);
MPEG4Extractor &operator=(const MPEG4Extractor &);
};