diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/MediaRecorder.java | 10 | ||||
-rw-r--r-- | media/java/android/media/MediaScanner.java | 6 | ||||
-rw-r--r-- | media/libmedia/MediaScanner.cpp | 4 | ||||
-rw-r--r-- | media/libstagefright/Android.mk | 4 | ||||
-rw-r--r-- | media/libstagefright/AwesomePlayer.cpp | 53 | ||||
-rw-r--r-- | media/libstagefright/DRMExtractor.cpp | 303 | ||||
-rw-r--r-- | media/libstagefright/DataSource.cpp | 9 | ||||
-rw-r--r-- | media/libstagefright/FileSource.cpp | 90 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Extractor.cpp | 277 | ||||
-rw-r--r-- | media/libstagefright/MediaExtractor.cpp | 13 | ||||
-rw-r--r-- | media/libstagefright/NuHTTPDataSource.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/StagefrightMediaScanner.cpp | 9 | ||||
-rw-r--r-- | media/libstagefright/httplive/M3UParser.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/include/AwesomePlayer.h | 7 | ||||
-rw-r--r-- | media/libstagefright/include/DRMExtractor.h | 61 | ||||
-rw-r--r-- | media/libstagefright/include/MPEG4Extractor.h | 17 | ||||
-rw-r--r-- | media/libstagefright/rtsp/ASessionDescription.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/rtsp/MyHandler.h | 2 |
18 files changed, 814 insertions, 57 deletions
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index d9f2302..4e87c73 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -316,7 +316,7 @@ public class MediaRecorder degrees != 270) { throw new IllegalArgumentException("Unsupported angle: " + degrees); } - setParameter(String.format("video-param-rotation-angle-degrees=%d", degrees)); + setParameter("video-param-rotation-angle-degrees=" + degrees); } /** @@ -435,7 +435,7 @@ public class MediaRecorder if (samplingRate <= 0) { throw new IllegalArgumentException("Audio sampling rate is not positive"); } - setParameter(String.format("audio-param-sampling-rate=%d", samplingRate)); + setParameter("audio-param-sampling-rate=" + samplingRate); } /** @@ -450,7 +450,7 @@ public class MediaRecorder if (numChannels <= 0) { throw new IllegalArgumentException("Number of channels is not positive"); } - setParameter(String.format("audio-param-number-of-channels=%d", numChannels)); + setParameter("audio-param-number-of-channels=" + numChannels); } /** @@ -466,7 +466,7 @@ public class MediaRecorder if (bitRate <= 0) { throw new IllegalArgumentException("Audio encoding bit rate is not positive"); } - setParameter(String.format("audio-param-encoding-bitrate=%d", bitRate)); + setParameter("audio-param-encoding-bitrate=" + bitRate); } /** @@ -482,7 +482,7 @@ public class MediaRecorder if (bitRate <= 0) { throw new IllegalArgumentException("Video encoding bit rate is not positive"); } - setParameter(String.format("video-param-encoding-bitrate=%d", bitRate)); + setParameter("video-param-encoding-bitrate=" + bitRate); } /** diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 47e1058..ab2c6ea 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -1220,8 +1220,12 @@ public class MediaScanner prescan(path); File file = new File(path); + + // lastModified is in milliseconds on Files. + long lastModifiedSeconds = file.lastModified() / 1000; + // always scan the file, so we can return the content://media Uri for existing files - return mClient.doScanFile(path, mimeType, file.lastModified(), file.length(), true); + return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), true); } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); return null; diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp index 6f581d3..c5112a5 100644 --- a/media/libmedia/MediaScanner.cpp +++ b/media/libmedia/MediaScanner.cpp @@ -81,13 +81,13 @@ status_t MediaScanner::processDirectory( } static bool fileMatchesExtension(const char* path, const char* extensions) { - char* extension = strrchr(path, '.'); + const char* extension = strrchr(path, '.'); if (!extension) return false; ++extension; // skip the dot if (extension[0] == 0) return false; while (extensions[0]) { - char* comma = strchr(extensions, ','); + const char* comma = strchr(extensions, ','); size_t length = (comma ? comma - extensions : strlen(extensions)); if (length == strlen(extension) && strncasecmp(extension, extensions, length) == 0) return true; extensions += length; diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 615f3e8..d674547 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -11,6 +11,7 @@ LOCAL_SRC_FILES:= \ AwesomePlayer.cpp \ CameraSource.cpp \ DataSource.cpp \ + DRMExtractor.cpp \ ESDS.cpp \ FileSource.cpp \ HTTPStream.cpp \ @@ -59,7 +60,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 d99990b..6dc61c7 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -247,7 +247,8 @@ AwesomePlayer::AwesomePlayer() mExtractorFlags(0), mLastVideoBuffer(NULL), mVideoBuffer(NULL), - mSuspensionState(NULL) { + mSuspensionState(NULL), + mDecryptHandle(NULL) { CHECK_EQ(mClient.connect(), OK); DataSource::RegisterDefaultSniffers(); @@ -346,6 +347,12 @@ status_t AwesomePlayer::setDataSource_l( return UNKNOWN_ERROR; } + dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient); + if (mDecryptHandle != NULL + && RightsStatus::RIGHTS_VALID != mDecryptHandle->status) { + notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE); + } + return setDataSource_l(extractor); } @@ -421,6 +428,13 @@ void AwesomePlayer::reset() { } void AwesomePlayer::reset_l() { + if (mDecryptHandle != NULL) { + mDrmManagerClient->setPlaybackStatus(mDecryptHandle, + Playback::STOP, 0); + mDecryptHandle = NULL; + mDrmManagerClient = NULL; + } + if (mFlags & PREPARING) { mFlags |= PREPARE_CANCELLED; if (mConnectingDataSource != NULL) { @@ -761,6 +775,13 @@ status_t AwesomePlayer::play_l() { bool deferredAudioSeek = false; + if (mDecryptHandle != NULL) { + int64_t position; + getPosition(&position); + mDrmManagerClient->setPlaybackStatus(mDecryptHandle, + Playback::START, position / 1000); + } + if (mAudioSource != NULL) { if (mAudioPlayer == NULL) { if (mAudioSink != NULL) { @@ -778,6 +799,11 @@ status_t AwesomePlayer::play_l() { mFlags &= ~(PLAYING | FIRST_FRAME); + if (mDecryptHandle != NULL) { + mDrmManagerClient->setPlaybackStatus(mDecryptHandle, + Playback::STOP, 0); + } + return err; } @@ -905,6 +931,11 @@ status_t AwesomePlayer::pause_l(bool at_eos) { mFlags &= ~PLAYING; + if (mDecryptHandle != NULL) { + mDrmManagerClient->setPlaybackStatus(mDecryptHandle, + Playback::PAUSE, 0); + } + return OK; } @@ -1022,6 +1053,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); + } } } @@ -1254,6 +1292,13 @@ void AwesomePlayer::onVideoEvent() { TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource; + if (mDecryptHandle != NULL) { + mDrmManagerClient->setPlaybackStatus(mDecryptHandle, + Playback::PAUSE, 0); + mDrmManagerClient->setPlaybackStatus(mDecryptHandle, + Playback::START, timeUs / 1000); + } + if (mFlags & FIRST_FRAME) { mFlags &= ~FIRST_FRAME; @@ -1665,6 +1710,12 @@ status_t AwesomePlayer::finishSetDataSource_l() { return UNKNOWN_ERROR; } + dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient); + if (mDecryptHandle != NULL + && RightsStatus::RIGHTS_VALID != mDecryptHandle->status) { + notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE); + } + return setDataSource_l(extractor); } diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp new file mode 100644 index 0000000..aa9ad23 --- /dev/null +++ b/media/libstagefright/DRMExtractor.cpp @@ -0,0 +1,303 @@ +/* + * 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, + sp<AMessage> *) { + { + 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 49eac62..0b8997c 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -22,6 +22,7 @@ #include "include/MPEG2TSExtractor.h" #include "include/NuCachedSource2.h" #include "include/NuHTTPDataSource.h" +#include "include/DRMExtractor.h" #include "matroska/MatroskaExtractor.h" @@ -31,6 +32,8 @@ #include <media/stagefright/MediaErrors.h> #include <utils/String8.h> +#include <cutils/properties.h> + namespace android { bool DataSource::getUInt16(off_t offset, uint16_t *x) { @@ -104,6 +107,12 @@ void DataSource::RegisterDefaultSniffers() { RegisterSniffer(SniffAMR); RegisterSniffer(SniffMPEG2TS); RegisterSniffer(SniffMP3); + + char value[PROPERTY_VALUE_MAX]; + if (property_get("drm.service.enabled", value, NULL) + && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { + RegisterSniffer(SniffDRM); + } } // static diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp index dd2579b..b46d8d0 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 { @@ -61,13 +81,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) { @@ -87,4 +112,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 34064c8..5497322 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -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; @@ -572,7 +755,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; } @@ -1020,6 +1207,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; @@ -1069,6 +1270,8 @@ status_t MPEG4Extractor::parseTrackHeader( duration = U32_AT(&buffer[20]); } + mLastTrack->meta->setInt32(kKeyTrackID, id); + size_t matrixOffset = dynSize + 16; int32_t a00 = U32_AT(&buffer[matrixOffset]); int32_t a01 = U32_AT(&buffer[matrixOffset + 4]); @@ -1712,9 +1915,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(); @@ -1723,40 +1932,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 8a5fb11..965c370 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -24,6 +24,7 @@ #include "include/WAVExtractor.h" #include "include/OggExtractor.h" #include "include/MPEG2TSExtractor.h" +#include "include/DRMExtractor.h" #include "matroska/MatroskaExtractor.h" @@ -63,6 +64,18 @@ sp<MediaExtractor> MediaExtractor::Create( mime, confidence); } + if (!strncmp(mime, "drm", 3)) { + const 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/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp index 6d4fbcb..af247d5 100644 --- a/media/libstagefright/NuHTTPDataSource.cpp +++ b/media/libstagefright/NuHTTPDataSource.cpp @@ -58,7 +58,7 @@ static bool ParseURL( path->setTo(slashPos); } - char *colonPos = strchr(host->string(), ':'); + const char *colonPos = strchr(host->string(), ':'); if (colonPos != NULL) { unsigned long x; diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 10c6e41..7a600d7 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -127,10 +127,11 @@ status_t StagefrightMediaScanner::processFile( || !strcasecmp(extension, ".rtttl") || !strcasecmp(extension, ".rtx") || !strcasecmp(extension, ".ota")) { - return HandleMIDI(path, &client); - } - - if (mRetriever->setDataSource(path) == OK) { + status_t status = HandleMIDI(path, &client); + if (status != OK) { + return status; + } + } else if (mRetriever->setDataSource(path) == OK) { const char *value; if ((value = mRetriever->extractMetadata( METADATA_KEY_MIMETYPE)) != NULL) { diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index a68c641..90f3d6d 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -96,7 +96,7 @@ static bool MakeURL(const char *baseURL, const char *url, AString *out) { out->setTo(baseURL); out->append(url); } else { - char *slashPos = strrchr(baseURL, '/'); + const char *slashPos = strrchr(baseURL, '/'); if (slashPos > &baseURL[6]) { out->setTo(baseURL, slashPos - baseURL); diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index c059e60..e352928 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -26,6 +26,7 @@ #include <media/stagefright/OMXClient.h> #include <media/stagefright/TimeSource.h> #include <utils/threads.h> +#include <drm/DrmManagerClient.h> namespace android { @@ -41,6 +42,9 @@ struct ARTSPController; struct ARTPSession; struct UDPPusher; +class DrmManagerClinet; +class DecryptHandle; + struct AwesomeRenderer : public RefBase { AwesomeRenderer() {} @@ -216,6 +220,9 @@ private: } } *mSuspensionState; + DrmManagerClient *mDrmManagerClient; + DecryptHandle *mDecryptHandle; + status_t setDataSource_l( const char *uri, const KeyedVector<String8, String8> *headers = NULL); diff --git a/media/libstagefright/include/DRMExtractor.h b/media/libstagefright/include/DRMExtractor.h new file mode 100644 index 0000000..cafc812 --- /dev/null +++ b/media/libstagefright/include/DRMExtractor.h @@ -0,0 +1,61 @@ +/* + * 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 { + +struct AMessage; +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, + sp<AMessage> *); + +} // namespace android + +#endif // DRM_EXTRACTOR_H_ + diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index 2610b0e..bc2e4dc 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -39,6 +39,10 @@ public: virtual sp<MetaData> getMetaData(); + // for DRM + virtual void setDrmFlag(bool flag); + virtual char* getDrmTrackInfo(size_t trackID, int *len); + protected: virtual ~MPEG4Extractor(); @@ -71,6 +75,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); + status_t parseTrackHeader(off_t data_offset, off_t data_size); MPEG4Extractor(const MPEG4Extractor &); diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index aa2618e..f03f7a2 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -201,7 +201,7 @@ void ASessionDescription::getFormatType( AString format; getFormat(index, &format); - char *lastSpacePos = strrchr(format.c_str(), ' '); + const char *lastSpacePos = strrchr(format.c_str(), ' '); CHECK(lastSpacePos != NULL); char *end; diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 7bf534d..72a2fdb 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -1197,7 +1197,7 @@ private: out->setTo(baseURL); out->append(url); } else { - char *slashPos = strrchr(baseURL, '/'); + const char *slashPos = strrchr(baseURL, '/'); if (slashPos > &baseURL[6]) { out->setTo(baseURL, slashPos - baseURL); |