diff options
Diffstat (limited to 'media')
58 files changed, 1863 insertions, 561 deletions
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 3be0651..e012116 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -70,15 +70,16 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES += libinstantssq -LOCAL_WHOLE_STATIC_LIBRARY := libmedia_helper +LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper LOCAL_MODULE:= libmedia LOCAL_C_INCLUDES := \ $(TOP)/frameworks/native/include/media/openmax \ + $(TOP)/frameworks/av/include/media/ \ $(TOP)/frameworks/av/media/libstagefright \ - external/icu/icu4c/source/common \ - external/icu/icu4c/source/i18n \ + $(TOP)/external/icu/icu4c/source/common \ + $(TOP)/external/icu/icu4c/source/i18n \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 3486d21..1742fbe 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -582,9 +582,13 @@ const sp<IAudioPolicyService>& AudioSystem::get_audio_policy_service() } binder->linkToDeath(gAudioPolicyServiceClient); gAudioPolicyService = interface_cast<IAudioPolicyService>(binder); - gAudioPolicyService->registerClient(gAudioPolicyServiceClient); gLock.unlock(); + // Registering the client takes the AudioPolicyService lock. + // Don't hold the AudioSystem lock at the same time. + gAudioPolicyService->registerClient(gAudioPolicyServiceClient); } else { + // There exists a benign race condition where gAudioPolicyService + // is set, but gAudioPolicyServiceClient is not yet registered. gLock.unlock(); } return gAudioPolicyService; diff --git a/media/libmedia/CharacterEncodingDetector.cpp b/media/libmedia/CharacterEncodingDetector.cpp index 7d1ddfd..41994dc 100644 --- a/media/libmedia/CharacterEncodingDetector.cpp +++ b/media/libmedia/CharacterEncodingDetector.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "CharacterEncodingDector" #include <utils/Log.h> -#include "CharacterEncodingDetector.h" +#include <CharacterEncodingDetector.h> #include "CharacterEncodingDetectorTables.h" #include "utils/Vector.h" @@ -118,10 +118,12 @@ void CharacterEncodingDetector::detectAndConvert() { int32_t matches; const UCharsetMatch** ucma = ucsdet_detectAll(csd, &matches, &status); bool goodmatch = true; + int highest = 0; const UCharsetMatch* bestCombinedMatch = getPreferred(buf, strlen(buf), - ucma, matches, &goodmatch); + ucma, matches, &goodmatch, &highest); - if (!goodmatch && strlen(buf) < 20) { + ALOGV("goodmatch: %s, highest: %d", goodmatch ? "true" : "false", highest); + if (!goodmatch && (highest < 15 || strlen(buf) < 20)) { ALOGV("not a good match, trying with more data"); // This string might be too short for ICU to do anything useful with. // (real world example: "Björk" in ISO-8859-1 might be detected as GB18030, because @@ -146,9 +148,10 @@ void CharacterEncodingDetector::detectAndConvert() { ucsdet_setText(csd, buf, strlen(buf), &status); ucma = ucsdet_detectAll(csd, &matches, &status); bestCombinedMatch = getPreferred(buf, strlen(buf), - ucma, matches, &goodmatch); - if (!goodmatch) { + ucma, matches, &goodmatch, &highest); + if (!goodmatch && highest <= 15) { ALOGV("still not a good match after adding printable tags"); + bestCombinedMatch = NULL; } } else { ALOGV("no printable tags to add"); @@ -157,6 +160,8 @@ void CharacterEncodingDetector::detectAndConvert() { if (bestCombinedMatch != NULL) { combinedenc = ucsdet_getName(bestCombinedMatch, &status); + } else { + combinedenc = "ISO-8859-1"; } } @@ -199,10 +204,17 @@ void CharacterEncodingDetector::detectAndConvert() { if (strcmp(enc,"UTF-8") != 0) { // only convert if the source encoding isn't already UTF-8 ALOGV("@@@ using converter %s for %s", enc, mNames.getEntry(i)); + status = U_ZERO_ERROR; UConverter *conv = ucnv_open(enc, &status); if (U_FAILURE(status)) { - ALOGE("could not create UConverter for %s", enc); - continue; + ALOGW("could not create UConverter for %s (%d), falling back to ISO-8859-1", + enc, status); + status = U_ZERO_ERROR; + conv = ucnv_open("ISO-8859-1", &status); + if (U_FAILURE(status)) { + ALOGW("could not create UConverter for ISO-8859-1 either"); + continue; + } } // convert from native encoding to UTF-8 @@ -224,7 +236,16 @@ void CharacterEncodingDetector::detectAndConvert() { } else { // zero terminate *target = 0; - mValues.setEntry(i, buffer); + // strip trailing spaces + while (--target > buffer && *target == ' ') { + *target = 0; + } + // skip leading spaces + char *start = buffer; + while (*start == ' ') { + start++; + } + mValues.setEntry(i, start); } delete[] buffer; @@ -261,7 +282,7 @@ void CharacterEncodingDetector::detectAndConvert() { const UCharsetMatch *CharacterEncodingDetector::getPreferred( const char *input, size_t len, const UCharsetMatch** ucma, size_t nummatches, - bool *goodmatch) { + bool *goodmatch, int *highestmatch) { *goodmatch = false; Vector<const UCharsetMatch*> matches; @@ -316,11 +337,17 @@ const UCharsetMatch *CharacterEncodingDetector::getPreferred( } ALOGV("%zu: %s %d", i, encname, confidence); + status = U_ZERO_ERROR; UConverter *conv = ucnv_open(encname, &status); + int demerit = 0; + if (U_FAILURE(status)) { + ALOGV("failed to open %s: %d", encname, status); + confidence = 0; + demerit += 1000; + } const char *source = input; const char *sourceLimit = input + len; status = U_ZERO_ERROR; - int demerit = 0; int frequentchars = 0; int totalchars = 0; while (true) { @@ -337,7 +364,8 @@ const UCharsetMatch *CharacterEncodingDetector::getPreferred( if (c < 0x20 || (c >= 0x7f && c <= 0x009f)) { ALOGV("control character %x", c); demerit += 100; - } else if ((c >= 0xa0 && c <= 0xbe) // symbols, superscripts + } else if ((c == 0xa0) // no-break space + || (c >= 0xa2 && c <= 0xbe) // symbols, superscripts || (c == 0xd7) || (c == 0xf7) // multiplication and division signs || (c >= 0x2000 && c <= 0x209f)) { // punctuation, superscripts ALOGV("unlikely character %x", c); @@ -408,10 +436,14 @@ const UCharsetMatch *CharacterEncodingDetector::getPreferred( } else { ALOGV("runner up: '%s' w/ %d confidence", ucsdet_getName(matches[runnerupidx], &status), runnerup); + if (runnerup < 0) { + runnerup = 0; + } if ((highest - runnerup) > 15) { *goodmatch = true; } } + *highestmatch = highest; return matches[highestidx]; } diff --git a/media/libmedia/CharacterEncodingDetector.h b/media/libmedia/CharacterEncodingDetector.h deleted file mode 100644 index 7b5ed86..0000000 --- a/media/libmedia/CharacterEncodingDetector.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2013 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 _CHARACTER_ENCODING_DETECTOR_H -#define _CHARACTER_ENCODING_DETECTOR_H - -#include <media/mediascanner.h> - -#include "StringArray.h" - -#include "unicode/ucnv.h" -#include "unicode/ucsdet.h" -#include "unicode/ustring.h" - -namespace android { - -class CharacterEncodingDetector { - - public: - CharacterEncodingDetector(); - ~CharacterEncodingDetector(); - - void addTag(const char *name, const char *value); - size_t size(); - - void detectAndConvert(); - status_t getTag(int index, const char **name, const char**value); - - private: - const UCharsetMatch *getPreferred( - const char *input, size_t len, - const UCharsetMatch** ucma, size_t matches, - bool *goodmatch); - - bool isFrequent(const uint16_t *values, uint32_t c); - - // cached name and value strings, for native encoding support. - // TODO: replace these with byte blob arrays that don't require the data to be - // singlenullbyte-terminated - StringArray mNames; - StringArray mValues; - - UConverter* mUtf8Conv; -}; - - - -}; // namespace android - -#endif diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index d2e181b..e2e6042 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -87,6 +87,7 @@ const MediaProfiles::NameToTagMap MediaProfiles::sCamcorderQualityNameMap[] = { {"highspeed480p", CAMCORDER_QUALITY_HIGH_SPEED_480P}, {"highspeed720p", CAMCORDER_QUALITY_HIGH_SPEED_720P}, {"highspeed1080p", CAMCORDER_QUALITY_HIGH_SPEED_1080P}, + {"highspeed2160p", CAMCORDER_QUALITY_HIGH_SPEED_2160P}, }; #if LOG_NDEBUG diff --git a/media/libmedia/MediaScannerClient.cpp b/media/libmedia/MediaScannerClient.cpp index 1661f04..9f803cb 100644 --- a/media/libmedia/MediaScannerClient.cpp +++ b/media/libmedia/MediaScannerClient.cpp @@ -25,14 +25,10 @@ namespace android { -MediaScannerClient::MediaScannerClient() - : mEncodingDetector(NULL) -{ +MediaScannerClient::MediaScannerClient() { } -MediaScannerClient::~MediaScannerClient() -{ - delete mEncodingDetector; +MediaScannerClient::~MediaScannerClient() { } void MediaScannerClient::setLocale(const char* locale) @@ -40,31 +36,16 @@ void MediaScannerClient::setLocale(const char* locale) mLocale = locale; // not currently used } -void MediaScannerClient::beginFile() -{ - delete mEncodingDetector; - mEncodingDetector = new CharacterEncodingDetector(); +void MediaScannerClient::beginFile() { } status_t MediaScannerClient::addStringTag(const char* name, const char* value) { - mEncodingDetector->addTag(name, value); + handleStringTag(name, value); return OK; } -void MediaScannerClient::endFile() -{ - mEncodingDetector->detectAndConvert(); - - int size = mEncodingDetector->size(); - if (size) { - for (int i = 0; i < size; i++) { - const char *name; - const char *value; - mEncodingDetector->getTag(i, &name, &value); - handleStringTag(name, value); - } - } +void MediaScannerClient::endFile() { } } // namespace android diff --git a/media/libmedia/StringArray.h b/media/libmedia/StringArray.h deleted file mode 100644 index ae47085..0000000 --- a/media/libmedia/StringArray.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -// -// Sortable array of strings. STL-ish, but STL-free. -// -#ifndef _LIBS_MEDIA_STRING_ARRAY_H -#define _LIBS_MEDIA_STRING_ARRAY_H - -#include <stdlib.h> -#include <string.h> - -namespace android { - -// -// An expanding array of strings. Add, get, sort, delete. -// -class StringArray { -public: - StringArray(); - virtual ~StringArray(); - - // - // Add a string. A copy of the string is made. - // - bool push_back(const char* str); - - // - // Delete an entry. - // - void erase(int idx); - - // - // Sort the array. - // - void sort(int (*compare)(const void*, const void*)); - - // - // Pass this to the sort routine to do an ascending alphabetical sort. - // - static int cmpAscendingAlpha(const void* pstr1, const void* pstr2); - - // - // Get the #of items in the array. - // - inline int size(void) const { return mCurrent; } - - // - // Return entry N. - // [should use operator[] here] - // - const char* getEntry(int idx) const { - return (unsigned(idx) >= unsigned(mCurrent)) ? NULL : mArray[idx]; - } - - // - // Set entry N to specified string. - // [should use operator[] here] - // - void setEntry(int idx, const char* str); - -private: - int mMax; - int mCurrent; - char** mArray; -}; - -}; // namespace android - -#endif // _LIBS_MEDIA_STRING_ARRAY_H diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 6cd377a..9611ac7 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -50,6 +50,7 @@ MediaPlayer::MediaPlayer() mListener = NULL; mCookie = NULL; mStreamType = AUDIO_STREAM_MUSIC; + mAudioAttributesParcel = NULL; mCurrentPosition = -1; mSeekPosition = -1; mCurrentState = MEDIA_PLAYER_IDLE; @@ -68,6 +69,10 @@ MediaPlayer::MediaPlayer() MediaPlayer::~MediaPlayer() { ALOGV("destructor"); + if (mAudioAttributesParcel != NULL) { + delete mAudioAttributesParcel; + mAudioAttributesParcel = NULL; + } AudioSystem::releaseAudioSessionId(mAudioSessionId, -1); disconnect(); IPCThreadState::self()->flushCommands(); @@ -237,6 +242,9 @@ status_t MediaPlayer::prepareAsync_l() { if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) { mPlayer->setAudioStreamType(mStreamType); + if (mAudioAttributesParcel != NULL) { + mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel); + } mCurrentState = MEDIA_PLAYER_PREPARING; return mPlayer->prepareAsync(); } @@ -662,8 +670,17 @@ status_t MediaPlayer::setParameter(int key, const Parcel& request) if (mPlayer != NULL) { return mPlayer->setParameter(key, request); } - ALOGV("setParameter: no active player"); - return INVALID_OPERATION; + switch (key) { + case KEY_PARAMETER_AUDIO_ATTRIBUTES: + // no player, save the marshalled audio attributes + if (mAudioAttributesParcel != NULL) { delete mAudioAttributesParcel; }; + mAudioAttributesParcel = new Parcel(); + mAudioAttributesParcel->appendFrom(&request, 0, request.dataSize()); + return OK; + default: + ALOGV("setParameter: no active player"); + return INVALID_OPERATION; + } } status_t MediaPlayer::getParameter(int key, Parcel *reply) diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 0c7e590..adc066d 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -28,6 +28,7 @@ LOCAL_SHARED_LIBRARIES := \ libcamera_client \ libcrypto \ libcutils \ + libdrmframework \ liblog \ libdl \ libgui \ diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index b5bd988..c8cb7ed 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -204,6 +204,8 @@ const int32_t kAudioAttributesMarshallTagFlattenTags = 1; // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | content_type | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | flags | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | kAudioAttributesMarshallTagFlattenTags | // ignore tags if not found @@ -219,6 +221,7 @@ void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attribu { attributes->usage = (audio_usage_t) parcel.readInt32(); attributes->content_type = (audio_content_type_t) parcel.readInt32(); + attributes->source = (audio_source_t) parcel.readInt32(); attributes->flags = (audio_flags_mask_t) parcel.readInt32(); const bool hasFlattenedTag = (parcel.readInt32() == kAudioAttributesMarshallTagFlattenTags); if (hasFlattenedTag) { diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index cdb7e69..8e1987a 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -32,6 +32,8 @@ #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> +#include "../../libstagefright/include/DRMExtractor.h" #include "../../libstagefright/include/NuCachedSource2.h" #include "../../libstagefright/include/WVMExtractor.h" @@ -49,6 +51,7 @@ NuPlayer::GenericSource::GenericSource( mIsWidevine(false), mUIDValid(uidValid), mUID(uid), + mDrmManagerClient(NULL), mMetaDataSize(-1ll), mBitrate(-1ll), mPollBufferingGeneration(0) { @@ -57,12 +60,18 @@ NuPlayer::GenericSource::GenericSource( } void NuPlayer::GenericSource::resetDataSource() { + mAudioTimeUs = 0; + mVideoTimeUs = 0; mHTTPService.clear(); mUri.clear(); mUriHeaders.clear(); mFd = -1; mOffset = 0; mLength = 0; + setDrmPlaybackStatusIfNeeded(Playback::STOP, 0); + mDecryptHandle = NULL; + mDrmManagerClient = NULL; + mStarted = false; } status_t NuPlayer::GenericSource::setDataSource( @@ -130,6 +139,10 @@ status_t NuPlayer::GenericSource::initFromDataSource() { return UNKNOWN_ERROR; } + if (extractor->getDrmFlag()) { + checkDrmStatus(mDataSource); + } + sp<MetaData> fileMeta = extractor->getMetaData(); if (fileMeta != NULL) { int64_t duration; @@ -141,13 +154,17 @@ status_t NuPlayer::GenericSource::initFromDataSource() { int32_t totalBitrate = 0; for (size_t i = 0; i < extractor->countTracks(); ++i) { + sp<MediaSource> track = extractor->getTrack(i); + sp<MetaData> meta = extractor->getTrackMetaData(i); const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); - sp<MediaSource> track = extractor->getTrack(i); - + // Do the string compare immediately with "mime", + // we can't assume "mime" would stay valid after another + // extractor operation, some extractors might modify meta + // during getTrack() and make it invalid. if (!strncasecmp(mime, "audio/", 6)) { if (mAudioTrack.mSource == NULL) { mAudioTrack.mIndex = i; @@ -199,6 +216,28 @@ status_t NuPlayer::GenericSource::initFromDataSource() { return OK; } +void NuPlayer::GenericSource::checkDrmStatus(const sp<DataSource>& dataSource) { + dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient); + if (mDecryptHandle != NULL) { + CHECK(mDrmManagerClient); + if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) { + sp<AMessage> msg = dupNotify(); + msg->setInt32("what", kWhatDrmNoLicense); + msg->post(); + } + } +} + +int64_t NuPlayer::GenericSource::getLastReadPosition() { + if (mAudioTrack.mSource != NULL) { + return mAudioTimeUs; + } else if (mVideoTrack.mSource != NULL) { + return mVideoTimeUs; + } else { + return 0; + } +} + status_t NuPlayer::GenericSource::setBuffers( bool audio, Vector<MediaBuffer *> &buffers) { if (mIsWidevine && !audio) { @@ -280,7 +319,14 @@ void NuPlayer::GenericSource::onPrepareAsync() { } if (mVideoTrack.mSource != NULL) { - notifyVideoSizeChanged(getFormat(false /* audio */)); + sp<MetaData> meta = doGetFormatMeta(false /* audio */); + sp<AMessage> msg = new AMessage; + err = convertMetaDataToMessage(meta, &msg); + if(err != OK) { + notifyPreparedAndCleanup(err); + return; + } + notifyVideoSizeChanged(msg); } notifyFlagsChanged( @@ -384,7 +430,7 @@ void NuPlayer::GenericSource::start() { mAudioTrack.mPackets = new AnotherPacketSource(mAudioTrack.mSource->getFormat()); - readBuffer(MEDIA_TRACK_TYPE_AUDIO); + postReadBuffer(MEDIA_TRACK_TYPE_AUDIO); } if (mVideoTrack.mSource != NULL) { @@ -392,8 +438,37 @@ void NuPlayer::GenericSource::start() { mVideoTrack.mPackets = new AnotherPacketSource(mVideoTrack.mSource->getFormat()); - readBuffer(MEDIA_TRACK_TYPE_VIDEO); + postReadBuffer(MEDIA_TRACK_TYPE_VIDEO); + } + + setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000); + mStarted = true; +} + +void NuPlayer::GenericSource::stop() { + // nothing to do, just account for DRM playback status + setDrmPlaybackStatusIfNeeded(Playback::STOP, 0); + mStarted = false; +} + +void NuPlayer::GenericSource::pause() { + // nothing to do, just account for DRM playback status + setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0); + mStarted = false; +} + +void NuPlayer::GenericSource::resume() { + // nothing to do, just account for DRM playback status + setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000); + mStarted = true; +} + +void NuPlayer::GenericSource::setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position) { + if (mDecryptHandle != NULL) { + mDrmManagerClient->setPlaybackStatus(mDecryptHandle, playbackStatus, position); } + mSubtitleTrack.mPackets = new AnotherPacketSource(NULL); + mTimedTextTrack.mPackets = new AnotherPacketSource(NULL); } status_t NuPlayer::GenericSource::feedMoreTSData() { @@ -550,6 +625,37 @@ void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) { } break; } + + case kWhatGetFormat: + { + onGetFormatMeta(msg); + break; + } + + case kWhatGetSelectedTrack: + { + onGetSelectedTrack(msg); + break; + } + + case kWhatSelectTrack: + { + onSelectTrack(msg); + break; + } + + case kWhatSeek: + { + onSeek(msg); + break; + } + + case kWhatReadBuffer: + { + onReadBuffer(msg); + break; + } + default: Source::onMessageReceived(msg); break; @@ -625,6 +731,34 @@ void NuPlayer::GenericSource::sendTextData( } sp<MetaData> NuPlayer::GenericSource::getFormatMeta(bool audio) { + sp<AMessage> msg = new AMessage(kWhatGetFormat, id()); + msg->setInt32("audio", audio); + + sp<AMessage> response; + void *format; + status_t err = msg->postAndAwaitResponse(&response); + if (err == OK && response != NULL) { + CHECK(response->findPointer("format", &format)); + return (MetaData *)format; + } else { + return NULL; + } +} + +void NuPlayer::GenericSource::onGetFormatMeta(sp<AMessage> msg) const { + int32_t audio; + CHECK(msg->findInt32("audio", &audio)); + + sp<AMessage> response = new AMessage; + sp<MetaData> format = doGetFormatMeta(audio); + response->setPointer("format", format.get()); + + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + response->postReply(replyID); +} + +sp<MetaData> NuPlayer::GenericSource::doGetFormatMeta(bool audio) const { sp<MediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource; if (source == NULL) { @@ -644,7 +778,7 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit( if (mIsWidevine && !audio) { // try to read a buffer as we may not have been able to the last time - readBuffer(MEDIA_TRACK_TYPE_VIDEO, -1ll); + postReadBuffer(MEDIA_TRACK_TYPE_VIDEO); } status_t finalResult; @@ -655,18 +789,7 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit( status_t result = track->mPackets->dequeueAccessUnit(accessUnit); if (!track->mPackets->hasBufferAvailable(&finalResult)) { - readBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO, -1ll); - } - - if (mSubtitleTrack.mSource == NULL && mTimedTextTrack.mSource == NULL) { - return result; - } - - if (mSubtitleTrack.mSource != NULL) { - CHECK(mSubtitleTrack.mPackets != NULL); - } - if (mTimedTextTrack.mSource != NULL) { - CHECK(mTimedTextTrack.mPackets != NULL); + postReadBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO); } if (result != OK) { @@ -760,6 +883,35 @@ sp<AMessage> NuPlayer::GenericSource::getTrackInfo(size_t trackIndex) const { } ssize_t NuPlayer::GenericSource::getSelectedTrack(media_track_type type) const { + sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id()); + msg->setInt32("type", type); + + sp<AMessage> response; + int32_t index; + status_t err = msg->postAndAwaitResponse(&response); + if (err == OK && response != NULL) { + CHECK(response->findInt32("index", &index)); + return index; + } else { + return -1; + } +} + +void NuPlayer::GenericSource::onGetSelectedTrack(sp<AMessage> msg) const { + int32_t tmpType; + CHECK(msg->findInt32("type", &tmpType)); + media_track_type type = (media_track_type)tmpType; + + sp<AMessage> response = new AMessage; + ssize_t index = doGetSelectedTrack(type); + response->setInt32("index", index); + + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + response->postReply(replyID); +} + +ssize_t NuPlayer::GenericSource::doGetSelectedTrack(media_track_type type) const { const Track *track = NULL; switch (type) { case MEDIA_TRACK_TYPE_VIDEO: @@ -787,6 +939,34 @@ ssize_t NuPlayer::GenericSource::getSelectedTrack(media_track_type type) const { status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select) { ALOGV("%s track: %zu", select ? "select" : "deselect", trackIndex); + sp<AMessage> msg = new AMessage(kWhatSelectTrack, id()); + msg->setInt32("trackIndex", trackIndex); + msg->setInt32("select", trackIndex); + + sp<AMessage> response; + status_t err = msg->postAndAwaitResponse(&response); + if (err == OK && response != NULL) { + CHECK(response->findInt32("err", &err)); + } + + return err; +} + +void NuPlayer::GenericSource::onSelectTrack(sp<AMessage> msg) { + int32_t trackIndex, select; + CHECK(msg->findInt32("trackIndex", &trackIndex)); + CHECK(msg->findInt32("select", &select)); + + sp<AMessage> response = new AMessage; + status_t err = doSelectTrack(trackIndex, select); + response->setInt32("err", err); + + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + response->postReply(replyID); +} + +status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select) { if (trackIndex >= mSources.size()) { return BAD_INDEX; } @@ -857,6 +1037,32 @@ status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select) { } status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) { + sp<AMessage> msg = new AMessage(kWhatSeek, id()); + msg->setInt64("seekTimeUs", seekTimeUs); + + sp<AMessage> response; + status_t err = msg->postAndAwaitResponse(&response); + if (err == OK && response != NULL) { + CHECK(response->findInt32("err", &err)); + } + + return err; +} + +void NuPlayer::GenericSource::onSeek(sp<AMessage> msg) { + int64_t seekTimeUs; + CHECK(msg->findInt64("seekTimeUs", &seekTimeUs)); + + sp<AMessage> response = new AMessage; + status_t err = doSeek(seekTimeUs); + response->setInt32("err", err); + + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + response->postReply(replyID); +} + +status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) { if (mVideoTrack.mSource != NULL) { int64_t actualTimeUs; readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, &actualTimeUs); @@ -868,6 +1074,10 @@ status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) { readBuffer(MEDIA_TRACK_TYPE_AUDIO, seekTimeUs); } + setDrmPlaybackStatusIfNeeded(Playback::START, seekTimeUs / 1000); + if (!mStarted) { + setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0); + } return OK; } @@ -937,6 +1147,19 @@ sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer( return ab; } +void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) { + sp<AMessage> msg = new AMessage(kWhatReadBuffer, id()); + msg->setInt32("trackType", trackType); + msg->post(); +} + +void NuPlayer::GenericSource::onReadBuffer(sp<AMessage> msg) { + int32_t tmpType; + CHECK(msg->findInt32("trackType", &tmpType)); + media_track_type trackType = (media_track_type)tmpType; + readBuffer(trackType); +} + void NuPlayer::GenericSource::readBuffer( media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) { Track *track; @@ -985,6 +1208,14 @@ void NuPlayer::GenericSource::readBuffer( options.clearSeekTo(); if (err == OK) { + int64_t timeUs; + CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs)); + if (trackType == MEDIA_TRACK_TYPE_AUDIO) { + mAudioTimeUs = timeUs; + } else if (trackType == MEDIA_TRACK_TYPE_VIDEO) { + mVideoTimeUs = timeUs; + } + // formatChange && seeking: track whose source is changed during selection // formatChange && !seeking: track whose source is not changed during selection // !formatChange: normal seek diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index 663bfae..50ff98a 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -27,6 +27,8 @@ namespace android { +class DecryptHandle; +class DrmManagerClient; struct AnotherPacketSource; struct ARTSPController; struct DataSource; @@ -49,6 +51,9 @@ struct NuPlayer::GenericSource : public NuPlayer::Source { virtual void prepareAsync(); virtual void start(); + virtual void stop(); + virtual void pause(); + virtual void resume(); virtual status_t feedMoreTSData(); @@ -79,6 +84,11 @@ private: kWhatSendTimedTextData, kWhatChangeAVSource, kWhatPollBuffering, + kWhatGetFormat, + kWhatGetSelectedTrack, + kWhatSelectTrack, + kWhatSeek, + kWhatReadBuffer, }; Vector<sp<MediaSource> > mSources; @@ -90,7 +100,9 @@ private: }; Track mAudioTrack; + int64_t mAudioTimeUs; Track mVideoTrack; + int64_t mVideoTimeUs; Track mSubtitleTrack; Track mTimedTextTrack; @@ -111,6 +123,9 @@ private: sp<DataSource> mDataSource; sp<NuCachedSource2> mCachedSource; sp<WVMExtractor> mWVMExtractor; + DrmManagerClient *mDrmManagerClient; + sp<DecryptHandle> mDecryptHandle; + bool mStarted; String8 mContentType; AString mSniffedMIME; off64_t mMetaDataSize; @@ -122,11 +137,26 @@ private: void resetDataSource(); status_t initFromDataSource(); + void checkDrmStatus(const sp<DataSource>& dataSource); + int64_t getLastReadPosition(); + void setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position); status_t prefillCacheIfNecessary(); void notifyPreparedAndCleanup(status_t err); + void onGetFormatMeta(sp<AMessage> msg) const; + sp<MetaData> doGetFormatMeta(bool audio) const; + + void onGetSelectedTrack(sp<AMessage> msg) const; + ssize_t doGetSelectedTrack(media_track_type type) const; + + void onSelectTrack(sp<AMessage> msg); + status_t doSelectTrack(size_t trackIndex, bool select); + + void onSeek(sp<AMessage> msg); + status_t doSeek(int64_t seekTimeUs); + void onPrepareAsync(); void fetchTextData( @@ -142,6 +172,8 @@ private: media_track_type trackType, int64_t *actualTimeUs = NULL); + void postReadBuffer(media_track_type trackType); + void onReadBuffer(sp<AMessage> msg); void readBuffer( media_track_type trackType, int64_t seekTimeUs = -1ll, int64_t *actualTimeUs = NULL, bool formatChange = false); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index b79a5dd..df3e992 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -145,6 +145,7 @@ private: NuPlayer::NuPlayer() : mUIDValid(false), mSourceFlags(0), + mCurrentPositionUs(0), mVideoIsAVC(false), mOffloadAudio(false), mCurrentOffloadInfo(AUDIO_INFO_INITIALIZER), @@ -280,7 +281,7 @@ void NuPlayer::setVideoSurfaceTextureAsync( msg->setObject( "native-window", new NativeWindowWrapper( - new Surface(bufferProducer))); + new Surface(bufferProducer, true /* controlledByApp */))); } msg->post(); @@ -540,6 +541,14 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { static_cast<NativeWindowWrapper *>(obj.get()))); if (obj != NULL) { + if (mStarted && mVideoDecoder != NULL) { + // Issue a seek to refresh the video screen only if started otherwise + // the extractor may not yet be started and will assert. + // If the video decoder is not set (perhaps audio only in this case) + // do not perform a seek as it is not needed. + mDeferredActions.push_back(new SeekAction(mCurrentPositionUs)); + } + // If there is a new surface texture, instantiate decoders // again if possible. mDeferredActions.push_back( @@ -756,7 +765,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGV("initiating %s decoder shutdown", audio ? "audio" : "video"); - (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown(); + getDecoder(audio)->initiateShutdown(); if (audio) { mFlushingAudio = SHUTTING_DOWN_DECODER; @@ -860,6 +869,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } else if (what == Renderer::kWhatPosition) { int64_t positionUs; CHECK(msg->findInt64("positionUs", &positionUs)); + mCurrentPositionUs = positionUs; CHECK(msg->findInt64("videoLateByUs", &mVideoLateByUs)); @@ -984,6 +994,8 @@ void NuPlayer::finishFlushIfPossible() { ALOGV("both audio and video are flushed now."); + mPendingAudioAccessUnit.clear(); + if (mTimeDiscontinuityPending) { mRenderer->signalTimeDiscontinuity(); mTimeDiscontinuityPending = false; @@ -1232,7 +1244,8 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { CHECK(msg->findMessage("reply", &reply)); if ((audio && mFlushingAudio != NONE) - || (!audio && mFlushingVideo != NONE)) { + || (!audio && mFlushingVideo != NONE) + || mSource == NULL) { reply->setInt32("err", INFO_DISCONTINUITY); reply->post(); return OK; @@ -1240,14 +1253,47 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { sp<ABuffer> accessUnit; + // Aggregate smaller buffers into a larger buffer. + // The goal is to reduce power consumption. + // Unfortunately this does not work with the software AAC decoder. + // TODO optimize buffer size for power consumption + // The offload read buffer size is 32 KB but 24 KB uses less power. + const int kAudioBigBufferSizeBytes = 24 * 1024; + bool doBufferAggregation = (audio && mOffloadAudio); + sp<ABuffer> biggerBuffer; + bool needMoreData = false; + int numSmallBuffers = 0; + bool gotTime = false; + bool dropAccessUnit; do { - status_t err = mSource->dequeueAccessUnit(audio, &accessUnit); + status_t err; + // Did we save an accessUnit earlier because of a discontinuity? + if (audio && (mPendingAudioAccessUnit != NULL)) { + accessUnit = mPendingAudioAccessUnit; + mPendingAudioAccessUnit.clear(); + err = mPendingAudioErr; + ALOGV("feedDecoderInputData() use mPendingAudioAccessUnit"); + } else { + err = mSource->dequeueAccessUnit(audio, &accessUnit); + } if (err == -EWOULDBLOCK) { - return err; + if (biggerBuffer == NULL) { + return err; + } else { + break; // Reply with data that we already have. + } } else if (err != OK) { if (err == INFO_DISCONTINUITY) { + if (biggerBuffer != NULL) { + // We already have some data so save this for later. + mPendingAudioErr = err; + mPendingAudioAccessUnit = accessUnit; + accessUnit.clear(); + ALOGD("feedDecoderInputData() save discontinuity for later"); + break; + } int32_t type; CHECK(accessUnit->meta()->findInt32("discontinuity", &type)); @@ -1292,7 +1338,24 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { mTimeDiscontinuityPending = mTimeDiscontinuityPending || timeChange; - if (mFlushingAudio == NONE && mFlushingVideo == NONE) { + bool seamlessFormatChange = false; + sp<AMessage> newFormat = mSource->getFormat(audio); + if (formatChange) { + seamlessFormatChange = + getDecoder(audio)->supportsSeamlessFormatChange(newFormat); + // treat seamless format change separately + formatChange = !seamlessFormatChange; + } + bool shutdownOrFlush = formatChange || timeChange; + + // We want to queue up scan-sources only once per discontinuity. + // We control this by doing it only if neither audio nor video are + // flushing or shutting down. (After handling 1st discontinuity, one + // of the flushing states will not be NONE.) + // No need to scan sources if this discontinuity does not result + // in a flush or shutdown, as the flushing state will stay NONE. + if (mFlushingAudio == NONE && mFlushingVideo == NONE && + shutdownOrFlush) { // And we'll resume scanning sources once we're done // flushing. mDeferredActions.push_front( @@ -1300,16 +1363,17 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { &NuPlayer::performScanSources)); } - if (formatChange || timeChange) { - - sp<AMessage> newFormat = mSource->getFormat(audio); - sp<Decoder> &decoder = audio ? mAudioDecoder : mVideoDecoder; - if (formatChange && !decoder->supportsSeamlessFormatChange(newFormat)) { - flushDecoder(audio, /* needShutdown = */ true); - } else { - flushDecoder(audio, /* needShutdown = */ false); - err = OK; - } + if (formatChange /* not seamless */) { + // must change decoder + flushDecoder(audio, /* needShutdown = */ true); + } else if (timeChange) { + // need to flush + flushDecoder(audio, /* needShutdown = */ false, newFormat); + err = OK; + } else if (seamlessFormatChange) { + // reuse existing decoder and don't flush + updateDecoderFormatWithoutFlush(audio, newFormat); + err = OK; } else { // This stream is unaffected by the discontinuity return -EWOULDBLOCK; @@ -1334,7 +1398,52 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { dropAccessUnit = true; ++mNumFramesDropped; } - } while (dropAccessUnit); + + size_t smallSize = accessUnit->size(); + needMoreData = false; + if (doBufferAggregation && (biggerBuffer == NULL) + // Don't bother if only room for a few small buffers. + && (smallSize < (kAudioBigBufferSizeBytes / 3))) { + // Create a larger buffer for combining smaller buffers from the extractor. + biggerBuffer = new ABuffer(kAudioBigBufferSizeBytes); + biggerBuffer->setRange(0, 0); // start empty + } + + if (biggerBuffer != NULL) { + int64_t timeUs; + bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs); + // Will the smaller buffer fit? + size_t bigSize = biggerBuffer->size(); + size_t roomLeft = biggerBuffer->capacity() - bigSize; + // Should we save this small buffer for the next big buffer? + // If the first small buffer did not have a timestamp then save + // any buffer that does have a timestamp until the next big buffer. + if ((smallSize > roomLeft) + || (!gotTime && (numSmallBuffers > 0) && smallTimestampValid)) { + mPendingAudioErr = err; + mPendingAudioAccessUnit = accessUnit; + accessUnit.clear(); + } else { + // Append small buffer to the bigger buffer. + memcpy(biggerBuffer->base() + bigSize, accessUnit->data(), smallSize); + bigSize += smallSize; + biggerBuffer->setRange(0, bigSize); + + // Keep looping until we run out of room in the biggerBuffer. + needMoreData = true; + + // Grab time from first small buffer if available. + if ((numSmallBuffers == 0) && smallTimestampValid) { + biggerBuffer->meta()->setInt64("timeUs", timeUs); + gotTime = true; + } + + ALOGV("feedDecoderInputData() #%d, smallSize = %zu, bigSize = %zu, capacity = %zu", + numSmallBuffers, smallSize, bigSize, biggerBuffer->capacity()); + numSmallBuffers++; + } + } + } while (dropAccessUnit || needMoreData); // ALOGV("returned a valid buffer of %s data", audio ? "audio" : "video"); @@ -1350,7 +1459,13 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { mCCDecoder->decode(accessUnit); } - reply->setBuffer("buffer", accessUnit); + if (biggerBuffer != NULL) { + ALOGV("feedDecoderInputData() reply with aggregated buffer, %d", numSmallBuffers); + reply->setBuffer("buffer", biggerBuffer); + } else { + reply->setBuffer("buffer", accessUnit); + } + reply->post(); return OK; @@ -1488,20 +1603,23 @@ void NuPlayer::notifyListener(int msg, int ext1, int ext2, const Parcel *in) { driver->notifyListener(msg, ext1, ext2, in); } -void NuPlayer::flushDecoder(bool audio, bool needShutdown) { +void NuPlayer::flushDecoder( + bool audio, bool needShutdown, const sp<AMessage> &newFormat) { ALOGV("[%s] flushDecoder needShutdown=%d", audio ? "audio" : "video", needShutdown); - if ((audio && mAudioDecoder == NULL) || (!audio && mVideoDecoder == NULL)) { + const sp<Decoder> &decoder = getDecoder(audio); + if (decoder == NULL) { ALOGI("flushDecoder %s without decoder present", audio ? "audio" : "video"); + return; } // Make sure we don't continue to scan sources until we finish flushing. ++mScanSourcesGeneration; mScanSourcesPending = false; - (audio ? mAudioDecoder : mVideoDecoder)->signalFlush(); + decoder->signalFlush(newFormat); mRenderer->flush(audio); FlushStatus newStatus = @@ -1515,9 +1633,27 @@ void NuPlayer::flushDecoder(bool audio, bool needShutdown) { ALOGE_IF(mFlushingVideo != NONE, "video flushDecoder() is called in state %d", mFlushingVideo); mFlushingVideo = newStatus; + + if (mCCDecoder != NULL) { + mCCDecoder->flush(); + } } } +void NuPlayer::updateDecoderFormatWithoutFlush( + bool audio, const sp<AMessage> &format) { + ALOGV("[%s] updateDecoderFormatWithoutFlush", audio ? "audio" : "video"); + + const sp<Decoder> &decoder = getDecoder(audio); + if (decoder == NULL) { + ALOGI("updateDecoderFormatWithoutFlush %s without decoder present", + audio ? "audio" : "video"); + return; + } + + decoder->signalUpdateFormat(format); +} + void NuPlayer::queueDecoderShutdown( bool audio, bool video, const sp<AMessage> &reply) { ALOGI("queueDecoderShutdown audio=%d, video=%d", audio, video); @@ -1626,6 +1762,14 @@ void NuPlayer::performSeek(int64_t seekTimeUs) { seekTimeUs, seekTimeUs / 1E6); + if (mSource == NULL) { + // This happens when reset occurs right before the loop mode + // asynchronously seeks to the start of the stream. + LOG_ALWAYS_FATAL_IF(mAudioDecoder != NULL || mVideoDecoder != NULL, + "mSource is NULL and decoders not NULL audio(%p) video(%p)", + mAudioDecoder.get(), mVideoDecoder.get()); + return; + } mSource->seekTo(seekTimeUs); ++mTimedTextGeneration; @@ -1688,6 +1832,9 @@ void NuPlayer::performReset() { ++mScanSourcesGeneration; mScanSourcesPending = false; + ++mAudioDecoderGeneration; + ++mVideoDecoderGeneration; + if (mRendererLooper != NULL) { if (mRenderer != NULL) { mRendererLooper->unregisterHandler(mRenderer->id()); @@ -1880,6 +2027,12 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { break; } + case Source::kWhatDrmNoLicense: + { + notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE); + break; + } + default: TRESPASS(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 96306db..89ae11c 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -25,6 +25,7 @@ namespace android { struct ABuffer; +struct AMessage; struct MetaData; struct NuPlayerDriver; @@ -120,6 +121,7 @@ private: sp<Source> mSource; uint32_t mSourceFlags; sp<NativeWindowWrapper> mNativeWindow; + int64_t mCurrentPositionUs; sp<MediaPlayerBase::AudioSink> mAudioSink; sp<Decoder> mVideoDecoder; bool mVideoIsAVC; @@ -156,6 +158,9 @@ private: // notion of time has changed. bool mTimeDiscontinuityPending; + sp<ABuffer> mPendingAudioAccessUnit; + status_t mPendingAudioErr; + FlushStatus mFlushingAudio; FlushStatus mFlushingVideo; @@ -169,6 +174,10 @@ private: bool mStarted; + inline const sp<Decoder> &getDecoder(bool audio) { + return audio ? mAudioDecoder : mVideoDecoder; + } + void openAudioSink(const sp<AMessage> &format, bool offloadOnly); void closeAudioSink(); @@ -185,7 +194,9 @@ private: void finishFlushIfPossible(); - void flushDecoder(bool audio, bool needShutdown); + void flushDecoder( + bool audio, bool needShutdown, const sp<AMessage> &newFormat = NULL); + void updateDecoderFormatWithoutFlush(bool audio, const sp<AMessage> &format); static bool IsFlushingState(FlushStatus state, bool *needShutdown = NULL); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 47c46d6..163a0b5 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -71,6 +71,19 @@ status_t PostAndAwaitResponse( return err; } +void NuPlayer::Decoder::rememberCodecSpecificData(const sp<AMessage> &format) { + mCSDsForCurrentFormat.clear(); + for (int32_t i = 0; ; ++i) { + AString tag = "csd-"; + tag.append(i); + sp<ABuffer> buffer; + if (!format->findBuffer(tag.c_str(), &buffer)) { + break; + } + mCSDsForCurrentFormat.push(buffer); + } +} + void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { CHECK(mCodec == NULL); @@ -123,6 +136,8 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { handleError(err); return; } + rememberCodecSpecificData(format); + // the following should work in configured state CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat)); CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat)); @@ -189,6 +204,12 @@ void NuPlayer::Decoder::configure(const sp<AMessage> &format) { msg->post(); } +void NuPlayer::Decoder::signalUpdateFormat(const sp<AMessage> &format) { + sp<AMessage> msg = new AMessage(kWhatUpdateFormat, id()); + msg->setMessage("format", format); + msg->post(); +} + status_t NuPlayer::Decoder::getInputBuffers(Vector<sp<ABuffer> > *buffers) const { sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id()); msg->setPointer("buffers", buffers); @@ -199,6 +220,8 @@ status_t NuPlayer::Decoder::getInputBuffers(Vector<sp<ABuffer> > *buffers) const void NuPlayer::Decoder::handleError(int32_t err) { + mCodec->release(); + sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatError); notify->setInt32("err", err); @@ -229,6 +252,15 @@ bool NuPlayer::Decoder::handleAnInputBuffer() { reply->setSize("buffer-ix", bufferIx); reply->setInt32("generation", mBufferGeneration); + if (!mCSDsToSubmit.isEmpty()) { + sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0); + ALOGI("[%s] resubmitting CSD", mComponentName.c_str()); + reply->setBuffer("buffer", buffer); + mCSDsToSubmit.removeAt(0); + reply->post(); + return true; + } + sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatFillThisBuffer); notify->setBuffer("buffer", mInputBuffers[bufferIx]); @@ -312,10 +344,12 @@ void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { uint32_t flags = 0; CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); - int32_t eos; - // we do not expect CODECCONFIG or SYNCFRAME for decoder + int32_t eos, csd; + // we do not expect SYNCFRAME for decoder if (buffer->meta()->findInt32("eos", &eos) && eos) { flags |= MediaCodec::BUFFER_FLAG_EOS; + } else if (buffer->meta()->findInt32("csd", &csd) && csd) { + flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG; } // copy into codec buffer @@ -448,6 +482,7 @@ void NuPlayer::Decoder::onFlush() { status_t err = OK; if (mCodec != NULL) { err = mCodec->flush(); + mCSDsToSubmit = mCSDsForCurrentFormat; // copy operator ++mBufferGeneration; } @@ -515,6 +550,14 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatUpdateFormat: + { + sp<AMessage> format; + CHECK(msg->findMessage("format", &format)); + rememberCodecSpecificData(format); + break; + } + case kWhatGetInputBuffers: { uint32_t replyID; @@ -552,7 +595,18 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { { if (!isStaleReply(msg)) { onInputBufferFilled(msg); + } else { + /* release any MediaBuffer passed in the stale buffer */ + sp<ABuffer> buffer; + MediaBuffer *mediaBuffer = NULL; + if (msg->findBuffer("buffer", &buffer) && + buffer->meta()->findPointer( + "mediaBuffer", (void **)&mediaBuffer) && + mediaBuffer != NULL) { + mediaBuffer->release(); + } } + break; } @@ -566,6 +620,10 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { case kWhatFlush: { + sp<AMessage> format; + if (msg->findMessage("new-format", &format)) { + rememberCodecSpecificData(format); + } onFlush(); break; } @@ -588,8 +646,12 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { } } -void NuPlayer::Decoder::signalFlush() { - (new AMessage(kWhatFlush, id()))->post(); +void NuPlayer::Decoder::signalFlush(const sp<AMessage> &format) { + sp<AMessage> msg = new AMessage(kWhatFlush, id()); + if (format != NULL) { + msg->setMessage("new-format", format); + } + msg->post(); } void NuPlayer::Decoder::signalResume() { @@ -665,72 +727,28 @@ bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetF return seamless; } -struct NuPlayer::CCDecoder::CCData { +struct CCData { CCData(uint8_t type, uint8_t data1, uint8_t data2) : mType(type), mData1(data1), mData2(data2) { } + bool getChannel(size_t *channel) const { + if (mData1 >= 0x10 && mData1 <= 0x1f) { + *channel = (mData1 >= 0x18 ? 1 : 0) + (mType ? 2 : 0); + return true; + } + return false; + } uint8_t mType; uint8_t mData1; uint8_t mData2; }; -NuPlayer::CCDecoder::CCDecoder(const sp<AMessage> ¬ify) - : mNotify(notify), - mTrackCount(0), - mSelectedTrack(-1) { -} - -size_t NuPlayer::CCDecoder::getTrackCount() const { - return mTrackCount; -} - -sp<AMessage> NuPlayer::CCDecoder::getTrackInfo(size_t index) const { - CHECK(index == 0); - - sp<AMessage> format = new AMessage(); - - format->setInt32("type", MEDIA_TRACK_TYPE_SUBTITLE); - format->setString("language", "und"); - format->setString("mime", MEDIA_MIMETYPE_TEXT_CEA_608); - format->setInt32("auto", 1); - format->setInt32("default", 1); - format->setInt32("forced", 0); - - return format; -} - -status_t NuPlayer::CCDecoder::selectTrack(size_t index, bool select) { - CHECK(index < mTrackCount); - - if (select) { - if (mSelectedTrack == (ssize_t)index) { - ALOGE("track %zu already selected", index); - return BAD_VALUE; - } - ALOGV("selected track %zu", index); - mSelectedTrack = index; - } else { - if (mSelectedTrack != (ssize_t)index) { - ALOGE("track %zu is not selected", index); - return BAD_VALUE; - } - ALOGV("unselected track %zu", index); - mSelectedTrack = -1; - } - - return OK; -} - -bool NuPlayer::CCDecoder::isSelected() const { - return mSelectedTrack >= 0 && mSelectedTrack < (int32_t)mTrackCount; -} - -bool NuPlayer::CCDecoder::isNullPad(CCData *cc) const { +static bool isNullPad(CCData *cc) { return cc->mData1 < 0x10 && cc->mData2 < 0x10; } -void NuPlayer::CCDecoder::dumpBytePair(const sp<ABuffer> &ccBuf) const { +static void dumpBytePair(const sp<ABuffer> &ccBuf) { size_t offset = 0; AString out; @@ -792,6 +810,78 @@ void NuPlayer::CCDecoder::dumpBytePair(const sp<ABuffer> &ccBuf) const { ALOGI("%s", out.c_str()); } +NuPlayer::CCDecoder::CCDecoder(const sp<AMessage> ¬ify) + : mNotify(notify), + mCurrentChannel(0), + mSelectedTrack(-1) { + for (size_t i = 0; i < sizeof(mTrackIndices)/sizeof(mTrackIndices[0]); ++i) { + mTrackIndices[i] = -1; + } +} + +size_t NuPlayer::CCDecoder::getTrackCount() const { + return mFoundChannels.size(); +} + +sp<AMessage> NuPlayer::CCDecoder::getTrackInfo(size_t index) const { + if (!isTrackValid(index)) { + return NULL; + } + + sp<AMessage> format = new AMessage(); + + format->setInt32("type", MEDIA_TRACK_TYPE_SUBTITLE); + format->setString("language", "und"); + format->setString("mime", MEDIA_MIMETYPE_TEXT_CEA_608); + //CC1, field 0 channel 0 + bool isDefaultAuto = (mFoundChannels[index] == 0); + format->setInt32("auto", isDefaultAuto); + format->setInt32("default", isDefaultAuto); + format->setInt32("forced", 0); + + return format; +} + +status_t NuPlayer::CCDecoder::selectTrack(size_t index, bool select) { + if (!isTrackValid(index)) { + return BAD_VALUE; + } + + if (select) { + if (mSelectedTrack == (ssize_t)index) { + ALOGE("track %zu already selected", index); + return BAD_VALUE; + } + ALOGV("selected track %zu", index); + mSelectedTrack = index; + } else { + if (mSelectedTrack != (ssize_t)index) { + ALOGE("track %zu is not selected", index); + return BAD_VALUE; + } + ALOGV("unselected track %zu", index); + mSelectedTrack = -1; + } + + return OK; +} + +bool NuPlayer::CCDecoder::isSelected() const { + return mSelectedTrack >= 0 && mSelectedTrack < (int32_t) getTrackCount(); +} + +bool NuPlayer::CCDecoder::isTrackValid(size_t index) const { + return index < getTrackCount(); +} + +int32_t NuPlayer::CCDecoder::getTrackIndex(size_t channel) const { + if (channel < sizeof(mTrackIndices)/sizeof(mTrackIndices[0])) { + return mTrackIndices[channel]; + } + return -1; +} + +// returns true if a new CC track is found bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) { int64_t timeUs; CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); @@ -801,7 +891,7 @@ bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) { return false; } - bool hasCC = false; + bool trackAdded = false; NALBitReader br(sei->data() + 1, sei->size() - 1); // sei_message() @@ -836,8 +926,6 @@ bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) { && itu_t_t35_provider_code == 0x0031 && user_identifier == 'GA94' && user_data_type_code == 0x3) { - hasCC = true; - // MPEG_cc_data() // ATSC A/53 Part 4: 6.2.3.1 br.skipBits(1); //process_em_data_flag @@ -867,6 +955,12 @@ bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) { && (cc_type == 0 || cc_type == 1)) { CCData cc(cc_type, cc_data_1, cc_data_2); if (!isNullPad(&cc)) { + size_t channel; + if (cc.getChannel(&channel) && getTrackIndex(channel) < 0) { + mTrackIndices[channel] = mFoundChannels.size(); + mFoundChannels.push_back(channel); + trackAdded = true; + } memcpy(ccBuf->data() + ccBuf->size(), (void *)&cc, sizeof(cc)); ccBuf->setRange(0, ccBuf->size() + sizeof(CCData)); @@ -889,13 +983,33 @@ bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) { br.skipBits(payload_size * 8); } - return hasCC; + return trackAdded; } -void NuPlayer::CCDecoder::decode(const sp<ABuffer> &accessUnit) { - if (extractFromSEI(accessUnit) && mTrackCount == 0) { - mTrackCount++; +sp<ABuffer> NuPlayer::CCDecoder::filterCCBuf( + const sp<ABuffer> &ccBuf, size_t index) { + sp<ABuffer> filteredCCBuf = new ABuffer(ccBuf->size()); + filteredCCBuf->setRange(0, 0); + + size_t cc_count = ccBuf->size() / sizeof(CCData); + const CCData* cc_data = (const CCData*)ccBuf->data(); + for (size_t i = 0; i < cc_count; ++i) { + size_t channel; + if (cc_data[i].getChannel(&channel)) { + mCurrentChannel = channel; + } + if (mCurrentChannel == mFoundChannels[index]) { + memcpy(filteredCCBuf->data() + filteredCCBuf->size(), + (void *)&cc_data[i], sizeof(CCData)); + filteredCCBuf->setRange(0, filteredCCBuf->size() + sizeof(CCData)); + } + } + return filteredCCBuf; +} + +void NuPlayer::CCDecoder::decode(const sp<ABuffer> &accessUnit) { + if (extractFromSEI(accessUnit)) { ALOGI("Found CEA-608 track"); sp<AMessage> msg = mNotify->dup(); msg->setInt32("what", kWhatTrackAdded); @@ -905,13 +1019,18 @@ void NuPlayer::CCDecoder::decode(const sp<ABuffer> &accessUnit) { } void NuPlayer::CCDecoder::display(int64_t timeUs) { + if (!isTrackValid(mSelectedTrack)) { + ALOGE("Could not find current track(index=%d)", mSelectedTrack); + return; + } + ssize_t index = mCCMap.indexOfKey(timeUs); if (index < 0) { ALOGV("cc for timestamp %" PRId64 " not found", timeUs); return; } - sp<ABuffer> &ccBuf = mCCMap.editValueAt(index); + sp<ABuffer> ccBuf = filterCCBuf(mCCMap.valueAt(index), mSelectedTrack); if (ccBuf->size() > 0) { #if 0 @@ -932,5 +1051,9 @@ void NuPlayer::CCDecoder::display(int64_t timeUs) { mCCMap.removeItemsAt(0, index + 1); } +void NuPlayer::CCDecoder::flush() { + mCCMap.clear(); +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index c6fc237..cc1bdff 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -36,7 +36,8 @@ struct NuPlayer::Decoder : public AHandler { virtual void init(); status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const; - virtual void signalFlush(); + virtual void signalFlush(const sp<AMessage> &format = NULL); + virtual void signalUpdateFormat(const sp<AMessage> &format); virtual void signalResume(); virtual void initiateShutdown(); @@ -67,6 +68,7 @@ private: kWhatRenderBuffer = 'rndr', kWhatFlush = 'flus', kWhatShutdown = 'shuD', + kWhatUpdateFormat = 'uFmt', }; sp<AMessage> mNotify; @@ -80,6 +82,8 @@ private: Vector<sp<ABuffer> > mInputBuffers; Vector<sp<ABuffer> > mOutputBuffers; + Vector<sp<ABuffer> > mCSDsForCurrentFormat; + Vector<sp<ABuffer> > mCSDsToSubmit; Vector<bool> mInputBufferIsDequeued; Vector<MediaBuffer *> mMediaBuffers; @@ -103,6 +107,7 @@ private: AString mComponentName; bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const; + void rememberCodecSpecificData(const sp<AMessage> &format); DISALLOW_EVIL_CONSTRUCTORS(Decoder); }; @@ -121,18 +126,20 @@ struct NuPlayer::CCDecoder : public RefBase { bool isSelected() const; void decode(const sp<ABuffer> &accessUnit); void display(int64_t timeUs); + void flush(); private: - struct CCData; - sp<AMessage> mNotify; KeyedVector<int64_t, sp<ABuffer> > mCCMap; - size_t mTrackCount; + size_t mCurrentChannel; int32_t mSelectedTrack; + int32_t mTrackIndices[4]; + Vector<size_t> mFoundChannels; - bool isNullPad(CCData *cc) const; - void dumpBytePair(const sp<ABuffer> &ccBuf) const; + bool isTrackValid(size_t index) const; + int32_t getTrackIndex(size_t channel) const; bool extractFromSEI(const sp<ABuffer> &accessUnit); + sp<ABuffer> filterCCBuf(const sp<ABuffer> &ccBuf, size_t index); DISALLOW_EVIL_CONSTRUCTORS(CCDecoder); }; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp index c9be0dd..ab7906a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp @@ -115,9 +115,12 @@ void NuPlayer::DecoderPassThrough::requestABuffer() { notify->post(); mPendingBuffers++; - sp<AMessage> message = new AMessage(kWhatRequestABuffer, id()); - message->setInt32("generation", mBufferGeneration); - message->post(); + // pending buffers will already result in requestABuffer + if (mPendingBuffers < kMaxPendingBuffers) { + sp<AMessage> message = new AMessage(kWhatRequestABuffer, id()); + message->setInt32("generation", mBufferGeneration); + message->post(); + } return; } @@ -155,9 +158,7 @@ void android::NuPlayer::DecoderPassThrough::onInputBufferFilled( void NuPlayer::DecoderPassThrough::onBufferConsumed(int32_t size) { mPendingBuffers--; mCachedBytes -= size; - sp<AMessage> message = new AMessage(kWhatRequestABuffer, id()); - message->setInt32("generation", mBufferGeneration); - message->post(); + requestABuffer(); } void NuPlayer::DecoderPassThrough::onFlush() { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 6ef0ec4..df19aef 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -290,8 +290,9 @@ status_t NuPlayerDriver::stop() { // fall through case STATE_PAUSED: + mState = STATE_STOPPED; notifyListener_l(MEDIA_STOPPED); - // fall through + break; case STATE_PREPARED: case STATE_STOPPED: @@ -317,6 +318,8 @@ status_t NuPlayerDriver::pause() { return OK; case STATE_RUNNING: + setPauseStartedTimeIfNeeded(); + mState = STATE_PAUSED; notifyListener_l(MEDIA_PAUSED); mPlayer->pause(); break; @@ -325,9 +328,6 @@ status_t NuPlayerDriver::pause() { return INVALID_OPERATION; } - setPauseStartedTimeIfNeeded(); - mState = STATE_PAUSED; - return OK; } @@ -571,8 +571,10 @@ void NuPlayerDriver::notifyDuration(int64_t durationUs) { void NuPlayerDriver::notifyPosition(int64_t positionUs) { Mutex::Autolock autoLock(mLock); - mPositionUs = positionUs; - mNotifyTimeRealUs = ALooper::GetNowUs(); + if (isPlaying()) { + mPositionUs = positionUs; + mNotifyTimeRealUs = ALooper::GetNowUs(); + } } void NuPlayerDriver::notifySeekComplete() { @@ -635,12 +637,13 @@ void NuPlayerDriver::notifyListener_l( switch (msg) { case MEDIA_PLAYBACK_COMPLETE: { - if (mLooping) { - mLock.unlock(); - mPlayer->seekToAsync(0); - mLock.lock(); - break; - } else { + if (mState != STATE_RESET_IN_PROGRESS) { + if (mLooping) { + mPlayer->seekToAsync(0); + break; + } + + mPlayer->pause(); mState = STATE_PAUSED; } // fall through @@ -688,15 +691,17 @@ void NuPlayerDriver::notifyPrepareCompleted(status_t err) { mAsyncResult = err; if (err == OK) { + // update state before notifying client, so that if client calls back into NuPlayerDriver + // in response, NuPlayerDriver has the right state + mState = STATE_PREPARED; if (mIsAsyncPrepare) { notifyListener_l(MEDIA_PREPARED); } - mState = STATE_PREPARED; } else { + mState = STATE_UNPREPARED; if (mIsAsyncPrepare) { notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); } - mState = STATE_UNPREPARED; } mCondition.broadcast(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index a3c976d..067784b 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -58,7 +58,8 @@ NuPlayer::Renderer::Renderer( mVideoRenderingStartGeneration(0), mAudioRenderingStartGeneration(0), mLastPositionUpdateUs(-1ll), - mVideoLateByUs(0ll) { + mVideoLateByUs(0ll), + mVideoSampleReceived(false) { } NuPlayer::Renderer::~Renderer() { @@ -315,7 +316,7 @@ size_t NuPlayer::Renderer::AudioSinkCallback( size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { Mutex::Autolock autoLock(mLock); - if (!offloadingAudio()) { + if (!offloadingAudio() || mPaused) { return 0; } @@ -410,8 +411,11 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { if (entry->mBuffer == NULL) { // EOS - - notifyEOS(true /* audio */, entry->mFinalResult); + int64_t postEOSDelayUs = 0; + if (mAudioSink->needsTrailingPadding()) { + postEOSDelayUs = getAudioPendingPlayoutUs() + 1000 * mAudioSink->latency(); + } + notifyEOS(true /* audio */, entry->mFinalResult, postEOSDelayUs); mAudioQueue.erase(mAudioQueue.begin()); entry = NULL; @@ -421,26 +425,11 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { if (entry->mOffset == 0) { int64_t mediaTimeUs; CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); - ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6); - mAnchorTimeMediaUs = mediaTimeUs; - uint32_t numFramesPlayed; - CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK); - - uint32_t numFramesPendingPlayout = - mNumFramesWritten - numFramesPlayed; - - int64_t realTimeOffsetUs = - (mAudioSink->latency() / 2 /* XXX */ - + numFramesPendingPlayout - * mAudioSink->msecsPerFrame()) * 1000ll; - - // ALOGI("realTimeOffsetUs = %lld us", realTimeOffsetUs); - - mAnchorTimeRealUs = - ALooper::GetNowUs() + realTimeOffsetUs; + mAnchorTimeRealUs = ALooper::GetNowUs() + + getAudioPendingPlayoutUs() + 1000 * mAudioSink->latency() / 2; } size_t copy = entry->mBuffer->size() - entry->mOffset; @@ -494,8 +483,18 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { return !mAudioQueue.empty(); } +int64_t NuPlayer::Renderer::getAudioPendingPlayoutUs() { + uint32_t numFramesPlayed; + CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK); + + uint32_t numFramesPendingPlayout = mNumFramesWritten - numFramesPlayed; + return numFramesPendingPlayout * mAudioSink->msecsPerFrame() * 1000; +} + void NuPlayer::Renderer::postDrainVideoQueue() { - if (mDrainVideoQueuePending || mSyncQueues || mPaused) { + if (mDrainVideoQueuePending + || mSyncQueues + || (mPaused && mVideoSampleReceived)) { return; } @@ -574,16 +573,22 @@ void NuPlayer::Renderer::onDrainVideoQueue() { realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs; } - mVideoLateByUs = ALooper::GetNowUs() - realTimeUs; - bool tooLate = (mVideoLateByUs > 40000); + bool tooLate = false; + + if (!mPaused) { + mVideoLateByUs = ALooper::GetNowUs() - realTimeUs; + tooLate = (mVideoLateByUs > 40000); - if (tooLate) { - ALOGV("video late by %lld us (%.2f secs)", - mVideoLateByUs, mVideoLateByUs / 1E6); + if (tooLate) { + ALOGV("video late by %lld us (%.2f secs)", + mVideoLateByUs, mVideoLateByUs / 1E6); + } else { + ALOGV("rendering video at media time %.2f secs", + (mFlags & FLAG_REAL_TIME ? realTimeUs : + (realTimeUs + mAnchorTimeMediaUs - mAnchorTimeRealUs)) / 1E6); + } } else { - ALOGV("rendering video at media time %.2f secs", - (mFlags & FLAG_REAL_TIME ? realTimeUs : - (realTimeUs + mAnchorTimeMediaUs - mAnchorTimeRealUs)) / 1E6); + mVideoLateByUs = 0ll; } entry->mNotifyConsumed->setInt32("render", !tooLate); @@ -591,12 +596,15 @@ void NuPlayer::Renderer::onDrainVideoQueue() { mVideoQueue.erase(mVideoQueue.begin()); entry = NULL; - if (!mVideoRenderingStarted) { - mVideoRenderingStarted = true; - notifyVideoRenderingStart(); - } + mVideoSampleReceived = true; - notifyIfMediaRenderingStarted(); + if (!mPaused) { + if (!mVideoRenderingStarted) { + mVideoRenderingStarted = true; + notifyVideoRenderingStart(); + } + notifyIfMediaRenderingStarted(); + } notifyPosition(); } @@ -607,12 +615,12 @@ void NuPlayer::Renderer::notifyVideoRenderingStart() { notify->post(); } -void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult) { +void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult, int64_t delayUs) { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatEOS); notify->setInt32("audio", static_cast<int32_t>(audio)); notify->setInt32("finalResult", finalResult); - notify->post(); + notify->post(delayUs); } void NuPlayer::Renderer::notifyAudioOffloadTearDown() { @@ -795,6 +803,7 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) { prepareForMediaRenderingStart(); } + mVideoSampleReceived = false; notifyFlushComplete(audio); } @@ -884,13 +893,16 @@ void NuPlayer::Renderer::notifyPosition() { } void NuPlayer::Renderer::onPause() { - CHECK(!mPaused); - + if (mPaused) { + ALOGW("Renderer::onPause() called while already paused!"); + return; + } { Mutex::Autolock autoLock(mLock); ++mAudioQueueGeneration; ++mVideoQueueGeneration; prepareForMediaRenderingStart(); + mPaused = true; } mDrainAudioQueuePending = false; @@ -902,8 +914,6 @@ void NuPlayer::Renderer::onPause() { ALOGV("now paused audio queue has %d entries, video has %d entries", mAudioQueue.size(), mVideoQueue.size()); - - mPaused = true; } void NuPlayer::Renderer::onResume() { @@ -915,9 +925,9 @@ void NuPlayer::Renderer::onResume() { mAudioSink->start(); } + Mutex::Autolock autoLock(mLock); mPaused = false; - Mutex::Autolock autoLock(mLock); if (!mAudioQueue.empty()) { postDrainAudioQueue_l(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index 1cba1a0..5c7d2d7 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -119,6 +119,7 @@ private: bool mSyncQueues; bool mPaused; + bool mVideoSampleReceived; bool mVideoRenderingStarted; int32_t mVideoRenderingStartGeneration; int32_t mAudioRenderingStartGeneration; @@ -129,6 +130,7 @@ private: size_t fillAudioBuffer(void *buffer, size_t size); bool onDrainAudioQueue(); + int64_t getAudioPendingPlayoutUs(); void postDrainAudioQueue_l(int64_t delayUs = 0); void onDrainVideoQueue(); @@ -146,7 +148,7 @@ private: void onResume(); void onAudioOffloadTearDown(); - void notifyEOS(bool audio, status_t finalResult); + void notifyEOS(bool audio, status_t finalResult, int64_t delayUs = 0); void notifyFlushComplete(bool audio); void notifyPosition(); void notifyVideoLateBy(int64_t lateByUs); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 45657c2..7ccf3b1 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -51,6 +51,7 @@ struct NuPlayer::Source : public AHandler { kWhatSubtitleData, kWhatTimedTextData, kWhatQueueDecoderShutdown, + kWhatDrmNoLicense, }; // The provides message is used to notify the player about various diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 05676b7..0060b88 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1245,13 +1245,13 @@ status_t ACodec::configureCodec( tunneled != 0) { ALOGI("Configuring TUNNELED video playback."); - int64_t audioHwSync = 0; - if (!msg->findInt64("audio-hw-sync", &audioHwSync)) { + int32_t audioHwSync = 0; + if (!msg->findInt32("audio-hw-sync", &audioHwSync)) { ALOGW("No Audio HW Sync provided for video tunnel"); } err = configureTunneledVideoPlayback(audioHwSync, nativeWindow); if (err != OK) { - ALOGE("configureTunneledVideoPlayback(%" PRId64 ",%p) failed!", + ALOGE("configureTunneledVideoPlayback(%d,%p) failed!", audioHwSync, nativeWindow.get()); return err; } @@ -1898,7 +1898,7 @@ status_t ACodec::setupRawAudioFormat( } status_t ACodec::configureTunneledVideoPlayback( - int64_t audioHwSync, const sp<ANativeWindow> &nativeWindow) { + int32_t audioHwSync, const sp<ANativeWindow> &nativeWindow) { native_handle_t* sidebandHandle; status_t err = mOMX->configureVideoTunnelMode( @@ -2313,7 +2313,6 @@ static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) { return 0; } OMX_U32 ret = frameRate * iFramesInterval; - CHECK(ret > 1); return ret; } diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index a0d9b29..7c4f92a 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -62,6 +62,7 @@ LOCAL_SRC_FILES:= \ avc_utils.cpp \ LOCAL_C_INCLUDES:= \ + $(TOP)/frameworks/av/include/media/ \ $(TOP)/frameworks/av/include/media/stagefright/timedtext \ $(TOP)/frameworks/native/include/media/hardware \ $(TOP)/frameworks/native/include/media/openmax \ @@ -70,6 +71,8 @@ LOCAL_C_INCLUDES:= \ $(TOP)/external/openssl/include \ $(TOP)/external/libvpx/libwebm \ $(TOP)/system/netd/include \ + $(TOP)/external/icu/icu4c/source/common \ + $(TOP)/external/icu/icu4c/source/i18n \ LOCAL_SHARED_LIBRARIES := \ libbinder \ diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 91aa9ab..9d6bb9a 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -280,7 +280,20 @@ status_t MediaCodec::configure( } sp<AMessage> response; - return PostAndAwaitResponse(msg, &response); + status_t err = PostAndAwaitResponse(msg, &response); + + if (err != OK && err != INVALID_OPERATION) { + // MediaCodec now set state to UNINITIALIZED upon any fatal error. + // To maintain backward-compatibility, do a reset() to put codec + // back into INITIALIZED state. + // But don't reset if the err is INVALID_OPERATION, which means + // the configure failure is due to wrong state. + + ALOGE("configure failed with err 0x%08x, resetting...", err); + reset(); + } + + return err; } status_t MediaCodec::createInputSurface( @@ -743,13 +756,15 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { case CONFIGURING: { - setState(INITIALIZED); + setState(actionCode == ACTION_CODE_FATAL ? + UNINITIALIZED : INITIALIZED); break; } case STARTING: { - setState(CONFIGURED); + setState(actionCode == ACTION_CODE_FATAL ? + UNINITIALIZED : CONFIGURED); break; } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 78758da..a8806c8 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -994,7 +994,6 @@ static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) { return 0; } OMX_U32 ret = frameRate * iFramesInterval - 1; - CHECK(ret > 1); return ret; } diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index 8cc41e7..101fc8a 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -32,6 +32,7 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXCodec.h> #include <media/stagefright/MediaDefs.h> +#include <CharacterEncodingDetector.h> namespace android { @@ -450,32 +451,58 @@ void StagefrightMetadataRetriever::parseMetaData() { struct Map { int from; int to; + const char *name; }; static const Map kMap[] = { - { kKeyMIMEType, METADATA_KEY_MIMETYPE }, - { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER }, - { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER }, - { kKeyAlbum, METADATA_KEY_ALBUM }, - { kKeyArtist, METADATA_KEY_ARTIST }, - { kKeyAlbumArtist, METADATA_KEY_ALBUMARTIST }, - { kKeyAuthor, METADATA_KEY_AUTHOR }, - { kKeyComposer, METADATA_KEY_COMPOSER }, - { kKeyDate, METADATA_KEY_DATE }, - { kKeyGenre, METADATA_KEY_GENRE }, - { kKeyTitle, METADATA_KEY_TITLE }, - { kKeyYear, METADATA_KEY_YEAR }, - { kKeyWriter, METADATA_KEY_WRITER }, - { kKeyCompilation, METADATA_KEY_COMPILATION }, - { kKeyLocation, METADATA_KEY_LOCATION }, + { kKeyMIMEType, METADATA_KEY_MIMETYPE, NULL }, + { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER, "tracknumber" }, + { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER, "discnumber" }, + { kKeyAlbum, METADATA_KEY_ALBUM, "album" }, + { kKeyArtist, METADATA_KEY_ARTIST, "artist" }, + { kKeyAlbumArtist, METADATA_KEY_ALBUMARTIST, "albumartist" }, + { kKeyAuthor, METADATA_KEY_AUTHOR, NULL }, + { kKeyComposer, METADATA_KEY_COMPOSER, "composer" }, + { kKeyDate, METADATA_KEY_DATE, NULL }, + { kKeyGenre, METADATA_KEY_GENRE, "genre" }, + { kKeyTitle, METADATA_KEY_TITLE, "title" }, + { kKeyYear, METADATA_KEY_YEAR, "year" }, + { kKeyWriter, METADATA_KEY_WRITER, "writer" }, + { kKeyCompilation, METADATA_KEY_COMPILATION, "compilation" }, + { kKeyLocation, METADATA_KEY_LOCATION, NULL }, }; + static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]); + CharacterEncodingDetector *detector = new CharacterEncodingDetector(); + for (size_t i = 0; i < kNumMapEntries; ++i) { const char *value; if (meta->findCString(kMap[i].from, &value)) { - mMetaData.add(kMap[i].to, String8(value)); + if (kMap[i].name) { + // add to charset detector + detector->addTag(kMap[i].name, value); + } else { + // directly add to output list + mMetaData.add(kMap[i].to, String8(value)); + } + } + } + + detector->detectAndConvert(); + int size = detector->size(); + if (size) { + for (int i = 0; i < size; i++) { + const char *name; + const char *value; + detector->getTag(i, &name, &value); + for (size_t j = 0; j < kNumMapEntries; ++j) { + if (kMap[j].name && !strcmp(kMap[j].name, name)) { + mMetaData.add(kMap[j].to, String8(value)); + } + } } } + delete detector; const void *data; uint32_t type; diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp index da50c56..1fdb244 100644 --- a/media/libstagefright/TimedEventQueue.cpp +++ b/media/libstagefright/TimedEventQueue.cpp @@ -338,7 +338,7 @@ void TimedEventQueue::acquireWakeLock_l() status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK, binder, String16("TimedEventQueue"), - String16("media")); + String16("media")); // not oneway IPCThreadState::self()->restoreCallingIdentity(token); if (status == NO_ERROR) { mWakeLockToken = binder; @@ -363,7 +363,7 @@ void TimedEventQueue::releaseWakeLock_l(bool force) CHECK(mWakeLockToken != 0); if (mPowerManager != 0) { int64_t token = IPCThreadState::self()->clearCallingIdentity(); - mPowerManager->releaseWakeLock(mWakeLockToken, 0); + mPowerManager->releaseWakeLock(mWakeLockToken, 0); // not oneway IPCThreadState::self()->restoreCallingIdentity(token); } mWakeLockToken.clear(); diff --git a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp index e783540..8aff5b6 100644 --- a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp +++ b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp @@ -825,9 +825,10 @@ void SoftAVCEncoder::onQueueFilled(OMX_U32 /* portIndex */) { videoInput.coding_timestamp = (inHeader->nTimeStamp + 500) / 1000; // in ms uint8_t *inputData = NULL; if (mStoreMetaDataInBuffers) { - if (inHeader->nFilledLen != 8) { + if (inHeader->nFilledLen != (sizeof(OMX_U32) + sizeof(buffer_handle_t))) { ALOGE("MetaData buffer is wrong size! " - "(got %u bytes, expected 8)", inHeader->nFilledLen); + "(got %u bytes, expected %d)", inHeader->nFilledLen, + sizeof(OMX_U32) + sizeof(buffer_handle_t)); mSignalledError = true; notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); return; @@ -992,7 +993,7 @@ uint8_t *SoftAVCEncoder::extractGrallocData(void *data, buffer_handle_t *buffer) kMetadataBufferTypeGrallocSource, type); return NULL; } - buffer_handle_t imgBuffer = *(buffer_handle_t*)((uint8_t*)data + 4); + buffer_handle_t imgBuffer = *(buffer_handle_t*)((uint8_t*)data + sizeof(OMX_U32)); const Rect rect(mVideoWidth, mVideoHeight); uint8_t *img; diff --git a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h index 23d5ff1..cfa9ca5 100644 --- a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h +++ b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h @@ -67,10 +67,6 @@ private: kNumBuffers = 2, }; - enum { - kStoreMetaDataExtensionIndex = OMX_IndexVendorStartUnused + 1 - }; - // OMX input buffer's timestamp and flags typedef struct { int64_t mTimeUs; diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h index cc4ea8f..c59a1b9 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h @@ -56,10 +56,6 @@ private: kNumBuffers = 2, }; - enum { - kStoreMetaDataExtensionIndex = OMX_IndexVendorStartUnused + 1 - }; - // OMX input buffer's timestamp and flags typedef struct { int64_t mTimeUs; diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index 423a057..2f63bdd 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -23,9 +23,6 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/MediaDefs.h> -#include "vpx/vpx_decoder.h" -#include "vpx/vpx_codec.h" -#include "vpx/vp8dx.h" namespace android { @@ -41,7 +38,8 @@ SoftVPX::SoftVPX( NULL /* profileLevels */, 0 /* numProfileLevels */, 320 /* width */, 240 /* height */, callbacks, appData, component), mMode(codingType == OMX_VIDEO_CodingVP8 ? MODE_VP8 : MODE_VP9), - mCtx(NULL) { + mCtx(NULL), + mImg(NULL) { initPorts(kNumBuffers, 768 * 1024 /* inputBufferSize */, kNumBuffers, codingType == OMX_VIDEO_CodingVP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9); @@ -118,36 +116,50 @@ void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { } } - if (vpx_codec_decode( - (vpx_codec_ctx_t *)mCtx, - inHeader->pBuffer + inHeader->nOffset, - inHeader->nFilledLen, - NULL, - 0)) { - ALOGE("on2 decoder failed to decode frame."); + if (mImg == NULL) { + if (vpx_codec_decode( + (vpx_codec_ctx_t *)mCtx, + inHeader->pBuffer + inHeader->nOffset, + inHeader->nFilledLen, + NULL, + 0)) { + ALOGE("on2 decoder failed to decode frame."); - notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); - return; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + vpx_codec_iter_t iter = NULL; + mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); } - vpx_codec_iter_t iter = NULL; - vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); - - if (img != NULL) { - CHECK_EQ(img->fmt, IMG_FMT_I420); + if (mImg != NULL) { + CHECK_EQ(mImg->fmt, IMG_FMT_I420); - uint32_t width = img->d_w; - uint32_t height = img->d_h; + uint32_t width = mImg->d_w; + uint32_t height = mImg->d_h; if (width != mWidth || height != mHeight) { mWidth = width; mHeight = height; - updatePortDefinitions(); - - notify(OMX_EventPortSettingsChanged, 1, 0, NULL); - mOutputPortSettingsChange = AWAITING_DISABLED; - return; + if (!mIsAdaptive || width > mAdaptiveMaxWidth || height > mAdaptiveMaxHeight) { + if (mIsAdaptive) { + if (width > mAdaptiveMaxWidth) { + mAdaptiveMaxWidth = width; + } + if (height > mAdaptiveMaxHeight) { + mAdaptiveMaxHeight = height; + } + } + updatePortDefinitions(); + notify(OMX_EventPortSettingsChanged, kOutputPortIndex, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } else { + updatePortDefinitions(); + notify(OMX_EventPortSettingsChanged, kOutputPortIndex, + OMX_IndexConfigCommonOutputCrop, NULL); + } } outHeader->nOffset = 0; @@ -155,31 +167,38 @@ void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0; outHeader->nTimeStamp = inHeader->nTimeStamp; - const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y]; - uint8_t *dst = outHeader->pBuffer; - for (size_t i = 0; i < img->d_h; ++i) { - memcpy(dst, srcLine, img->d_w); + uint32_t buffer_stride = mIsAdaptive ? mAdaptiveMaxWidth : mWidth; + uint32_t buffer_height = mIsAdaptive ? mAdaptiveMaxHeight : mHeight; - srcLine += img->stride[PLANE_Y]; - dst += img->d_w; + const uint8_t *srcLine = (const uint8_t *)mImg->planes[PLANE_Y]; + uint8_t *dst = outHeader->pBuffer; + for (size_t i = 0; i < buffer_height; ++i) { + if (i < mImg->d_h) { + memcpy(dst, srcLine, mImg->d_w); + srcLine += mImg->stride[PLANE_Y]; + } + dst += buffer_stride; } - srcLine = (const uint8_t *)img->planes[PLANE_U]; - for (size_t i = 0; i < img->d_h / 2; ++i) { - memcpy(dst, srcLine, img->d_w / 2); - - srcLine += img->stride[PLANE_U]; - dst += img->d_w / 2; + srcLine = (const uint8_t *)mImg->planes[PLANE_U]; + for (size_t i = 0; i < buffer_height / 2; ++i) { + if (i < mImg->d_h / 2) { + memcpy(dst, srcLine, mImg->d_w / 2); + srcLine += mImg->stride[PLANE_U]; + } + dst += buffer_stride / 2; } - srcLine = (const uint8_t *)img->planes[PLANE_V]; - for (size_t i = 0; i < img->d_h / 2; ++i) { - memcpy(dst, srcLine, img->d_w / 2); - - srcLine += img->stride[PLANE_V]; - dst += img->d_w / 2; + srcLine = (const uint8_t *)mImg->planes[PLANE_V]; + for (size_t i = 0; i < buffer_height / 2; ++i) { + if (i < mImg->d_h / 2) { + memcpy(dst, srcLine, mImg->d_w / 2); + srcLine += mImg->stride[PLANE_V]; + } + dst += buffer_stride / 2; } + mImg = NULL; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h index cd5eb28..8f68693 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.h +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h @@ -20,6 +20,10 @@ #include "SoftVideoDecoderOMXComponent.h" +#include "vpx/vpx_decoder.h" +#include "vpx/vpx_codec.h" +#include "vpx/vp8dx.h" + namespace android { struct SoftVPX : public SoftVideoDecoderOMXComponent { @@ -47,6 +51,8 @@ private: void *mCtx; + vpx_image_t *mImg; + status_t initDecoder(); DISALLOW_EVIL_CONSTRUCTORS(SoftVPX); diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index c5a83d1..5b4c954 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -91,10 +91,6 @@ protected: const char *name, OMX_INDEXTYPE *index); private: - enum { - kStoreMetaDataExtensionIndex = OMX_IndexVendorStartUnused + 1, - }; - enum TemporalReferences { // For 1 layer case: reference all (last, golden, and alt ref), but only // update last. diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp index ab137b6..ac2b103 100644 --- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp +++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp @@ -122,6 +122,7 @@ void SoftwareRenderer::resetFormatIfChanged(const sp<AMessage> &format) { break; } case OMX_COLOR_Format32bitARGB8888: + case OMX_COLOR_Format32BitRGBA8888: { halFormat = HAL_PIXEL_FORMAT_RGBA_8888; bufWidth = (mCropWidth + 1) & ~1; @@ -308,6 +309,15 @@ void SoftwareRenderer::render( *dstPtr++ = a; // alpha last (ARGB to RGBA) } } + } else if (mColorFormat == OMX_COLOR_Format32BitRGBA8888) { + uint8_t* srcPtr = (uint8_t*)data; + uint8_t* dstPtr = (uint8_t*)dst; + + for (size_t y = 0; y < (size_t)mCropHeight; ++y) { + memcpy(dstPtr, srcPtr, mCropWidth * 4); + srcPtr += mWidth * 4; + dstPtr += buf->stride * 4; + } } else { LOG_ALWAYS_FATAL("bad color format %#x", mColorFormat); } diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml index 9b930bc..c97be28 100644 --- a/media/libstagefright/data/media_codecs_google_video.xml +++ b/media/libstagefright/data/media_codecs_google_video.xml @@ -16,18 +16,89 @@ <Included> <Decoders> - <MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es" /> - <MediaCodec name="OMX.google.h263.decoder" type="video/3gpp" /> - <MediaCodec name="OMX.google.h264.decoder" type="video/avc" /> - <MediaCodec name="OMX.google.hevc.decoder" type="video/hevc" /> - <MediaCodec name="OMX.google.vp8.decoder" type="video/x-vnd.on2.vp8" /> - <MediaCodec name="OMX.google.vp9.decoder" type="video/x-vnd.on2.vp9" /> + <MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es"> + <!-- profiles and levels: ProfileSimple : Level3 --> + <Limit name="size" min="2x2" max="352x288" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="blocks-per-second" range="12-11880" /> + <Limit name="bitrate" range="1-384000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + <MediaCodec name="OMX.google.h263.decoder" type="video/3gpp"> + <!-- profiles and levels: ProfileBaseline : Level30, ProfileBaseline : Level45 + ProfileISWV2 : Level30, ProfileISWV2 : Level45 --> + <Limit name="size" min="2x2" max="352x288" /> + <Limit name="alignment" value="2x2" /> + <Limit name="bitrate" range="1-384000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + <MediaCodec name="OMX.google.h264.decoder" type="video/avc"> + <!-- profiles and levels: ProfileBaseline : Level51 --> + <Limit name="size" min="2x2" max="2048x2048" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="blocks-per-second" range="1-983040" /> + <Limit name="bitrate" range="1-40000000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + <MediaCodec name="OMX.google.hevc.decoder" type="video/hevc"> + <!-- profiles and levels: ProfileMain : MainTierLevel51 --> + <Limit name="size" min="2x2" max="2048x2048" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="8x8" /> + <Limit name="block-count" range="1-139264" /> + <Limit name="blocks-per-second" range="1-2000000" /> + <Limit name="bitrate" range="1-10000000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + <MediaCodec name="OMX.google.vp8.decoder" type="video/x-vnd.on2.vp8"> + <Limit name="size" min="2x2" max="2048x2048" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="blocks-per-second" range="1-1000000" /> + <Limit name="bitrate" range="1-40000000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> + <MediaCodec name="OMX.google.vp9.decoder" type="video/x-vnd.on2.vp9"> + <Limit name="size" min="2x2" max="2048x2048" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="blocks-per-second" range="1-500000" /> + <Limit name="bitrate" range="1-40000000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> </Decoders> <Encoders> - <MediaCodec name="OMX.google.h263.encoder" type="video/3gpp" /> - <MediaCodec name="OMX.google.h264.encoder" type="video/avc" /> - <MediaCodec name="OMX.google.mpeg4.encoder" type="video/mp4v-es" /> - <MediaCodec name="OMX.google.vp8.encoder" type="video/x-vnd.on2.vp8" /> + <MediaCodec name="OMX.google.h263.encoder" type="video/3gpp"> + <!-- profiles and levels: ProfileBaseline : Level45 --> + <Limit name="size" min="2x2" max="176x144" /> + <Limit name="alignment" value="2x2" /> + <Limit name="bitrate" range="1-128000" /> + </MediaCodec> + <MediaCodec name="OMX.google.h264.encoder" type="video/avc"> + <!-- profiles and levels: ProfileBaseline : Level2 --> + <Limit name="size" min="2x2" max="896x896" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="blocks-per-second" range="1-11880" /> + <Limit name="bitrate" range="1-2000000" /> + </MediaCodec> + <MediaCodec name="OMX.google.mpeg4.encoder" type="video/mp4v-es"> + <!-- profiles and levels: ProfileCore : Level2 --> + <Limit name="size" min="2x2" max="176x144" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="blocks-per-second" range="12-1485" /> + <Limit name="bitrate" range="1-64000" /> + </MediaCodec> + <MediaCodec name="OMX.google.vp8.encoder" type="video/x-vnd.on2.vp8"> + <!-- profiles and levels: ProfileMain : Level_Version0-3 --> + <Limit name="size" min="2x2" max="2048x2048" /> + <Limit name="alignment" value="2x2" /> + <Limit name="bitrate" range="1-40000000" /> + <Feature name="bitrate-modes" value="VBR,CBR" /> + </MediaCodec> </Encoders> </Included> diff --git a/media/libstagefright/filters/Android.mk b/media/libstagefright/filters/Android.mk index ce18827..5e895e1 100644 --- a/media/libstagefright/filters/Android.mk +++ b/media/libstagefright/filters/Android.mk @@ -5,10 +5,13 @@ LOCAL_NDK_STL_VARIANT := stlport_static LOCAL_SRC_FILES := \ ColorConvert.cpp \ + GraphicBufferListener.cpp \ IntrinsicBlurFilter.cpp \ MediaFilter.cpp \ + RSFilter.cpp \ SaturationFilter.cpp \ saturationARGB.rs \ + SimpleFilter.cpp \ ZeroFilter.cpp LOCAL_C_INCLUDES := \ diff --git a/media/libstagefright/filters/ColorConvert.cpp b/media/libstagefright/filters/ColorConvert.cpp index b2afdcc..a5039f9 100644 --- a/media/libstagefright/filters/ColorConvert.cpp +++ b/media/libstagefright/filters/ColorConvert.cpp @@ -88,4 +88,24 @@ void convertYUV420spToRGB888( } } +// HACK - not even slightly optimized +// TODO: remove when RGBA support is added to SoftwareRenderer +void convertRGBAToARGB( + uint8_t *src, int32_t width, int32_t height, uint32_t stride, + uint8_t *dest) { + for (size_t i = 0; i < height; ++i) { + for (size_t j = 0; j < width; ++j) { + uint8_t r = *src++; + uint8_t g = *src++; + uint8_t b = *src++; + uint8_t a = *src++; + *dest++ = a; + *dest++ = r; + *dest++ = g; + *dest++ = b; + } + src += (stride - width) * 4; + } +} + } // namespace android diff --git a/media/libstagefright/filters/ColorConvert.h b/media/libstagefright/filters/ColorConvert.h index 16519f1..13faa02 100644 --- a/media/libstagefright/filters/ColorConvert.h +++ b/media/libstagefright/filters/ColorConvert.h @@ -33,6 +33,11 @@ void convertYUV420spToRGB888( uint8_t *pY, uint8_t *pUV, int32_t width, int32_t height, uint8_t *dest); +// TODO: remove when RGBA support is added to SoftwareRenderer +void convertRGBAToARGB( + uint8_t *src, int32_t width, int32_t height, uint32_t stride, + uint8_t *dest); + } // namespace android #endif // COLOR_CONVERT_H_ diff --git a/media/libstagefright/filters/GraphicBufferListener.cpp b/media/libstagefright/filters/GraphicBufferListener.cpp new file mode 100644 index 0000000..e493137 --- /dev/null +++ b/media/libstagefright/filters/GraphicBufferListener.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2014 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "GraphicBufferListener" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaErrors.h> + +#include "GraphicBufferListener.h" + +namespace android { + +status_t GraphicBufferListener::init( + const sp<AMessage> ¬ify, + size_t bufferWidth, size_t bufferHeight, size_t bufferCount) { + mNotify = notify; + + String8 name("GraphicBufferListener"); + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + mConsumer->setConsumerName(name); + mConsumer->setDefaultBufferSize(bufferWidth, bufferHeight); + mConsumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN); + + status_t err = mConsumer->setMaxAcquiredBufferCount(bufferCount); + if (err != NO_ERROR) { + ALOGE("Unable to set BQ max acquired buffer count to %u: %d", + bufferCount, err); + return err; + } + + wp<BufferQueue::ConsumerListener> listener = + static_cast<BufferQueue::ConsumerListener*>(this); + sp<BufferQueue::ProxyConsumerListener> proxy = + new BufferQueue::ProxyConsumerListener(listener); + + err = mConsumer->consumerConnect(proxy, false); + if (err != NO_ERROR) { + ALOGE("Error connecting to BufferQueue: %s (%d)", + strerror(-err), err); + return err; + } + + ALOGV("init() successful."); + + return OK; +} + +void GraphicBufferListener::onFrameAvailable() { + ALOGV("onFrameAvailable() called"); + + { + Mutex::Autolock autoLock(mMutex); + mNumFramesAvailable++; + } + + sp<AMessage> notify = mNotify->dup(); + mNotify->setWhat(kWhatFrameAvailable); + mNotify->post(); +} + +void GraphicBufferListener::onBuffersReleased() { + ALOGV("onBuffersReleased() called"); + // nothing to do +} + +void GraphicBufferListener::onSidebandStreamChanged() { + ALOGW("GraphicBufferListener cannot consume sideband streams."); + // nothing to do +} + +BufferQueue::BufferItem GraphicBufferListener::getBufferItem() { + BufferQueue::BufferItem item; + + { + Mutex::Autolock autoLock(mMutex); + if (mNumFramesAvailable <= 0) { + ALOGE("getBuffer() called with no frames available"); + return item; + } + mNumFramesAvailable--; + } + + status_t err = mConsumer->acquireBuffer(&item, 0); + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // shouldn't happen, since we track num frames available + ALOGE("frame was not available"); + item.mBuf = -1; + return item; + } else if (err != OK) { + ALOGE("acquireBuffer returned err=%d", err); + item.mBuf = -1; + return item; + } + + // Wait for it to become available. + err = item.mFence->waitForever("GraphicBufferListener::getBufferItem"); + if (err != OK) { + ALOGW("failed to wait for buffer fence: %d", err); + // keep going + } + + // If this is the first time we're seeing this buffer, add it to our + // slot table. + if (item.mGraphicBuffer != NULL) { + ALOGV("setting mBufferSlot %d", item.mBuf); + mBufferSlot[item.mBuf] = item.mGraphicBuffer; + } + + return item; +} + +sp<GraphicBuffer> GraphicBufferListener::getBuffer( + BufferQueue::BufferItem item) { + sp<GraphicBuffer> buf; + if (item.mBuf < 0 || item.mBuf >= BufferQueue::NUM_BUFFER_SLOTS) { + ALOGE("getBuffer() received invalid BufferItem: mBuf==%d", item.mBuf); + return buf; + } + + buf = mBufferSlot[item.mBuf]; + CHECK(buf.get() != NULL); + + return buf; +} + +status_t GraphicBufferListener::releaseBuffer( + BufferQueue::BufferItem item) { + if (item.mBuf < 0 || item.mBuf >= BufferQueue::NUM_BUFFER_SLOTS) { + ALOGE("getBuffer() received invalid BufferItem: mBuf==%d", item.mBuf); + return ERROR_OUT_OF_RANGE; + } + + mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + + return OK; +} + +} // namespace android diff --git a/media/libstagefright/filters/GraphicBufferListener.h b/media/libstagefright/filters/GraphicBufferListener.h new file mode 100644 index 0000000..aefac0d --- /dev/null +++ b/media/libstagefright/filters/GraphicBufferListener.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 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 GRAPHIC_BUFFER_LISTENER_H_ +#define GRAPHIC_BUFFER_LISTENER_H_ + +#include <gui/BufferQueue.h> + +namespace android { + +struct AMessage; + +struct GraphicBufferListener : public BufferQueue::ConsumerListener { +public: + GraphicBufferListener() {}; + + status_t init( + const sp<AMessage> ¬ify, + size_t bufferWidth, size_t bufferHeight, size_t bufferCount); + + virtual void onFrameAvailable(); + virtual void onBuffersReleased(); + virtual void onSidebandStreamChanged(); + + // Returns the handle to the producer side of the BufferQueue. Buffers + // queued on this will be received by GraphicBufferListener. + sp<IGraphicBufferProducer> getIGraphicBufferProducer() const { + return mProducer; + } + + BufferQueue::BufferItem getBufferItem(); + sp<GraphicBuffer> getBuffer(BufferQueue::BufferItem item); + status_t releaseBuffer(BufferQueue::BufferItem item); + + enum { + kWhatFrameAvailable = 'frav', + }; + +private: + sp<AMessage> mNotify; + size_t mNumFramesAvailable; + + mutable Mutex mMutex; + + // Our BufferQueue interfaces. mProducer is passed to the producer through + // getIGraphicBufferProducer, and mConsumer is used internally to retrieve + // the buffers queued by the producer. + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; + + // Cache of GraphicBuffers from the buffer queue. + sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS]; +}; + +} // namespace android + +#endif // GRAPHIC_BUFFER_LISTENER_H diff --git a/media/libstagefright/filters/IntrinsicBlurFilter.cpp b/media/libstagefright/filters/IntrinsicBlurFilter.cpp index cca97ef..cbcf699 100644 --- a/media/libstagefright/filters/IntrinsicBlurFilter.cpp +++ b/media/libstagefright/filters/IntrinsicBlurFilter.cpp @@ -27,13 +27,25 @@ namespace android { +status_t IntrinsicBlurFilter::configure(const sp<AMessage> &msg) { + status_t err = SimpleFilter::configure(msg); + if (err != OK) { + return err; + } + + if (!msg->findString("cacheDir", &mCacheDir)) { + ALOGE("Failed to find cache directory in config message."); + return NAME_NOT_FOUND; + } + + return OK; +} + status_t IntrinsicBlurFilter::start() { // TODO: use a single RS context object for entire application mRS = new RSC::RS(); - // only legitimate because this is a standalone executable - // TODO: do we need to dynamically determine the cache directory? - if (!mRS->init("/system/bin")) { + if (!mRS->init(mCacheDir.c_str())) { ALOGE("Failed to initialize RenderScript context."); return NO_INIT; } diff --git a/media/libstagefright/filters/IntrinsicBlurFilter.h b/media/libstagefright/filters/IntrinsicBlurFilter.h index b88e2c2..4707ab7 100644 --- a/media/libstagefright/filters/IntrinsicBlurFilter.h +++ b/media/libstagefright/filters/IntrinsicBlurFilter.h @@ -26,6 +26,7 @@ struct IntrinsicBlurFilter : public SimpleFilter { public: IntrinsicBlurFilter() : mBlurRadius(1.f) {}; + virtual status_t configure(const sp<AMessage> &msg); virtual status_t start(); virtual void reset(); virtual status_t setParameters(const sp<AMessage> &msg); @@ -36,6 +37,7 @@ protected: virtual ~IntrinsicBlurFilter() {}; private: + AString mCacheDir; RSC::sp<RSC::RS> mRS; RSC::sp<RSC::Allocation> mAllocIn; RSC::sp<RSC::Allocation> mAllocOut; diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp index 89934d4..c5289b6 100644 --- a/media/libstagefright/filters/MediaFilter.cpp +++ b/media/libstagefright/filters/MediaFilter.cpp @@ -22,6 +22,7 @@ #include <binder/MemoryDealer.h> +#include <media/stagefright/BufferProducerWrapper.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> @@ -30,7 +31,10 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaFilter.h> +#include "ColorConvert.h" +#include "GraphicBufferListener.h" #include "IntrinsicBlurFilter.h" +#include "RSFilter.h" #include "SaturationFilter.h" #include "ZeroFilter.h" @@ -41,7 +45,8 @@ static const size_t kBufferCountActual = 4; MediaFilter::MediaFilter() : mState(UNINITIALIZED), - mGeneration(0) { + mGeneration(0), + mGraphicBufferListener(NULL) { } MediaFilter::~MediaFilter() { @@ -154,6 +159,21 @@ void MediaFilter::onMessageReceived(const sp<AMessage> &msg) { onSetParameters(msg); break; } + case kWhatCreateInputSurface: + { + onCreateInputSurface(); + break; + } + case GraphicBufferListener::kWhatFrameAvailable: + { + onInputFrameAvailable(); + break; + } + case kWhatSignalEndOfInputStream: + { + onSignalEndOfInputStream(); + break; + } default: { ALOGE("Message not handled:\n%s", msg->debugString().c_str()); @@ -402,7 +422,11 @@ void MediaFilter::processBuffers() { inputInfo->mBufferID, inputInfo->mData->size(), outputInfo->mBufferID, outputInfo->mData->size()); - postFillThisBuffer(inputInfo); + if (mGraphicBufferListener != NULL) { + delete inputInfo; + } else { + postFillThisBuffer(inputInfo); + } postDrainThisBuffer(outputInfo); // prevent any corner case where buffers could get stuck in queue @@ -420,6 +444,8 @@ void MediaFilter::onAllocateComponent(const sp<AMessage> &msg) { mFilter = new SaturationFilter; } else if (!strcasecmp(name, "android.filter.intrinsicblur")) { mFilter = new IntrinsicBlurFilter; + } else if (!strcasecmp(name, "android.filter.RenderScript")) { + mFilter = new RSFilter; } else { ALOGE("Unrecognized filter name: %s", name); signalError(NAME_NOT_FOUND); @@ -468,13 +494,21 @@ void MediaFilter::onConfigureComponent(const sp<AMessage> &msg) { if (!msg->findInt32("color-format", &mColorFormatIn)) { // default to OMX_COLOR_Format32bitARGB8888 mColorFormatIn = OMX_COLOR_Format32bitARGB8888; + msg->setInt32("color-format", mColorFormatIn); } mColorFormatOut = mColorFormatIn; + mMaxOutputSize = mWidth * mHeight * 4; // room for ARGB8888 + AString cacheDir; + if (!msg->findString("cacheDir", &cacheDir)) { + ALOGE("Failed to find cache directory in config message."); + signalError(NAME_NOT_FOUND); + return; + } + status_t err; - err = mFilter->configure( - mWidth, mHeight, mStride, mSliceHeight, mColorFormatIn); + err = mFilter->configure(msg); if (err != (status_t)OK) { ALOGE("Failed to configure filter component, err %d", err); signalError(err); @@ -600,13 +634,12 @@ void MediaFilter::onInputBufferFilled(const sp<AMessage> &msg) { mInputEOSResult = err; } - ALOGV("Handled kWhatInputBufferFilled. [ID %u]", - bufferID); + ALOGV("Handled kWhatInputBufferFilled. [ID %u]", bufferID); } void MediaFilter::onOutputBufferDrained(const sp<AMessage> &msg) { IOMX::buffer_id bufferID; - CHECK(msg->findInt32("buffer-id",(int32_t*) &bufferID)); + CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID)); BufferInfo *info = findBufferByID(kPortIndexOutput, bufferID); if (mState != STARTED) { @@ -690,4 +723,94 @@ void MediaFilter::onSetParameters(const sp<AMessage> &msg) { } } +void MediaFilter::onCreateInputSurface() { + CHECK(mState == CONFIGURED); + + mGraphicBufferListener = new GraphicBufferListener; + + sp<AMessage> notify = new AMessage(); + notify->setTarget(id()); + status_t err = mGraphicBufferListener->init( + notify, mStride, mSliceHeight, kBufferCountActual); + + if (err != OK) { + ALOGE("Failed to init mGraphicBufferListener: %d", err); + signalError(err); + return; + } + + sp<AMessage> reply = mNotify->dup(); + reply->setInt32("what", CodecBase::kWhatInputSurfaceCreated); + reply->setObject( + "input-surface", + new BufferProducerWrapper( + mGraphicBufferListener->getIGraphicBufferProducer())); + reply->post(); +} + +void MediaFilter::onInputFrameAvailable() { + BufferQueue::BufferItem item = mGraphicBufferListener->getBufferItem(); + sp<GraphicBuffer> buf = mGraphicBufferListener->getBuffer(item); + + // get pointer to graphic buffer + void* bufPtr; + buf->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &bufPtr); + + // HACK - there is no OMX_COLOR_FORMATTYPE value for RGBA, so the format + // conversion is hardcoded until we add this. + // TODO: check input format and convert only if necessary + // copy RGBA graphic buffer into temporary ARGB input buffer + BufferInfo *inputInfo = new BufferInfo; + inputInfo->mData = new ABuffer(buf->getWidth() * buf->getHeight() * 4); + ALOGV("Copying surface data into temp buffer."); + convertRGBAToARGB( + (uint8_t*)bufPtr, buf->getWidth(), buf->getHeight(), + buf->getStride(), inputInfo->mData->data()); + inputInfo->mBufferID = item.mBuf; + inputInfo->mGeneration = mGeneration; + inputInfo->mOutputFlags = 0; + inputInfo->mStatus = BufferInfo::OWNED_BY_US; + inputInfo->mData->meta()->setInt64("timeUs", item.mTimestamp / 1000); + + mAvailableInputBuffers.push_back(inputInfo); + + mGraphicBufferListener->releaseBuffer(item); + + signalProcessBuffers(); +} + +void MediaFilter::onSignalEndOfInputStream() { + // if using input surface, need to send an EOS output buffer + if (mGraphicBufferListener != NULL) { + Vector<BufferInfo> *outputBufs = &mBuffers[kPortIndexOutput]; + BufferInfo* eosBuf; + bool foundBuf = false; + for (size_t i = 0; i < kBufferCountActual; i++) { + eosBuf = &outputBufs->editItemAt(i); + if (eosBuf->mStatus == BufferInfo::OWNED_BY_US) { + foundBuf = true; + break; + } + } + + if (!foundBuf) { + ALOGE("onSignalEndOfInputStream failed to find an output buffer"); + return; + } + + eosBuf->mOutputFlags = OMX_BUFFERFLAG_EOS; + eosBuf->mGeneration = mGeneration; + eosBuf->mData->setRange(0, 0); + postDrainThisBuffer(eosBuf); + ALOGV("Posted EOS on output buffer %zu", eosBuf->mBufferID); + } + + mPortEOS[kPortIndexOutput] = true; + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", CodecBase::kWhatSignaledInputEOS); + notify->post(); + + ALOGV("Output stream saw EOS."); +} + } // namespace android diff --git a/media/libstagefright/filters/RSFilter.cpp b/media/libstagefright/filters/RSFilter.cpp new file mode 100644 index 0000000..b569945 --- /dev/null +++ b/media/libstagefright/filters/RSFilter.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "RSFilter" + +#include <utils/Log.h> + +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> + +#include "RSFilter.h" + +namespace android { + +RSFilter::RSFilter() { + +} + +RSFilter::~RSFilter() { + +} + +status_t RSFilter::configure(const sp<AMessage> &msg) { + status_t err = SimpleFilter::configure(msg); + if (err != OK) { + return err; + } + + if (!msg->findString("cacheDir", &mCacheDir)) { + ALOGE("Failed to find cache directory in config message."); + return NAME_NOT_FOUND; + } + + sp<RenderScriptWrapper> wrapper; + if (!msg->findObject("rs-wrapper", (sp<RefBase>*)&wrapper)) { + ALOGE("Failed to find RenderScriptWrapper in config message."); + return NAME_NOT_FOUND; + } + + mRS = wrapper->mContext; + mCallback = wrapper->mCallback; + + return OK; +} + +status_t RSFilter::start() { + // 32-bit elements for ARGB8888 + RSC::sp<const RSC::Element> e = RSC::Element::U8_4(mRS); + + RSC::Type::Builder tb(mRS, e); + tb.setX(mWidth); + tb.setY(mHeight); + RSC::sp<const RSC::Type> t = tb.create(); + + mAllocIn = RSC::Allocation::createTyped(mRS, t); + mAllocOut = RSC::Allocation::createTyped(mRS, t); + + return OK; +} + +void RSFilter::reset() { + mCallback.clear(); + mAllocOut.clear(); + mAllocIn.clear(); + mRS.clear(); +} + +status_t RSFilter::setParameters(const sp<AMessage> &msg) { + return mCallback->handleSetParameters(msg); +} + +status_t RSFilter::processBuffers( + const sp<ABuffer> &srcBuffer, const sp<ABuffer> &outBuffer) { + mAllocIn->copy1DRangeFrom(0, mWidth * mHeight, srcBuffer->data()); + mCallback->processBuffers(mAllocIn.get(), mAllocOut.get()); + mAllocOut->copy1DRangeTo(0, mWidth * mHeight, outBuffer->data()); + + return OK; +} + +} // namespace android diff --git a/media/libstagefright/filters/RSFilter.h b/media/libstagefright/filters/RSFilter.h new file mode 100644 index 0000000..c5b5074 --- /dev/null +++ b/media/libstagefright/filters/RSFilter.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 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 RS_FILTER_H_ +#define RS_FILTER_H_ + +#include <media/stagefright/RenderScriptWrapper.h> +#include <RenderScript.h> + +#include "SimpleFilter.h" + +namespace android { + +struct AString; + +struct RSFilter : public SimpleFilter { +public: + RSFilter(); + + virtual status_t configure(const sp<AMessage> &msg); + virtual status_t start(); + virtual void reset(); + virtual status_t setParameters(const sp<AMessage> &msg); + virtual status_t processBuffers( + const sp<ABuffer> &srcBuffer, const sp<ABuffer> &outBuffer); + +protected: + virtual ~RSFilter(); + +private: + AString mCacheDir; + sp<RenderScriptWrapper::RSFilterCallback> mCallback; + RSC::sp<RSC::RS> mRS; + RSC::sp<RSC::Allocation> mAllocIn; + RSC::sp<RSC::Allocation> mAllocOut; +}; + +} // namespace android + +#endif // RS_FILTER_H_ diff --git a/media/libstagefright/filters/SaturationFilter.cpp b/media/libstagefright/filters/SaturationFilter.cpp index 1963c20..ba5f75a 100644 --- a/media/libstagefright/filters/SaturationFilter.cpp +++ b/media/libstagefright/filters/SaturationFilter.cpp @@ -27,13 +27,25 @@ namespace android { +status_t SaturationFilter::configure(const sp<AMessage> &msg) { + status_t err = SimpleFilter::configure(msg); + if (err != OK) { + return err; + } + + if (!msg->findString("cacheDir", &mCacheDir)) { + ALOGE("Failed to find cache directory in config message."); + return NAME_NOT_FOUND; + } + + return OK; +} + status_t SaturationFilter::start() { // TODO: use a single RS context object for entire application mRS = new RSC::RS(); - // only legitimate because this is a standalone executable - // TODO: do we need to dynamically determine the cache directory? - if (!mRS->init("/system/bin")) { + if (!mRS->init(mCacheDir.c_str())) { ALOGE("Failed to initialize RenderScript context."); return NO_INIT; } diff --git a/media/libstagefright/filters/SaturationFilter.h b/media/libstagefright/filters/SaturationFilter.h index fe1c35f..0545021 100644 --- a/media/libstagefright/filters/SaturationFilter.h +++ b/media/libstagefright/filters/SaturationFilter.h @@ -28,6 +28,7 @@ struct SaturationFilter : public SimpleFilter { public: SaturationFilter() : mSaturation(1.f) {}; + virtual status_t configure(const sp<AMessage> &msg); virtual status_t start(); virtual void reset(); virtual status_t setParameters(const sp<AMessage> &msg); @@ -38,6 +39,7 @@ protected: virtual ~SaturationFilter() {}; private: + AString mCacheDir; RSC::sp<RSC::RS> mRS; RSC::sp<RSC::Allocation> mAllocIn; RSC::sp<RSC::Allocation> mAllocOut; diff --git a/media/libstagefright/filters/SimpleFilter.cpp b/media/libstagefright/filters/SimpleFilter.cpp new file mode 100644 index 0000000..6c1ca2c --- /dev/null +++ b/media/libstagefright/filters/SimpleFilter.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 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 <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> + +#include "SimpleFilter.h" + +namespace android { + +status_t SimpleFilter::configure(const sp<AMessage> &msg) { + CHECK(msg->findInt32("width", &mWidth)); + CHECK(msg->findInt32("height", &mHeight)); + if (!msg->findInt32("stride", &mStride)) { + mStride = mWidth; + } + if (!msg->findInt32("slice-height", &mSliceHeight)) { + mSliceHeight = mHeight; + } + CHECK(msg->findInt32("color-format", &mColorFormatIn)); + mColorFormatOut = mColorFormatIn; + + return OK; +} + +} // namespace android diff --git a/media/libstagefright/filters/SimpleFilter.h b/media/libstagefright/filters/SimpleFilter.h index a99ca05..4cd37ef 100644 --- a/media/libstagefright/filters/SimpleFilter.h +++ b/media/libstagefright/filters/SimpleFilter.h @@ -31,19 +31,7 @@ public: SimpleFilter() : mWidth(0), mHeight(0), mStride(0), mSliceHeight(0), mColorFormatIn(0), mColorFormatOut(0) {}; - virtual status_t configure( - int32_t srcWidth, int32_t srcHeight, - int32_t srcStride, int32_t srcSliceHeight, - int32_t srcColorFormat) { - mWidth = srcWidth; - mHeight = srcHeight; - mStride = srcStride; - mSliceHeight = srcSliceHeight; - mColorFormatIn = srcColorFormat; - mColorFormatOut = mColorFormatIn; - - return OK; - } + virtual status_t configure(const sp<AMessage> &msg); virtual status_t start() = 0; virtual void reset() = 0; diff --git a/media/libstagefright/filters/saturationARGB.rs b/media/libstagefright/filters/saturationARGB.rs index 20cfab9..1de9dd8 100644 --- a/media/libstagefright/filters/saturationARGB.rs +++ b/media/libstagefright/filters/saturationARGB.rs @@ -24,6 +24,8 @@ const static float3 gMonoMult = {0.299f, 0.587f, 0.114f}; float gSaturation = 1.0f; void root(const uchar4 *v_in, uchar4 *v_out) { + v_out->x = v_in->x; // don't modify A + // get RGB, scale 0-255 uchar to 0-1.0 float float3 rgb = {v_in->y * 0.003921569f, v_in->z * 0.003921569f, v_in->w * 0.003921569f}; @@ -32,7 +34,6 @@ void root(const uchar4 *v_in, uchar4 *v_out) { float3 result = dot(rgb, gMonoMult); result = mix(result, rgb, gSaturation); - v_out->x = v_in->x; // don't modify A v_out->y = (uchar)clamp((result.r * 255.f + 0.5f), 0.f, 255.f); v_out->z = (uchar)clamp((result.g * 255.f + 0.5f), 0.f, 255.f); v_out->w = (uchar)clamp((result.b * 255.f + 0.5f), 0.f, 255.f); diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp index ebf9d8d..88b1c92 100644 --- a/media/libstagefright/foundation/ALooper.cpp +++ b/media/libstagefright/foundation/ALooper.cpp @@ -68,14 +68,14 @@ int64_t ALooper::GetNowUs() { ALooper::ALooper() : mRunningLocally(false) { + // clean up stale AHandlers. Doing it here instead of in the destructor avoids + // the side effect of objects being deleted from the unregister function recursively. + gLooperRoster.unregisterStaleHandlers(); } ALooper::~ALooper() { stop(); - - // Since this looper is "dead" (or as good as dead by now), - // have ALooperRoster unregister any handlers still registered for it. - gLooperRoster.unregisterStaleHandlers(); + // stale AHandlers are now cleaned up in the constructor of the next ALooper to come along } void ALooper::setName(const char *name) { diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp index 0c181ff..e0dc768 100644 --- a/media/libstagefright/foundation/ALooperRoster.cpp +++ b/media/libstagefright/foundation/ALooperRoster.cpp @@ -72,50 +72,40 @@ void ALooperRoster::unregisterHandler(ALooper::handler_id handlerID) { } void ALooperRoster::unregisterStaleHandlers() { - Mutex::Autolock autoLock(mLock); - for (size_t i = mHandlers.size(); i-- > 0;) { - const HandlerInfo &info = mHandlers.valueAt(i); + Vector<sp<ALooper> > activeLoopers; + { + Mutex::Autolock autoLock(mLock); - sp<ALooper> looper = info.mLooper.promote(); - if (looper == NULL) { - ALOGV("Unregistering stale handler %d", mHandlers.keyAt(i)); - mHandlers.removeItemsAt(i); + for (size_t i = mHandlers.size(); i-- > 0;) { + const HandlerInfo &info = mHandlers.valueAt(i); + + sp<ALooper> looper = info.mLooper.promote(); + if (looper == NULL) { + ALOGV("Unregistering stale handler %d", mHandlers.keyAt(i)); + mHandlers.removeItemsAt(i); + } else { + // At this point 'looper' might be the only sp<> keeping + // the object alive. To prevent it from going out of scope + // and having ~ALooper call this method again recursively + // and then deadlocking because of the Autolock above, add + // it to a Vector which will go out of scope after the lock + // has been released. + activeLoopers.add(looper); + } } } } status_t ALooperRoster::postMessage( const sp<AMessage> &msg, int64_t delayUs) { - Mutex::Autolock autoLock(mLock); - return postMessage_l(msg, delayUs); -} - -status_t ALooperRoster::postMessage_l( - const sp<AMessage> &msg, int64_t delayUs) { - ssize_t index = mHandlers.indexOfKey(msg->target()); - if (index < 0) { - ALOGW("failed to post message '%s'. Target handler not registered.", - msg->debugString().c_str()); - return -ENOENT; - } - - const HandlerInfo &info = mHandlers.valueAt(index); - - sp<ALooper> looper = info.mLooper.promote(); + sp<ALooper> looper = findLooper(msg->target()); if (looper == NULL) { - ALOGW("failed to post message. " - "Target handler %d still registered, but object gone.", - msg->target()); - - mHandlers.removeItemsAt(index); return -ENOENT; } - looper->post(msg, delayUs); - return OK; } @@ -169,18 +159,23 @@ sp<ALooper> ALooperRoster::findLooper(ALooper::handler_id handlerID) { status_t ALooperRoster::postAndAwaitResponse( const sp<AMessage> &msg, sp<AMessage> *response) { + sp<ALooper> looper = findLooper(msg->target()); + + if (looper == NULL) { + ALOGW("failed to post message. " + "Target handler %d still registered, but object gone.", + msg->target()); + response->clear(); + return -ENOENT; + } + Mutex::Autolock autoLock(mLock); uint32_t replyID = mNextReplyID++; msg->setInt32("replyID", replyID); - status_t err = postMessage_l(msg, 0 /* delayUs */); - - if (err != OK) { - response->clear(); - return err; - } + looper->post(msg, 0 /* delayUs */); ssize_t index; while ((index = mReplies.indexOfKey(replyID)) < 0) { diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp index d268aa4..bc3e3fb 100644 --- a/media/libstagefright/foundation/AMessage.cpp +++ b/media/libstagefright/foundation/AMessage.cpp @@ -14,6 +14,11 @@ * limitations under the License. */ +#define LOG_TAG "AMessage" +//#define LOG_NDEBUG 0 +//#define DUMP_STATS +#include <cutils/log.h> + #include "AMessage.h" #include <ctype.h> @@ -60,12 +65,14 @@ ALooper::handler_id AMessage::target() const { void AMessage::clear() { for (size_t i = 0; i < mNumItems; ++i) { Item *item = &mItems[i]; - freeItem(item); + delete[] item->mName; + item->mName = NULL; + freeItemValue(item); } mNumItems = 0; } -void AMessage::freeItem(Item *item) { +void AMessage::freeItemValue(Item *item) { switch (item->mType) { case kTypeString: { @@ -88,25 +95,85 @@ void AMessage::freeItem(Item *item) { } } -AMessage::Item *AMessage::allocateItem(const char *name) { - name = AAtomizer::Atomize(name); +#ifdef DUMP_STATS +#include <utils/Mutex.h> + +Mutex gLock; +static int32_t gFindItemCalls = 1; +static int32_t gDupCalls = 1; +static int32_t gAverageNumItems = 0; +static int32_t gAverageNumChecks = 0; +static int32_t gAverageNumMemChecks = 0; +static int32_t gAverageDupItems = 0; +static int32_t gLastChecked = -1; + +static void reportStats() { + int32_t time = (ALooper::GetNowUs() / 1000); + if (time / 1000 != gLastChecked / 1000) { + gLastChecked = time; + ALOGI("called findItemIx %zu times (for len=%.1f i=%.1f/%.1f mem) dup %zu times (for len=%.1f)", + gFindItemCalls, + gAverageNumItems / (float)gFindItemCalls, + gAverageNumChecks / (float)gFindItemCalls, + gAverageNumMemChecks / (float)gFindItemCalls, + gDupCalls, + gAverageDupItems / (float)gDupCalls); + gFindItemCalls = gDupCalls = 1; + gAverageNumItems = gAverageNumChecks = gAverageNumMemChecks = gAverageDupItems = 0; + gLastChecked = time; + } +} +#endif +inline size_t AMessage::findItemIndex(const char *name, size_t len) const { +#ifdef DUMP_STATS + size_t memchecks = 0; +#endif size_t i = 0; - while (i < mNumItems && mItems[i].mName != name) { - ++i; + for (; i < mNumItems; i++) { + if (len != mItems[i].mNameLength) { + continue; + } +#ifdef DUMP_STATS + ++memchecks; +#endif + if (!memcmp(mItems[i].mName, name, len)) { + break; + } + } +#ifdef DUMP_STATS + { + Mutex::Autolock _l(gLock); + ++gFindItemCalls; + gAverageNumItems += mNumItems; + gAverageNumMemChecks += memchecks; + gAverageNumChecks += i; + reportStats(); } +#endif + return i; +} + +// assumes item's name was uninitialized or NULL +void AMessage::Item::setName(const char *name, size_t len) { + mNameLength = len; + mName = new char[len + 1]; + memcpy((void*)mName, name, len + 1); +} +AMessage::Item *AMessage::allocateItem(const char *name) { + size_t len = strlen(name); + size_t i = findItemIndex(name, len); Item *item; if (i < mNumItems) { item = &mItems[i]; - freeItem(item); + freeItemValue(item); } else { CHECK(mNumItems < kMaxNumItems); i = mNumItems++; item = &mItems[i]; - - item->mName = name; + item->setName(name, len); } return item; @@ -114,31 +181,18 @@ AMessage::Item *AMessage::allocateItem(const char *name) { const AMessage::Item *AMessage::findItem( const char *name, Type type) const { - name = AAtomizer::Atomize(name); - - for (size_t i = 0; i < mNumItems; ++i) { + size_t i = findItemIndex(name, strlen(name)); + if (i < mNumItems) { const Item *item = &mItems[i]; + return item->mType == type ? item : NULL; - if (item->mName == name) { - return item->mType == type ? item : NULL; - } } - return NULL; } bool AMessage::contains(const char *name) const { - name = AAtomizer::Atomize(name); - - for (size_t i = 0; i < mNumItems; ++i) { - const Item *item = &mItems[i]; - - if (item->mName == name) { - return true; - } - } - - return false; + size_t i = findItemIndex(name, strlen(name)); + return i < mNumItems; } #define BASIC_TYPE(NAME,FIELDNAME,TYPENAME) \ @@ -297,11 +351,20 @@ sp<AMessage> AMessage::dup() const { sp<AMessage> msg = new AMessage(mWhat, mTarget); msg->mNumItems = mNumItems; +#ifdef DUMP_STATS + { + Mutex::Autolock _l(gLock); + ++gDupCalls; + gAverageDupItems += mNumItems; + reportStats(); + } +#endif + for (size_t i = 0; i < mNumItems; ++i) { const Item *from = &mItems[i]; Item *to = &msg->mItems[i]; - to->mName = from->mName; + to->setName(from->mName, from->mNameLength); to->mType = from->mType; switch (from->mType) { @@ -472,11 +535,11 @@ sp<AMessage> AMessage::FromParcel(const Parcel &parcel) { sp<AMessage> msg = new AMessage(what); msg->mNumItems = static_cast<size_t>(parcel.readInt32()); - for (size_t i = 0; i < msg->mNumItems; ++i) { Item *item = &msg->mItems[i]; - item->mName = AAtomizer::Atomize(parcel.readCString()); + const char *name = parcel.readCString(); + item->setName(name, strlen(name)); item->mType = static_cast<Type>(parcel.readInt32()); switch (item->mType) { diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 8667a6b..7b18348 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -1019,11 +1019,19 @@ bool LiveSession::hasDynamicDuration() const { } size_t LiveSession::getTrackCount() const { - return mPlaylist->getTrackCount(); + if (mPlaylist == NULL) { + return 0; + } else { + return mPlaylist->getTrackCount(); + } } sp<AMessage> LiveSession::getTrackInfo(size_t trackIndex) const { - return mPlaylist->getTrackInfo(trackIndex); + if (mPlaylist == NULL) { + return NULL; + } else { + return mPlaylist->getTrackInfo(trackIndex); + } } status_t LiveSession::selectTrack(size_t index, bool select) { diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index 80cb2d0..82a4c39 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -490,11 +490,11 @@ status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) { mStreamTypeMask = streamTypeMask; - mStartTimeUs = startTimeUs; mSegmentStartTimeUs = segmentStartTimeUs; mDiscontinuitySeq = startDiscontinuitySeq; - if (mStartTimeUs >= 0ll) { + if (startTimeUs >= 0) { + mStartTimeUs = startTimeUs; mSeqNumber = -1; mStartup = true; mPrepared = false; @@ -754,6 +754,9 @@ void PlaylistFetcher::onDownloadNext() { if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) { // If this is a live session, start 3 segments from the end on connect mSeqNumber = lastSeqNumberInPlaylist - 3; + if (mSeqNumber < firstSeqNumberInPlaylist) { + mSeqNumber = firstSeqNumberInPlaylist; + } } else { mSeqNumber = getSeqNumberForTime(mStartTimeUs); mStartTimeUs -= getSegmentStartTimeUs(mSeqNumber); diff --git a/media/libstagefright/include/SimpleSoftOMXComponent.h b/media/libstagefright/include/SimpleSoftOMXComponent.h index f8c61eb..591b38e 100644 --- a/media/libstagefright/include/SimpleSoftOMXComponent.h +++ b/media/libstagefright/include/SimpleSoftOMXComponent.h @@ -58,6 +58,11 @@ protected: } mTransition; }; + enum { + kStoreMetaDataExtensionIndex = OMX_IndexVendorStartUnused + 1, + kPrepareForAdaptivePlaybackIndex, + }; + void addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def); virtual OMX_ERRORTYPE internalGetParameter( diff --git a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h index 7f200dd..ee553d9 100644 --- a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h +++ b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h @@ -55,6 +55,9 @@ protected: virtual OMX_ERRORTYPE getConfig( OMX_INDEXTYPE index, OMX_PTR params); + virtual OMX_ERRORTYPE getExtensionIndex( + const char *name, OMX_INDEXTYPE *index); + void initPorts(OMX_U32 numInputBuffers, OMX_U32 inputBufferSize, OMX_U32 numOutputBuffers, @@ -68,6 +71,8 @@ protected: kMaxPortIndex = 1, }; + bool mIsAdaptive; + uint32_t mAdaptiveMaxWidth, mAdaptiveMaxHeight; uint32_t mWidth, mHeight; uint32_t mCropLeft, mCropTop, mCropWidth, mCropHeight; diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp index 1c383f7..69b572e 100644 --- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp @@ -22,6 +22,7 @@ #include "include/SoftVideoDecoderOMXComponent.h" +#include <media/hardware/HardwareAPI.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/AMessage.h> @@ -50,6 +51,9 @@ SoftVideoDecoderOMXComponent::SoftVideoDecoderOMXComponent( OMX_PTR appData, OMX_COMPONENTTYPE **component) : SimpleSoftOMXComponent(name, callbacks, appData, component), + mIsAdaptive(false), + mAdaptiveMaxWidth(0), + mAdaptiveMaxHeight(0), mWidth(width), mHeight(height), mCropLeft(0), @@ -127,8 +131,8 @@ void SoftVideoDecoderOMXComponent::updatePortDefinitions() { def->format.video.nSliceHeight = def->format.video.nFrameHeight; def = &editPortInfo(kOutputPortIndex)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; + def->format.video.nFrameWidth = mIsAdaptive ? mAdaptiveMaxWidth : mWidth; + def->format.video.nFrameHeight = mIsAdaptive ? mAdaptiveMaxHeight : mHeight; def->format.video.nStride = def->format.video.nFrameWidth; def->format.video.nSliceHeight = def->format.video.nFrameHeight; @@ -199,7 +203,10 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter( OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( OMX_INDEXTYPE index, const OMX_PTR params) { - switch (index) { + // Include extension index OMX_INDEXEXTTYPE. + const int32_t indexFull = index; + + switch (indexFull) { case OMX_IndexParamStandardComponentRole: { const OMX_PARAM_COMPONENTROLETYPE *roleParams = @@ -230,6 +237,24 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( return OMX_ErrorNone; } + case kPrepareForAdaptivePlaybackIndex: + { + const PrepareForAdaptivePlaybackParams* adaptivePlaybackParams = + (const PrepareForAdaptivePlaybackParams *)params; + mIsAdaptive = adaptivePlaybackParams->bEnable; + if (mIsAdaptive) { + mAdaptiveMaxWidth = adaptivePlaybackParams->nMaxFrameWidth; + mAdaptiveMaxHeight = adaptivePlaybackParams->nMaxFrameHeight; + mWidth = mAdaptiveMaxWidth; + mHeight = mAdaptiveMaxHeight; + } else { + mAdaptiveMaxWidth = 0; + mAdaptiveMaxHeight = 0; + } + updatePortDefinitions(); + return OMX_ErrorNone; + } + default: return SimpleSoftOMXComponent::internalSetParameter(index, params); } @@ -259,6 +284,16 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig( } } +OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex( + const char *name, OMX_INDEXTYPE *index) { + if (!strcmp(name, "OMX.google.android.index.prepareForAdaptivePlayback")) { + *(int32_t*)index = kPrepareForAdaptivePlaybackIndex; + return OMX_ErrorNone; + } + + return SimpleSoftOMXComponent::getExtensionIndex(name, index); +} + void SoftVideoDecoderOMXComponent::onReset() { mOutputPortSettingsChange = NONE; } |