BufferQueue: Add producer buffer-released callback

Add a callback to the producer side, onBufferReleased, which will be
called every time the consumer releases a buffer back to the
BufferQueue. This will enable a buffer stream splitter to work
autonomously without having to block on dequeueBuffer.

The binder object used for the callback replaces the generic IBinder
token that was passed into IGraphicBufferProducer::connect to detect
the death of the producer. If a producer does not wish to listen for
buffer release events, it can pass in an instance of the
DummyProducerListener class defined in IProducerListener.h, if it even
cares about death events (BufferQueue doesn't enforce the token being
non-NULL, though perhaps we should).

Change-Id: I23935760673524abeafea2b58dccc3583b368710
This commit is contained in:
Dan Stoza 2014-03-21 13:05:51 -07:00
parent 96668903e4
commit f0eaf25e92
18 changed files with 191 additions and 56 deletions

View File

@ -232,16 +232,8 @@ public:
// will usually be the one obtained from dequeueBuffer.
virtual void cancelBuffer(int buf, const sp<Fence>& fence);
// connect attempts to connect a producer API to the BufferQueue. This
// must be called before any other IGraphicBufferProducer methods are
// called except for getAllocator. A consumer must already be connected.
//
// This method will fail if connect was previously called on the
// BufferQueue and no corresponding disconnect call was made (i.e. if
// it's still connected to a producer).
//
// APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
virtual status_t connect(const sp<IBinder>& token,
// See IGraphicBufferProducer::connect
virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output);
// disconnect attempts to disconnect a producer API from the BufferQueue.

View File

@ -46,9 +46,9 @@
namespace android {
class BufferItem;
class IBinder;
class IConsumerListener;
class IGraphicBufferAlloc;
class IProducerListener;
class BufferQueueCore : public virtual RefBase {
@ -162,7 +162,7 @@ private:
// mConnectedProducerToken is used to set a binder death notification on
// the producer.
sp<IBinder> mConnectedProducerToken;
sp<IProducerListener> mConnectedProducerListener;
// mSlots is an array of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer

View File

@ -140,7 +140,7 @@ public:
// it's still connected to a producer).
//
// APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
virtual status_t connect(const sp<IBinder>& token,
virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output);
// disconnect attempts to disconnect a producer API from the BufferQueue.

View File

@ -32,6 +32,7 @@
namespace android {
// ----------------------------------------------------------------------------
class IProducerListener;
class NativeHandle;
class Surface;
@ -344,9 +345,11 @@ public:
// This method will fail if the connect was previously called on the
// IGraphicBufferProducer and no corresponding disconnect call was made.
//
// The token needs to be any opaque binder object that lives in the
// producer process -- it is solely used for obtaining a death notification
// when the producer is killed.
// The listener is an optional binder callback object that can be used if
// the producer wants to be notified when the consumer releases a buffer
// back to the BufferQueue. It is also used to detect the death of the
// producer. If only the latter functionality is desired, there is a
// DummyProducerListener class in IProducerListener.h that can be used.
//
// The api should be one of the NATIVE_WINDOW_API_* values in <window.h>
//
@ -370,7 +373,7 @@ public:
//
// Additional negative errors may be returned by the internals, they
// should be treated as opaque fatal unrecoverable errors.
virtual status_t connect(const sp<IBinder>& token,
virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) = 0;
// disconnect attempts to disconnect a client API from the

View File

@ -0,0 +1,67 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_GUI_IPRODUCERLISTENER_H
#define ANDROID_GUI_IPRODUCERLISTENER_H
#include <binder/IInterface.h>
#include <utils/RefBase.h>
namespace android {
// ProducerListener is the interface through which the BufferQueue notifies the
// producer of events that the producer may wish to react to. Because the
// producer will generally have a mutex that is locked during calls from the
// producer to the BufferQueue, these calls from the BufferQueue to the
// producer *MUST* be called only when the BufferQueue mutex is NOT locked.
class ProducerListener : public virtual RefBase
{
public:
ProducerListener() {}
virtual ~ProducerListener() {}
// onBufferReleased is called from IGraphicBufferConsumer::releaseBuffer to
// notify the producer that a new buffer is free and ready to be dequeued.
//
// This is called without any lock held and can be called concurrently by
// multiple threads.
virtual void onBufferReleased() = 0; // Asynchronous
};
class IProducerListener : public ProducerListener, public IInterface
{
public:
DECLARE_META_INTERFACE(ProducerListener)
};
class BnProducerListener : public BnInterface<IProducerListener>
{
public:
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = 0);
};
class DummyProducerListener : public BnProducerListener
{
public:
virtual void onBufferReleased() {}
};
} // namespace android
#endif

View File

@ -21,6 +21,7 @@ LOCAL_SRC_FILES:= \
IDisplayEventConnection.cpp \
IGraphicBufferAlloc.cpp \
IGraphicBufferProducer.cpp \
IProducerListener.cpp \
ISensorEventConnection.cpp \
ISensorServer.cpp \
ISurfaceComposer.cpp \

View File

@ -119,9 +119,9 @@ void BufferQueue::cancelBuffer(int buf, const sp<Fence>& fence) {
mProducer->cancelBuffer(buf, fence);
}
status_t BufferQueue::connect(const sp<IBinder>& token,
status_t BufferQueue::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) {
return mProducer->connect(token, api, producerControlledByApp, output);
return mProducer->connect(listener, api, producerControlledByApp, output);
}
status_t BufferQueue::disconnect(int api) {

View File

@ -24,6 +24,7 @@
#include <gui/BufferQueueCore.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferAlloc.h>
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
@ -47,7 +48,7 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
mConsumerListener(),
mConsumerUsageBits(0),
mConnectedApi(NO_CONNECTED_API),
mConnectedProducerToken(),
mConnectedProducerListener(),
mSlots(),
mQueue(),
mOverrideMaxBufferCount(0),

View File

@ -25,6 +25,7 @@
#include <gui/BufferQueueProducer.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferAlloc.h>
#include <gui/IProducerListener.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@ -654,7 +655,7 @@ int BufferQueueProducer::query(int what, int *outValue) {
return NO_ERROR;
}
status_t BufferQueueProducer::connect(const sp<android::IBinder> &token,
status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput *output) {
ATRACE_CALL();
Mutex::Autolock lock(mCore->mMutex);
@ -711,16 +712,16 @@ status_t BufferQueueProducer::connect(const sp<android::IBinder> &token,
// Set up a death notification so that we can disconnect
// automatically if the remote producer dies
if (token != NULL && token->remoteBinder() != NULL) {
status = token->linkToDeath(
if (listener != NULL &&
listener->asBinder()->remoteBinder() != NULL) {
status = listener->asBinder()->linkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
if (status == NO_ERROR) {
mCore->mConnectedProducerToken = token;
} else {
if (status != NO_ERROR) {
BQ_LOGE("connect(P): linkToDeath failed: %s (%d)",
strerror(-status), status);
}
}
mCore->mConnectedProducerListener = listener;
break;
default:
BQ_LOGE("connect(P): unknown API %d", api);
@ -759,14 +760,15 @@ status_t BufferQueueProducer::disconnect(int api) {
mCore->freeAllBuffersLocked();
// Remove our death notification callback if we have one
sp<IBinder> token = mCore->mConnectedProducerToken;
if (token != NULL) {
if (mCore->mConnectedProducerListener != NULL) {
sp<IBinder> token =
mCore->mConnectedProducerListener->asBinder();
// This can fail if we're here because of the death
// notification, but we just ignore it
token->unlinkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
}
mCore->mConnectedProducerToken = NULL;
mCore->mConnectedProducerListener = NULL;
mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
mCore->mSidebandStream.clear();
mCore->mDequeueCondition.broadcast();

View File

@ -27,6 +27,7 @@
#include <binder/IInterface.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
namespace android {
// ----------------------------------------------------------------------------
@ -171,11 +172,16 @@ public:
return result;
}
virtual status_t connect(const sp<IBinder>& token,
virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeStrongBinder(token);
if (listener != NULL) {
data.writeInt32(1);
data.writeStrongBinder(listener->asBinder());
} else {
data.writeInt32(0);
}
data.writeInt32(api);
data.writeInt32(producerControlledByApp);
status_t result = remote()->transact(CONNECT, data, &reply);
@ -308,13 +314,16 @@ status_t BnGraphicBufferProducer::onTransact(
} break;
case CONNECT: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
sp<IBinder> token = data.readStrongBinder();
sp<IProducerListener> listener;
if (data.readInt32() == 1) {
listener = IProducerListener::asInterface(data.readStrongBinder());
}
int api = data.readInt32();
bool producerControlledByApp = data.readInt32();
QueueBufferOutput* const output =
reinterpret_cast<QueueBufferOutput *>(
reply->writeInplace(sizeof(QueueBufferOutput)));
status_t res = connect(token, api, producerControlledByApp, output);
status_t res = connect(listener, api, producerControlledByApp, output);
reply->writeInt32(res);
return NO_ERROR;
} break;

View File

@ -0,0 +1,53 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <binder/Parcel.h>
#include <gui/IProducerListener.h>
namespace android {
enum {
ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
};
class BpProducerListener : public BpInterface<IProducerListener>
{
public:
BpProducerListener(const sp<IBinder>& impl)
: BpInterface<IProducerListener>(impl) {}
virtual void onBufferReleased() {
Parcel data, reply;
data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY);
}
};
IMPLEMENT_META_INTERFACE(ProducerListener, "android.gui.IProducerListener")
status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags) {
switch (code) {
case ON_BUFFER_RELEASED:
CHECK_INTERFACE(IProducerListener, data, reply);
onBufferReleased();
return NO_ERROR;
}
return BBinder::onTransact(code, data, reply, flags);
}
} // namespace android

View File

@ -27,6 +27,7 @@
#include <ui/Fence.h>
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/GLConsumer.h>
@ -513,10 +514,10 @@ int Surface::dispatchUnlockAndPost(va_list args __attribute__((unused))) {
int Surface::connect(int api) {
ATRACE_CALL();
ALOGV("Surface::connect");
static sp<BBinder> sLife = new BBinder();
static sp<IProducerListener> listener = new DummyProducerListener();
Mutex::Autolock lock(mMutex);
IGraphicBufferProducer::QueueBufferOutput output;
int err = mGraphicBufferProducer->connect(sLife, api, mProducerControlledByApp, &output);
int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
if (err == NO_ERROR) {
uint32_t numPendingBuffers = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,

View File

@ -17,17 +17,19 @@
#define LOG_TAG "BufferQueue_test"
//#define LOG_NDEBUG 0
#include <gtest/gtest.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <gui/BufferQueue.h>
#include <gui/IProducerListener.h>
#include <ui/GraphicBuffer.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <gui/BufferQueue.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <gtest/gtest.h>
namespace android {
@ -141,7 +143,8 @@ TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
sp<DummyConsumer> dc(new DummyConsumer);
mConsumer->consumerConnect(dc, false);
IGraphicBufferProducer::QueueBufferOutput qbo;
mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &qbo);
mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
&qbo);
mProducer->setBufferCount(4);
int slot;
@ -207,8 +210,8 @@ TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) {
sp<DummyConsumer> dc(new DummyConsumer);
ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
IGraphicBufferProducer::QueueBufferOutput output;
ASSERT_EQ(OK,
mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
NATIVE_WINDOW_API_CPU, false, &output));
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(-1)); // Index too low
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(
@ -260,8 +263,8 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) {
sp<DummyConsumer> dc(new DummyConsumer);
ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
IGraphicBufferProducer::QueueBufferOutput output;
ASSERT_EQ(OK,
mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
NATIVE_WINDOW_API_CPU, false, &output));
int slot;
sp<Fence> fence;
@ -318,8 +321,8 @@ TEST_F(BufferQueueTest, MoveFromConsumerToProducer) {
sp<DummyConsumer> dc(new DummyConsumer);
ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
IGraphicBufferProducer::QueueBufferOutput output;
ASSERT_EQ(OK,
mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
NATIVE_WINDOW_API_CPU, false, &output));
int slot;
sp<Fence> fence;

View File

@ -25,13 +25,14 @@
#include <ui/GraphicBuffer.h>
#include <gui/BufferQueue.h>
#include <gui/IProducerListener.h>
#include <vector>
#define ASSERT_OK(x) ASSERT_EQ(OK, (x))
#define EXPECT_OK(x) EXPECT_EQ(OK, (x))
#define TEST_TOKEN ((IBinder*)(NULL))
#define TEST_TOKEN ((IProducerListener*)(NULL))
#define TEST_API NATIVE_WINDOW_API_CPU
#define TEST_API_OTHER NATIVE_WINDOW_API_EGL // valid API that's not TEST_API
#define TEST_CONTROLLED_BY_APP false

View File

@ -454,11 +454,12 @@ int VirtualDisplaySurface::query(int what, int* value) {
return mSource[SOURCE_SINK]->query(what, value);
}
status_t VirtualDisplaySurface::connect(const sp<IBinder>& token,
status_t VirtualDisplaySurface::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp,
QueueBufferOutput* output) {
QueueBufferOutput qbo;
status_t result = mSource[SOURCE_SINK]->connect(token, api, producerControlledByApp, &qbo);
status_t result = mSource[SOURCE_SINK]->connect(listener, api,
producerControlledByApp, &qbo);
if (result == NO_ERROR) {
updateQueueBufferOutput(qbo);
*output = mQueueBufferOutput;

View File

@ -27,6 +27,7 @@ namespace android {
// ---------------------------------------------------------------------------
class HWComposer;
class IProducerListener;
/* This DisplaySurface implementation supports virtual displays, where GLES
* and/or HWC compose into a buffer that is then passed to an arbitrary
@ -105,7 +106,7 @@ private:
const QueueBufferInput& input, QueueBufferOutput* output);
virtual void cancelBuffer(int pslot, const sp<Fence>& fence);
virtual int query(int what, int* value);
virtual status_t connect(const sp<IBinder>& token,
virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output);
virtual status_t disconnect(int api);
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);

View File

@ -88,9 +88,9 @@ int MonitoredProducer::query(int what, int* value) {
return mProducer->query(what, value);
}
status_t MonitoredProducer::connect(const sp<IBinder>& token, int api,
bool producerControlledByApp, QueueBufferOutput* output) {
return mProducer->connect(token, api, producerControlledByApp, output);
status_t MonitoredProducer::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) {
return mProducer->connect(listener, api, producerControlledByApp, output);
}
status_t MonitoredProducer::disconnect(int api) {

View File

@ -21,7 +21,7 @@
namespace android {
class IBinder;
class IProducerListener;
class NativeHandle;
class SurfaceFlinger;
@ -45,7 +45,7 @@ public:
QueueBufferOutput* output);
virtual void cancelBuffer(int slot, const sp<Fence>& fence);
virtual int query(int what, int* value);
virtual status_t connect(const sp<IBinder>& token, int api,
virtual status_t connect(const sp<IProducerListener>& token, int api,
bool producerControlledByApp, QueueBufferOutput* output);
virtual status_t disconnect(int api);
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);