diff options
50 files changed, 1053 insertions, 963 deletions
diff --git a/cmds/stagefright/muxer.cpp b/cmds/stagefright/muxer.cpp index 0d04760..36fa3b5 100644 --- a/cmds/stagefright/muxer.cpp +++ b/cmds/stagefright/muxer.cpp @@ -143,7 +143,7 @@ static int muxing( ssize_t newTrackIndex = muxer->addTrack(format); if (newTrackIndex < 0) { - fprintf(stderr, "%s track (%d) unsupported by muxer\n", + fprintf(stderr, "%s track (%zu) unsupported by muxer\n", isAudio ? "audio" : "video", i); } else { diff --git a/include/media/MediaResourcePolicy.h b/include/media/MediaResourcePolicy.h index 1e1c341..9bc2eec 100644 --- a/include/media/MediaResourcePolicy.h +++ b/include/media/MediaResourcePolicy.h @@ -29,7 +29,7 @@ extern const char kPolicySupportsSecureWithNonSecureCodec[]; class MediaResourcePolicy { public: MediaResourcePolicy(); - MediaResourcePolicy(String8 type, uint64_t value); + MediaResourcePolicy(String8 type, String8 value); void readFromParcel(const Parcel &parcel); void writeToParcel(Parcel *parcel) const; @@ -37,7 +37,7 @@ public: String8 toString() const; String8 mType; - uint64_t mValue; + String8 mValue; }; }; // namespace android diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index cdb923d..b8327a3 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -216,6 +216,7 @@ private: int32_t mChannelMask; unsigned mDequeueCounter; bool mStoreMetaDataInOutputBuffers; + bool mLegacyAdaptiveExperiment; int32_t mMetaDataBuffersToSubmit; size_t mNumUndequeuedBuffers; @@ -236,9 +237,6 @@ private: status_t freeBuffer(OMX_U32 portIndex, size_t i); status_t handleSetSurface(const sp<Surface> &surface); - status_t setNativeWindowSizeFormatAndUsage( - ANativeWindow *nativeWindow /* nonnull */, - int width, int height, int format, int rotation, int usage); status_t setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow /* nonnull */); status_t configureOutputBuffersFromNativeWindow( @@ -332,8 +330,6 @@ private: status_t initNativeWindow(); - status_t pushBlankBuffersToNativeWindow(); - // Returns true iff all buffers on the given port have status // OWNED_BY_US or OWNED_BY_NATIVE_WINDOW. bool allYourBuffersAreBelongToUs(OMX_U32 portIndex); diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h index f5d523d..82c768d 100644 --- a/include/media/stagefright/MediaCodec.h +++ b/include/media/stagefright/MediaCodec.h @@ -164,6 +164,11 @@ protected: virtual void onMessageReceived(const sp<AMessage> &msg); private: + // used by ResourceManagerClient + status_t reclaim(); + friend struct ResourceManagerClient; + +private: enum State { UNINITIALIZED, INITIALIZING, @@ -224,6 +229,7 @@ private: kFlagGatherCodecSpecificData = 512, kFlagIsAsync = 1024, kFlagIsComponentAllocated = 2048, + kFlagPushBlankBuffersOnShutdown = 4096, }; struct BufferInfo { @@ -261,6 +267,7 @@ private: }; State mState; + bool mReleasedByResourceManager; sp<ALooper> mLooper; sp<ALooper> mCodecLooper; sp<CodecBase> mCodec; @@ -320,7 +327,7 @@ private: static status_t PostAndAwaitResponse( const sp<AMessage> &msg, sp<AMessage> *response); - static void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err); + void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err); status_t init(const AString &name, bool nameIsType, bool encoder); diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h index 9d1d675..ce34338 100644 --- a/include/media/stagefright/MediaCodecList.h +++ b/include/media/stagefright/MediaCodecList.h @@ -54,7 +54,7 @@ struct MediaCodecList : public BnMediaCodecList { static sp<IMediaCodecList> getLocalInstance(); // only to be used in getLocalInstance - void updateDetailsForMultipleCodecs(const KeyedVector<AString, CodecSettings>& updates); + void parseTopLevelXMLFile(const char *path, bool ignore_errors = false); private: class BinderDeathObserver : public IBinder::DeathRecipient { @@ -97,7 +97,6 @@ private: status_t initCheck() const; void parseXMLFile(const char *path); - void parseTopLevelXMLFile(const char *path, bool ignore_errors = false); static void StartElementHandlerWrapper( void *me, const char *name, const char **attrs); diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 84b1b1a..7fabcb3 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -298,7 +298,6 @@ private: status_t queueBufferToNativeWindow(BufferInfo *info); status_t cancelBufferToNativeWindow(BufferInfo *info); BufferInfo* dequeueBufferFromNativeWindow(); - status_t pushBlankBuffersToNativeWindow(); status_t freeBuffersOnPort( OMX_U32 portIndex, bool onlyThoseWeOwn = false); @@ -347,7 +346,6 @@ private: status_t configureCodec(const sp<MetaData> &meta); - status_t applyRotation(); status_t waitForBufferFilled_l(); int64_t getDecodingTimeUs(); diff --git a/include/media/stagefright/SurfaceUtils.h b/include/media/stagefright/SurfaceUtils.h new file mode 100644 index 0000000..c1a9c0a --- /dev/null +++ b/include/media/stagefright/SurfaceUtils.h @@ -0,0 +1,34 @@ +/* + * Copyright 2015 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 SURFACE_UTILS_H_ + +#define SURFACE_UTILS_H_ + +#include <utils/Errors.h> + +struct ANativeWindow; + +namespace android { + +status_t setNativeWindowSizeFormatAndUsage( + ANativeWindow *nativeWindow /* nonnull */, + int width, int height, int format, int rotation, int usage); +status_t pushBlankBuffersToNativeWindow(ANativeWindow *nativeWindow /* nonnull */); + +} // namespace android + +#endif // SURFACE_UTILS_H_ diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h index 1d0e2cb..a97dd9b 100644 --- a/include/media/stagefright/foundation/ADebug.h +++ b/include/media/stagefright/foundation/ADebug.h @@ -108,6 +108,26 @@ struct ADebug { // remove redundant segments of a codec name, and return a newly allocated // string suitable for debugging static char *GetDebugName(const char *name); + + inline static bool isExperimentEnabled( + const char *name __unused /* nonnull */, bool allow __unused = true) { +#ifdef ENABLE_STAGEFRIGHT_EXPERIMENTS + if (!strcmp(name, "legacy-adaptive")) { + return getExperimentFlag(allow, name, 2, 1); // every other day + } else if (!strcmp(name, "legacy-setsurface")) { + return getExperimentFlag(allow, name, 3, 1); // every third day + } else { + ALOGE("unknown experiment '%s' (disabled)", name); + } +#endif + return false; + } + +private: + // pass in allow, so we can print in the log if the experiment is disabled + static bool getExperimentFlag( + bool allow, const char *name, uint64_t modulo, uint64_t limit, + uint64_t plus = 0, uint64_t timeDivisor = 24 * 60 * 60 /* 1 day */); }; } // namespace android diff --git a/media/libmedia/IMediaHTTPConnection.cpp b/media/libmedia/IMediaHTTPConnection.cpp index 2ff7658..09137ef 100644 --- a/media/libmedia/IMediaHTTPConnection.cpp +++ b/media/libmedia/IMediaHTTPConnection.cpp @@ -24,6 +24,7 @@ #include <binder/Parcel.h> #include <utils/String8.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaErrors.h> namespace android { @@ -106,11 +107,18 @@ struct BpMediaHTTPConnection : public BpInterface<IMediaHTTPConnection> { return UNKNOWN_ERROR; } - int32_t len = reply.readInt32(); + size_t len = reply.readInt32(); - if (len > 0) { - memcpy(buffer, mMemory->pointer(), len); + if (len > size) { + ALOGE("requested %zu, got %zu", size, len); + return ERROR_OUT_OF_RANGE; } + if (len > mMemory->size()) { + ALOGE("got %zu, but memory has %zu", len, mMemory->size()); + return ERROR_OUT_OF_RANGE; + } + + memcpy(buffer, mMemory->pointer(), len); return len; } diff --git a/media/libmedia/IMediaHTTPService.cpp b/media/libmedia/IMediaHTTPService.cpp index f30d0f3..0c16a2b 100644 --- a/media/libmedia/IMediaHTTPService.cpp +++ b/media/libmedia/IMediaHTTPService.cpp @@ -44,6 +44,7 @@ struct BpMediaHTTPService : public BpInterface<IMediaHTTPService> { status_t err = reply.readInt32(); if (err != OK) { + ALOGE("Unable to make HTTP connection (err = %d)", err); return NULL; } diff --git a/media/libmedia/MediaResourcePolicy.cpp b/media/libmedia/MediaResourcePolicy.cpp index 139a38c..5210825 100644 --- a/media/libmedia/MediaResourcePolicy.cpp +++ b/media/libmedia/MediaResourcePolicy.cpp @@ -24,25 +24,25 @@ namespace android { const char kPolicySupportsMultipleSecureCodecs[] = "supports-multiple-secure-codecs"; const char kPolicySupportsSecureWithNonSecureCodec[] = "supports-secure-with-non-secure-codec"; -MediaResourcePolicy::MediaResourcePolicy() : mValue(0) {} +MediaResourcePolicy::MediaResourcePolicy() {} -MediaResourcePolicy::MediaResourcePolicy(String8 type, uint64_t value) +MediaResourcePolicy::MediaResourcePolicy(String8 type, String8 value) : mType(type), mValue(value) {} void MediaResourcePolicy::readFromParcel(const Parcel &parcel) { mType = parcel.readString8(); - mValue = parcel.readUint64(); + mValue = parcel.readString8(); } void MediaResourcePolicy::writeToParcel(Parcel *parcel) const { parcel->writeString8(mType); - parcel->writeUint64(mValue); + parcel->writeString8(mValue); } String8 MediaResourcePolicy::toString() const { String8 str; - str.appendFormat("%s:%llu", mType.string(), (unsigned long long)mValue); + str.appendFormat("%s:%s", mType.string(), mValue.string()); return str; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 4f64426..1fb4365 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -189,7 +189,8 @@ NuPlayer::NuPlayer() mVideoFpsHint(-1.f), mStarted(false), mPaused(false), - mPausedByClient(false) { + mPausedByClient(false), + mPausedForBuffering(false) { clearFlushComplete(); } @@ -423,7 +424,19 @@ void NuPlayer::writeTrackInfo( CHECK(format->findInt32("type", &trackType)); AString mime; - CHECK(format->findString("mime", &mime)); + if (!format->findString("mime", &mime)) { + // Java MediaPlayer only uses mimetype for subtitle and timedtext tracks. + // If we can't find the mimetype here it means that we wouldn't be needing + // the mimetype on the Java end. We still write a placeholder mime to keep the + // (de)serialization logic simple. + if (trackType == MEDIA_TRACK_TYPE_AUDIO) { + mime = "audio/"; + } else if (trackType == MEDIA_TRACK_TYPE_VIDEO) { + mime = "video/"; + } else { + TRESPASS(); + } + } AString lang; CHECK(format->findString("language", &lang)); @@ -671,7 +684,10 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { { ALOGV("kWhatStart"); if (mStarted) { - onResume(); + // do not resume yet if the source is still buffering + if (!mPausedForBuffering) { + onResume(); + } } else { onStart(); } @@ -1995,9 +2011,10 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { case Source::kWhatPauseOnBufferingStart: { // ignore if not playing - if (mStarted && !mPausedByClient) { + if (mStarted) { ALOGI("buffer low, pausing..."); + mPausedForBuffering = true; onPause(); } // fall-thru @@ -2012,10 +2029,15 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { case Source::kWhatResumeOnBufferingEnd: { // ignore if not playing - if (mStarted && !mPausedByClient) { + if (mStarted) { ALOGI("buffer ready, resuming..."); - onResume(); + mPausedForBuffering = false; + + // do not resume yet if client didn't unpause + if (!mPausedByClient) { + onResume(); + } } // fall-thru } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 6b7d71e..df9debc 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -203,6 +203,9 @@ private: // still become true, when we pause internally due to buffering. bool mPausedByClient; + // Pause state as requested by source (internally) due to buffering + bool mPausedForBuffering; + inline const sp<DecoderBase> &getDecoder(bool audio) { return audio ? mAudioDecoder : mVideoDecoder; } diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 22395cc..b9ae125 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -42,6 +42,7 @@ #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> #include <media/stagefright/PersistentSurface.h> +#include <media/stagefright/SurfaceUtils.h> #include <media/hardware/HardwareAPI.h> #include <OMX_AudioExt.h> @@ -421,6 +422,7 @@ ACodec::ACodec() mChannelMask(0), mDequeueCounter(0), mStoreMetaDataInOutputBuffers(false), + mLegacyAdaptiveExperiment(false), mMetaDataBuffersToSubmit(0), mRepeatFrameDelayUs(-1ll), mMaxPtsGapUs(-1ll), @@ -609,12 +611,16 @@ status_t ACodec::handleSetSurface(const sp<Surface> &surface) { return err; } + // need to enable allocation when attaching + surface->getIGraphicBufferProducer()->allowAllocation(true); + // for meta data mode, we move dequeud buffers to the new surface. // for non-meta mode, we must move all registered buffers for (size_t i = 0; i < buffers.size(); ++i) { const BufferInfo &info = buffers[i]; // skip undequeued buffers for meta data mode if (mStoreMetaDataInOutputBuffers + && !mLegacyAdaptiveExperiment && info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) { ALOGV("skipping buffer %p", info.mGraphicBuffer->getNativeBuffer()); continue; @@ -631,7 +637,7 @@ status_t ACodec::handleSetSurface(const sp<Surface> &surface) { } // cancel undequeued buffers to new surface - if (!mStoreMetaDataInOutputBuffers) { + if (!mStoreMetaDataInOutputBuffers || mLegacyAdaptiveExperiment) { for (size_t i = 0; i < buffers.size(); ++i) { const BufferInfo &info = buffers[i]; if (info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) { @@ -650,6 +656,11 @@ status_t ACodec::handleSetSurface(const sp<Surface> &surface) { (void)surface->getIGraphicBufferProducer()->allowAllocation(false); } + // push blank buffers to previous window if requested + if (mFlags & kFlagPushBlankBuffersToNativeWindowOnShutdown) { + pushBlankBuffersToNativeWindow(mNativeWindow.get()); + } + mNativeWindow = nativeWindow; return OK; } @@ -748,82 +759,6 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { return OK; } -status_t ACodec::setNativeWindowSizeFormatAndUsage( - ANativeWindow *nativeWindow /* nonnull */, - int width, int height, int format, int rotation, int usage) { - status_t err = native_window_set_buffers_dimensions(nativeWindow, width, height); - if (err != 0) { - ALOGE("native_window_set_buffers_dimensions failed: %s (%d)", strerror(-err), -err); - return err; - } - - err = native_window_set_buffers_format(nativeWindow, format); - if (err != 0) { - ALOGE("native_window_set_buffers_format failed: %s (%d)", strerror(-err), -err); - return err; - } - - int transform = 0; - if ((rotation % 90) == 0) { - switch ((rotation / 90) & 3) { - case 1: transform = HAL_TRANSFORM_ROT_90; break; - case 2: transform = HAL_TRANSFORM_ROT_180; break; - case 3: transform = HAL_TRANSFORM_ROT_270; break; - default: transform = 0; break; - } - } - - err = native_window_set_buffers_transform(nativeWindow, transform); - if (err != 0) { - ALOGE("native_window_set_buffers_transform failed: %s (%d)", strerror(-err), -err); - return err; - } - - // Make sure to check whether either Stagefright or the video decoder - // requested protected buffers. - if (usage & GRALLOC_USAGE_PROTECTED) { - // Verify that the ANativeWindow sends images directly to - // SurfaceFlinger. - int queuesToNativeWindow = 0; - err = nativeWindow->query( - nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &queuesToNativeWindow); - if (err != 0) { - ALOGE("error authenticating native window: %s (%d)", strerror(-err), -err); - return err; - } - if (queuesToNativeWindow != 1) { - ALOGE("native window could not be authenticated"); - return PERMISSION_DENIED; - } - } - - int consumerUsage = 0; - err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_CONSUMER_USAGE_BITS, &consumerUsage); - if (err != 0) { - ALOGW("failed to get consumer usage bits. ignoring"); - err = 0; - } - - int finalUsage = usage | consumerUsage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP; - ALOGV("gralloc usage: %#x(ACodec) + %#x(Consumer) = %#x", usage, consumerUsage, finalUsage); - err = native_window_set_usage(nativeWindow, finalUsage); - if (err != 0) { - ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err); - return err; - } - - err = native_window_set_scaling_mode( - nativeWindow, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - if (err != 0) { - ALOGE("native_window_set_scaling_mode failed: %s (%d)", strerror(-err), -err); - return err; - } - - ALOGD("set up nativeWindow %p for %dx%d, color %#x, rotation %d, usage %#x", - nativeWindow, width, height, format, rotation, finalUsage); - return OK; -} - status_t ACodec::setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow /* nonnull */) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); @@ -849,6 +784,8 @@ status_t ACodec::setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow usage |= GRALLOC_USAGE_PROTECTED; } + usage |= GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP; + ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec)", omxUsage, usage); return setNativeWindowSizeFormatAndUsage( nativeWindow, @@ -1063,6 +1000,45 @@ status_t ACodec::allocateOutputMetaDataBuffers() { mComponentName.c_str(), info.mBufferID, mem->pointer()); } + if (mLegacyAdaptiveExperiment) { + // preallocate and preregister buffers + static_cast<Surface *>(mNativeWindow.get()) + ->getIGraphicBufferProducer()->allowAllocation(true); + + ALOGV("[%s] Allocating %u buffers from a native window of size %u on " + "output port", + mComponentName.c_str(), bufferCount, bufferSize); + + // Dequeue buffers then cancel them all + for (OMX_U32 i = 0; i < bufferCount; i++) { + BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); + + ANativeWindowBuffer *buf; + err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); + if (err != 0) { + ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); + break; + } + + sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false)); + mOMX->updateGraphicBufferInMeta( + mNode, kPortIndexOutput, graphicBuffer, info->mBufferID); + info->mStatus = BufferInfo::OWNED_BY_US; + info->mGraphicBuffer = graphicBuffer; + } + + for (OMX_U32 i = 0; i < mBuffers[kPortIndexOutput].size(); i++) { + BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); + status_t error = cancelBufferToNativeWindow(info); + if (err == OK) { + err = error; + } + } + + static_cast<Surface*>(mNativeWindow.get()) + ->getIGraphicBufferProducer()->allowAllocation(false); + } + mMetaDataBuffersToSubmit = bufferCount - minUndequeuedBuffers; return err; } @@ -1114,26 +1090,57 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { return NULL; } - if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) { - ALOGE("dequeueBuffer failed."); - return NULL; - } + do { + if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) { + ALOGE("dequeueBuffer failed."); + return NULL; + } + + bool stale = false; + for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) { + BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); + + if (info->mGraphicBuffer != NULL && + info->mGraphicBuffer->handle == buf->handle) { + // Since consumers can attach buffers to BufferQueues, it is possible + // that a known yet stale buffer can return from a surface that we + // once used. We can simply ignore this as we have already dequeued + // this buffer properly. NOTE: this does not eliminate all cases, + // e.g. it is possible that we have queued the valid buffer to the + // NW, and a stale copy of the same buffer gets dequeued - which will + // be treated as the valid buffer by ACodec. + if (info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) { + ALOGI("dequeued stale buffer %p. discarding", buf); + stale = true; + break; + } + ALOGV("dequeued buffer %p", info->mGraphicBuffer->getNativeBuffer()); + info->mStatus = BufferInfo::OWNED_BY_US; + + return info; + } + } + + // It is also possible to receive a previously unregistered buffer + // in non-meta mode. These should be treated as stale buffers. The + // same is possible in meta mode, in which case, it will be treated + // as a normal buffer, which is not desirable. + // TODO: fix this. + if (!stale && (!mStoreMetaDataInOutputBuffers || mLegacyAdaptiveExperiment)) { + ALOGI("dequeued unrecognized (stale) buffer %p. discarding", buf); + stale = true; + } + if (stale) { + // TODO: detach stale buffer, but there is no API yet to do it. + buf = NULL; + } + } while (buf == NULL); + // get oldest undequeued buffer BufferInfo *oldest = NULL; for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); - - if (info->mGraphicBuffer != NULL && - info->mGraphicBuffer->handle == buf->handle) { - CHECK_EQ((int)info->mStatus, - (int)BufferInfo::OWNED_BY_NATIVE_WINDOW); - - info->mStatus = BufferInfo::OWNED_BY_US; - - return info; - } - if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW && (oldest == NULL || // avoid potential issues from counter rolling over @@ -1452,6 +1459,7 @@ status_t ACodec::configureCodec( bool haveNativeWindow = msg->findObject("native-window", &obj) && obj != NULL && video && !encoder; mStoreMetaDataInOutputBuffers = false; + mLegacyAdaptiveExperiment = false; if (video && !encoder) { inputFormat->setInt32("adaptive-playback", false); @@ -1589,6 +1597,9 @@ status_t ACodec::configureCodec( ALOGV("[%s] storeMetaDataInBuffers succeeded", mComponentName.c_str()); mStoreMetaDataInOutputBuffers = true; + mLegacyAdaptiveExperiment = ADebug::isExperimentEnabled( + "legacy-adaptive", !msg->contains("no-experiments")); + inputFormat->setInt32("adaptive-playback", true); } @@ -4123,137 +4134,6 @@ void ACodec::signalError(OMX_ERRORTYPE error, status_t internalError) { notify->post(); } -status_t ACodec::pushBlankBuffersToNativeWindow() { - status_t err = NO_ERROR; - ANativeWindowBuffer* anb = NULL; - int numBufs = 0; - int minUndequeuedBufs = 0; - - // We need to reconnect to the ANativeWindow as a CPU client to ensure that - // no frames get dropped by SurfaceFlinger assuming that these are video - // frames. - err = native_window_api_disconnect(mNativeWindow.get(), - NATIVE_WINDOW_API_MEDIA); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", - strerror(-err), -err); - return err; - } - - err = native_window_api_connect(mNativeWindow.get(), - NATIVE_WINDOW_API_CPU); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: api_connect failed: %s (%d)", - strerror(-err), -err); - return err; - } - - err = setNativeWindowSizeFormatAndUsage( - mNativeWindow.get(), 1, 1, HAL_PIXEL_FORMAT_RGBX_8888, 0, GRALLOC_USAGE_SW_WRITE_OFTEN); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: set format failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - static_cast<Surface*>(mNativeWindow.get()) - ->getIGraphicBufferProducer()->allowAllocation(true); - - err = mNativeWindow->query(mNativeWindow.get(), - NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query " - "failed: %s (%d)", strerror(-err), -err); - goto error; - } - - numBufs = minUndequeuedBufs + 1; - err = native_window_set_buffer_count(mNativeWindow.get(), numBufs); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - // We push numBufs + 1 buffers to ensure that we've drawn into the same - // buffer twice. This should guarantee that the buffer has been displayed - // on the screen and then been replaced, so an previous video frames are - // guaranteed NOT to be currently displayed. - for (int i = 0; i < numBufs + 1; i++) { - err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - - // Fill the buffer with the a 1x1 checkerboard pattern ;) - uint32_t* img = NULL; - err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: lock failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - *img = 0; - - err = buf->unlock(); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: unlock failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - err = mNativeWindow->queueBuffer(mNativeWindow.get(), - buf->getNativeBuffer(), -1); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - anb = NULL; - } - -error: - - if (err != NO_ERROR) { - // Clean up after an error. - if (anb != NULL) { - mNativeWindow->cancelBuffer(mNativeWindow.get(), anb, -1); - } - - native_window_api_disconnect(mNativeWindow.get(), - NATIVE_WINDOW_API_CPU); - native_window_api_connect(mNativeWindow.get(), - NATIVE_WINDOW_API_MEDIA); - - return err; - } else { - // Clean up after success. - err = native_window_api_disconnect(mNativeWindow.get(), - NATIVE_WINDOW_API_CPU); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", - strerror(-err), -err); - return err; - } - - err = native_window_api_connect(mNativeWindow.get(), - NATIVE_WINDOW_API_MEDIA); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: api_connect failed: %s (%d)", - strerror(-err), -err); - return err; - } - - return NO_ERROR; - } -} - //////////////////////////////////////////////////////////////////////////////// ACodec::PortDescription::PortDescription() { @@ -4334,7 +4214,9 @@ bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) { sp<RefBase> obj; CHECK(msg->findObject("surface", &obj)); - status_t err = mCodec->handleSetSurface(static_cast<Surface *>(obj.get())); + status_t err = + ADebug::isExperimentEnabled("legacy-setsurface") ? BAD_VALUE : + mCodec->handleSetSurface(static_cast<Surface *>(obj.get())); sp<AMessage> response = new AMessage; response->setInt32("err", err); @@ -6148,7 +6030,7 @@ void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() { // them has made it to the display. This allows the OMX // component teardown to zero out any protected buffers // without the risk of scanning out one of those buffers. - mCodec->pushBlankBuffersToNativeWindow(); + pushBlankBuffersToNativeWindow(mCodec->mNativeWindow.get()); } mCodec->changeState(mCodec->mIdleToLoadedState); diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 45581f3..6010558 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -57,6 +57,7 @@ LOCAL_SRC_FILES:= \ StagefrightMediaScanner.cpp \ StagefrightMetadataRetriever.cpp \ SurfaceMediaSource.cpp \ + SurfaceUtils.cpp \ ThrottledSource.cpp \ TimeSource.cpp \ TimedEventQueue.cpp \ @@ -123,7 +124,7 @@ LOCAL_SHARED_LIBRARIES += \ libdl \ libRScpp \ -LOCAL_CFLAGS += -Wno-multichar -Werror -Wall +LOCAL_CFLAGS += -Wno-multichar -Werror -Wall -DENABLE_STAGEFRIGHT_EXPERIMENTS LOCAL_CLANG := true LOCAL_MODULE:= libstagefright diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 6573afc..5c0afa9 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1981,6 +1981,10 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { size = 0; } + if (SIZE_MAX - chunk_size <= size) { + return ERROR_MALFORMED; + } + uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size]; if (buffer == NULL) { return ERROR_MALFORMED; @@ -2014,14 +2018,22 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { *offset += chunk_size; if (mFileMetaData != NULL) { - ALOGV("chunk_data_size = %lld and data_offset = %lld", - (long long)chunk_data_size, (long long)data_offset); + ALOGV("chunk_data_size = %" PRId64 " and data_offset = %" PRId64, + chunk_data_size, data_offset); + + if (chunk_data_size < 0 || static_cast<uint64_t>(chunk_data_size) >= SIZE_MAX - 1) { + return ERROR_MALFORMED; + } sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1); if (mDataSource->readAt( data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) { return ERROR_IO; } const int kSkipBytesOfDataBox = 16; + if (chunk_data_size <= kSkipBytesOfDataBox) { + return ERROR_MALFORMED; + } + mFileMetaData->setData( kKeyAlbumArt, MetaData::TYPE_NONE, buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox); @@ -2605,11 +2617,11 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) { } status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int depth) { - if (size < 4) { + if (size < 4 || size == SIZE_MAX) { return ERROR_MALFORMED; } - uint8_t *buffer = new (std::nothrow) uint8_t[size]; + uint8_t *buffer = new (std::nothrow) uint8_t[size + 1]; if (buffer == NULL) { return ERROR_MALFORMED; } @@ -2678,6 +2690,10 @@ status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int dept int len16 = 0; // Number of UTF-16 characters // smallest possible valid UTF-16 string w BOM: 0xfe 0xff 0x00 0x00 + if (size < 6) { + return ERROR_MALFORMED; + } + if (size - 6 >= 4) { len16 = ((size - 6) / 2) - 1; // don't include 0x0000 terminator framedata = (char16_t *)(buffer + 6); @@ -2701,6 +2717,7 @@ status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int dept } if (isUTF8) { + buffer[size] = 0; mFileMetaData->setCString(metadataKey, (const char *)buffer + 6); } else { // Convert from UTF-16 string to UTF-8 string. diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index ed4f682..4080391 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -46,6 +46,7 @@ #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> #include <media/stagefright/PersistentSurface.h> +#include <media/stagefright/SurfaceUtils.h> #include <private/android_filesystem_config.h> #include <utils/Log.h> #include <utils/Singleton.h> @@ -61,7 +62,7 @@ static int64_t getId(sp<IResourceManagerClient> client) { } static bool isResourceError(status_t err) { - return (err == OMX_ErrorInsufficientResources); + return (err == NO_MEMORY); } static const int kMaxRetry = 2; @@ -75,7 +76,7 @@ struct ResourceManagerClient : public BnResourceManagerClient { // codec is already gone. return true; } - status_t err = codec->release(); + status_t err = codec->reclaim(); if (err != OK) { ALOGW("ResourceManagerClient failed to release codec with err %d", err); } @@ -335,6 +336,7 @@ sp<PersistentSurface> MediaCodec::CreatePersistentInputSurface() { MediaCodec::MediaCodec(const sp<ALooper> &looper) : mState(UNINITIALIZED), + mReleasedByResourceManager(false), mLooper(looper), mCodec(NULL), mReplyID(0), @@ -376,10 +378,15 @@ status_t MediaCodec::PostAndAwaitResponse( return err; } -// static void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) { + int32_t finalErr = err; + if (mReleasedByResourceManager) { + // override the err code if MediaCodec has been released by ResourceManager. + finalErr = DEAD_OBJECT; + } + sp<AMessage> response = new AMessage; - response->setInt32("err", err); + response->setInt32("err", finalErr); response->postReply(replyID); } @@ -653,6 +660,14 @@ status_t MediaCodec::stop() { return PostAndAwaitResponse(msg, &response); } +status_t MediaCodec::reclaim() { + sp<AMessage> msg = new AMessage(kWhatRelease, this); + msg->setInt32("reclaimed", 1); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + status_t MediaCodec::release() { sp<AMessage> msg = new AMessage(kWhatRelease, this); @@ -919,6 +934,10 @@ status_t MediaCodec::getBufferAndFormat( sp<ABuffer> *buffer, sp<AMessage> *format) { // use mutex instead of a context switch + if (mReleasedByResourceManager) { + return DEAD_OBJECT; + } + buffer->clear(); format->clear(); if (!isExecuting()) { @@ -1008,20 +1027,19 @@ bool MediaCodec::handleDequeueInputBuffer(const sp<AReplyToken> &replyID, bool n } bool MediaCodec::handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool newRequest) { - sp<AMessage> response = new AMessage; - if (!isExecuting() || (mFlags & kFlagIsAsync) || (newRequest && (mFlags & kFlagDequeueOutputPending))) { - response->setInt32("err", INVALID_OPERATION); + PostReplyWithError(replyID, INVALID_OPERATION); } else if (mFlags & kFlagStickyError) { - response->setInt32("err", getStickyError()); + PostReplyWithError(replyID, getStickyError()); } else if (mFlags & kFlagOutputBuffersChanged) { - response->setInt32("err", INFO_OUTPUT_BUFFERS_CHANGED); + PostReplyWithError(replyID, INFO_OUTPUT_BUFFERS_CHANGED); mFlags &= ~kFlagOutputBuffersChanged; } else if (mFlags & kFlagOutputFormatChanged) { - response->setInt32("err", INFO_FORMAT_CHANGED); + PostReplyWithError(replyID, INFO_FORMAT_CHANGED); mFlags &= ~kFlagOutputFormatChanged; } else { + sp<AMessage> response = new AMessage; ssize_t index = dequeuePortBuffer(kPortIndexOutput); if (index < 0) { @@ -1056,10 +1074,9 @@ bool MediaCodec::handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool } response->setInt32("flags", flags); + response->postReply(replyID); } - response->postReply(replyID); - return true; } @@ -1659,6 +1676,11 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { sp<AMessage> format; CHECK(msg->findMessage("format", &format)); + int32_t push; + if (msg->findInt32("push-blank-buffers-on-shutdown", &push) && push != 0) { + mFlags |= kFlagPushBlankBuffersOnShutdown; + } + if (obj != NULL) { format->setObject("native-window", obj); status_t err = handleSetSurface(static_cast<Surface *>(obj.get())); @@ -1725,6 +1747,10 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { } else { if (err == OK) { if (mFlags & kFlagUsesSoftwareRenderer) { + if (mSoftRenderer != NULL + && (mFlags & kFlagPushBlankBuffersOnShutdown)) { + pushBlankBuffersToNativeWindow(mSurface.get()); + } mSoftRenderer = new SoftwareRenderer(surface); // TODO: check if this was successful } else { @@ -1808,6 +1834,20 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); + // already stopped/released + if (mState == UNINITIALIZED && mReleasedByResourceManager) { + sp<AMessage> response = new AMessage; + response->setInt32("err", OK); + response->postReply(replyID); + break; + } + + int32_t reclaimed = 0; + msg->findInt32("reclaimed", &reclaimed); + if (reclaimed) { + mReleasedByResourceManager = true; + } + if (!((mFlags & kFlagIsComponentAllocated) && targetState == UNINITIALIZED) // See 1 && mState != INITIALIZED && mState != CONFIGURED && !isExecuting()) { @@ -1821,6 +1861,8 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { // and it should be in this case, no harm to allow a release() // if we're already uninitialized. sp<AMessage> response = new AMessage; + // TODO: we shouldn't throw an exception for stop/release. Change this to wait until + // the previous stop/release completes and then reply with OK. status_t err = mState == targetState ? OK : INVALID_OPERATION; response->setInt32("err", err); if (err == OK && targetState == UNINITIALIZED) { @@ -1848,6 +1890,10 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { msg->what() == kWhatStop /* keepComponentAllocated */); returnBuffersToCodec(); + + if (mSoftRenderer != NULL && (mFlags & kFlagPushBlankBuffersOnShutdown)) { + pushBlankBuffersToNativeWindow(mSurface.get()); + } break; } diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index f12a913..d2352bc 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -24,7 +24,9 @@ #include <media/IMediaCodecList.h> #include <media/IMediaPlayerService.h> +#include <media/IResourceManagerService.h> #include <media/MediaCodecInfo.h> +#include <media/MediaResourcePolicy.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> @@ -44,8 +46,6 @@ static Mutex sInitMutex; static MediaCodecList *gCodecList = NULL; -static const char *kProfilingResults = "/data/misc/media/media_codecs_profiling_results.xml"; - static bool parseBoolean(const char *s) { if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) { return true; @@ -61,7 +61,6 @@ sp<IMediaCodecList> MediaCodecList::sCodecList; // static sp<IMediaCodecList> MediaCodecList::getLocalInstance() { bool profilingNeeded = false; - KeyedVector<AString, CodecSettings> updates; Vector<sp<MediaCodecInfo>> infos; { @@ -89,13 +88,13 @@ sp<IMediaCodecList> MediaCodecList::getLocalInstance() { } if (profilingNeeded) { - profileCodecs(infos, &updates); + profileCodecs(infos); } { Mutex::Autolock autoLock(sInitMutex); - if (updates.size() > 0) { - gCodecList->updateDetailsForMultipleCodecs(updates); + if (profilingNeeded) { + gCodecList->parseTopLevelXMLFile(kProfilingResults, true /* ignore_errors */); } return sCodecList; @@ -145,19 +144,6 @@ MediaCodecList::MediaCodecList() parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */); } -void MediaCodecList::updateDetailsForMultipleCodecs( - const KeyedVector<AString, CodecSettings>& updates) { - if (updates.size() == 0) { - return; - } - - exportResultsToXML(kProfilingResults, updates); - - for (size_t i = 0; i < updates.size(); ++i) { - applyCodecSettings(updates.keyAt(i), updates.valueAt(i), &mCodecInfos); - } -} - void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) { // get href_base char *href_base_end = strrchr(codecs_xml, '/'); @@ -187,6 +173,25 @@ void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_er return; } + Vector<MediaResourcePolicy> policies; + AString value; + if (mGlobalSettings->findString(kPolicySupportsMultipleSecureCodecs, &value)) { + policies.push_back( + MediaResourcePolicy( + String8(kPolicySupportsMultipleSecureCodecs), + String8(value.c_str()))); + } + if (policies.size() > 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("media.resource_manager")); + sp<IResourceManagerService> service = interface_cast<IResourceManagerService>(binder); + if (service == NULL) { + ALOGE("MediaCodecList: failed to get ResourceManagerService"); + } else { + service->config(policies); + } + } + for (size_t i = mCodecInfos.size(); i-- > 0;) { const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get(); if (info.mCaps.size() == 0) { diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp index 265b1ea..0d95676 100644 --- a/media/libstagefright/MediaCodecListOverrides.cpp +++ b/media/libstagefright/MediaCodecListOverrides.cpp @@ -24,12 +24,14 @@ #include <media/ICrypto.h> #include <media/IMediaCodecList.h> #include <media/MediaCodecInfo.h> - +#include <media/MediaResourcePolicy.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MediaCodec.h> namespace android { +const char *kProfilingResults = "/data/misc/media/media_codecs_profiling_results.xml"; + // a limit to avoid allocating unreasonable number of codec instances in the measurement. // this should be in sync with the MAX_SUPPORTED_INSTANCES defined in MediaCodecInfo.java. static const int kMaxInstances = 32; @@ -171,20 +173,6 @@ static size_t doProfileCodecs( return codecs.size(); } -static void printLongString(const char *buf, size_t size) { - AString print; - const char *start = buf; - size_t len; - size_t totalLen = size; - while (totalLen > 0) { - len = (totalLen > 500) ? 500 : totalLen; - print.setTo(start, len); - ALOGV("%s", print.c_str()); - totalLen -= len; - start += len; - } -} - bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2) { ssize_t pos = s.find(delimiter.c_str()); if (pos < 0) { @@ -207,11 +195,22 @@ bool splitString( return true; } +void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos) { + CodecSettings global_results; + KeyedVector<AString, CodecSettings> encoder_results; + KeyedVector<AString, CodecSettings> decoder_results; + profileCodecs(infos, &global_results, &encoder_results, &decoder_results); + exportResultsToXML(kProfilingResults, global_results, encoder_results, decoder_results); +} + void profileCodecs( const Vector<sp<MediaCodecInfo>> &infos, - KeyedVector<AString, CodecSettings> *results, + CodecSettings *global_results, + KeyedVector<AString, CodecSettings> *encoder_results, + KeyedVector<AString, CodecSettings> *decoder_results, bool forceToMeasure) { KeyedVector<AString, sp<MediaCodecInfo::Capabilities>> codecsNeedMeasure; + AString supportMultipleSecureCodecs = "true"; for (size_t i = 0; i < infos.size(); ++i) { const sp<MediaCodecInfo> info = infos[i]; AString name = info->getCodecName(); @@ -240,157 +239,93 @@ void profileCodecs( AString key = name; key.append(" "); key.append(mimes[i]); - key.append(" "); - key.append(info->isEncoder() ? "encoder" : "decoder"); - results->add(key, settings); - } - } - } -} - -void applyCodecSettings( - const AString& codecInfo, - const CodecSettings &settings, - Vector<sp<MediaCodecInfo>> *infos) { - AString name; - AString mime; - AString type; - if (!splitString(codecInfo, " ", &name, &mime, &type)) { - return; - } - for (size_t i = 0; i < infos->size(); ++i) { - const sp<MediaCodecInfo> &info = infos->itemAt(i); - if (name != info->getCodecName()) { - continue; - } + if (info->isEncoder()) { + encoder_results->add(key, settings); + } else { + decoder_results->add(key, settings); + } - Vector<AString> mimes; - info->getSupportedMimes(&mimes); - for (size_t j = 0; j < mimes.size(); ++j) { - if (mimes[j] != mime) { - continue; - } - const sp<MediaCodecInfo::Capabilities> &caps = info->getCapabilitiesFor(mime.c_str()); - for (size_t k = 0; k < settings.size(); ++k) { - caps->getDetails()->setString( - settings.keyAt(k).c_str(), settings.valueAt(k).c_str()); + if (name.endsWith(".secure")) { + if (max <= 1) { + supportMultipleSecureCodecs = "false"; + } + } } } } + global_results->add(kPolicySupportsMultipleSecureCodecs, supportMultipleSecureCodecs); } -void exportResultsToXML(const char *fileName, const KeyedVector<AString, CodecSettings>& results) { -#if LOG_NDEBUG == 0 - ALOGE("measurement results"); +static AString globalResultsToXml(const CodecSettings& results) { + AString ret; for (size_t i = 0; i < results.size(); ++i) { - ALOGE("key %s", results.keyAt(i).c_str()); - const CodecSettings &settings = results.valueAt(i); - for (size_t j = 0; j < settings.size(); ++j) { - ALOGE("name %s value %s", settings.keyAt(j).c_str(), settings.valueAt(j).c_str()); - } - } -#endif - - AString overrides; - FILE *f = fopen(fileName, "rb"); - if (f != NULL) { - fseek(f, 0, SEEK_END); - long size = ftell(f); - rewind(f); - - char *buf = (char *)malloc(size); - if (fread(buf, size, 1, f) == 1) { - overrides.setTo(buf, size); - if (!LOG_NDEBUG) { - ALOGV("Existing overrides:"); - printLongString(buf, size); - } - } else { - ALOGE("Failed to read %s", fileName); - } - fclose(f); - free(buf); + AString setting = AStringPrintf( + " <Setting name=\"%s\" value=\"%s\" />\n", + results.keyAt(i).c_str(), + results.valueAt(i).c_str()); + ret.append(setting); } + return ret; +} +static AString codecResultsToXml(const KeyedVector<AString, CodecSettings>& results) { + AString ret; for (size_t i = 0; i < results.size(); ++i) { AString name; AString mime; - AString type; - if (!splitString(results.keyAt(i), " ", &name, &mime, &type)) { + if (!splitString(results.keyAt(i), " ", &name, &mime)) { continue; } - name = AStringPrintf("\"%s\"", name.c_str()); - mime = AStringPrintf("\"%s\"", mime.c_str()); - ALOGV("name(%s) mime(%s) type(%s)", name.c_str(), mime.c_str(), type.c_str()); - ssize_t posCodec = overrides.find(name.c_str()); - size_t posInsert = 0; - if (posCodec < 0) { - AString encodersDecoders = (type == "encoder") ? "<Encoders>" : "<Decoders>"; - AString encodersDecodersEnd = (type == "encoder") ? "</Encoders>" : "</Decoders>"; - ssize_t posEncodersDecoders = overrides.find(encodersDecoders.c_str()); - if (posEncodersDecoders < 0) { - AString mediaCodecs = "<MediaCodecs>"; - ssize_t posMediaCodec = overrides.find(mediaCodecs.c_str()); - if (posMediaCodec < 0) { - posMediaCodec = overrides.size(); - overrides.insert("\n<MediaCodecs>\n</MediaCodecs>\n", posMediaCodec); - posMediaCodec = overrides.find(mediaCodecs.c_str(), posMediaCodec); - } - posEncodersDecoders = posMediaCodec + mediaCodecs.size(); - AString codecs = AStringPrintf( - "\n %s\n %s", encodersDecoders.c_str(), encodersDecodersEnd.c_str()); - overrides.insert(codecs.c_str(), posEncodersDecoders); - posEncodersDecoders = overrides.find(encodersDecoders.c_str(), posEncodersDecoders); - } - posCodec = posEncodersDecoders + encodersDecoders.size(); - AString codec = AStringPrintf( - "\n <MediaCodec name=%s type=%s update=\"true\" >\n </MediaCodec>", - name.c_str(), - mime.c_str()); - overrides.insert(codec.c_str(), posCodec); - posCodec = overrides.find(name.c_str()); - } - - // insert to existing entry - ssize_t posMime = overrides.find(mime.c_str(), posCodec); - ssize_t posEnd = overrides.find(">", posCodec); - if (posEnd < 0) { - ALOGE("Format error in overrides file."); - return; - } - if (posMime < 0 || posMime > posEnd) { - // new mime for an existing component - AString codecEnd = "</MediaCodec>"; - posInsert = overrides.find(codecEnd.c_str(), posCodec) + codecEnd.size(); - AString codec = AStringPrintf( - "\n <MediaCodec name=%s type=%s update=\"true\" >\n </MediaCodec>", - name.c_str(), - mime.c_str()); - overrides.insert(codec.c_str(), posInsert); - posInsert = overrides.find(">", posInsert) + 1; - } else { - posInsert = posEnd + 1; - } - + AString codec = + AStringPrintf(" <MediaCodec name=\"%s\" type=\"%s\" update=\"true\" >\n", + name.c_str(), + mime.c_str()); + ret.append(codec); CodecSettings settings = results.valueAt(i); for (size_t i = 0; i < settings.size(); ++i) { // WARNING: we assume all the settings are "Limit". Currently we have only one type // of setting in this case, which is "max-supported-instances". - AString strInsert = AStringPrintf( - "\n <Limit name=\"%s\" value=\"%s\" />", + AString setting = AStringPrintf( + " <Limit name=\"%s\" value=\"%s\" />\n", settings.keyAt(i).c_str(), settings.valueAt(i).c_str()); - overrides.insert(strInsert, posInsert); + ret.append(setting); } + ret.append(" </MediaCodec>\n"); } + return ret; +} - if (!LOG_NDEBUG) { - ALOGV("New overrides:"); - printLongString(overrides.c_str(), overrides.size()); +void exportResultsToXML( + const char *fileName, + const CodecSettings& global_results, + const KeyedVector<AString, CodecSettings>& encoder_results, + const KeyedVector<AString, CodecSettings>& decoder_results) { + if (global_results.size() == 0 && encoder_results.size() == 0 && decoder_results.size() == 0) { + return; + } + + AString overrides; + overrides.append("<MediaCodecs>\n"); + if (global_results.size() > 0) { + overrides.append(" <Settings>\n"); + overrides.append(globalResultsToXml(global_results)); + overrides.append(" </Settings>\n"); + } + if (encoder_results.size() > 0) { + overrides.append(" <Encoders>\n"); + overrides.append(codecResultsToXml(encoder_results)); + overrides.append(" </Encoders>\n"); + } + if (decoder_results.size() > 0) { + overrides.append(" <Decoders>\n"); + overrides.append(codecResultsToXml(decoder_results)); + overrides.append(" </Decoders>\n"); } + overrides.append("</MediaCodecs>\n"); - f = fopen(fileName, "wb"); + FILE *f = fopen(fileName, "wb"); if (f == NULL) { ALOGE("Failed to open %s for writing.", fileName); return; diff --git a/media/libstagefright/MediaCodecListOverrides.h b/media/libstagefright/MediaCodecListOverrides.h index c6cc2ea..e350d2a 100644 --- a/media/libstagefright/MediaCodecListOverrides.h +++ b/media/libstagefright/MediaCodecListOverrides.h @@ -26,24 +26,28 @@ namespace android { +extern const char *kProfilingResults; + struct MediaCodecInfo; bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2); -bool splitString( - const AString &s, const AString &delimiter, AString *s1, AString *s2, AString *s3); +// profile codecs and save the result to xml file named kProfilingResults. +void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos); +// profile codecs and save the result to global_results, encoder_results and decoder_results. void profileCodecs( const Vector<sp<MediaCodecInfo>> &infos, - KeyedVector<AString, CodecSettings> *results, - bool forceToMeasure = false); // forceToMeasure is mainly for testing - -void applyCodecSettings( - const AString& codecInfo, - const CodecSettings &settings, - Vector<sp<MediaCodecInfo>> *infos); - -void exportResultsToXML(const char *fileName, const KeyedVector<AString, CodecSettings>& results); + CodecSettings *global_results, + KeyedVector<AString, CodecSettings> *encoder_results, + KeyedVector<AString, CodecSettings> *decoder_results, + bool forceToMeasure = false); + +void exportResultsToXML( + const char *fileName, + const CodecSettings& global_results, + const KeyedVector<AString, CodecSettings>& encoder_results, + const KeyedVector<AString, CodecSettings>& decoder_results); } // namespace android diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 8d4bab8..aa6a7c0 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -43,6 +43,7 @@ #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXCodec.h> +#include <media/stagefright/SurfaceUtils.h> #include <media/stagefright/Utils.h> #include <media/stagefright/SkipCutBuffer.h> #include <utils/Vector.h> @@ -1783,35 +1784,6 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { return OK; } -status_t OMXCodec::applyRotation() { - sp<MetaData> meta = mSource->getFormat(); - - int32_t rotationDegrees; - if (!meta->findInt32(kKeyRotation, &rotationDegrees)) { - rotationDegrees = 0; - } - - uint32_t transform; - switch (rotationDegrees) { - case 0: transform = 0; break; - case 90: transform = HAL_TRANSFORM_ROT_90; break; - case 180: transform = HAL_TRANSFORM_ROT_180; break; - case 270: transform = HAL_TRANSFORM_ROT_270; break; - default: transform = 0; break; - } - - status_t err = OK; - - if (transform) { - err = native_window_set_buffers_transform( - mNativeWindow.get(), transform); - ALOGE("native_window_set_buffers_transform failed: %s (%d)", - strerror(-err), -err); - } - - return err; -} - status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { // Get the number of buffers needed. OMX_PARAM_PORTDEFINITIONTYPE def; @@ -1825,30 +1797,11 @@ status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { return err; } - err = native_window_set_buffers_dimensions( - mNativeWindow.get(), - def.format.video.nFrameWidth, - def.format.video.nFrameHeight); - - if (err != 0) { - ALOGE("native_window_set_buffers_dimensions failed: %s (%d)", - strerror(-err), -err); - return err; - } - - err = native_window_set_buffers_format( - mNativeWindow.get(), - def.format.video.eColorFormat); - - if (err != 0) { - ALOGE("native_window_set_buffers_format failed: %s (%d)", - strerror(-err), -err); - return err; - } + sp<MetaData> meta = mSource->getFormat(); - err = applyRotation(); - if (err != OK) { - return err; + int32_t rotationDegrees; + if (!meta->findInt32(kKeyRotation, &rotationDegrees)) { + rotationDegrees = 0; } // Set up the native window. @@ -1859,34 +1812,19 @@ status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { // XXX: Currently this error is logged, but not fatal. usage = 0; } + if (mFlags & kEnableGrallocUsageProtected) { usage |= GRALLOC_USAGE_PROTECTED; } - // Make sure to check whether either Stagefright or the video decoder - // requested protected buffers. - if (usage & GRALLOC_USAGE_PROTECTED) { - // Verify that the ANativeWindow sends images directly to - // SurfaceFlinger. - int queuesToNativeWindow = 0; - err = mNativeWindow->query( - mNativeWindow.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, - &queuesToNativeWindow); - if (err != 0) { - ALOGE("error authenticating native window: %d", err); - return err; - } - if (queuesToNativeWindow != 1) { - ALOGE("native window could not be authenticated"); - return PERMISSION_DENIED; - } - } - - ALOGV("native_window_set_usage usage=0x%x", usage); - err = native_window_set_usage( - mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP); + err = setNativeWindowSizeFormatAndUsage( + mNativeWindow.get(), + def.format.video.nFrameWidth, + def.format.video.nFrameHeight, + def.format.video.eColorFormat, + rotationDegrees, + usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP); if (err != 0) { - ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err); return err; } @@ -2053,156 +1991,6 @@ OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() { return bufInfo; } -status_t OMXCodec::pushBlankBuffersToNativeWindow() { - status_t err = NO_ERROR; - ANativeWindowBuffer* anb = NULL; - int numBufs = 0; - int minUndequeuedBufs = 0; - - // We need to reconnect to the ANativeWindow as a CPU client to ensure that - // no frames get dropped by SurfaceFlinger assuming that these are video - // frames. - err = native_window_api_disconnect(mNativeWindow.get(), - NATIVE_WINDOW_API_MEDIA); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", - strerror(-err), -err); - return err; - } - - err = native_window_api_connect(mNativeWindow.get(), - NATIVE_WINDOW_API_CPU); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: api_connect failed: %s (%d)", - strerror(-err), -err); - return err; - } - - err = native_window_set_buffers_dimensions(mNativeWindow.get(), 1, 1); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: set_buffers_dimensions failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - err = native_window_set_buffers_format(mNativeWindow.get(), HAL_PIXEL_FORMAT_RGBX_8888); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: set_buffers_format failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - err = native_window_set_usage(mNativeWindow.get(), - GRALLOC_USAGE_SW_WRITE_OFTEN); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: set_usage failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - err = native_window_set_scaling_mode(mNativeWindow.get(), - NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - if (err != OK) { - ALOGE("error pushing blank frames: set_scaling_mode failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - err = mNativeWindow->query(mNativeWindow.get(), - NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query " - "failed: %s (%d)", strerror(-err), -err); - goto error; - } - - numBufs = minUndequeuedBufs + 1; - err = native_window_set_buffer_count(mNativeWindow.get(), numBufs); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - // We push numBufs + 1 buffers to ensure that we've drawn into the same - // buffer twice. This should guarantee that the buffer has been displayed - // on the screen and then been replaced, so an previous video frames are - // guaranteed NOT to be currently displayed. - for (int i = 0; i < numBufs + 1; i++) { - err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - - // Fill the buffer with the a 1x1 checkerboard pattern ;) - uint32_t* img = NULL; - err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: lock failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - *img = 0; - - err = buf->unlock(); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: unlock failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - err = mNativeWindow->queueBuffer(mNativeWindow.get(), - buf->getNativeBuffer(), -1); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)", - strerror(-err), -err); - goto error; - } - - anb = NULL; - } - -error: - - if (err != NO_ERROR) { - // Clean up after an error. - if (anb != NULL) { - mNativeWindow->cancelBuffer(mNativeWindow.get(), anb, -1); - } - - native_window_api_disconnect(mNativeWindow.get(), - NATIVE_WINDOW_API_CPU); - native_window_api_connect(mNativeWindow.get(), - NATIVE_WINDOW_API_MEDIA); - - return err; - } else { - // Clean up after success. - err = native_window_api_disconnect(mNativeWindow.get(), - NATIVE_WINDOW_API_CPU); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", - strerror(-err), -err); - return err; - } - - err = native_window_api_connect(mNativeWindow.get(), - NATIVE_WINDOW_API_MEDIA); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: api_connect failed: %s (%d)", - strerror(-err), -err); - return err; - } - - return NO_ERROR; - } -} - int64_t OMXCodec::getDecodingTimeUs() { CHECK(mIsEncoder && mIsVideo); @@ -2784,7 +2572,7 @@ void OMXCodec::onStateChange(OMX_STATETYPE newState) { // them has made it to the display. This allows the OMX // component teardown to zero out any protected buffers // without the risk of scanning out one of those buffers. - pushBlankBuffersToNativeWindow(); + pushBlankBuffersToNativeWindow(mNativeWindow.get()); } setState(IDLE_TO_LOADED); diff --git a/media/libstagefright/SurfaceUtils.cpp b/media/libstagefright/SurfaceUtils.cpp new file mode 100644 index 0000000..6b62e43 --- /dev/null +++ b/media/libstagefright/SurfaceUtils.cpp @@ -0,0 +1,215 @@ +/* + * Copyright 2015 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 "SurfaceUtils" +#include <utils/Log.h> + +#include <media/stagefright/SurfaceUtils.h> + +#include <gui/Surface.h> + +namespace android { + +status_t setNativeWindowSizeFormatAndUsage( + ANativeWindow *nativeWindow /* nonnull */, + int width, int height, int format, int rotation, int usage) { + status_t err = native_window_set_buffers_dimensions(nativeWindow, width, height); + if (err != NO_ERROR) { + ALOGE("native_window_set_buffers_dimensions failed: %s (%d)", strerror(-err), -err); + return err; + } + + err = native_window_set_buffers_format(nativeWindow, format); + if (err != NO_ERROR) { + ALOGE("native_window_set_buffers_format failed: %s (%d)", strerror(-err), -err); + return err; + } + + int transform = 0; + if ((rotation % 90) == 0) { + switch ((rotation / 90) & 3) { + case 1: transform = HAL_TRANSFORM_ROT_90; break; + case 2: transform = HAL_TRANSFORM_ROT_180; break; + case 3: transform = HAL_TRANSFORM_ROT_270; break; + default: transform = 0; break; + } + } + + err = native_window_set_buffers_transform(nativeWindow, transform); + if (err != NO_ERROR) { + ALOGE("native_window_set_buffers_transform failed: %s (%d)", strerror(-err), -err); + return err; + } + + // Make sure to check whether either Stagefright or the video decoder + // requested protected buffers. + if (usage & GRALLOC_USAGE_PROTECTED) { + // Verify that the ANativeWindow sends images directly to + // SurfaceFlinger. + int queuesToNativeWindow = 0; + err = nativeWindow->query( + nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &queuesToNativeWindow); + if (err != NO_ERROR) { + ALOGE("error authenticating native window: %s (%d)", strerror(-err), -err); + return err; + } + if (queuesToNativeWindow != 1) { + ALOGE("native window could not be authenticated"); + return PERMISSION_DENIED; + } + } + + int consumerUsage = 0; + err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_CONSUMER_USAGE_BITS, &consumerUsage); + if (err != NO_ERROR) { + ALOGW("failed to get consumer usage bits. ignoring"); + err = NO_ERROR; + } + + int finalUsage = usage | consumerUsage; + ALOGV("gralloc usage: %#x(producer) + %#x(consumer) = %#x", usage, consumerUsage, finalUsage); + err = native_window_set_usage(nativeWindow, finalUsage); + if (err != NO_ERROR) { + ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err); + return err; + } + + err = native_window_set_scaling_mode( + nativeWindow, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + if (err != NO_ERROR) { + ALOGE("native_window_set_scaling_mode failed: %s (%d)", strerror(-err), -err); + return err; + } + + ALOGD("set up nativeWindow %p for %dx%d, color %#x, rotation %d, usage %#x", + nativeWindow, width, height, format, rotation, finalUsage); + return NO_ERROR; +} + +status_t pushBlankBuffersToNativeWindow(ANativeWindow *nativeWindow /* nonnull */) { + status_t err = NO_ERROR; + ANativeWindowBuffer* anb = NULL; + int numBufs = 0; + int minUndequeuedBufs = 0; + + // We need to reconnect to the ANativeWindow as a CPU client to ensure that + // no frames get dropped by SurfaceFlinger assuming that these are video + // frames. + err = native_window_api_disconnect(nativeWindow, NATIVE_WINDOW_API_MEDIA); + if (err != NO_ERROR) { + ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", strerror(-err), -err); + return err; + } + + err = native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_CPU); + if (err != NO_ERROR) { + ALOGE("error pushing blank frames: api_connect failed: %s (%d)", strerror(-err), -err); + (void)native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_MEDIA); + return err; + } + + err = setNativeWindowSizeFormatAndUsage( + nativeWindow, 1, 1, HAL_PIXEL_FORMAT_RGBX_8888, 0, GRALLOC_USAGE_SW_WRITE_OFTEN); + if (err != NO_ERROR) { + goto error; + } + + static_cast<Surface*>(nativeWindow)->getIGraphicBufferProducer()->allowAllocation(true); + + err = nativeWindow->query(nativeWindow, + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs); + if (err != NO_ERROR) { + ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query " + "failed: %s (%d)", strerror(-err), -err); + goto error; + } + + numBufs = minUndequeuedBufs + 1; + err = native_window_set_buffer_count(nativeWindow, numBufs); + if (err != NO_ERROR) { + ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)", strerror(-err), -err); + goto error; + } + + // We push numBufs + 1 buffers to ensure that we've drawn into the same + // buffer twice. This should guarantee that the buffer has been displayed + // on the screen and then been replaced, so an previous video frames are + // guaranteed NOT to be currently displayed. + for (int i = 0; i < numBufs + 1; i++) { + err = native_window_dequeue_buffer_and_wait(nativeWindow, &anb); + if (err != NO_ERROR) { + ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)", + strerror(-err), -err); + break; + } + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + + // Fill the buffer with the a 1x1 checkerboard pattern ;) + uint32_t *img = NULL; + err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + if (err != NO_ERROR) { + ALOGE("error pushing blank frames: lock failed: %s (%d)", strerror(-err), -err); + break; + } + + *img = 0; + + err = buf->unlock(); + if (err != NO_ERROR) { + ALOGE("error pushing blank frames: unlock failed: %s (%d)", strerror(-err), -err); + break; + } + + err = nativeWindow->queueBuffer(nativeWindow, buf->getNativeBuffer(), -1); + if (err != NO_ERROR) { + ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)", strerror(-err), -err); + break; + } + + anb = NULL; + } + +error: + + if (anb != NULL) { + nativeWindow->cancelBuffer(nativeWindow, anb, -1); + anb = NULL; + } + + // Clean up after success or error. + status_t err2 = native_window_api_disconnect(nativeWindow, NATIVE_WINDOW_API_CPU); + if (err2 != NO_ERROR) { + ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", strerror(-err2), -err2); + if (err == NO_ERROR) { + err = err2; + } + } + + err2 = native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_MEDIA); + if (err2 != NO_ERROR) { + ALOGE("error pushing blank frames: api_connect failed: %s (%d)", strerror(-err), -err); + if (err == NO_ERROR) { + err = err2; + } + } + + return err; +} + +} // namespace android + diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp index 8f356b6..c559682 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -364,7 +364,7 @@ void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { } else { numFrames = vorbis_dsp_pcmout( mState, (int16_t *)outHeader->pBuffer, - kMaxNumSamplesPerBuffer); + (kMaxNumSamplesPerBuffer / mVi->channels)); if (numFrames < 0) { ALOGE("vorbis_dsp_pcmout returned %d", numFrames); diff --git a/media/libstagefright/foundation/ADebug.cpp b/media/libstagefright/foundation/ADebug.cpp index ec4a960..0d1cea4 100644 --- a/media/libstagefright/foundation/ADebug.cpp +++ b/media/libstagefright/foundation/ADebug.cpp @@ -19,6 +19,7 @@ #include <ctype.h> #define LOG_TAG "ADebug" +#include <cutils/atomic.h> #include <utils/Log.h> #include <utils/misc.h> @@ -113,5 +114,43 @@ char *ADebug::GetDebugName(const char *name) { return debugName; } +//static +bool ADebug::getExperimentFlag( + bool allow, const char *name, uint64_t modulo, + uint64_t limit, uint64_t plus, uint64_t timeDivisor) { + static volatile int32_t haveSerial = 0; + static uint64_t serialNum; + if (!android_atomic_acquire_load(&haveSerial)) { + // calculate initial counter value based on serial number + static char serial[PROPERTY_VALUE_MAX]; + property_get("ro.serialno", serial, "0"); + uint64_t num = 0; // it is okay for this number to overflow + for (size_t i = 0; i < NELEM(serial) && serial[i] != '\0'; ++i) { + const char &c = serial[i]; + // try to use most letters of serialno + if (isdigit(c)) { + num = num * 10 + (c - '0'); + } else if (islower(c)) { + num = num * 26 + (c - 'a'); + } else if (isupper(c)) { + num = num * 26 + (c - 'A'); + } else { + num = num * 256 + c; + } + } + ALOGI("got serial"); + serialNum = num; + android_atomic_release_store(1, &haveSerial); + } + ALOGI("serial: %llu, time: %llu", (long long)serialNum, (long long)time(NULL)); + // MINOR: use modulo for counter and time, so that their sum does not + // roll over, and mess up the correlation between related experiments. + // e.g. keep (a mod 2N) = 0 impl (a mod N) = 0 + time_t counter = (time(NULL) / timeDivisor) % modulo + plus + serialNum % modulo; + bool enable = allow && (counter % modulo < limit); + ALOGI("experiment '%s': %s", name, enable ? "ENABLED" : "disabled"); + return enable; +} + } // namespace android diff --git a/media/libstagefright/http/MediaHTTP.cpp b/media/libstagefright/http/MediaHTTP.cpp index bb89567..2d9b3d4 100644 --- a/media/libstagefright/http/MediaHTTP.cpp +++ b/media/libstagefright/http/MediaHTTP.cpp @@ -30,12 +30,11 @@ namespace android { MediaHTTP::MediaHTTP(const sp<IMediaHTTPConnection> &conn) - : mInitCheck(NO_INIT), + : mInitCheck((conn != NULL) ? OK : NO_INIT), mHTTPConnection(conn), mCachedSizeValid(false), mCachedSize(0ll), mDrmManagerClient(NULL) { - mInitCheck = OK; } MediaHTTP::~MediaHTTP() { @@ -171,6 +170,10 @@ void MediaHTTP::getDrmInfo( } String8 MediaHTTP::getUri() { + if (mInitCheck != OK) { + return String8::empty(); + } + String8 uri; if (OK == mHTTPConnection->getUri(&uri)) { return uri; diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index d8c38e7..64a8532 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -1503,11 +1503,10 @@ void LiveSession::changeConfiguration( ALOGV("discarding fetcher-%d", fetcher->getFetcherID()); fetcher->stopAsync(); } else { - float threshold = -1.0f; // always finish fetching by default + float threshold = 0.0f; // default to pause after current block (47Kbytes) bool disconnect = false; if (timeUs >= 0ll) { // seeking, no need to finish fetching - threshold = 0.0f; disconnect = true; } else if (delayRemoval) { // adapting, abort if remaining of current segment is over threshold diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index b8cc5d1..4851528 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -1434,11 +1434,17 @@ bool PlaylistFetcher::adjustSeqNumberWithAnchorTime(int64_t anchorTimeUs) { int64_t minDiffUs, maxDiffUs; if (mSeekMode == LiveSession::kSeekModeNextSample) { + // if the previous fetcher paused in the middle of a segment, we + // want to start at a segment that overlaps the last sample minDiffUs = -mPlaylist->getTargetDuration(); maxDiffUs = 0ll; } else { + // if the previous fetcher paused at the end of a segment, ideally + // we want to start at the segment that's roughly aligned with its + // next segment, but if the two variants are not well aligned we + // adjust the diff to within (-T/2, T/2) minDiffUs = -mPlaylist->getTargetDuration() / 2; - maxDiffUs = mPlaylist->getTargetDuration(); + maxDiffUs = mPlaylist->getTargetDuration() / 2; } int32_t oldSeqNumber = mSeqNumber; @@ -1621,6 +1627,9 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu ALOGE("MPEG2 Transport streams do not contain subtitles."); return ERROR_MALFORMED; } + if (stream == LiveSession::STREAMTYPE_METADATA) { + continue; + } ATSParser::SourceType type =LiveSession::getSourceTypeForStream(stream); sp<AnotherPacketSource> source = static_cast<AnotherPacketSource *>( diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 00f071b..ba17e90 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -1673,21 +1673,11 @@ private: } size_t n = strlen(baseURL); - if (baseURL[n - 1] == '/') { - out->setTo(baseURL); - out->append(url); - } else { - const char *slashPos = strrchr(baseURL, '/'); - - if (slashPos > &baseURL[6]) { - out->setTo(baseURL, slashPos - baseURL); - } else { - out->setTo(baseURL); - } - + out->setTo(baseURL); + if (baseURL[n - 1] != '/') { out->append("/"); - out->append(url); } + out->append(url); return true; } diff --git a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp index 170cde3..cee62a3 100644 --- a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp +++ b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp @@ -31,29 +31,8 @@ namespace android { static const char kTestOverridesStr[] = "<MediaCodecs>\n" " <Settings>\n" -" <Setting name=\"max-max-supported-instances\" value=\"8\" update=\"true\" />\n" -" </Settings>\n" -" <Encoders>\n" -" <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n" -" <Quirk name=\"requires-allocate-on-input-ports\" />\n" -" <Limit name=\"bitrate\" range=\"1-20000000\" />\n" -" <Feature name=\"can-swap-width-height\" />\n" -" </MediaCodec>\n" -" </Encoders>\n" -" <Decoders>\n" -" <MediaCodec name=\"OMX.qcom.video.decoder.avc\" type=\"video/avc\" update=\"true\" >\n" -" <Quirk name=\"requires-allocate-on-input-ports\" />\n" -" <Limit name=\"size\" min=\"64x64\" max=\"1920x1088\" />\n" -" </MediaCodec>\n" -" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"different_mime\" update=\"true\" >\n" -" </MediaCodec>\n" -" </Decoders>\n" -"</MediaCodecs>\n"; - -static const char kTestOverridesStrNew1[] = -"<MediaCodecs>\n" -" <Settings>\n" -" <Setting name=\"max-max-supported-instances\" value=\"8\" update=\"true\" />\n" +" <Setting name=\"supports-multiple-secure-codecs\" value=\"false\" />\n" +" <Setting name=\"supports-secure-with-non-secure-codec\" value=\"true\" />\n" " </Settings>\n" " <Encoders>\n" " <MediaCodec name=\"OMX.qcom.video.encoder.avc\" type=\"video/avc\" update=\"true\" >\n" @@ -61,57 +40,21 @@ static const char kTestOverridesStrNew1[] = " </MediaCodec>\n" " <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n" " <Limit name=\"max-supported-instances\" value=\"4\" />\n" -" <Quirk name=\"requires-allocate-on-input-ports\" />\n" -" <Limit name=\"bitrate\" range=\"1-20000000\" />\n" -" <Feature name=\"can-swap-width-height\" />\n" " </MediaCodec>\n" " </Encoders>\n" " <Decoders>\n" -" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n" -" <Limit name=\"max-supported-instances\" value=\"3\" />\n" -" </MediaCodec>\n" -" <MediaCodec name=\"OMX.qcom.video.decoder.h263\" type=\"video/3gpp\" update=\"true\" >\n" -" <Limit name=\"max-supported-instances\" value=\"4\" />\n" -" </MediaCodec>\n" " <MediaCodec name=\"OMX.qcom.video.decoder.avc.secure\" type=\"video/avc\" update=\"true\" >\n" " <Limit name=\"max-supported-instances\" value=\"1\" />\n" " </MediaCodec>\n" -" <MediaCodec name=\"OMX.qcom.video.decoder.avc\" type=\"video/avc\" update=\"true\" >\n" -" <Quirk name=\"requires-allocate-on-input-ports\" />\n" -" <Limit name=\"size\" min=\"64x64\" max=\"1920x1088\" />\n" -" </MediaCodec>\n" -" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"different_mime\" update=\"true\" >\n" +" <MediaCodec name=\"OMX.qcom.video.decoder.h263\" type=\"video/3gpp\" update=\"true\" >\n" +" <Limit name=\"max-supported-instances\" value=\"4\" />\n" " </MediaCodec>\n" " <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"video/mpeg2\" update=\"true\" >\n" " <Limit name=\"max-supported-instances\" value=\"3\" />\n" " </MediaCodec>\n" -" </Decoders>\n" -"</MediaCodecs>\n"; - -static const char kTestOverridesStrNew2[] = -"\n" -"<MediaCodecs>\n" -" <Encoders>\n" -" <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n" -" <Limit name=\"max-supported-instances\" value=\"4\" />\n" -" </MediaCodec>\n" -" <MediaCodec name=\"OMX.qcom.video.encoder.avc\" type=\"video/avc\" update=\"true\" >\n" -" <Limit name=\"max-supported-instances\" value=\"4\" />\n" -" </MediaCodec>\n" -" </Encoders>\n" -" <Decoders>\n" " <MediaCodec name=\"OMX.qcom.video.decoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n" " <Limit name=\"max-supported-instances\" value=\"3\" />\n" " </MediaCodec>\n" -" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"video/mpeg2\" update=\"true\" >\n" -" <Limit name=\"max-supported-instances\" value=\"3\" />\n" -" </MediaCodec>\n" -" <MediaCodec name=\"OMX.qcom.video.decoder.h263\" type=\"video/3gpp\" update=\"true\" >\n" -" <Limit name=\"max-supported-instances\" value=\"4\" />\n" -" </MediaCodec>\n" -" <MediaCodec name=\"OMX.qcom.video.decoder.avc.secure\" type=\"video/avc\" update=\"true\" >\n" -" <Limit name=\"max-supported-instances\" value=\"1\" />\n" -" </MediaCodec>\n" " </Decoders>\n" "</MediaCodecs>\n"; @@ -119,53 +62,6 @@ class MediaCodecListOverridesTest : public ::testing::Test { public: MediaCodecListOverridesTest() {} - void verifyOverrides(const KeyedVector<AString, CodecSettings> &overrides) { - EXPECT_EQ(3u, overrides.size()); - - EXPECT_TRUE(overrides.keyAt(0) == "OMX.qcom.video.decoder.avc video/avc decoder"); - const CodecSettings &settings0 = overrides.valueAt(0); - EXPECT_EQ(1u, settings0.size()); - EXPECT_TRUE(settings0.keyAt(0) == "max-supported-instances"); - EXPECT_TRUE(settings0.valueAt(0) == "4"); - - EXPECT_TRUE(overrides.keyAt(1) == "OMX.qcom.video.encoder.avc video/avc encoder"); - const CodecSettings &settings1 = overrides.valueAt(1); - EXPECT_EQ(1u, settings1.size()); - EXPECT_TRUE(settings1.keyAt(0) == "max-supported-instances"); - EXPECT_TRUE(settings1.valueAt(0) == "3"); - - EXPECT_TRUE(overrides.keyAt(2) == "global"); - const CodecSettings &settings2 = overrides.valueAt(2); - EXPECT_EQ(3u, settings2.size()); - EXPECT_TRUE(settings2.keyAt(0) == "max-max-supported-instances"); - EXPECT_TRUE(settings2.valueAt(0) == "8"); - EXPECT_TRUE(settings2.keyAt(1) == "supports-multiple-secure-codecs"); - EXPECT_TRUE(settings2.valueAt(1) == "false"); - EXPECT_TRUE(settings2.keyAt(2) == "supports-secure-with-non-secure-codec"); - EXPECT_TRUE(settings2.valueAt(2) == "true"); - } - - void verifySetting(const sp<AMessage> &details, const char *name, const char *value) { - AString value1; - EXPECT_TRUE(details->findString(name, &value1)); - EXPECT_TRUE(value1 == value); - } - - void createTestInfos(Vector<sp<MediaCodecInfo>> *infos) { - const char *name = "OMX.qcom.video.decoder.avc"; - const bool encoder = false; - const char *mime = "video/avc"; - sp<MediaCodecInfo> info = new MediaCodecInfo(name, encoder, mime); - infos->push_back(info); - const sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(mime); - const sp<AMessage> details = caps->getDetails(); - details->setString("cap1", "value1"); - details->setString("max-max-supported-instances", "16"); - - info = new MediaCodecInfo("anothercodec", true, "anothermime"); - infos->push_back(info); - } - void addMaxInstancesSetting( const AString &key, const AString &value, @@ -175,16 +71,34 @@ public: results->add(key, settings); } - void exportTestResultsToXML(const char *fileName) { - KeyedVector<AString, CodecSettings> r; - addMaxInstancesSetting("OMX.qcom.video.decoder.avc.secure video/avc decoder", "1", &r); - addMaxInstancesSetting("OMX.qcom.video.decoder.h263 video/3gpp decoder", "4", &r); - addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg2 video/mpeg2 decoder", "3", &r); - addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg4 video/mp4v-es decoder", "3", &r); - addMaxInstancesSetting("OMX.qcom.video.encoder.avc video/avc encoder", "4", &r); - addMaxInstancesSetting("OMX.qcom.video.encoder.mpeg4 video/mp4v-es encoder", "4", &r); + void verifyProfileResults(const KeyedVector<AString, CodecSettings> &results) { + EXPECT_LT(0u, results.size()); + for (size_t i = 0; i < results.size(); ++i) { + AString key = results.keyAt(i); + CodecSettings settings = results.valueAt(i); + EXPECT_EQ(1u, settings.size()); + EXPECT_TRUE(settings.keyAt(0) == "max-supported-instances"); + AString valueS = settings.valueAt(0); + int32_t value = strtol(valueS.c_str(), NULL, 10); + EXPECT_LT(0, value); + ALOGV("profileCodecs results %s %s", key.c_str(), valueS.c_str()); + } + } - exportResultsToXML(fileName, r); + void exportTestResultsToXML(const char *fileName) { + CodecSettings gR; + gR.add("supports-multiple-secure-codecs", "false"); + gR.add("supports-secure-with-non-secure-codec", "true"); + KeyedVector<AString, CodecSettings> eR; + addMaxInstancesSetting("OMX.qcom.video.encoder.avc video/avc", "4", &eR); + addMaxInstancesSetting("OMX.qcom.video.encoder.mpeg4 video/mp4v-es", "4", &eR); + KeyedVector<AString, CodecSettings> dR; + addMaxInstancesSetting("OMX.qcom.video.decoder.avc.secure video/avc", "1", &dR); + addMaxInstancesSetting("OMX.qcom.video.decoder.h263 video/3gpp", "4", &dR); + addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg2 video/mpeg2", "3", &dR); + addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg4 video/mp4v-es", "3", &dR); + + exportResultsToXML(fileName, gR, eR, dR); } }; @@ -198,18 +112,6 @@ TEST_F(MediaCodecListOverridesTest, splitString) { EXPECT_TRUE(splitString(s, delimiter, &s1, &s2)); EXPECT_TRUE(s1 == "abc"); EXPECT_TRUE(s2 == "123"); - - s = "abc123xyz"; - delimiter = ","; - AString s3; - EXPECT_FALSE(splitString(s, delimiter, &s1, &s2, &s3)); - s = "abc,123xyz"; - EXPECT_FALSE(splitString(s, delimiter, &s1, &s2, &s3)); - s = "abc,123,xyz"; - EXPECT_TRUE(splitString(s, delimiter, &s1, &s2, &s3)); - EXPECT_TRUE(s1 == "abc"); - EXPECT_TRUE(s2 == "123" ); - EXPECT_TRUE(s3 == "xyz"); } // TODO: the codec component never returns OMX_EventCmdComplete in unit test. @@ -219,76 +121,16 @@ TEST_F(MediaCodecListOverridesTest, DISABLED_profileCodecs) { for (size_t i = 0; i < list->countCodecs(); ++i) { infos.push_back(list->getCodecInfo(i)); } - KeyedVector<AString, CodecSettings> results; - profileCodecs(infos, &results, true /* forceToMeasure */); - EXPECT_LT(0u, results.size()); - for (size_t i = 0; i < results.size(); ++i) { - AString key = results.keyAt(i); - CodecSettings settings = results.valueAt(i); - EXPECT_EQ(1u, settings.size()); - EXPECT_TRUE(settings.keyAt(0) == "max-supported-instances"); - AString valueS = settings.valueAt(0); - int32_t value = strtol(valueS.c_str(), NULL, 10); - EXPECT_LT(0, value); - ALOGV("profileCodecs results %s %s", key.c_str(), valueS.c_str()); - } -} - -TEST_F(MediaCodecListOverridesTest, applyCodecSettings) { - AString codecInfo = "OMX.qcom.video.decoder.avc video/avc decoder"; - Vector<sp<MediaCodecInfo>> infos; - createTestInfos(&infos); - CodecSettings settings; - settings.add("max-supported-instances", "3"); - settings.add("max-max-supported-instances", "8"); - applyCodecSettings(codecInfo, settings, &infos); - - EXPECT_EQ(2u, infos.size()); - EXPECT_TRUE(AString(infos[0]->getCodecName()) == "OMX.qcom.video.decoder.avc"); - const sp<AMessage> details = infos[0]->getCapabilitiesFor("video/avc")->getDetails(); - verifySetting(details, "max-supported-instances", "3"); - verifySetting(details, "max-max-supported-instances", "8"); - - EXPECT_TRUE(AString(infos[1]->getCodecName()) == "anothercodec"); - EXPECT_EQ(0u, infos[1]->getCapabilitiesFor("anothermime")->getDetails()->countEntries()); -} - -TEST_F(MediaCodecListOverridesTest, exportResultsToExistingFile) { - const char *fileName = "/sdcard/mediacodec_list_overrides_test.xml"; - remove(fileName); - - FILE *f = fopen(fileName, "wb"); - if (f == NULL) { - ALOGW("Failed to open %s for writing.", fileName); - return; - } - EXPECT_EQ( - strlen(kTestOverridesStr), - fwrite(kTestOverridesStr, 1, strlen(kTestOverridesStr), f)); - fclose(f); - - exportTestResultsToXML(fileName); - - // verify - AString overrides; - f = fopen(fileName, "rb"); - ASSERT_TRUE(f != NULL); - fseek(f, 0, SEEK_END); - long size = ftell(f); - rewind(f); - - char *buf = (char *)malloc(size); - EXPECT_EQ((size_t)1, fread(buf, size, 1, f)); - overrides.setTo(buf, size); - fclose(f); - free(buf); - - EXPECT_TRUE(overrides == kTestOverridesStrNew1); - - remove(fileName); + CodecSettings global_results; + KeyedVector<AString, CodecSettings> encoder_results; + KeyedVector<AString, CodecSettings> decoder_results; + profileCodecs( + infos, &global_results, &encoder_results, &decoder_results, true /* forceToMeasure */); + verifyProfileResults(encoder_results); + verifyProfileResults(decoder_results); } -TEST_F(MediaCodecListOverridesTest, exportResultsToEmptyFile) { +TEST_F(MediaCodecListOverridesTest, exportTestResultsToXML) { const char *fileName = "/sdcard/mediacodec_list_overrides_test.xml"; remove(fileName); @@ -308,7 +150,7 @@ TEST_F(MediaCodecListOverridesTest, exportResultsToEmptyFile) { fclose(f); free(buf); - EXPECT_TRUE(overrides == kTestOverridesStrNew2); + EXPECT_TRUE(overrides == kTestOverridesStr); remove(fileName); } diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 485e320..93b1642 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -757,8 +757,12 @@ status_t AudioFlinger::setMasterVolume(float value) // assigned to HALs which do not have master volume support will apply // master volume during the mix operation. Threads with HALs which do // support master volume will simply ignore the setting. - for (size_t i = 0; i < mPlaybackThreads.size(); i++) + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i)->isDuplicating()) { + continue; + } mPlaybackThreads.valueAt(i)->setMasterVolume(value); + } return NO_ERROR; } @@ -875,8 +879,12 @@ status_t AudioFlinger::setMasterMute(bool muted) // assigned to HALs which do not have master mute support will apply master // mute during the mix operation. Threads with HALs which do support master // mute will simply ignore the setting. - for (size_t i = 0; i < mPlaybackThreads.size(); i++) + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i)->isDuplicating()) { + continue; + } mPlaybackThreads.valueAt(i)->setMasterMute(muted); + } return NO_ERROR; } @@ -1894,11 +1902,10 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output) if (thread->type() == ThreadBase::MIXER) { for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - if (mPlaybackThreads.valueAt(i)->type() == ThreadBase::DUPLICATING) { + if (mPlaybackThreads.valueAt(i)->isDuplicating()) { DuplicatingThread *dupThread = (DuplicatingThread *)mPlaybackThreads.valueAt(i).get(); dupThread->removeOutputTrack((MixerThread *)thread.get()); - } } } @@ -1927,7 +1934,7 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output) // The thread entity (active unit of execution) is no longer running here, // but the ThreadBase container still exists. - if (thread->type() != ThreadBase::DUPLICATING) { + if (!thread->isDuplicating()) { closeOutputFinish(thread); } @@ -2375,6 +2382,9 @@ AudioFlinger::PlaybackThread *AudioFlinger::primaryPlaybackThread_l() const { for (size_t i = 0; i < mPlaybackThreads.size(); i++) { PlaybackThread *thread = mPlaybackThreads.valueAt(i).get(); + if(thread->isDuplicating()) { + continue; + } AudioStreamOut *output = thread->getOutput(); if (output != NULL && output->audioHwDev == mPrimaryHardwareDev) { return thread; @@ -2689,7 +2699,7 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId, // Check whether the destination thread has a channel count of FCC_2, which is // currently required for (most) effects. Prevent moving the effect chain here rather // than disabling the addEffect_l() call in dstThread below. - if ((dstThread->type() == ThreadBase::MIXER || dstThread->type() == ThreadBase::DUPLICATING) && + if ((dstThread->type() == ThreadBase::MIXER || dstThread->isDuplicating()) && dstThread->mChannelCount != FCC_2) { ALOGW("moveEffectChain_l() effect chain failed because" " destination thread %p channel count(%u) != %u", diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 7040af4..959c140 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -775,7 +775,8 @@ inline void AudioMixer::track_t::adjustVolumeRamp(bool aux, bool useFloat) { if (useFloat) { for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) { - if (mVolumeInc[i] != 0 && fabs(mVolume[i] - mPrevVolume[i]) <= fabs(mVolumeInc[i])) { + if ((mVolumeInc[i] > 0 && mPrevVolume[i] + mVolumeInc[i] >= mVolume[i]) || + (mVolumeInc[i] < 0 && mPrevVolume[i] + mVolumeInc[i] <= mVolume[i])) { volumeInc[i] = 0; prevVolume[i] = volume[i] << 16; mVolumeInc[i] = 0.; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 2c4d801..f2af312 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -5215,10 +5215,13 @@ void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread) mOutputTracks[i]->destroy(); mOutputTracks.removeAt(i); updateWaitTime_l(); + if (thread->getOutput() == mOutput) { + mOutput = NULL; + } return; } } - ALOGV("removeOutputTrack(): unkonwn thread: %p", thread); + ALOGV("removeOutputTrack(): unknown thread: %p", thread); } // caller must hold mLock diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 0a5597f..37bacae 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -230,6 +230,8 @@ public: // static externally-visible type_t type() const { return mType; } + bool isDuplicating() const { return (mType == DUPLICATING); } + audio_io_handle_t id() const { return mId;} // dynamic externally-visible diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h index a2327ee..e6a767f 100755 --- a/services/audiopolicy/common/include/policy.h +++ b/services/audiopolicy/common/include/policy.h @@ -60,7 +60,7 @@ static inline bool is_state_in_call(int state) * * @return true if the device is a virtual one, false otherwise. */ -static bool is_virtual_input_device(audio_devices_t device) +static inline bool is_virtual_input_device(audio_devices_t device) { if ((device & AUDIO_DEVICE_BIT_IN) != 0) { device &= ~AUDIO_DEVICE_BIT_IN; @@ -78,7 +78,7 @@ static bool is_virtual_input_device(audio_devices_t device) * * @return true if the device needs distinguish on address, false otherwise.. */ -static bool device_distinguishes_on_address(audio_devices_t device) +static inline bool device_distinguishes_on_address(audio_devices_t device) { return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL & ~AUDIO_DEVICE_BIT_IN) != 0); } diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h index aa37eec..d1a2f4f 100644 --- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h @@ -29,7 +29,7 @@ namespace android { class DeviceDescriptor : public AudioPort, public AudioPortConfig { public: - DeviceDescriptor(const String8& name, audio_devices_t type); + DeviceDescriptor(audio_devices_t type); virtual ~DeviceDescriptor() {} @@ -50,10 +50,9 @@ public: status_t dump(int fd, int spaces, int index) const; void log() const; + String8 mTag; String8 mAddress; - static String8 emptyNameStr; - private: audio_devices_t mDeviceType; audio_port_handle_t mId; @@ -73,12 +72,12 @@ public: audio_devices_t types() const { return mDeviceTypes; } void loadDevicesFromType(audio_devices_t types); - void loadDevicesFromName(char *name, const DeviceVector& declaredDevices); + void loadDevicesFromTag(char *tag, const DeviceVector& declaredDevices); sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const; DeviceVector getDevicesFromType(audio_devices_t types) const; sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const; - sp<DeviceDescriptor> getDeviceFromName(const String8& name) const; + sp<DeviceDescriptor> getDeviceFromTag(const String8& tag) const; DeviceVector getDevicesFromTypeAddr(audio_devices_t type, String8 address) const; audio_devices_t getDevicesFromHwModule(audio_module_handle_t moduleHandle) const; diff --git a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp index 9ab1d61..89ef045 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp @@ -218,7 +218,7 @@ void ConfigParsingUtils::loadGlobalConfig(cnode *root, const sp<HwModule>& modul node = node->first_child; while (node) { if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) { - availableOutputDevices.loadDevicesFromName((char *)node->value, + availableOutputDevices.loadDevicesFromTag((char *)node->value, declaredDevices); ALOGV("loadGlobalConfig() Attached Output Devices %08x", availableOutputDevices.types()); @@ -228,13 +228,13 @@ void ConfigParsingUtils::loadGlobalConfig(cnode *root, const sp<HwModule>& modul ARRAY_SIZE(sDeviceTypeToEnumTable), (char *)node->value); if (device != AUDIO_DEVICE_NONE) { - defaultOutputDevice = new DeviceDescriptor(String8("default-output"), device); + defaultOutputDevice = new DeviceDescriptor(device); } else { ALOGW("loadGlobalConfig() default device not specified"); } ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", defaultOutputDevice->type()); } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) { - availableInputDevices.loadDevicesFromName((char *)node->value, + availableInputDevices.loadDevicesFromTag((char *)node->value, declaredDevices); ALOGV("loadGlobalConfig() Available InputDevices %08x", availableInputDevices.types()); } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) { diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 0715eea..797077a 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -24,13 +24,11 @@ namespace android { -String8 DeviceDescriptor::emptyNameStr = String8(""); - -DeviceDescriptor::DeviceDescriptor(const String8& name, audio_devices_t type) : - AudioPort(name, AUDIO_PORT_TYPE_DEVICE, +DeviceDescriptor::DeviceDescriptor(audio_devices_t type) : + AudioPort(String8(""), AUDIO_PORT_TYPE_DEVICE, audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE), - mAddress(""), mDeviceType(type), mId(0) + mTag(""), mAddress(""), mDeviceType(type), mId(0) { } @@ -142,24 +140,21 @@ void DeviceVector::loadDevicesFromType(audio_devices_t types) uint32_t i = 31 - __builtin_clz(types); uint32_t type = 1 << i; types &= ~type; - add(new DeviceDescriptor(String8("device_type"), type | role_bit)); + add(new DeviceDescriptor(type | role_bit)); } } -void DeviceVector::loadDevicesFromName(char *name, +void DeviceVector::loadDevicesFromTag(char *tag, const DeviceVector& declaredDevices) { - char *devName = strtok(name, "|"); - while (devName != NULL) { - if (strlen(devName) != 0) { + char *devTag = strtok(tag, "|"); + while (devTag != NULL) { + if (strlen(devTag) != 0) { audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceTypeToEnumTable, ARRAY_SIZE(sDeviceTypeToEnumTable), - devName); + devTag); if (type != AUDIO_DEVICE_NONE) { - devName = (char *)ConfigParsingUtils::enumToString(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - type); - sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(devName), type); + sp<DeviceDescriptor> dev = new DeviceDescriptor(type); if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX || type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) { dev->mAddress = String8("0"); @@ -167,13 +162,13 @@ void DeviceVector::loadDevicesFromName(char *name, add(dev); } else { sp<DeviceDescriptor> deviceDesc = - declaredDevices.getDeviceFromName(String8(devName)); + declaredDevices.getDeviceFromTag(String8(devTag)); if (deviceDesc != 0) { add(deviceDesc); } } } - devName = strtok(NULL, "|"); + devTag = strtok(NULL, "|"); } } @@ -239,11 +234,11 @@ DeviceVector DeviceVector::getDevicesFromTypeAddr( return devices; } -sp<DeviceDescriptor> DeviceVector::getDeviceFromName(const String8& name) const +sp<DeviceDescriptor> DeviceVector::getDeviceFromTag(const String8& tag) const { sp<DeviceDescriptor> device; for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->mName == name) { + if (itemAt(i)->mTag == tag) { device = itemAt(i); break; } diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp index e955447..7e2050b 100644 --- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -58,7 +58,7 @@ status_t HwModule::loadInput(cnode *root) } else if (strcmp(node->name, CHANNELS_TAG) == 0) { profile->loadInChannels((char *)node->value); } else if (strcmp(node->name, DEVICES_TAG) == 0) { - profile->mSupportedDevices.loadDevicesFromName((char *)node->value, + profile->mSupportedDevices.loadDevicesFromTag((char *)node->value, mDeclaredDevices); } else if (strcmp(node->name, FLAGS_TAG) == 0) { profile->mFlags = ConfigParsingUtils::parseInputFlagNames((char *)node->value); @@ -105,7 +105,7 @@ status_t HwModule::loadOutput(cnode *root) } else if (strcmp(node->name, CHANNELS_TAG) == 0) { profile->loadOutChannels((char *)node->value); } else if (strcmp(node->name, DEVICES_TAG) == 0) { - profile->mSupportedDevices.loadDevicesFromName((char *)node->value, + profile->mSupportedDevices.loadDevicesFromTag((char *)node->value, mDeclaredDevices); } else if (strcmp(node->name, FLAGS_TAG) == 0) { profile->mFlags = ConfigParsingUtils::parseOutputFlagNames((char *)node->value); @@ -154,7 +154,8 @@ status_t HwModule::loadDevice(cnode *root) ALOGW("loadDevice() bad type %08x", type); return BAD_VALUE; } - sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type); + sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(type); + deviceDesc->mTag = String8(root->name); node = root->first_child; while (node) { @@ -172,8 +173,8 @@ status_t HwModule::loadDevice(cnode *root) node = node->next; } - ALOGV("loadDevice() adding device name %s type %08x address %s", - deviceDesc->mName.string(), type, deviceDesc->mAddress.string()); + ALOGV("loadDevice() adding device tag %s type %08x address %s", + deviceDesc->mTag.string(), type, deviceDesc->mAddress.string()); mDeclaredDevices.add(deviceDesc); @@ -189,7 +190,7 @@ status_t HwModule::addOutputProfile(String8 name, const audio_config_t *config, profile->mChannelMasks.add(config->channel_mask); profile->mFormats.add(config->format); - sp<DeviceDescriptor> devDesc = new DeviceDescriptor(name, device); + sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device); devDesc->mAddress = address; profile->mSupportedDevices.add(devDesc); @@ -220,7 +221,7 @@ status_t HwModule::addInputProfile(String8 name, const audio_config_t *config, profile->mChannelMasks.add(config->channel_mask); profile->mFormats.add(config->format); - sp<DeviceDescriptor> devDesc = new DeviceDescriptor(name, device); + sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device); devDesc->mAddress = address; profile->mSupportedDevices.add(devDesc); @@ -350,7 +351,8 @@ sp<DeviceDescriptor> HwModuleCollection::getDeviceDescriptor(const audio_device } sp<DeviceDescriptor> devDesc = - new DeviceDescriptor(String8(device_name != NULL ? device_name : ""), device); + new DeviceDescriptor(device); + devDesc->mName = device_name; devDesc->mAddress = address; return devDesc; } diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk index b0ae835..8d43b89 100755 --- a/services/audiopolicy/enginedefault/Android.mk +++ b/services/audiopolicy/enginedefault/Android.mk @@ -43,6 +43,4 @@ LOCAL_SHARED_LIBRARIES += \ libutils \ libaudioutils \ -include external/stlport/libstlport.mk - include $(BUILD_SHARED_LIBRARY) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index b7eed62..0c02d93 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2648,7 +2648,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mUidCached = getuid(); mpClientInterface = clientInterface; - mDefaultOutputDevice = new DeviceDescriptor(String8("Speaker"), AUDIO_DEVICE_OUT_SPEAKER); + mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER); if (ConfigParsingUtils::loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, mHwModules, mAvailableInputDevices, mAvailableOutputDevices, mDefaultOutputDevice, mSpeakerDrcEnabled) != NO_ERROR) { @@ -4738,7 +4738,7 @@ void AudioPolicyManager::defaultAudioPolicyConfig(void) sp<HwModule> module; sp<IOProfile> profile; sp<DeviceDescriptor> defaultInputDevice = - new DeviceDescriptor(String8("builtin-mic"), AUDIO_DEVICE_IN_BUILTIN_MIC); + new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC); mAvailableOutputDevices.add(mDefaultOutputDevice); mAvailableInputDevices.add(defaultInputDevice); diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index bf1692d..9c4f9cd 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -128,7 +128,6 @@ status_t CameraDeviceClient::submitRequestList(List<sp<CaptureRequest> > request List<const CameraMetadata> metadataRequestList; int32_t requestId = mRequestIdCounter; uint32_t loopCounter = 0; - bool isReprocess = false; for (List<sp<CaptureRequest> >::iterator it = requests.begin(); it != requests.end(); ++it) { sp<CaptureRequest> request = *it; @@ -136,18 +135,15 @@ status_t CameraDeviceClient::submitRequestList(List<sp<CaptureRequest> > request ALOGE("%s: Camera %d: Sent null request.", __FUNCTION__, mCameraId); return BAD_VALUE; - } else if (it == requests.begin()) { - isReprocess = request->mIsReprocess; - if (isReprocess && !mInputStream.configured) { - ALOGE("%s: Camera %d: no input stream is configured."); + } else if (request->mIsReprocess) { + if (!mInputStream.configured) { + ALOGE("%s: Camera %d: no input stream is configured.", __FUNCTION__, mCameraId); return BAD_VALUE; - } else if (isReprocess && streaming) { - ALOGE("%s: Camera %d: streaming reprocess requests not supported."); + } else if (streaming) { + ALOGE("%s: Camera %d: streaming reprocess requests not supported.", __FUNCTION__, + mCameraId); return BAD_VALUE; } - } else if (isReprocess != request->mIsReprocess) { - ALOGE("%s: Camera %d: Sent regular and reprocess requests."); - return BAD_VALUE; } CameraMetadata metadata(request->mMetadata); @@ -196,7 +192,7 @@ status_t CameraDeviceClient::submitRequestList(List<sp<CaptureRequest> > request metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0], outputStreamIds.size()); - if (isReprocess) { + if (request->mIsReprocess) { metadata.update(ANDROID_REQUEST_INPUT_STREAMS, &mInputStream.id, 1); } diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp index 84c5754..2504bfd 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp @@ -187,6 +187,8 @@ status_t Camera3InputStream::disconnectLocked() { assert(mBuffersInFlight.size() == 0); + mConsumer->abandon(); + /** * no-op since we can't disconnect the producer from the consumer-side */ diff --git a/services/mediaresourcemanager/Android.mk b/services/mediaresourcemanager/Android.mk index 84218cf..b72230f 100644 --- a/services/mediaresourcemanager/Android.mk +++ b/services/mediaresourcemanager/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := ResourceManagerService.cpp +LOCAL_SRC_FILES := ResourceManagerService.cpp ServiceLog.cpp LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils liblog @@ -13,6 +13,9 @@ LOCAL_32_BIT_ONLY := true LOCAL_C_INCLUDES += \ $(TOPDIR)frameworks/av/include +LOCAL_CFLAGS += -Werror -Wall +LOCAL_CLANG := true + include $(BUILD_SHARED_LIBRARY) include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp index 17aac4e..3c093f9 100644 --- a/services/mediaresourcemanager/ResourceManagerService.cpp +++ b/services/mediaresourcemanager/ResourceManagerService.cpp @@ -29,6 +29,7 @@ #include <unistd.h> #include "ResourceManagerService.h" +#include "ServiceLog.h" namespace android { @@ -88,7 +89,7 @@ static ResourceInfo& getResourceInfoForEdit( return infos.editItemAt(infos.size() - 1); } -status_t ResourceManagerService::dump(int fd, const Vector<String16>& args) { +status_t ResourceManagerService::dump(int fd, const Vector<String16>& /* args */) { Mutex::Autolock lock(mLock); String8 result; @@ -103,16 +104,14 @@ status_t ResourceManagerService::dump(int fd, const Vector<String16>& args) { snprintf(buffer, SIZE, " SupportsSecureWithNonSecureCodec: %d\n", mSupportsSecureWithNonSecureCodec); result.append(buffer); - snprintf(buffer, SIZE, " Processes:\n"); - result.append(buffer); + result.append(" Processes:\n"); for (size_t i = 0; i < mMap.size(); ++i) { snprintf(buffer, SIZE, " Pid: %d\n", mMap.keyAt(i)); result.append(buffer); const ResourceInfos &infos = mMap.valueAt(i); for (size_t j = 0; j < infos.size(); ++j) { - snprintf(buffer, SIZE, " Client:\n"); - result.append(buffer); + result.append(" Client:\n"); snprintf(buffer, SIZE, " Id: %lld\n", (long long)infos[j].clientId); result.append(buffer); @@ -120,14 +119,15 @@ status_t ResourceManagerService::dump(int fd, const Vector<String16>& args) { result.append(buffer); Vector<MediaResource> resources = infos[j].resources; - snprintf(buffer, SIZE, " Resources:\n"); - result.append(buffer); + result.append(" Resources:\n"); for (size_t k = 0; k < resources.size(); ++k) { snprintf(buffer, SIZE, " %s\n", resources[k].toString().string()); result.append(buffer); } } } + result.append(" Logs:\n"); + result.append(mServiceLog->toString()); write(fd, result.string(), result.size()); return OK; @@ -135,27 +135,30 @@ status_t ResourceManagerService::dump(int fd, const Vector<String16>& args) { ResourceManagerService::ResourceManagerService() : mProcessInfo(new ProcessInfo()), + mServiceLog(new ServiceLog()), mSupportsMultipleSecureCodecs(true), mSupportsSecureWithNonSecureCodec(true) {} ResourceManagerService::ResourceManagerService(sp<ProcessInfoInterface> processInfo) : mProcessInfo(processInfo), + mServiceLog(new ServiceLog()), mSupportsMultipleSecureCodecs(true), mSupportsSecureWithNonSecureCodec(true) {} ResourceManagerService::~ResourceManagerService() {} void ResourceManagerService::config(const Vector<MediaResourcePolicy> &policies) { - ALOGV("config(%s)", getString(policies).string()); + String8 log = String8::format("config(%s)", getString(policies).string()); + mServiceLog->add(log); Mutex::Autolock lock(mLock); for (size_t i = 0; i < policies.size(); ++i) { String8 type = policies[i].mType; - uint64_t value = policies[i].mValue; + String8 value = policies[i].mValue; if (type == kPolicySupportsMultipleSecureCodecs) { - mSupportsMultipleSecureCodecs = (value != 0); + mSupportsMultipleSecureCodecs = (value == "true"); } else if (type == kPolicySupportsSecureWithNonSecureCodec) { - mSupportsSecureWithNonSecureCodec = (value != 0); + mSupportsSecureWithNonSecureCodec = (value == "true"); } } } @@ -165,8 +168,9 @@ void ResourceManagerService::addResource( int64_t clientId, const sp<IResourceManagerClient> client, const Vector<MediaResource> &resources) { - ALOGV("addResource(pid %d, clientId %lld, resources %s)", + String8 log = String8::format("addResource(pid %d, clientId %lld, resources %s)", pid, (long long) clientId, getString(resources).string()); + mServiceLog->add(log); Mutex::Autolock lock(mLock); ResourceInfos& infos = getResourceInfosForEdit(pid, mMap); @@ -176,7 +180,8 @@ void ResourceManagerService::addResource( } void ResourceManagerService::removeResource(int64_t clientId) { - ALOGV("removeResource(%lld)", (long long) clientId); + String8 log = String8::format("removeResource(%lld)", (long long) clientId); + mServiceLog->add(log); Mutex::Autolock lock(mLock); bool found = false; @@ -201,8 +206,9 @@ void ResourceManagerService::removeResource(int64_t clientId) { bool ResourceManagerService::reclaimResource( int callingPid, const Vector<MediaResource> &resources) { - ALOGV("reclaimResource(callingPid %d, resources %s)", + String8 log = String8::format("reclaimResource(callingPid %d, resources %s)", callingPid, getString(resources).string()); + mServiceLog->add(log); Vector<sp<IResourceManagerClient>> clients; { @@ -265,7 +271,8 @@ bool ResourceManagerService::reclaimResource( sp<IResourceManagerClient> failedClient; for (size_t i = 0; i < clients.size(); ++i) { - ALOGV("reclaimResource from client %p", clients[i].get()); + log = String8::format("reclaimResource from client %p", clients[i].get()); + mServiceLog->add(log); if (!clients[i]->reclaimResource()) { failedClient = clients[i]; break; diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h index 0c3d694..0d9d878 100644 --- a/services/mediaresourcemanager/ResourceManagerService.h +++ b/services/mediaresourcemanager/ResourceManagerService.h @@ -30,6 +30,7 @@ namespace android { +class ServiceLog; struct ProcessInfoInterface; struct ResourceInfo { @@ -96,6 +97,7 @@ private: mutable Mutex mLock; sp<ProcessInfoInterface> mProcessInfo; + sp<ServiceLog> mServiceLog; PidResourceInfosMap mMap; bool mSupportsMultipleSecureCodecs; bool mSupportsSecureWithNonSecureCodec; diff --git a/services/mediaresourcemanager/ServiceLog.cpp b/services/mediaresourcemanager/ServiceLog.cpp new file mode 100644 index 0000000..be7b308 --- /dev/null +++ b/services/mediaresourcemanager/ServiceLog.cpp @@ -0,0 +1,54 @@ +/* +** +** Copyright 2015, 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 "ServiceLog" +#include <utils/Log.h> + +#include <time.h> + +#include "ServiceLog.h" + +static const size_t kDefaultMaxNum = 100; + +namespace android { + +ServiceLog::ServiceLog() : mMaxNum(kDefaultMaxNum) {} +ServiceLog::ServiceLog(size_t maxNum) : mMaxNum(maxNum) {} + +void ServiceLog::add(const String8 &log) { + Mutex::Autolock lock(mLock); + time_t now = time(0); + char buf[64]; + strftime(buf, sizeof(buf), "%m-%d %T", localtime(&now)); + String8 formattedLog = String8::format("%s %s", buf, log.string()); + if (mLogs.add(formattedLog) == mMaxNum) { + mLogs.removeAt(0); + } +} + +String8 ServiceLog::toString() const { + Mutex::Autolock lock(mLock); + String8 result; + for (size_t i = 0; i < mLogs.size(); ++i) { + result.append(mLogs[i]); + result.append("\n"); + } + return result; +} + +} // namespace android diff --git a/services/mediaresourcemanager/ServiceLog.h b/services/mediaresourcemanager/ServiceLog.h new file mode 100644 index 0000000..14814ff --- /dev/null +++ b/services/mediaresourcemanager/ServiceLog.h @@ -0,0 +1,46 @@ +/* +** +** Copyright 2015, 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 ANDROID_SERVICELOG_H +#define ANDROID_SERVICELOG_H + +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/threads.h> +#include <utils/Vector.h> + +namespace android { + +class ServiceLog : public RefBase { +public: + ServiceLog(); + ServiceLog(size_t maxNum); + + void add(const String8 &log); + String8 toString() const; + +private: + int mMaxNum; + mutable Mutex mLock; + Vector<String8> mLogs; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_SERVICELOG_H diff --git a/services/mediaresourcemanager/test/Android.mk b/services/mediaresourcemanager/test/Android.mk index 228b62a..3b4ef0d 100644 --- a/services/mediaresourcemanager/test/Android.mk +++ b/services/mediaresourcemanager/test/Android.mk @@ -20,6 +20,35 @@ LOCAL_C_INCLUDES := \ frameworks/av/include \ frameworks/av/services/mediaresourcemanager \ +LOCAL_CFLAGS += -Werror -Wall +LOCAL_CLANG := true + +LOCAL_32_BIT_ONLY := true + +include $(BUILD_NATIVE_TEST) + +include $(CLEAR_VARS) + +LOCAL_MODULE := ServiceLog_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + ServiceLog_test.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libmedia \ + libresourcemanagerservice \ + libutils \ + +LOCAL_C_INCLUDES := \ + frameworks/av/include \ + frameworks/av/services/mediaresourcemanager \ + +LOCAL_CFLAGS += -Werror -Wall +LOCAL_CLANG := true + LOCAL_32_BIT_ONLY := true include $(BUILD_NATIVE_TEST) diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp index bccc7fa..3d53f1f 100644 --- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp +++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp @@ -180,17 +180,27 @@ protected: EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec); Vector<MediaResourcePolicy> policies1; - policies1.push_back(MediaResourcePolicy(String8(kPolicySupportsMultipleSecureCodecs), 1)); policies1.push_back( - MediaResourcePolicy(String8(kPolicySupportsSecureWithNonSecureCodec), 0)); + MediaResourcePolicy( + String8(kPolicySupportsMultipleSecureCodecs), + String8("true"))); + policies1.push_back( + MediaResourcePolicy( + String8(kPolicySupportsSecureWithNonSecureCodec), + String8("false"))); mService->config(policies1); EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs); EXPECT_FALSE(mService->mSupportsSecureWithNonSecureCodec); Vector<MediaResourcePolicy> policies2; - policies2.push_back(MediaResourcePolicy(String8(kPolicySupportsMultipleSecureCodecs), 0)); policies2.push_back( - MediaResourcePolicy(String8(kPolicySupportsSecureWithNonSecureCodec), 1)); + MediaResourcePolicy( + String8(kPolicySupportsMultipleSecureCodecs), + String8("false"))); + policies2.push_back( + MediaResourcePolicy( + String8(kPolicySupportsSecureWithNonSecureCodec), + String8("true"))); mService->config(policies2); EXPECT_FALSE(mService->mSupportsMultipleSecureCodecs); EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec); diff --git a/services/mediaresourcemanager/test/ServiceLog_test.cpp b/services/mediaresourcemanager/test/ServiceLog_test.cpp new file mode 100644 index 0000000..6ddcb87 --- /dev/null +++ b/services/mediaresourcemanager/test/ServiceLog_test.cpp @@ -0,0 +1,68 @@ +/* + * Copyright 2015 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 "ServiceLog_test" +#include <utils/Log.h> + +#include <gtest/gtest.h> + +#include "ServiceLog.h" + +namespace android { + +class ServiceLogTest : public ::testing::Test { +public: + ServiceLogTest() : mServiceLog(new ServiceLog(3)) { + } + +protected: + sp<ServiceLog> mServiceLog; +}; + +TEST_F(ServiceLogTest, addThenToString) { + mServiceLog->add(String8("log1")); + EXPECT_TRUE(mServiceLog->toString().contains("log1")); + ALOGV("toString:\n%s", mServiceLog->toString().string()); + + mServiceLog->add(String8("log2")); + EXPECT_TRUE(mServiceLog->toString().contains("log1")); + EXPECT_TRUE(mServiceLog->toString().contains("log2")); + ALOGV("toString:\n%s", mServiceLog->toString().string()); + + mServiceLog->add(String8("log3")); + EXPECT_TRUE(mServiceLog->toString().contains("log1")); + EXPECT_TRUE(mServiceLog->toString().contains("log2")); + EXPECT_TRUE(mServiceLog->toString().contains("log3")); + ALOGV("toString:\n%s", mServiceLog->toString().string()); + + mServiceLog->add(String8("log4")); + EXPECT_FALSE(mServiceLog->toString().contains("log1")); + EXPECT_TRUE(mServiceLog->toString().contains("log2")); + EXPECT_TRUE(mServiceLog->toString().contains("log3")); + EXPECT_TRUE(mServiceLog->toString().contains("log4")); + ALOGV("toString:\n%s", mServiceLog->toString().string()); + + mServiceLog->add(String8("log5")); + EXPECT_FALSE(mServiceLog->toString().contains("log1")); + EXPECT_FALSE(mServiceLog->toString().contains("log2")); + EXPECT_TRUE(mServiceLog->toString().contains("log3")); + EXPECT_TRUE(mServiceLog->toString().contains("log4")); + EXPECT_TRUE(mServiceLog->toString().contains("log5")); + ALOGV("toString:\n%s", mServiceLog->toString().string()); +} + +} // namespace android |