Loading include/media/stagefright/MediaCodec.h +38 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <gui/IGraphicBufferProducer.h> #include <media/hardware/CryptoAPI.h> #include <media/MediaResource.h> #include <media/stagefright/foundation/AHandler.h> #include <utils/Vector.h> Loading @@ -34,6 +35,8 @@ struct IBatteryStats; struct ICrypto; struct IMemory; struct MemoryDealer; class IResourceManagerClient; class IResourceManagerService; struct SoftwareRenderer; struct Surface; Loading Loading @@ -230,6 +233,30 @@ private: bool mOwnedByClient; }; struct ResourceManagerServiceProxy : public IBinder::DeathRecipient { ResourceManagerServiceProxy(); ~ResourceManagerServiceProxy(); void init(); // implements DeathRecipient virtual void binderDied(const wp<IBinder>& /*who*/); void addResource( int pid, int64_t clientId, const sp<IResourceManagerClient> client, const Vector<MediaResource> &resources); void removeResource(int64_t clientId); bool reclaimResource(int callingPid, const Vector<MediaResource> &resources); private: Mutex mLock; sp<IResourceManagerService> mService; }; State mState; sp<ALooper> mLooper; sp<ALooper> mCodecLooper; Loading @@ -245,14 +272,22 @@ private: sp<AMessage> mCallback; sp<MemoryDealer> mDealer; sp<IResourceManagerClient> mResourceManagerClient; sp<ResourceManagerServiceProxy> mResourceManagerService; bool mBatteryStatNotified; bool mIsVideo; int32_t mVideoWidth; int32_t mVideoHeight; // initial create parameters AString mInitName; bool mInitNameIsType; bool mInitIsEncoder; // configure parameter sp<AMessage> mConfigureMsg; // Used only to synchronize asynchronous getBufferAndFormat // across all the other (synchronous) buffer state change // operations, such as de/queueIn/OutputBuffer, start and Loading Loading @@ -320,6 +355,9 @@ private: void updateBatteryStat(); bool isExecuting() const; uint64_t getGraphicBufferSize(); void addResource(const char *type, uint64_t value); /* called to get the last codec error when the sticky flag is set. * if no such codec error is found, returns UNKNOWN_ERROR. */ Loading media/libstagefright/MediaCodec.cpp +234 −25 Original line number Diff line number Diff line Loading @@ -23,10 +23,12 @@ #include <binder/IBatteryStats.h> #include <binder/IMemory.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/MemoryDealer.h> #include <gui/Surface.h> #include <media/ICrypto.h> #include <media/IResourceManagerService.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> Loading @@ -47,6 +49,45 @@ namespace android { static inline int getCallingPid() { return IPCThreadState::self()->getCallingPid(); } static int64_t getId(sp<IResourceManagerClient> client) { return (int64_t) client.get(); } static bool isResourceError(status_t err) { return (err == OMX_ErrorInsufficientResources); } static const int kMaxRetry = 2; struct ResourceManagerClient : public BnResourceManagerClient { ResourceManagerClient(MediaCodec* codec) : mMediaCodec(codec) {} virtual bool reclaimResource() { sp<MediaCodec> codec = mMediaCodec.promote(); if (codec == NULL) { // codec is already gone. return true; } status_t err = codec->release(); if (err != OK) { ALOGW("ResourceManagerClient failed to release codec with err %d", err); } return (err == OK); } protected: virtual ~ResourceManagerClient() {} private: wp<MediaCodec> mMediaCodec; DISALLOW_EVIL_CONSTRUCTORS(ResourceManagerClient); }; struct MediaCodec::BatteryNotifier : public Singleton<BatteryNotifier> { BatteryNotifier(); virtual ~BatteryNotifier(); Loading Loading @@ -178,6 +219,65 @@ void MediaCodec::BatteryNotifier::onBatteryStatServiceDied() { // started. } MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy() { } MediaCodec::ResourceManagerServiceProxy::~ResourceManagerServiceProxy() { if (mService != NULL) { IInterface::asBinder(mService)->unlinkToDeath(this); } } void MediaCodec::ResourceManagerServiceProxy::init() { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("media.resource_manager")); mService = interface_cast<IResourceManagerService>(binder); if (mService == NULL) { ALOGE("Failed to get ResourceManagerService"); return; } if (IInterface::asBinder(mService)->linkToDeath(this) != OK) { mService.clear(); ALOGE("Failed to linkToDeath to ResourceManagerService."); return; } } void MediaCodec::ResourceManagerServiceProxy::binderDied(const wp<IBinder>& /*who*/) { ALOGW("ResourceManagerService died."); Mutex::Autolock _l(mLock); mService.clear(); } void MediaCodec::ResourceManagerServiceProxy::addResource( int pid, int64_t clientId, const sp<IResourceManagerClient> client, const Vector<MediaResource> &resources) { Mutex::Autolock _l(mLock); if (mService == NULL) { return; } mService->addResource(pid, clientId, client, resources); } void MediaCodec::ResourceManagerServiceProxy::removeResource(int64_t clientId) { Mutex::Autolock _l(mLock); if (mService == NULL) { return; } mService->removeResource(clientId); } bool MediaCodec::ResourceManagerServiceProxy::reclaimResource( int callingPid, const Vector<MediaResource> &resources) { Mutex::Autolock _l(mLock); if (mService == NULL) { return false; } return mService->reclaimResource(callingPid, resources); } // static sp<MediaCodec> MediaCodec::CreateByType( const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err) { Loading Loading @@ -208,10 +308,14 @@ MediaCodec::MediaCodec(const sp<ALooper> &looper) mCodec(NULL), mReplyID(0), mFlags(0), mResourceManagerClient(new ResourceManagerClient(this)), mResourceManagerService(new ResourceManagerServiceProxy()), mStickyError(OK), mSoftRenderer(NULL), mBatteryStatNotified(false), mIsVideo(false), mVideoWidth(0), mVideoHeight(0), mDequeueInputTimeoutGeneration(0), mDequeueInputReplyID(0), mDequeueOutputTimeoutGeneration(0), Loading @@ -221,6 +325,7 @@ MediaCodec::MediaCodec(const sp<ALooper> &looper) MediaCodec::~MediaCodec() { CHECK_EQ(mState, UNINITIALIZED); mResourceManagerService->removeResource(getId(mResourceManagerClient)); } // static Loading @@ -247,6 +352,8 @@ void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) } status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { mResourceManagerService->init(); // save init parameters for reset mInitName = name; mInitNameIsType = nameIsType; Loading @@ -266,12 +373,13 @@ status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { return NAME_NOT_FOUND; } bool needDedicatedLooper = false; bool secureCodec = false; if (nameIsType && !strncasecmp(name.c_str(), "video/", 6)) { needDedicatedLooper = true; mIsVideo = true; } else { AString tmp = name; if (tmp.endsWith(".secure")) { secureCodec = true; tmp.erase(tmp.size() - 7, 7); } const sp<IMediaCodecList> mcl = MediaCodecList::getInstance(); Loading @@ -282,14 +390,15 @@ status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { info->getSupportedMimes(&mimes); for (size_t i = 0; i < mimes.size(); i++) { if (mimes[i].startsWith("video/")) { needDedicatedLooper = true; mIsVideo = true; break; } } } } if (needDedicatedLooper) { if (mIsVideo) { // video codec needs dedicated looper if (mCodecLooper == NULL) { mCodecLooper = new ALooper; mCodecLooper->setName("CodecLooper"); Loading @@ -313,8 +422,25 @@ status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { msg->setInt32("encoder", encoder); } status_t err; Vector<MediaResource> resources; const char *type = secureCodec ? kResourceSecureCodec : kResourceNonSecureCodec; resources.push_back(MediaResource(String8(type), 1)); for (int i = 0; i <= kMaxRetry; ++i) { if (i > 0) { // Don't try to reclaim resource for the first time. if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) { break; } } sp<AMessage> response; return PostAndAwaitResponse(msg, &response); err = PostAndAwaitResponse(msg, &response); if (!isResourceError(err)) { break; } } return err; } status_t MediaCodec::setCallback(const sp<AMessage> &callback) { Loading @@ -332,6 +458,11 @@ status_t MediaCodec::configure( uint32_t flags) { sp<AMessage> msg = new AMessage(kWhatConfigure, this); if (mIsVideo) { format->findInt32("width", &mVideoWidth); format->findInt32("height", &mVideoHeight); } msg->setMessage("format", format); msg->setInt32("flags", flags); Loading @@ -345,9 +476,27 @@ status_t MediaCodec::configure( msg->setPointer("crypto", crypto.get()); } sp<AMessage> response; status_t err = PostAndAwaitResponse(msg, &response); // save msg for reset mConfigureMsg = msg; status_t err; Vector<MediaResource> resources; const char *type = (mFlags & kFlagIsSecure) ? kResourceSecureCodec : kResourceNonSecureCodec; resources.push_back(MediaResource(String8(type), 1)); // Don't know the buffer size at this point, but it's fine to use 1 because // the reclaimResource call doesn't consider the requester's buffer size for now. resources.push_back(MediaResource(String8(kResourceGraphicMemory), 1)); for (int i = 0; i <= kMaxRetry; ++i) { if (i > 0) { // Don't try to reclaim resource for the first time. if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) { break; } } sp<AMessage> response; err = PostAndAwaitResponse(msg, &response); if (err != OK && err != INVALID_OPERATION) { // MediaCodec now set state to UNINITIALIZED upon any fatal error. // To maintain backward-compatibility, do a reset() to put codec Loading @@ -358,7 +507,10 @@ status_t MediaCodec::configure( ALOGE("configure failed with err 0x%08x, resetting...", err); reset(); } if (!isResourceError(err)) { break; } } return err; } Loading @@ -382,11 +534,65 @@ status_t MediaCodec::createInputSurface( return err; } uint64_t MediaCodec::getGraphicBufferSize() { if (!mIsVideo) { return 0; } uint64_t size = 0; size_t portNum = sizeof(mPortBuffers) / sizeof((mPortBuffers)[0]); for (size_t i = 0; i < portNum; ++i) { // TODO: this is just an estimation, we should get the real buffer size from ACodec. size += mPortBuffers[i].size() * mVideoWidth * mVideoHeight * 3 / 2; } return size; } void MediaCodec::addResource(const char *type, uint64_t value) { Vector<MediaResource> resources; resources.push_back(MediaResource(String8(type), value)); mResourceManagerService->addResource( getCallingPid(), getId(mResourceManagerClient), mResourceManagerClient, resources); } status_t MediaCodec::start() { sp<AMessage> msg = new AMessage(kWhatStart, this); status_t err; Vector<MediaResource> resources; const char *type = (mFlags & kFlagIsSecure) ? kResourceSecureCodec : kResourceNonSecureCodec; resources.push_back(MediaResource(String8(type), 1)); // Don't know the buffer size at this point, but it's fine to use 1 because // the reclaimResource call doesn't consider the requester's buffer size for now. resources.push_back(MediaResource(String8(kResourceGraphicMemory), 1)); for (int i = 0; i <= kMaxRetry; ++i) { if (i > 0) { // Don't try to reclaim resource for the first time. if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) { break; } // Recover codec from previous error before retry start. err = reset(); if (err != OK) { ALOGE("retrying start: failed to reset codec"); break; } sp<AMessage> response; return PostAndAwaitResponse(msg, &response); err = PostAndAwaitResponse(mConfigureMsg, &response); if (err != OK) { ALOGE("retrying start: failed to configure codec"); break; } } sp<AMessage> response; err = PostAndAwaitResponse(msg, &response); if (!isResourceError(err)) { break; } } return err; } status_t MediaCodec::stop() { Loading Loading @@ -960,11 +1166,15 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { mFlags &= ~kFlagUsesSoftwareRenderer; } String8 resourceType; if (mComponentName.endsWith(".secure")) { mFlags |= kFlagIsSecure; resourceType = String8(kResourceSecureCodec); } else { mFlags &= ~kFlagIsSecure; resourceType = String8(kResourceNonSecureCodec); } addResource(resourceType, 1); (new AMessage)->postReply(mReplyID); break; Loading Loading @@ -1077,6 +1287,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { // We're always allocating output buffers after // allocating input buffers, so this is a good // indication that now all buffers are allocated. if (mIsVideo) { addResource(kResourceGraphicMemory, getGraphicBufferSize()); } setState(STARTED); (new AMessage)->postReply(mReplyID); } else { Loading Loading @@ -1256,6 +1469,8 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { } mFlags &= ~kFlagIsComponentAllocated; mResourceManagerService->removeResource(getId(mResourceManagerClient)); (new AMessage)->postReply(mReplyID); break; } Loading Loading @@ -2357,12 +2572,6 @@ status_t MediaCodec::amendOutputFormatWithCodecSpecificData( void MediaCodec::updateBatteryStat() { if (mState == CONFIGURED && !mBatteryStatNotified) { AString mime; CHECK(mOutputFormat != NULL && mOutputFormat->findString("mime", &mime)); mIsVideo = mime.startsWithIgnoreCase("video/"); BatteryNotifier& notifier(BatteryNotifier::getInstance()); if (mIsVideo) { Loading media/mediaserver/main_mediaserver.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ #include "CameraService.h" #include "MediaLogService.h" #include "MediaPlayerService.h" #include "ResourceManagerService.h" #include "service/AudioPolicyService.h" #include "SoundTriggerHwService.h" #include "RadioService.h" Loading Loading @@ -128,6 +129,7 @@ int main(int argc __unused, char** argv) ALOGI("ServiceManager: %p", sm.get()); AudioFlinger::instantiate(); MediaPlayerService::instantiate(); ResourceManagerService::instantiate(); CameraService::instantiate(); AudioPolicyService::instantiate(); SoundTriggerHwService::instantiate(); Loading services/mediaresourcemanager/ResourceManagerService.cpp +42 −2 Original line number Diff line number Diff line Loading @@ -126,6 +126,7 @@ void ResourceManagerService::addResource( Mutex::Autolock lock(mLock); ResourceInfos& infos = getResourceInfosForEdit(pid, mMap); ResourceInfo& info = getResourceInfoForEdit(clientId, client, infos); // TODO: do the merge instead of append. info.resources.appendVector(resources); } Loading Loading @@ -197,19 +198,58 @@ bool ResourceManagerService::reclaimResource( } } } if (clients.size() == 0) { // if we are here, run the third pass to free one codec with the same type. for (size_t i = 0; i < resources.size(); ++i) { String8 type = resources[i].mType; if (type == kResourceSecureCodec || type == kResourceNonSecureCodec) { sp<IResourceManagerClient> client; if (!getLowestPriorityBiggestClient_l(callingPid, type, &client)) { return false; } clients.push_back(client); } } } } if (clients.size() == 0) { return false; } sp<IResourceManagerClient> failedClient; for (size_t i = 0; i < clients.size(); ++i) { ALOGV("reclaimResource from client %p", clients[i].get()); if (!clients[i]->reclaimResource()) { return false; failedClient = clients[i]; break; } } return true; { Mutex::Autolock lock(mLock); bool found = false; for (size_t i = 0; i < mMap.size(); ++i) { ResourceInfos &infos = mMap.editValueAt(i); for (size_t j = 0; j < infos.size();) { if (infos[j].client == failedClient) { j = infos.removeAt(j); found = true; } else { ++j; } } if (found) { break; } } if (!found) { ALOGV("didn't find failed client"); } } return (failedClient == NULL); } bool ResourceManagerService::getAllClients_l( Loading services/mediaresourcemanager/test/ResourceManagerService_test.cpp +64 −4 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * 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. Loading Loading @@ -118,6 +118,20 @@ protected: client3->reset(); } // test set up // --------------------------------------------------------------------------------- // pid priority client type number // --------------------------------------------------------------------------------- // kTestPid1(30) 30 mTestClient1 secure codec 1 // graphic memory 200 // graphic memory 200 // --------------------------------------------------------------------------------- // kTestPid2(20) 20 mTestClient2 non-secure codec 1 // graphic memory 300 // ------------------------------------------- // mTestClient3 secure codec 1 // graphic memory 100 // --------------------------------------------------------------------------------- void addResource() { // kTestPid1 mTestClient1 Vector<MediaResource> resources1; Loading Loading @@ -202,10 +216,12 @@ protected: int lowPriorityPid = 100; EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients)); int midPriorityPid = 25; EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients)); // some higher priority process (e.g. kTestPid2) owns the resource, so getAllClients_l // will fail. EXPECT_FALSE(mService->getAllClients_l(midPriorityPid, type, &clients)); int highPriorityPid = 10; EXPECT_TRUE(mService->getAllClients_l(10, unknowType, &clients)); EXPECT_TRUE(mService->getAllClients_l(10, type, &clients)); EXPECT_TRUE(mService->getAllClients_l(highPriorityPid, unknowType, &clients)); EXPECT_TRUE(mService->getAllClients_l(highPriorityPid, type, &clients)); EXPECT_EQ(2u, clients.size()); EXPECT_EQ(mTestClient3, clients[0]); Loading Loading @@ -308,6 +324,30 @@ protected: // nothing left EXPECT_FALSE(mService->reclaimResource(10, resources)); } // ### secure codecs can coexist and secure codec can coexist with non-secure codec ### { addResource(); mService->mSupportsMultipleSecureCodecs = true; mService->mSupportsSecureWithNonSecureCodec = true; Vector<MediaResource> resources; resources.push_back(MediaResource(String8(kResourceSecureCodec), 1)); EXPECT_TRUE(mService->reclaimResource(10, resources)); // secure codec from lowest process got reclaimed verifyClients(true, false, false); // call again should reclaim another secure codec from lowest process EXPECT_TRUE(mService->reclaimResource(10, resources)); verifyClients(false, false, true); // nothing left EXPECT_FALSE(mService->reclaimResource(10, resources)); // clean up client 2 which still has non secure codec left mService->removeResource((int64_t) mTestClient2.get()); } } void testReclaimResourceNonSecure() { Loading Loading @@ -360,6 +400,26 @@ protected: // nothing left EXPECT_FALSE(mService->reclaimResource(10, resources)); } // ### secure codec can coexist with non-secure codec ### { addResource(); mService->mSupportsSecureWithNonSecureCodec = true; Vector<MediaResource> resources; resources.push_back(MediaResource(String8(kResourceNonSecureCodec), 1)); EXPECT_TRUE(mService->reclaimResource(10, resources)); // one non secure codec from lowest process got reclaimed verifyClients(false, true, false); // nothing left EXPECT_FALSE(mService->reclaimResource(10, resources)); // clean up client 1 and 3 which still have secure codec left mService->removeResource((int64_t) mTestClient1.get()); mService->removeResource((int64_t) mTestClient3.get()); } } void testGetLowestPriorityBiggestClient() { Loading Loading
include/media/stagefright/MediaCodec.h +38 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <gui/IGraphicBufferProducer.h> #include <media/hardware/CryptoAPI.h> #include <media/MediaResource.h> #include <media/stagefright/foundation/AHandler.h> #include <utils/Vector.h> Loading @@ -34,6 +35,8 @@ struct IBatteryStats; struct ICrypto; struct IMemory; struct MemoryDealer; class IResourceManagerClient; class IResourceManagerService; struct SoftwareRenderer; struct Surface; Loading Loading @@ -230,6 +233,30 @@ private: bool mOwnedByClient; }; struct ResourceManagerServiceProxy : public IBinder::DeathRecipient { ResourceManagerServiceProxy(); ~ResourceManagerServiceProxy(); void init(); // implements DeathRecipient virtual void binderDied(const wp<IBinder>& /*who*/); void addResource( int pid, int64_t clientId, const sp<IResourceManagerClient> client, const Vector<MediaResource> &resources); void removeResource(int64_t clientId); bool reclaimResource(int callingPid, const Vector<MediaResource> &resources); private: Mutex mLock; sp<IResourceManagerService> mService; }; State mState; sp<ALooper> mLooper; sp<ALooper> mCodecLooper; Loading @@ -245,14 +272,22 @@ private: sp<AMessage> mCallback; sp<MemoryDealer> mDealer; sp<IResourceManagerClient> mResourceManagerClient; sp<ResourceManagerServiceProxy> mResourceManagerService; bool mBatteryStatNotified; bool mIsVideo; int32_t mVideoWidth; int32_t mVideoHeight; // initial create parameters AString mInitName; bool mInitNameIsType; bool mInitIsEncoder; // configure parameter sp<AMessage> mConfigureMsg; // Used only to synchronize asynchronous getBufferAndFormat // across all the other (synchronous) buffer state change // operations, such as de/queueIn/OutputBuffer, start and Loading Loading @@ -320,6 +355,9 @@ private: void updateBatteryStat(); bool isExecuting() const; uint64_t getGraphicBufferSize(); void addResource(const char *type, uint64_t value); /* called to get the last codec error when the sticky flag is set. * if no such codec error is found, returns UNKNOWN_ERROR. */ Loading
media/libstagefright/MediaCodec.cpp +234 −25 Original line number Diff line number Diff line Loading @@ -23,10 +23,12 @@ #include <binder/IBatteryStats.h> #include <binder/IMemory.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/MemoryDealer.h> #include <gui/Surface.h> #include <media/ICrypto.h> #include <media/IResourceManagerService.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> Loading @@ -47,6 +49,45 @@ namespace android { static inline int getCallingPid() { return IPCThreadState::self()->getCallingPid(); } static int64_t getId(sp<IResourceManagerClient> client) { return (int64_t) client.get(); } static bool isResourceError(status_t err) { return (err == OMX_ErrorInsufficientResources); } static const int kMaxRetry = 2; struct ResourceManagerClient : public BnResourceManagerClient { ResourceManagerClient(MediaCodec* codec) : mMediaCodec(codec) {} virtual bool reclaimResource() { sp<MediaCodec> codec = mMediaCodec.promote(); if (codec == NULL) { // codec is already gone. return true; } status_t err = codec->release(); if (err != OK) { ALOGW("ResourceManagerClient failed to release codec with err %d", err); } return (err == OK); } protected: virtual ~ResourceManagerClient() {} private: wp<MediaCodec> mMediaCodec; DISALLOW_EVIL_CONSTRUCTORS(ResourceManagerClient); }; struct MediaCodec::BatteryNotifier : public Singleton<BatteryNotifier> { BatteryNotifier(); virtual ~BatteryNotifier(); Loading Loading @@ -178,6 +219,65 @@ void MediaCodec::BatteryNotifier::onBatteryStatServiceDied() { // started. } MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy() { } MediaCodec::ResourceManagerServiceProxy::~ResourceManagerServiceProxy() { if (mService != NULL) { IInterface::asBinder(mService)->unlinkToDeath(this); } } void MediaCodec::ResourceManagerServiceProxy::init() { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("media.resource_manager")); mService = interface_cast<IResourceManagerService>(binder); if (mService == NULL) { ALOGE("Failed to get ResourceManagerService"); return; } if (IInterface::asBinder(mService)->linkToDeath(this) != OK) { mService.clear(); ALOGE("Failed to linkToDeath to ResourceManagerService."); return; } } void MediaCodec::ResourceManagerServiceProxy::binderDied(const wp<IBinder>& /*who*/) { ALOGW("ResourceManagerService died."); Mutex::Autolock _l(mLock); mService.clear(); } void MediaCodec::ResourceManagerServiceProxy::addResource( int pid, int64_t clientId, const sp<IResourceManagerClient> client, const Vector<MediaResource> &resources) { Mutex::Autolock _l(mLock); if (mService == NULL) { return; } mService->addResource(pid, clientId, client, resources); } void MediaCodec::ResourceManagerServiceProxy::removeResource(int64_t clientId) { Mutex::Autolock _l(mLock); if (mService == NULL) { return; } mService->removeResource(clientId); } bool MediaCodec::ResourceManagerServiceProxy::reclaimResource( int callingPid, const Vector<MediaResource> &resources) { Mutex::Autolock _l(mLock); if (mService == NULL) { return false; } return mService->reclaimResource(callingPid, resources); } // static sp<MediaCodec> MediaCodec::CreateByType( const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err) { Loading Loading @@ -208,10 +308,14 @@ MediaCodec::MediaCodec(const sp<ALooper> &looper) mCodec(NULL), mReplyID(0), mFlags(0), mResourceManagerClient(new ResourceManagerClient(this)), mResourceManagerService(new ResourceManagerServiceProxy()), mStickyError(OK), mSoftRenderer(NULL), mBatteryStatNotified(false), mIsVideo(false), mVideoWidth(0), mVideoHeight(0), mDequeueInputTimeoutGeneration(0), mDequeueInputReplyID(0), mDequeueOutputTimeoutGeneration(0), Loading @@ -221,6 +325,7 @@ MediaCodec::MediaCodec(const sp<ALooper> &looper) MediaCodec::~MediaCodec() { CHECK_EQ(mState, UNINITIALIZED); mResourceManagerService->removeResource(getId(mResourceManagerClient)); } // static Loading @@ -247,6 +352,8 @@ void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) } status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { mResourceManagerService->init(); // save init parameters for reset mInitName = name; mInitNameIsType = nameIsType; Loading @@ -266,12 +373,13 @@ status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { return NAME_NOT_FOUND; } bool needDedicatedLooper = false; bool secureCodec = false; if (nameIsType && !strncasecmp(name.c_str(), "video/", 6)) { needDedicatedLooper = true; mIsVideo = true; } else { AString tmp = name; if (tmp.endsWith(".secure")) { secureCodec = true; tmp.erase(tmp.size() - 7, 7); } const sp<IMediaCodecList> mcl = MediaCodecList::getInstance(); Loading @@ -282,14 +390,15 @@ status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { info->getSupportedMimes(&mimes); for (size_t i = 0; i < mimes.size(); i++) { if (mimes[i].startsWith("video/")) { needDedicatedLooper = true; mIsVideo = true; break; } } } } if (needDedicatedLooper) { if (mIsVideo) { // video codec needs dedicated looper if (mCodecLooper == NULL) { mCodecLooper = new ALooper; mCodecLooper->setName("CodecLooper"); Loading @@ -313,8 +422,25 @@ status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { msg->setInt32("encoder", encoder); } status_t err; Vector<MediaResource> resources; const char *type = secureCodec ? kResourceSecureCodec : kResourceNonSecureCodec; resources.push_back(MediaResource(String8(type), 1)); for (int i = 0; i <= kMaxRetry; ++i) { if (i > 0) { // Don't try to reclaim resource for the first time. if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) { break; } } sp<AMessage> response; return PostAndAwaitResponse(msg, &response); err = PostAndAwaitResponse(msg, &response); if (!isResourceError(err)) { break; } } return err; } status_t MediaCodec::setCallback(const sp<AMessage> &callback) { Loading @@ -332,6 +458,11 @@ status_t MediaCodec::configure( uint32_t flags) { sp<AMessage> msg = new AMessage(kWhatConfigure, this); if (mIsVideo) { format->findInt32("width", &mVideoWidth); format->findInt32("height", &mVideoHeight); } msg->setMessage("format", format); msg->setInt32("flags", flags); Loading @@ -345,9 +476,27 @@ status_t MediaCodec::configure( msg->setPointer("crypto", crypto.get()); } sp<AMessage> response; status_t err = PostAndAwaitResponse(msg, &response); // save msg for reset mConfigureMsg = msg; status_t err; Vector<MediaResource> resources; const char *type = (mFlags & kFlagIsSecure) ? kResourceSecureCodec : kResourceNonSecureCodec; resources.push_back(MediaResource(String8(type), 1)); // Don't know the buffer size at this point, but it's fine to use 1 because // the reclaimResource call doesn't consider the requester's buffer size for now. resources.push_back(MediaResource(String8(kResourceGraphicMemory), 1)); for (int i = 0; i <= kMaxRetry; ++i) { if (i > 0) { // Don't try to reclaim resource for the first time. if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) { break; } } sp<AMessage> response; err = PostAndAwaitResponse(msg, &response); if (err != OK && err != INVALID_OPERATION) { // MediaCodec now set state to UNINITIALIZED upon any fatal error. // To maintain backward-compatibility, do a reset() to put codec Loading @@ -358,7 +507,10 @@ status_t MediaCodec::configure( ALOGE("configure failed with err 0x%08x, resetting...", err); reset(); } if (!isResourceError(err)) { break; } } return err; } Loading @@ -382,11 +534,65 @@ status_t MediaCodec::createInputSurface( return err; } uint64_t MediaCodec::getGraphicBufferSize() { if (!mIsVideo) { return 0; } uint64_t size = 0; size_t portNum = sizeof(mPortBuffers) / sizeof((mPortBuffers)[0]); for (size_t i = 0; i < portNum; ++i) { // TODO: this is just an estimation, we should get the real buffer size from ACodec. size += mPortBuffers[i].size() * mVideoWidth * mVideoHeight * 3 / 2; } return size; } void MediaCodec::addResource(const char *type, uint64_t value) { Vector<MediaResource> resources; resources.push_back(MediaResource(String8(type), value)); mResourceManagerService->addResource( getCallingPid(), getId(mResourceManagerClient), mResourceManagerClient, resources); } status_t MediaCodec::start() { sp<AMessage> msg = new AMessage(kWhatStart, this); status_t err; Vector<MediaResource> resources; const char *type = (mFlags & kFlagIsSecure) ? kResourceSecureCodec : kResourceNonSecureCodec; resources.push_back(MediaResource(String8(type), 1)); // Don't know the buffer size at this point, but it's fine to use 1 because // the reclaimResource call doesn't consider the requester's buffer size for now. resources.push_back(MediaResource(String8(kResourceGraphicMemory), 1)); for (int i = 0; i <= kMaxRetry; ++i) { if (i > 0) { // Don't try to reclaim resource for the first time. if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) { break; } // Recover codec from previous error before retry start. err = reset(); if (err != OK) { ALOGE("retrying start: failed to reset codec"); break; } sp<AMessage> response; return PostAndAwaitResponse(msg, &response); err = PostAndAwaitResponse(mConfigureMsg, &response); if (err != OK) { ALOGE("retrying start: failed to configure codec"); break; } } sp<AMessage> response; err = PostAndAwaitResponse(msg, &response); if (!isResourceError(err)) { break; } } return err; } status_t MediaCodec::stop() { Loading Loading @@ -960,11 +1166,15 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { mFlags &= ~kFlagUsesSoftwareRenderer; } String8 resourceType; if (mComponentName.endsWith(".secure")) { mFlags |= kFlagIsSecure; resourceType = String8(kResourceSecureCodec); } else { mFlags &= ~kFlagIsSecure; resourceType = String8(kResourceNonSecureCodec); } addResource(resourceType, 1); (new AMessage)->postReply(mReplyID); break; Loading Loading @@ -1077,6 +1287,9 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { // We're always allocating output buffers after // allocating input buffers, so this is a good // indication that now all buffers are allocated. if (mIsVideo) { addResource(kResourceGraphicMemory, getGraphicBufferSize()); } setState(STARTED); (new AMessage)->postReply(mReplyID); } else { Loading Loading @@ -1256,6 +1469,8 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { } mFlags &= ~kFlagIsComponentAllocated; mResourceManagerService->removeResource(getId(mResourceManagerClient)); (new AMessage)->postReply(mReplyID); break; } Loading Loading @@ -2357,12 +2572,6 @@ status_t MediaCodec::amendOutputFormatWithCodecSpecificData( void MediaCodec::updateBatteryStat() { if (mState == CONFIGURED && !mBatteryStatNotified) { AString mime; CHECK(mOutputFormat != NULL && mOutputFormat->findString("mime", &mime)); mIsVideo = mime.startsWithIgnoreCase("video/"); BatteryNotifier& notifier(BatteryNotifier::getInstance()); if (mIsVideo) { Loading
media/mediaserver/main_mediaserver.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ #include "CameraService.h" #include "MediaLogService.h" #include "MediaPlayerService.h" #include "ResourceManagerService.h" #include "service/AudioPolicyService.h" #include "SoundTriggerHwService.h" #include "RadioService.h" Loading Loading @@ -128,6 +129,7 @@ int main(int argc __unused, char** argv) ALOGI("ServiceManager: %p", sm.get()); AudioFlinger::instantiate(); MediaPlayerService::instantiate(); ResourceManagerService::instantiate(); CameraService::instantiate(); AudioPolicyService::instantiate(); SoundTriggerHwService::instantiate(); Loading
services/mediaresourcemanager/ResourceManagerService.cpp +42 −2 Original line number Diff line number Diff line Loading @@ -126,6 +126,7 @@ void ResourceManagerService::addResource( Mutex::Autolock lock(mLock); ResourceInfos& infos = getResourceInfosForEdit(pid, mMap); ResourceInfo& info = getResourceInfoForEdit(clientId, client, infos); // TODO: do the merge instead of append. info.resources.appendVector(resources); } Loading Loading @@ -197,19 +198,58 @@ bool ResourceManagerService::reclaimResource( } } } if (clients.size() == 0) { // if we are here, run the third pass to free one codec with the same type. for (size_t i = 0; i < resources.size(); ++i) { String8 type = resources[i].mType; if (type == kResourceSecureCodec || type == kResourceNonSecureCodec) { sp<IResourceManagerClient> client; if (!getLowestPriorityBiggestClient_l(callingPid, type, &client)) { return false; } clients.push_back(client); } } } } if (clients.size() == 0) { return false; } sp<IResourceManagerClient> failedClient; for (size_t i = 0; i < clients.size(); ++i) { ALOGV("reclaimResource from client %p", clients[i].get()); if (!clients[i]->reclaimResource()) { return false; failedClient = clients[i]; break; } } return true; { Mutex::Autolock lock(mLock); bool found = false; for (size_t i = 0; i < mMap.size(); ++i) { ResourceInfos &infos = mMap.editValueAt(i); for (size_t j = 0; j < infos.size();) { if (infos[j].client == failedClient) { j = infos.removeAt(j); found = true; } else { ++j; } } if (found) { break; } } if (!found) { ALOGV("didn't find failed client"); } } return (failedClient == NULL); } bool ResourceManagerService::getAllClients_l( Loading
services/mediaresourcemanager/test/ResourceManagerService_test.cpp +64 −4 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * 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. Loading Loading @@ -118,6 +118,20 @@ protected: client3->reset(); } // test set up // --------------------------------------------------------------------------------- // pid priority client type number // --------------------------------------------------------------------------------- // kTestPid1(30) 30 mTestClient1 secure codec 1 // graphic memory 200 // graphic memory 200 // --------------------------------------------------------------------------------- // kTestPid2(20) 20 mTestClient2 non-secure codec 1 // graphic memory 300 // ------------------------------------------- // mTestClient3 secure codec 1 // graphic memory 100 // --------------------------------------------------------------------------------- void addResource() { // kTestPid1 mTestClient1 Vector<MediaResource> resources1; Loading Loading @@ -202,10 +216,12 @@ protected: int lowPriorityPid = 100; EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients)); int midPriorityPid = 25; EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients)); // some higher priority process (e.g. kTestPid2) owns the resource, so getAllClients_l // will fail. EXPECT_FALSE(mService->getAllClients_l(midPriorityPid, type, &clients)); int highPriorityPid = 10; EXPECT_TRUE(mService->getAllClients_l(10, unknowType, &clients)); EXPECT_TRUE(mService->getAllClients_l(10, type, &clients)); EXPECT_TRUE(mService->getAllClients_l(highPriorityPid, unknowType, &clients)); EXPECT_TRUE(mService->getAllClients_l(highPriorityPid, type, &clients)); EXPECT_EQ(2u, clients.size()); EXPECT_EQ(mTestClient3, clients[0]); Loading Loading @@ -308,6 +324,30 @@ protected: // nothing left EXPECT_FALSE(mService->reclaimResource(10, resources)); } // ### secure codecs can coexist and secure codec can coexist with non-secure codec ### { addResource(); mService->mSupportsMultipleSecureCodecs = true; mService->mSupportsSecureWithNonSecureCodec = true; Vector<MediaResource> resources; resources.push_back(MediaResource(String8(kResourceSecureCodec), 1)); EXPECT_TRUE(mService->reclaimResource(10, resources)); // secure codec from lowest process got reclaimed verifyClients(true, false, false); // call again should reclaim another secure codec from lowest process EXPECT_TRUE(mService->reclaimResource(10, resources)); verifyClients(false, false, true); // nothing left EXPECT_FALSE(mService->reclaimResource(10, resources)); // clean up client 2 which still has non secure codec left mService->removeResource((int64_t) mTestClient2.get()); } } void testReclaimResourceNonSecure() { Loading Loading @@ -360,6 +400,26 @@ protected: // nothing left EXPECT_FALSE(mService->reclaimResource(10, resources)); } // ### secure codec can coexist with non-secure codec ### { addResource(); mService->mSupportsSecureWithNonSecureCodec = true; Vector<MediaResource> resources; resources.push_back(MediaResource(String8(kResourceNonSecureCodec), 1)); EXPECT_TRUE(mService->reclaimResource(10, resources)); // one non secure codec from lowest process got reclaimed verifyClients(false, true, false); // nothing left EXPECT_FALSE(mService->reclaimResource(10, resources)); // clean up client 1 and 3 which still have secure codec left mService->removeResource((int64_t) mTestClient1.get()); mService->removeResource((int64_t) mTestClient3.get()); } } void testGetLowestPriorityBiggestClient() { Loading