From dcd25efb46c41c8d24a0a9cf61fb57f84149709e 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 --- media/libstagefright/DRMExtractor.cpp | 302 ++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 media/libstagefright/DRMExtractor.cpp (limited to 'media/libstagefright/DRMExtractor.cpp') 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 + -- cgit v1.1