summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/DRMExtractor.cpp
diff options
context:
space:
mode:
authorGloria Wang <gwang@google.com>2010-06-22 13:55:38 -0700
committerGloria Wang <gwang@google.com>2010-10-25 12:16:56 -0700
commitdcd25efb46c41c8d24a0a9cf61fb57f84149709e (patch)
tree6b6d165ba952924f1cd1b77f3a0903e5da31445a /media/libstagefright/DRMExtractor.cpp
parent2272ee27d9022d173b6eab45c409b3c3f57f30ec (diff)
downloadframeworks_av-dcd25efb46c41c8d24a0a9cf61fb57f84149709e.zip
frameworks_av-dcd25efb46c41c8d24a0a9cf61fb57f84149709e.tar.gz
frameworks_av-dcd25efb46c41c8d24a0a9cf61fb57f84149709e.tar.bz2
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
Diffstat (limited to 'media/libstagefright/DRMExtractor.cpp')
-rw-r--r--media/libstagefright/DRMExtractor.cpp302
1 files changed, 302 insertions, 0 deletions
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 <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
+