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/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() {}
|
||||
|
||||
|
||||
@@ -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 &);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
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/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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
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();
|
||||
|
||||
// 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 &);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user