Bug 1003712: extend MediaResourceManagerService to support video encoder and no-wait mode. r=rjesup,jhlin,sotaro

This commit is contained in:
John Lin 2014-06-04 14:52:21 -04:00
parent ecf1a3c65c
commit edbb0cfe38
6 changed files with 387 additions and 97 deletions

View File

@ -80,7 +80,7 @@ OMXCodecProxy::~OMXCodecProxy()
IPCThreadState::self()->flushCommands();
if (mManagerService.get() && mClient.get()) {
mManagerService->cancelClient(mClient);
mManagerService->cancelClient(mClient, IMediaResourceManagerService::HW_VIDEO_DECODER);
}
mSource.clear();
@ -126,7 +126,7 @@ void OMXCodecProxy::requestResource()
return;
}
mManagerService->requestMediaResource(mClient, MediaResourceManagerClient::HW_VIDEO_DECODER);
mManagerService->requestMediaResource(mClient, IMediaResourceManagerService::HW_VIDEO_DECODER, true /* will wait */);
}
bool OMXCodecProxy::IsWaitingResources()

View File

@ -34,20 +34,23 @@ public:
{
}
virtual void requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType)
virtual status_t requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType, bool willWait)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaResourceManagerService::getInterfaceDescriptor());
data.writeStrongBinder(client->asBinder());
data.writeInt32(resourceType);
data.writeInt32(willWait ? 1 : 0);
remote()->transact(REQUEST_MEDIA_RESOURCE, data, &reply);
return reply.readInt32();
}
virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client)
virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client, int resourceType)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaResourceManagerService::getInterfaceDescriptor());
data.writeStrongBinder(client->asBinder());
data.writeInt32(resourceType);
remote()->transact(DEREGISTER_CLIENT, data, &reply);
return reply.readInt32();
}
@ -66,14 +69,17 @@ status_t BnMediaResourceManagerService::onTransact(
CHECK_INTERFACE(IMediaResourceManagerService, data, reply);
sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(data.readStrongBinder());
int resourceType = data.readInt32();
requestMediaResource(client, resourceType);
bool willWait = (data.readInt32() == 1);
status_t result = requestMediaResource(client, resourceType, willWait);
reply->writeInt32(result);
return NO_ERROR;
} break;
case DEREGISTER_CLIENT: {
CHECK_INTERFACE(IMediaResourceManagerService, data, reply);
sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(data.readStrongBinder());
cancelClient(client);
reply->writeInt32(NO_ERROR);
int resourceType = data.readInt32();
status_t result = cancelClient(client, resourceType);
reply->writeInt32(result);
return NO_ERROR;
} break;
default:

View File

@ -22,10 +22,36 @@ class IMediaResourceManagerService : public IInterface
public:
DECLARE_META_INTERFACE(MediaResourceManagerService);
// Enumeration for the resource types
enum ResourceType {
HW_VIDEO_DECODER = 0,
HW_AUDIO_DECODER, // Not supported currently.
HW_VIDEO_ENCODER,
HW_CAMERA, // Not supported currently.
NUM_OF_RESOURCE_TYPES,
INVALID_RESOURCE_TYPE = -1
};
enum ErrorCode {
RESOURCE_NOT_AVAILABLE = -EAGAIN
};
// Request a media resource for IMediaResourceManagerClient.
virtual void requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType) = 0;
// client is the binder that service will notify (through
// IMediaResourceManagerClient::statusChanged()) when request status changed.
// resourceType is type of resource that client would like to request.
// willWait indicates that, when the resource is not currently available
// (i.e., already in use by another client), if the client wants to wait. If
// true, client will be put into a (FIFO) waiting list and be notified when
// resource is available.
// For unsupported types, this function returns BAD_TYPE. For supported
// types, it always returns OK when willWait is true; otherwise it will
// return true when resouce is available, or RESOURCE_NOT_AVAILABLE when
// resouce is in use.
virtual status_t requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType, bool willWait) = 0;
// Cancel a media resource request and a resource allocated to IMediaResourceManagerClient.
virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client) = 0;
// Client must call this function after it's done with the media resource requested.
virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client, int resourceType) = 0;
};

View File

@ -22,12 +22,6 @@ public:
CLIENT_STATE_RESOURCE_ASSIGNED,
CLIENT_STATE_SHUTDOWN
};
// Enumeration for the resource types
enum ResourceType {
HW_VIDEO_DECODER,
HW_AUDIO_DECODER,
HW_CAMERA
};
struct EventListener : public virtual RefBase {
// Notifies a change of media resource request status.

View File

@ -7,6 +7,8 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaResourceManagerService"
#include <mozilla/Assertions.h>
#include <binder/IServiceManager.h>
#include <media/stagefright/foundation/AMessage.h>
#include <utils/Log.h>
@ -16,14 +18,16 @@
namespace android {
const char* MediaResourceManagerService::kMsgKeyResourceType = "res-type";
/* static */
void MediaResourceManagerService::instantiate() {
defaultServiceManager()->addService(
String16("media.resource_manager"), new MediaResourceManagerService());
String16("media.resource_manager"),
new MediaResourceManagerService());
}
MediaResourceManagerService::MediaResourceManagerService()
: mVideoDecoderCount(VIDEO_DECODER_COUNT)
{
mLooper = new ALooper;
mLooper->setName("MediaResourceManagerService");
@ -49,99 +53,319 @@ void MediaResourceManagerService::binderDied(const wp<IBinder>& who)
Mutex::Autolock autoLock(mLock);
sp<IBinder> binder = who.promote();
if (binder != NULL) {
cancelClientLocked(binder);
mResources.forgetClient(binder);
}
}
}
void MediaResourceManagerService::requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType)
status_t MediaResourceManagerService::requestMediaResource(const sp<IMediaResourceManagerClient>& client,
int resourceType, bool willWait)
{
if (resourceType != MediaResourceManagerClient::HW_VIDEO_DECODER) {
// Support only HW_VIDEO_DECODER
return;
ResourceType type = static_cast<ResourceType>(resourceType);
// Support only HW_VIDEO_DECODER and HW_VIDEO_ENCODER.
switch (type) {
case HW_VIDEO_DECODER:
case HW_VIDEO_ENCODER:
break;
default:
// Type not supported.
return BAD_TYPE;
}
{
Mutex::Autolock autoLock(mLock);
sp<IBinder> binder = client->asBinder();
mVideoCodecRequestQueue.push_back(binder);
binder->linkToDeath(this);
}
Mutex::Autolock autoLock(mLock);
sp<AMessage> notify =
new AMessage(kNotifyRequest, mReflector->id());
// Must know if it will be granted or not - if there are enough unfufilled requests to
// use up the resource, fail. Otherwise we know that enqueuing under lock will succeed.
if (!willWait &&
(mResources.findAvailableResource(type, mResources.countRequests(type) + 1) ==
NAME_NOT_FOUND)) {
return RESOURCE_NOT_AVAILABLE;
}
// We could early-return here without enqueuing IF we can do the rest of
// the allocation safely here. However, enqueuing ensures there's only
// one copy of that code, and that any callbacks are made from the same
// context.
sp<IBinder> binder = client->asBinder();
mResources.enqueueRequest(binder, type);
binder->linkToDeath(this);
sp<AMessage> notify = new AMessage(kNotifyRequest, mReflector->id());
notify->setInt32(kMsgKeyResourceType, resourceType);
// Post AMessage to MediaResourceManagerService via ALooper.
notify->post();
return OK;
}
status_t MediaResourceManagerService::cancelClient(const sp<IMediaResourceManagerClient>& client)
status_t MediaResourceManagerService::cancelClient(const sp<IMediaResourceManagerClient>& client,
int resourceType)
{
{
Mutex::Autolock autoLock(mLock);
sp<IBinder> binder = client->asBinder();
cancelClientLocked(binder);
}
Mutex::Autolock autoLock(mLock);
sp<AMessage> notify =
new AMessage(kNotifyRequest, mReflector->id());
// Post AMessage to MediaResourceManagerService via ALooper.
sp<IBinder> binder = client->asBinder();
cancelClientLocked(binder, static_cast<ResourceType>(resourceType));
sp<AMessage> notify = new AMessage(kNotifyRequest, mReflector->id());
notify->setInt32(kMsgKeyResourceType, resourceType);
// Next!
// Note: since we held the lock while releasing and then posting, if there is
// a queue, no willWait==false entries can jump into the queue thinking they'll
// get the resource.
notify->post();
return NO_ERROR;
}
// Extract resource type from message.
static int32_t getResourceType(const sp<AMessage>& message)
{
int32_t resourceType = MediaResourceManagerService::INVALID_RESOURCE_TYPE;
return message->findInt32(MediaResourceManagerService::kMsgKeyResourceType, &resourceType) ?
resourceType : MediaResourceManagerService::INVALID_RESOURCE_TYPE;
}
// Called on ALooper thread.
void MediaResourceManagerService::onMessageReceived(const sp<AMessage> &msg)
{
Mutex::Autolock autoLock(mLock);
ResourceType type = static_cast<ResourceType>(getResourceType(msg));
// Exit if no request.
if (mVideoCodecRequestQueue.empty()) {
// Note: a message is sent both for "I added an entry to the queue"
// (which may succeed, typically if the queue is empty), and for "I gave
// up the resource", in which case it's "give to the next waiting client,
// or no one".
// Exit if no resource is available, but leave the client in the waiting
// list.
int found = mResources.findAvailableResource(type);
if (found == NAME_NOT_FOUND) {
return;
}
// Check if resource is available
int found = -1;
for (int i=0 ; i<mVideoDecoderCount ; i++) {
if (!mVideoDecoderSlots[i].mClient.get()) {
// Exit if no request.
if (!mResources.hasRequest(type)) {
return;
}
const sp<IBinder>& req = mResources.nextRequest(type);
mResources.aquireResource(req, type, found);
// Notify resource assignment to the client.
sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(req);
client->statusChanged(MediaResourceManagerClient::CLIENT_STATE_RESOURCE_ASSIGNED);
mResources.dequeueRequest(type);
}
void MediaResourceManagerService::cancelClientLocked(const sp<IBinder>& binder,
ResourceType resourceType)
{
mResources.forgetClient(binder, resourceType);
binder->unlinkToDeath(this);
}
MediaResourceManagerService::ResourceTable::ResourceTable()
{
// Populate types of resources.
for (int type = 0; type < NUM_OF_RESOURCE_TYPES; type++) {
ssize_t index = mMap.add(static_cast<ResourceType>(type), Resources());
Resources& resources = mMap.editValueAt(index);
int available;
switch (type) {
case HW_VIDEO_DECODER:
available = VIDEO_DECODER_COUNT;
break;
case HW_VIDEO_ENCODER:
available = VIDEO_ENCODER_COUNT;
break;
default:
available = 0;
break;
}
resources.mSlots.insertAt(0, available);
}
}
MediaResourceManagerService::ResourceTable::~ResourceTable() {
// Remove resouces.
mMap.clear();
}
bool MediaResourceManagerService::ResourceTable::supportsType(ResourceType type)
{
return mMap.indexOfKey(type) != NAME_NOT_FOUND;
}
ssize_t MediaResourceManagerService::ResourceTable::findAvailableResource(ResourceType type,
size_t numberNeeded)
{
MOZ_ASSERT(numberNeeded > 0);
ssize_t found = mMap.indexOfKey(type);
if (found == NAME_NOT_FOUND) {
// Unsupported type.
return found;
}
const Slots& slots = mMap.valueAt(found).mSlots;
found = NAME_NOT_FOUND;
for (size_t i = 0; i < slots.size(); i++) {
if (slots[i].mClient != nullptr) {
// Already in use.
continue;
}
if (--numberNeeded == 0) {
found = i;
break;
}
}
// Exit if no resource is available.
if (found == -1) {
return;
}
// Assign resource to IMediaResourceManagerClient
Fifo::iterator front(mVideoCodecRequestQueue.begin());
mVideoDecoderSlots[found].mClient = *front;
mVideoCodecRequestQueue.erase(front);
// Notify resource assignment to the client.
sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(mVideoDecoderSlots[found].mClient);
client->statusChanged(MediaResourceManagerClient::CLIENT_STATE_RESOURCE_ASSIGNED);
return found;
}
void MediaResourceManagerService::cancelClientLocked(const sp<IBinder>& binder)
bool MediaResourceManagerService::ResourceTable::isOwnedByClient(const sp<IBinder>& client,
ResourceType type,
size_t index)
{
// Clear the request from request queue.
Fifo::iterator it(mVideoCodecRequestQueue.begin());
while (it != mVideoCodecRequestQueue.end()) {
if ((*it).get() == binder.get()) {
mVideoCodecRequestQueue.erase(it);
ResourceSlot* slot = resourceOfTypeAt(type, index);
return slot && slot->mClient == client;
}
status_t MediaResourceManagerService::ResourceTable::aquireResource(const sp<IBinder>& client,
ResourceType type,
size_t index)
{
ResourceSlot* slot = resourceOfTypeAt(type, index);
// Resouce should not be in use.
MOZ_ASSERT(slot && slot->mClient == nullptr);
if (!slot) {
return NAME_NOT_FOUND;
} else if (slot->mClient != nullptr) {
// Resource already in use by other client.
return PERMISSION_DENIED;
}
slot->mClient = client;
return OK;
}
MediaResourceManagerService::ResourceSlot*
MediaResourceManagerService::ResourceTable::resourceOfTypeAt(ResourceType type,
size_t index)
{
ssize_t found = mMap.indexOfKey(type);
if (found == NAME_NOT_FOUND) {
// Unsupported type.
return nullptr;
}
Slots& slots = mMap.editValueAt(found).mSlots;
MOZ_ASSERT(index < slots.size());
if (index >= slots.size()) {
// Index out of range.
return nullptr;
}
return &(slots.editItemAt(index));
}
bool MediaResourceManagerService::ResourceTable::hasRequest(ResourceType type)
{
ssize_t found = mMap.indexOfKey(type);
if (found == NAME_NOT_FOUND) {
// Unsupported type.
return nullptr;
}
const Fifo& queue = mMap.valueAt(found).mRequestQueue;
return !queue.empty();
}
uint32_t MediaResourceManagerService::ResourceTable::countRequests(ResourceType type)
{
ssize_t found = mMap.indexOfKey(type);
if (found == NAME_NOT_FOUND) {
// Unsupported type.
return 0;
}
const Fifo& queue = mMap.valueAt(found).mRequestQueue;
return queue.size();
}
const sp<IBinder>& MediaResourceManagerService::ResourceTable::nextRequest(ResourceType type)
{
ssize_t found = mMap.indexOfKey(type);
if (found == NAME_NOT_FOUND) {
// Unsupported type.
return nullptr;
}
const Fifo& queue = mMap.valueAt(found).mRequestQueue;
return *(queue.begin());
}
status_t MediaResourceManagerService::ResourceTable::enqueueRequest(const sp<IBinder>& client,
ResourceType type)
{
ssize_t found = mMap.indexOfKey(type);
if (found == NAME_NOT_FOUND) {
// Unsupported type.
return found;
}
mMap.editValueAt(found).mRequestQueue.push_back(client);
return OK;
}
status_t MediaResourceManagerService::ResourceTable::dequeueRequest(ResourceType type)
{
ssize_t found = mMap.indexOfKey(type);
if (found == NAME_NOT_FOUND) {
// Unsupported type.
return found;
}
Fifo& queue = mMap.editValueAt(found).mRequestQueue;
queue.erase(queue.begin());
return OK;
}
status_t MediaResourceManagerService::ResourceTable::forgetClient(const sp<IBinder>& client)
{
// Traverse all resources.
for (int i = 0; i < mMap.size(); i++) {
forgetClient(client, mMap.keyAt(i));
}
return OK;
}
status_t MediaResourceManagerService::ResourceTable::forgetClient(const sp<IBinder>& client, ResourceType type)
{
MOZ_ASSERT(supportsType(type));
Resources& resources = mMap.editValueFor(type);
// Remove pending requests for given client.
Fifo& queue = resources.mRequestQueue;
Fifo::iterator it(queue.begin());
while (it != queue.end()) {
if ((*it).get() == client.get()) {
queue.erase(it);
break;
}
it++;
}
// Clear the client from the resource
for (int i=0 ; i<mVideoDecoderCount ; i++) {
if (mVideoDecoderSlots[i].mClient.get() == binder.get()) {
mVideoDecoderSlots[i].mClient = NULL;
// Revoke ownership for given client.
Slots& slots = resources.mSlots;
for (int i = 0; i < slots.size(); i++) {
ResourceSlot& slot = slots.editItemAt(i);
if (client.get() == slot.mClient.get()) {
slot.mClient = nullptr;
}
}
binder->unlinkToDeath(this);
return OK;
}
}; // namespace android

View File

@ -10,8 +10,10 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <media/stagefright/foundation/ALooper.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
#include "IMediaResourceManagerClient.h"
#include "IMediaResourceManagerService.h"
@ -20,20 +22,27 @@ namespace android {
/**
* Manage permissions of using media resources(hw decoder, hw encoder, camera)
* XXX Current implementaion support only one hw video decoder.
* XXX Current implementation supports only one hw video codec.
* Need to extend to support multiple instance and other resources.
*/
class MediaResourceManagerService: public BnMediaResourceManagerService,
public IBinder::DeathRecipient
{
public:
// The maximum number of hardware decoders available.
enum { VIDEO_DECODER_COUNT = 1 };
enum {
kNotifyRequest = 'noti'
// The maximum number of hardware resoureces available.
enum
{
VIDEO_DECODER_COUNT = 1,
VIDEO_ENCODER_COUNT = 1
};
enum
{
kNotifyRequest = 'noti',
};
static const char* kMsgKeyResourceType;
// Instantiate MediaResourceManagerService and register to service manager.
// If service manager is not present, wait until service manager becomes present.
static void instantiate();
@ -42,8 +51,10 @@ public:
virtual void binderDied(const wp<IBinder>& who);
// derived from IMediaResourceManagerService
virtual void requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType);
virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client);
virtual status_t requestMediaResource(const sp<IMediaResourceManagerClient>& client,
int resourceType, bool willWait);
virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client,
int resourceType);
// Receive a message from AHandlerReflector.
// Called on ALooper thread.
@ -53,30 +64,54 @@ protected:
MediaResourceManagerService();
virtual ~MediaResourceManagerService();
protected:
private:
// Represent a media resouce.
// Hold a IMediaResourceManagerClient that got a media resource as IBinder.
struct ResourceSlot {
ResourceSlot ()
{
}
sp<IBinder> mClient;
};
struct ResourceSlot
{
sp<IBinder> mClient;
};
typedef Vector<ResourceSlot> Slots;
void cancelClientLocked(const sp<IBinder>& binder);
// mVideoDecoderSlots is the array of slots that represent a media resource.
ResourceSlot mVideoDecoderSlots[VIDEO_DECODER_COUNT];
// The maximum number of hardware decoders available on the device.
int mVideoDecoderCount;
// The lock protects mVideoDecoderSlots and mVideoCodecRequestQueue called
// from multiple threads.
Mutex mLock;
typedef List<sp<IBinder> > Fifo;
// Queue of media resource requests.
// Hold IMediaResourceManagerClient that requesting a media resource as IBinder.
Fifo mVideoCodecRequestQueue;
struct Resources
{
// Queue of media resource requests. Hold IMediaResourceManagerClient that
// requesting a media resource as IBinder.
Fifo mRequestQueue;
// All resources that can be requested. Hold |ResourceSlot|s that track
// their usage.
Slots mSlots;
};
typedef KeyedVector<ResourceType, Resources> ResourcesMap;
// Manages requests from clients and availability of resources.
class ResourceTable
{
ResourceTable();
~ResourceTable();
// Resource operations.
bool supportsType(ResourceType type);
ssize_t findAvailableResource(ResourceType type, size_t number_needed = 1);
bool isOwnedByClient(const sp<IBinder>& client, ResourceType type, size_t index);
status_t aquireResource(const sp<IBinder>& client, ResourceType type, size_t index);
ResourceSlot* resourceOfTypeAt(ResourceType type, size_t index);
// Request operations.
bool hasRequest(ResourceType type);
uint32_t countRequests(ResourceType type);
const sp<IBinder>& nextRequest(ResourceType type);
status_t enqueueRequest(const sp<IBinder>& client, ResourceType type);
status_t dequeueRequest(ResourceType type);
status_t forgetClient(const sp<IBinder>& client, ResourceType type);
status_t forgetClient(const sp<IBinder>& client);
friend class MediaResourceManagerService;
// A map for all types of supported resources.
ResourcesMap mMap;
};
void cancelClientLocked(const sp<IBinder>& binder, ResourceType resourceType);
// ALooper is a message loop used in stagefright.
// It creates a thread for messages and handles messages in the thread.
@ -88,6 +123,11 @@ protected:
// http://developer.android.com/reference/android/os/Handler.html
sp<AHandlerReflector<MediaResourceManagerService> > mReflector;
// The lock protects manager operations called from multiple threads.
Mutex mLock;
// Keeps all the records.
ResourceTable mResources;
};
}; // namespace android