From d5770917a50a828cb4337c2a392b3e4a375624b9 Mon Sep 17 00:00:00 2001 From: Gloria Wang Date: Tue, 22 Jun 2010 13:55:38 -0700 Subject: 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 Change-Id: I4b9ef19165c9b4f44ff40eeededb9a665e78a90f --- include/media/stagefright/DataSource.h | 8 + include/media/stagefright/FileSource.h | 15 ++ include/media/stagefright/MediaErrors.h | 2 + include/media/stagefright/MediaExtractor.h | 6 + include/media/stagefright/MetaData.h | 2 + media/libstagefright/Android.mk | 4 +- media/libstagefright/AwesomePlayer.cpp | 65 +++++- media/libstagefright/DRMExtractor.cpp | 302 ++++++++++++++++++++++++++ media/libstagefright/DataSource.cpp | 2 + media/libstagefright/FileSource.cpp | 90 +++++++- media/libstagefright/MPEG4Extractor.cpp | 276 ++++++++++++++++++++--- media/libstagefright/MediaExtractor.cpp | 13 ++ media/libstagefright/include/AwesomePlayer.h | 6 + media/libstagefright/include/DRMExtractor.h | 59 +++++ media/libstagefright/include/MPEG4Extractor.h | 17 ++ 15 files changed, 826 insertions(+), 41 deletions(-) create mode 100644 media/libstagefright/DRMExtractor.cpp create mode 100644 media/libstagefright/include/DRMExtractor.h diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h index 6f7dc38..cd19eae 100644 --- a/include/media/stagefright/DataSource.h +++ b/include/media/stagefright/DataSource.h @@ -25,6 +25,7 @@ #include #include #include +#include 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() {} diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h index 8a215ea..4307263 100644 --- a/include/media/stagefright/FileSource.h +++ b/include/media/stagefright/FileSource.h @@ -23,6 +23,7 @@ #include #include #include +#include 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 &); }; diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h index 73d0f77..102885f 100644 --- a/include/media/stagefright/MediaErrors.h +++ b/include/media/stagefright/MediaErrors.h @@ -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 diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h index 21338ca..0fbb422 100644 --- a/include/media/stagefright/MediaExtractor.h +++ b/include/media/stagefright/MediaExtractor.h @@ -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() {} diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index dc2bd50..d1b5f27 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -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 diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 81f995b..cf5c3ca 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -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 \ diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 274dad9..7bbc9eb 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -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 &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; } diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp new file mode 100644 index 0000000..8aa5bb4 --- /dev/null +++ b/media/libstagefright/DRMExtractor.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace android { + +DrmManagerClient* gDrmManagerClient = NULL; + +class DRMSource : public MediaSource { +public: + DRMSource(const sp &mediaSource, + DecryptHandle* decryptHandle, int32_t trackId, DrmBuffer* ipmpBox); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp getFormat(); + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + +protected: + virtual ~DRMSource(); + +private: + sp 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, + 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 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 &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 DRMExtractor::getTrack(size_t index) { + sp 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 DRMExtractor::getTrackMetaData(size_t index, uint32_t flags) { + return mOriginalExtractor->getTrackMetaData(index, flags); +} + +sp DRMExtractor::getMetaData() { + return mOriginalExtractor->getMetaData(); +} + +static Mutex gDRMSnifferMutex; +bool SniffDRM( + const sp &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 + diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index a66f86b..d7dd4e5 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -19,6 +19,7 @@ #include "include/MPEG4Extractor.h" #include "include/WAVExtractor.h" #include "include/OggExtractor.h" +#include "include/DRMExtractor.h" #include #include @@ -94,6 +95,7 @@ void DataSource::RegisterDefaultSniffers() { RegisterSniffer(SniffAMR); RegisterSniffer(SniffWAV); RegisterSniffer(SniffOgg); + RegisterSniffer(SniffDRM); } // static diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp index b6f1af2..6b85048 100644 --- a/media/libstagefright/FileSource.cpp +++ b/media/libstagefright/FileSource.cpp @@ -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 diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index a41b2f4..d49696a 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -264,7 +264,9 @@ MPEG4Extractor::MPEG4Extractor(const sp &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 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, 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; - if (nalLength == 0) { - continue; - } + return ERROR_MALFORMED; + } + + if (nalLength == 0) { + continue; + } - CHECK(dstOffset + 4 <= mBuffer->size()); + 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; + 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); diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index 513f49c..a8019ee 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -23,6 +23,7 @@ #include "include/MPEG4Extractor.h" #include "include/WAVExtractor.h" #include "include/OggExtractor.h" +#include "include/DRMExtractor.h" #include #include @@ -57,6 +58,18 @@ sp 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); diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 9455743..f0fd0f2 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -25,6 +25,7 @@ #include #include #include +#include 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 *headers = NULL); diff --git a/media/libstagefright/include/DRMExtractor.h b/media/libstagefright/include/DRMExtractor.h new file mode 100644 index 0000000..01e226e --- /dev/null +++ b/media/libstagefright/include/DRMExtractor.h @@ -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 +#include + +namespace android { + +class DataSource; +class SampleTable; +class String8; +class DecryptHandle; + +class DRMExtractor : public MediaExtractor { +public: + DRMExtractor(const sp &source, const char *mime); + + virtual size_t countTracks(); + virtual sp getTrack(size_t index); + virtual sp getTrackMetaData(size_t index, uint32_t flags); + virtual sp getMetaData(); + +protected: + virtual ~DRMExtractor(); + +private: + sp mDataSource; + + sp mOriginalExtractor; + DecryptHandle* mDecryptHandle; + + DRMExtractor(const DRMExtractor &); + DRMExtractor &operator=(const DRMExtractor &); +}; + +bool SniffDRM( + const sp &source, String8 *mimeType, float *confidence); + +} // namespace android + +#endif // DRM_EXTRACTOR_H_ + diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index c8663d5..849bc89 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -38,6 +38,10 @@ public: virtual sp 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 &); }; -- cgit v1.1