mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
372 lines
11 KiB
C++
372 lines
11 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
//#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>
|
|
|
|
#include "MediaResourceManagerClient.h"
|
|
#include "MediaResourceManagerService.h"
|
|
|
|
namespace android {
|
|
|
|
const char* MediaResourceManagerService::kMsgKeyResourceType = "res-type";
|
|
|
|
/* static */
|
|
void MediaResourceManagerService::instantiate() {
|
|
defaultServiceManager()->addService(
|
|
String16("media.resource_manager"),
|
|
new MediaResourceManagerService());
|
|
}
|
|
|
|
MediaResourceManagerService::MediaResourceManagerService()
|
|
{
|
|
mLooper = new ALooper;
|
|
mLooper->setName("MediaResourceManagerService");
|
|
|
|
mReflector = new AHandlerReflector<MediaResourceManagerService>(this);
|
|
// Register AMessage handler to ALooper.
|
|
mLooper->registerHandler(mReflector);
|
|
// Start ALooper thread.
|
|
mLooper->start();
|
|
}
|
|
|
|
MediaResourceManagerService::~MediaResourceManagerService()
|
|
{
|
|
// Unregister AMessage handler from ALooper.
|
|
mLooper->unregisterHandler(mReflector->id());
|
|
// Stop ALooper thread.
|
|
mLooper->stop();
|
|
}
|
|
|
|
void MediaResourceManagerService::binderDied(const wp<IBinder>& who)
|
|
{
|
|
if (who != NULL) {
|
|
Mutex::Autolock autoLock(mLock);
|
|
sp<IBinder> binder = who.promote();
|
|
if (binder != NULL) {
|
|
mResources.forgetClient(binder);
|
|
}
|
|
}
|
|
}
|
|
|
|
status_t MediaResourceManagerService::requestMediaResource(const sp<IMediaResourceManagerClient>& client,
|
|
int resourceType, bool willWait)
|
|
{
|
|
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);
|
|
|
|
// 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,
|
|
int resourceType)
|
|
{
|
|
Mutex::Autolock autoLock(mLock);
|
|
|
|
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));
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
bool MediaResourceManagerService::ResourceTable::isOwnedByClient(const sp<IBinder>& client,
|
|
ResourceType type,
|
|
size_t index)
|
|
{
|
|
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++;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
}; // namespace android
|