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:
@@ -25,6 +25,7 @@
|
|||||||
#include <utils/List.h>
|
#include <utils/List.h>
|
||||||
#include <utils/RefBase.h>
|
#include <utils/RefBase.h>
|
||||||
#include <utils/threads.h>
|
#include <utils/threads.h>
|
||||||
|
#include <drm/DrmManagerClient.h>
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
@@ -67,6 +68,13 @@ public:
|
|||||||
static void RegisterSniffer(SnifferFunc func);
|
static void RegisterSniffer(SnifferFunc func);
|
||||||
static void RegisterDefaultSniffers();
|
static void RegisterDefaultSniffers();
|
||||||
|
|
||||||
|
// for DRM
|
||||||
|
virtual DecryptHandle* DrmInitialization(DrmManagerClient *client) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {};
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~DataSource() {}
|
virtual ~DataSource() {}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <media/stagefright/DataSource.h>
|
#include <media/stagefright/DataSource.h>
|
||||||
#include <media/stagefright/MediaErrors.h>
|
#include <media/stagefright/MediaErrors.h>
|
||||||
#include <utils/threads.h>
|
#include <utils/threads.h>
|
||||||
|
#include <drm/DrmManagerClient.h>
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
@@ -37,15 +38,29 @@ public:
|
|||||||
|
|
||||||
virtual status_t getSize(off_t *size);
|
virtual status_t getSize(off_t *size);
|
||||||
|
|
||||||
|
virtual DecryptHandle* DrmInitialization(DrmManagerClient *client);
|
||||||
|
|
||||||
|
virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~FileSource();
|
virtual ~FileSource();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FILE *mFile;
|
FILE *mFile;
|
||||||
|
int mFd;
|
||||||
int64_t mOffset;
|
int64_t mOffset;
|
||||||
int64_t mLength;
|
int64_t mLength;
|
||||||
Mutex mLock;
|
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(const FileSource &);
|
||||||
FileSource &operator=(const FileSource &);
|
FileSource &operator=(const FileSource &);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ enum {
|
|||||||
|
|
||||||
// Not technically an error.
|
// Not technically an error.
|
||||||
INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12,
|
INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12,
|
||||||
|
|
||||||
|
ERROR_NO_LICENSE = MEDIA_ERROR_BASE - 13,
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ public:
|
|||||||
// CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE
|
// CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE
|
||||||
virtual uint32_t flags() const;
|
virtual uint32_t flags() const;
|
||||||
|
|
||||||
|
// for DRM
|
||||||
|
virtual void setDrmFlag(bool flag) {};
|
||||||
|
virtual char* getDrmTrackInfo(size_t trackID, int *len) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MediaExtractor() {}
|
MediaExtractor() {}
|
||||||
virtual ~MediaExtractor() {}
|
virtual ~MediaExtractor() {}
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ enum {
|
|||||||
kKeyBufferID = 'bfID',
|
kKeyBufferID = 'bfID',
|
||||||
kKeyMaxInputSize = 'inpS',
|
kKeyMaxInputSize = 'inpS',
|
||||||
kKeyThumbnailTime = 'thbT', // int64_t (usecs)
|
kKeyThumbnailTime = 'thbT', // int64_t (usecs)
|
||||||
|
kKeyTrackID = 'trID',
|
||||||
|
kKeyIsDRM = 'idrm', // int32_t (bool)
|
||||||
|
|
||||||
kKeyAlbum = 'albu', // cstring
|
kKeyAlbum = 'albu', // cstring
|
||||||
kKeyArtist = 'arti', // cstring
|
kKeyArtist = 'arti', // cstring
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ LOCAL_SRC_FILES += \
|
|||||||
CachingDataSource.cpp \
|
CachingDataSource.cpp \
|
||||||
CameraSource.cpp \
|
CameraSource.cpp \
|
||||||
DataSource.cpp \
|
DataSource.cpp \
|
||||||
|
DRMExtractor.cpp \
|
||||||
FileSource.cpp \
|
FileSource.cpp \
|
||||||
HTTPDataSource.cpp \
|
HTTPDataSource.cpp \
|
||||||
HTTPStream.cpp \
|
HTTPStream.cpp \
|
||||||
@@ -61,7 +62,8 @@ LOCAL_SHARED_LIBRARIES := \
|
|||||||
libsonivox \
|
libsonivox \
|
||||||
libvorbisidec \
|
libvorbisidec \
|
||||||
libsurfaceflinger_client \
|
libsurfaceflinger_client \
|
||||||
libcamera_client
|
libcamera_client \
|
||||||
|
libdrmframework
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := \
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
libstagefright_aacdec \
|
libstagefright_aacdec \
|
||||||
|
|||||||
@@ -187,7 +187,8 @@ AwesomePlayer::AwesomePlayer()
|
|||||||
mExtractorFlags(0),
|
mExtractorFlags(0),
|
||||||
mLastVideoBuffer(NULL),
|
mLastVideoBuffer(NULL),
|
||||||
mVideoBuffer(NULL),
|
mVideoBuffer(NULL),
|
||||||
mSuspensionState(NULL) {
|
mSuspensionState(NULL),
|
||||||
|
mDecryptHandle(NULL) {
|
||||||
CHECK_EQ(mClient.connect(), OK);
|
CHECK_EQ(mClient.connect(), OK);
|
||||||
|
|
||||||
DataSource::RegisterDefaultSniffers();
|
DataSource::RegisterDefaultSniffers();
|
||||||
@@ -286,6 +287,17 @@ status_t AwesomePlayer::setDataSource_l(
|
|||||||
return UNKNOWN_ERROR;
|
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);
|
return setDataSource_l(extractor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,6 +328,11 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mExtractorFlags = extractor->flags();
|
mExtractorFlags = extractor->flags();
|
||||||
|
if (mDecryptHandle != NULL) {
|
||||||
|
if (DecryptApiType::ELEMENTARY_STREAM_BASED == mDecryptHandle->decryptApiType) {
|
||||||
|
mDrmManagerClient->consumeRights(mDecryptHandle, Action::PLAY, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -326,6 +343,15 @@ void AwesomePlayer::reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AwesomePlayer::reset_l() {
|
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) {
|
if (mFlags & PREPARING) {
|
||||||
mFlags |= PREPARE_CANCELLED;
|
mFlags |= PREPARE_CANCELLED;
|
||||||
if (mConnectingDataSource != NULL) {
|
if (mConnectingDataSource != NULL) {
|
||||||
@@ -568,6 +594,13 @@ status_t AwesomePlayer::play_l() {
|
|||||||
seekTo_l(0);
|
seekTo_l(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mDecryptHandle != NULL) {
|
||||||
|
int64_t position;
|
||||||
|
getPosition(&position);
|
||||||
|
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
|
||||||
|
Playback::START, position / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,6 +664,11 @@ status_t AwesomePlayer::pause_l() {
|
|||||||
|
|
||||||
mFlags &= ~PLAYING;
|
mFlags &= ~PLAYING;
|
||||||
|
|
||||||
|
if (mDecryptHandle != NULL) {
|
||||||
|
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
|
||||||
|
Playback::PAUSE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -727,6 +765,13 @@ void AwesomePlayer::seekAudioIfNecessary_l() {
|
|||||||
mWatchForAudioSeekComplete = true;
|
mWatchForAudioSeekComplete = true;
|
||||||
mWatchForAudioEOS = true;
|
mWatchForAudioEOS = true;
|
||||||
mSeekNotificationSent = false;
|
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;
|
mFlags |= FIRST_FRAME;
|
||||||
mSeeking = false;
|
mSeeking = false;
|
||||||
mSeekNotificationSent = false;
|
mSeekNotificationSent = false;
|
||||||
|
|
||||||
|
if (mDecryptHandle != NULL) {
|
||||||
|
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
|
||||||
|
Playback::PAUSE, 0);
|
||||||
|
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
|
||||||
|
Playback::START, timeUs / 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mFlags & FIRST_FRAME) {
|
if (mFlags & FIRST_FRAME) {
|
||||||
@@ -1137,6 +1189,17 @@ status_t AwesomePlayer::finishSetDataSource_l() {
|
|||||||
return UNKNOWN_ERROR;
|
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) {
|
if (dataSource->flags() & DataSource::kWantsPrefetching) {
|
||||||
mPrefetcher = new Prefetcher;
|
mPrefetcher = new Prefetcher;
|
||||||
}
|
}
|
||||||
|
|||||||
302
media/libstagefright/DRMExtractor.cpp
Normal file
302
media/libstagefright/DRMExtractor.cpp
Normal 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
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "include/MPEG4Extractor.h"
|
#include "include/MPEG4Extractor.h"
|
||||||
#include "include/WAVExtractor.h"
|
#include "include/WAVExtractor.h"
|
||||||
#include "include/OggExtractor.h"
|
#include "include/OggExtractor.h"
|
||||||
|
#include "include/DRMExtractor.h"
|
||||||
|
|
||||||
#include <media/stagefright/CachingDataSource.h>
|
#include <media/stagefright/CachingDataSource.h>
|
||||||
#include <media/stagefright/DataSource.h>
|
#include <media/stagefright/DataSource.h>
|
||||||
@@ -94,6 +95,7 @@ void DataSource::RegisterDefaultSniffers() {
|
|||||||
RegisterSniffer(SniffAMR);
|
RegisterSniffer(SniffAMR);
|
||||||
RegisterSniffer(SniffWAV);
|
RegisterSniffer(SniffWAV);
|
||||||
RegisterSniffer(SniffOgg);
|
RegisterSniffer(SniffOgg);
|
||||||
|
RegisterSniffer(SniffDRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|||||||
@@ -21,14 +21,26 @@ namespace android {
|
|||||||
|
|
||||||
FileSource::FileSource(const char *filename)
|
FileSource::FileSource(const char *filename)
|
||||||
: mFile(fopen(filename, "rb")),
|
: mFile(fopen(filename, "rb")),
|
||||||
|
mFd(fileno(mFile)),
|
||||||
mOffset(0),
|
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)
|
FileSource::FileSource(int fd, int64_t offset, int64_t length)
|
||||||
: mFile(fdopen(fd, "rb")),
|
: mFile(fdopen(fd, "rb")),
|
||||||
|
mFd(fd),
|
||||||
mOffset(offset),
|
mOffset(offset),
|
||||||
mLength(length) {
|
mLength(length),
|
||||||
|
mDecryptHandle(NULL),
|
||||||
|
mDrmManagerClient(NULL),
|
||||||
|
mDrmBufOffset(0),
|
||||||
|
mDrmBufSize(0),
|
||||||
|
mDrmBuf(NULL){
|
||||||
CHECK(offset >= 0);
|
CHECK(offset >= 0);
|
||||||
CHECK(length >= 0);
|
CHECK(length >= 0);
|
||||||
}
|
}
|
||||||
@@ -38,6 +50,14 @@ FileSource::~FileSource() {
|
|||||||
fclose(mFile);
|
fclose(mFile);
|
||||||
mFile = NULL;
|
mFile = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mDrmBuf != NULL) {
|
||||||
|
delete[] mDrmBuf;
|
||||||
|
mDrmBuf = NULL;
|
||||||
|
}
|
||||||
|
if (mDecryptHandle != NULL) {
|
||||||
|
mDrmManagerClient->closeDecryptSession(mDecryptHandle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t FileSource::initCheck() const {
|
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 (mDecryptHandle != NULL && DecryptApiType::CONTAINER_BASED
|
||||||
if (err < 0) {
|
== mDecryptHandle->decryptApiType) {
|
||||||
LOGE("seek to %lld failed", offset + mOffset);
|
return readAtDRM(offset, data, size);
|
||||||
return UNKNOWN_ERROR;
|
} 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) {
|
status_t FileSource::getSize(off_t *size) {
|
||||||
@@ -79,4 +104,53 @@ status_t FileSource::getSize(off_t *size) {
|
|||||||
return OK;
|
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
|
} // namespace android
|
||||||
|
|||||||
@@ -264,7 +264,9 @@ MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
|
|||||||
mHasVideo(false),
|
mHasVideo(false),
|
||||||
mFirstTrack(NULL),
|
mFirstTrack(NULL),
|
||||||
mLastTrack(NULL),
|
mLastTrack(NULL),
|
||||||
mFileMetaData(new MetaData) {
|
mFileMetaData(new MetaData),
|
||||||
|
mFirstSINF(NULL),
|
||||||
|
mIsDrm(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MPEG4Extractor::~MPEG4Extractor() {
|
MPEG4Extractor::~MPEG4Extractor() {
|
||||||
@@ -276,6 +278,15 @@ MPEG4Extractor::~MPEG4Extractor() {
|
|||||||
track = next;
|
track = next;
|
||||||
}
|
}
|
||||||
mFirstTrack = mLastTrack = NULL;
|
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() {
|
sp<MetaData> MPEG4Extractor::getMetaData() {
|
||||||
@@ -370,6 +381,178 @@ status_t MPEG4Extractor::readMetaData() {
|
|||||||
return err;
|
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) {
|
static void MakeFourCCString(uint32_t x, char *s) {
|
||||||
s[0] = x >> 24;
|
s[0] = x >> 24;
|
||||||
s[1] = (x >> 16) & 0xff;
|
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')) {
|
} else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
|
||||||
mHaveMetadata = true;
|
mHaveMetadata = true;
|
||||||
|
|
||||||
return UNKNOWN_ERROR; // Return a dummy error.
|
if (!mIsDrm) {
|
||||||
|
return UNKNOWN_ERROR; // Return a dummy error.
|
||||||
|
} else {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -618,6 +805,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
|
|||||||
height = U32_AT(&buffer[80]);
|
height = U32_AT(&buffer[80]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLastTrack->meta->setInt32(kKeyTrackID, id);
|
||||||
*offset += chunk_size;
|
*offset += chunk_size;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1050,6 +1238,20 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
|
|||||||
break;
|
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:
|
default:
|
||||||
{
|
{
|
||||||
*offset += chunk_size;
|
*offset += chunk_size;
|
||||||
@@ -1576,9 +1778,15 @@ status_t MPEG4Source::read(
|
|||||||
} else {
|
} else {
|
||||||
// Whole NAL units are returned but each fragment is prefixed by
|
// Whole NAL units are returned but each fragment is prefixed by
|
||||||
// the start code (0x00 00 00 01).
|
// the start code (0x00 00 00 01).
|
||||||
|
ssize_t num_bytes_read = 0;
|
||||||
ssize_t num_bytes_read =
|
int32_t drm = 0;
|
||||||
mDataSource->readAt(offset, mSrcBuffer, size);
|
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) {
|
if (num_bytes_read < (ssize_t)size) {
|
||||||
mBuffer->release();
|
mBuffer->release();
|
||||||
@@ -1587,40 +1795,46 @@ status_t MPEG4Source::read(
|
|||||||
return ERROR_IO;
|
return ERROR_IO;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *dstData = (uint8_t *)mBuffer->data();
|
if (usesDRM) {
|
||||||
size_t srcOffset = 0;
|
CHECK(mBuffer != NULL);
|
||||||
size_t dstOffset = 0;
|
mBuffer->set_range(0, size);
|
||||||
|
|
||||||
while (srcOffset < size) {
|
} else {
|
||||||
CHECK(srcOffset + mNALLengthSize <= size);
|
uint8_t *dstData = (uint8_t *)mBuffer->data();
|
||||||
size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
|
size_t srcOffset = 0;
|
||||||
srcOffset += mNALLengthSize;
|
size_t dstOffset = 0;
|
||||||
|
|
||||||
if (srcOffset + nalLength > size) {
|
while (srcOffset < size) {
|
||||||
mBuffer->release();
|
CHECK(srcOffset + mNALLengthSize <= size);
|
||||||
mBuffer = NULL;
|
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;
|
||||||
}
|
}
|
||||||
|
CHECK_EQ(srcOffset, size);
|
||||||
if (nalLength == 0) {
|
CHECK(mBuffer != NULL);
|
||||||
continue;
|
mBuffer->set_range(0, dstOffset);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
mBuffer->meta_data()->clear();
|
mBuffer->meta_data()->clear();
|
||||||
mBuffer->meta_data()->setInt64(
|
mBuffer->meta_data()->setInt64(
|
||||||
kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
|
kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "include/MPEG4Extractor.h"
|
#include "include/MPEG4Extractor.h"
|
||||||
#include "include/WAVExtractor.h"
|
#include "include/WAVExtractor.h"
|
||||||
#include "include/OggExtractor.h"
|
#include "include/OggExtractor.h"
|
||||||
|
#include "include/DRMExtractor.h"
|
||||||
|
|
||||||
#include <media/stagefright/DataSource.h>
|
#include <media/stagefright/DataSource.h>
|
||||||
#include <media/stagefright/MediaDefs.h>
|
#include <media/stagefright/MediaDefs.h>
|
||||||
@@ -57,6 +58,18 @@ sp<MediaExtractor> MediaExtractor::Create(
|
|||||||
mime, confidence);
|
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)
|
if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
|
||||||
|| !strcasecmp(mime, "audio/mp4")) {
|
|| !strcasecmp(mime, "audio/mp4")) {
|
||||||
return new MPEG4Extractor(source);
|
return new MPEG4Extractor(source);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <media/stagefright/HTTPDataSource.h>
|
#include <media/stagefright/HTTPDataSource.h>
|
||||||
#include <media/stagefright/OMXClient.h>
|
#include <media/stagefright/OMXClient.h>
|
||||||
#include <utils/threads.h>
|
#include <utils/threads.h>
|
||||||
|
#include <drm/DrmManagerClient.h>
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
@@ -35,6 +36,8 @@ struct MediaExtractor;
|
|||||||
struct MediaSource;
|
struct MediaSource;
|
||||||
struct Prefetcher;
|
struct Prefetcher;
|
||||||
struct TimeSource;
|
struct TimeSource;
|
||||||
|
class DrmManagerClinet;
|
||||||
|
class DecryptHandle;
|
||||||
|
|
||||||
struct AwesomeRenderer : public RefBase {
|
struct AwesomeRenderer : public RefBase {
|
||||||
AwesomeRenderer() {}
|
AwesomeRenderer() {}
|
||||||
@@ -195,6 +198,9 @@ private:
|
|||||||
}
|
}
|
||||||
} *mSuspensionState;
|
} *mSuspensionState;
|
||||||
|
|
||||||
|
DrmManagerClient *mDrmManagerClient;
|
||||||
|
DecryptHandle *mDecryptHandle;
|
||||||
|
|
||||||
status_t setDataSource_l(
|
status_t setDataSource_l(
|
||||||
const char *uri,
|
const char *uri,
|
||||||
const KeyedVector<String8, String8> *headers = NULL);
|
const KeyedVector<String8, String8> *headers = NULL);
|
||||||
|
|||||||
59
media/libstagefright/include/DRMExtractor.h
Normal file
59
media/libstagefright/include/DRMExtractor.h
Normal 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_
|
||||||
|
|
||||||
@@ -38,6 +38,10 @@ public:
|
|||||||
|
|
||||||
virtual sp<MetaData> getMetaData();
|
virtual sp<MetaData> getMetaData();
|
||||||
|
|
||||||
|
// for DRM
|
||||||
|
virtual void setDrmFlag(bool flag);
|
||||||
|
virtual char* getDrmTrackInfo(size_t trackID, int *len);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~MPEG4Extractor();
|
virtual ~MPEG4Extractor();
|
||||||
|
|
||||||
@@ -70,6 +74,19 @@ private:
|
|||||||
|
|
||||||
static status_t verifyTrack(Track *track);
|
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(const MPEG4Extractor &);
|
||||||
MPEG4Extractor &operator=(const MPEG4Extractor &);
|
MPEG4Extractor &operator=(const MPEG4Extractor &);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user