Add the SurfaceTexture C++ implementation.
This change adds the C++ implementation of SurfaceTexture and related classes. The goal of this is for a SurfaceTexture to be passed to camera service or Stagefright in place of a Surface to allow camera preview or decoded video frames to be streamed to an OpenGL ES texture that an application can use. Change-Id: I55c83a7017f1ecb81c9c9e3252cbd118b914296c
This commit is contained in:
parent
4956334065
commit
8ba32fade1
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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_ISURFACETEXTURE_H
|
||||
#define ANDROID_GUI_ISURFACETEXTURE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
#include <binder/IInterface.h>
|
||||
|
||||
#include <ui/GraphicBuffer.h>
|
||||
#include <ui/Rect.h>
|
||||
|
||||
namespace android {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class ISurfaceTexture : public IInterface
|
||||
{
|
||||
public:
|
||||
DECLARE_META_INTERFACE(SurfaceTexture);
|
||||
|
||||
// requestBuffer requests a new buffer for the given index. The server (i.e.
|
||||
// the ISurfaceTexture implementation) assigns the newly created buffer to
|
||||
// the given slot index, and the client is expected to mirror the
|
||||
// slot->buffer mapping so that it's not necessary to transfer a
|
||||
// GraphicBuffer for every dequeue operation.
|
||||
virtual sp<GraphicBuffer> requestBuffer(int slot, uint32_t w, uint32_t h,
|
||||
uint32_t format, uint32_t usage) = 0;
|
||||
|
||||
// setBufferCount sets the number of buffer slots available. Calling this
|
||||
// will also cause all buffer slots to be emptied. The caller should empty
|
||||
// its mirrored copy of the buffer slots when calling this method.
|
||||
virtual status_t setBufferCount(int bufferCount) = 0;
|
||||
|
||||
// dequeueBuffer requests a new buffer slot for the client to use. Ownership
|
||||
// of the slot is transfered to the client, meaning that the server will not
|
||||
// use the contents of the buffer associated with that slot. The slot index
|
||||
// returned may or may not contain a buffer. If the slot is empty the client
|
||||
// should call requestBuffer to assign a new buffer to that slot. The client
|
||||
// is expected to either call cancelBuffer on the dequeued slot or to fill
|
||||
// in the contents of its associated buffer contents and call queueBuffer.
|
||||
virtual status_t dequeueBuffer(int *slot) = 0;
|
||||
|
||||
// queueBuffer indicates that the client has finished filling in the
|
||||
// contents of the buffer associated with slot and transfers ownership of
|
||||
// that slot back to the server. It is not valid to call queueBuffer on a
|
||||
// slot that is not owned by the client or one for which a buffer associated
|
||||
// via requestBuffer.
|
||||
virtual status_t queueBuffer(int slot) = 0;
|
||||
|
||||
// cancelBuffer indicates that the client does not wish to fill in the
|
||||
// buffer associated with slot and transfers ownership of the slot back to
|
||||
// the server.
|
||||
virtual void cancelBuffer(int slot) = 0;
|
||||
|
||||
virtual status_t setCrop(const Rect& reg) = 0;
|
||||
virtual status_t setTransform(uint32_t transform) = 0;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class BnSurfaceTexture : public BnInterface<ISurfaceTexture>
|
||||
{
|
||||
public:
|
||||
virtual status_t onTransact( uint32_t code,
|
||||
const Parcel& data,
|
||||
Parcel* reply,
|
||||
uint32_t flags = 0);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_GUI_ISURFACETEXTURE_H
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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_SURFACETEXTURE_H
|
||||
#define ANDROID_GUI_SURFACETEXTURE_H
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
|
||||
#include <gui/ISurfaceTexture.h>
|
||||
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <utils/threads.h>
|
||||
|
||||
#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
|
||||
|
||||
namespace android {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class SurfaceTexture : public BnSurfaceTexture {
|
||||
public:
|
||||
enum { MIN_BUFFER_SLOTS = 3 };
|
||||
enum { NUM_BUFFER_SLOTS = 32 };
|
||||
|
||||
// tex indicates the name OpenGL texture to which images are to be streamed.
|
||||
// This texture name cannot be changed once the SurfaceTexture is created.
|
||||
SurfaceTexture(GLuint tex);
|
||||
|
||||
virtual ~SurfaceTexture();
|
||||
|
||||
// setBufferCount updates the number of available buffer slots. After
|
||||
// calling this all buffer slots are both unallocated and owned by the
|
||||
// SurfaceTexture object (i.e. they are not owned by the client).
|
||||
virtual status_t setBufferCount(int bufferCount);
|
||||
|
||||
virtual sp<GraphicBuffer> requestBuffer(int buf, uint32_t w, uint32_t h,
|
||||
uint32_t format, uint32_t usage);
|
||||
|
||||
// dequeueBuffer gets the next buffer slot index for the client to use. If a
|
||||
// buffer slot is available then that slot index is written to the location
|
||||
// pointed to by the buf argument and a status of OK is returned. If no
|
||||
// slot is available then a status of -EBUSY is returned and buf is
|
||||
// unmodified.
|
||||
virtual status_t dequeueBuffer(int *buf);
|
||||
|
||||
virtual status_t queueBuffer(int buf);
|
||||
virtual void cancelBuffer(int buf);
|
||||
virtual status_t setCrop(const Rect& reg);
|
||||
virtual status_t setTransform(uint32_t transform);
|
||||
|
||||
// updateTexImage sets the image contents of the target texture to that of
|
||||
// the most recently queued buffer.
|
||||
//
|
||||
// This call may only be made while the OpenGL ES context to which the
|
||||
// target texture belongs is bound to the calling thread.
|
||||
status_t updateTexImage();
|
||||
|
||||
private:
|
||||
|
||||
// freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
|
||||
// all slots.
|
||||
void freeAllBuffers();
|
||||
|
||||
// createImage creates a new EGLImage from a GraphicBuffer.
|
||||
EGLImageKHR createImage(EGLDisplay dpy,
|
||||
const sp<GraphicBuffer>& graphicBuffer);
|
||||
|
||||
enum { INVALID_BUFFER_SLOT = -1 };
|
||||
|
||||
struct BufferSlot {
|
||||
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
|
||||
// if no buffer has been allocated.
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mEglImage is the EGLImage created from mGraphicBuffer.
|
||||
EGLImageKHR mEglImage;
|
||||
|
||||
// mEglDisplay is the EGLDisplay used to create mEglImage.
|
||||
EGLDisplay mEglDisplay;
|
||||
|
||||
// mOwnedByClient indicates whether the slot is currently accessible to a
|
||||
// client and should not be used by the SurfaceTexture object. It gets
|
||||
// set to true when dequeueBuffer returns the slot and is reset to false
|
||||
// when the client calls either queueBuffer or cancelBuffer on the slot.
|
||||
bool mOwnedByClient;
|
||||
};
|
||||
|
||||
// mSlots is the array of buffer slots that must be mirrored on the client
|
||||
// side. This allows buffer ownership to be transferred between the client
|
||||
// and server without sending a GraphicBuffer over binder. The entire array
|
||||
// is initialized to NULL at construction time, and buffers are allocated
|
||||
// for a slot when requestBuffer is called with that slot's index.
|
||||
BufferSlot mSlots[NUM_BUFFER_SLOTS];
|
||||
|
||||
// mBufferCount is the number of buffer slots that the client and server
|
||||
// must maintain. It defaults to MIN_BUFFER_SLOTS and can be changed by
|
||||
// calling setBufferCount.
|
||||
int mBufferCount;
|
||||
|
||||
// mCurrentTexture is the buffer slot index of the buffer that is currently
|
||||
// bound to the OpenGL texture. A value of INVALID_BUFFER_SLOT, indicating
|
||||
// that no buffer is currently bound to the texture.
|
||||
int mCurrentTexture;
|
||||
|
||||
// mLastQueued is the buffer slot index of the most recently enqueued buffer.
|
||||
// At construction time it is initialized to INVALID_BUFFER_SLOT, and is
|
||||
// updated each time queueBuffer is called.
|
||||
int mLastQueued;
|
||||
|
||||
// mTexName is the name of the OpenGL texture to which streamed images will
|
||||
// be bound when updateTexImage is called. It is set at construction time
|
||||
// changed with a call to setTexName.
|
||||
const GLuint mTexName;
|
||||
|
||||
// mMutex is the mutex used to prevent concurrent access to the member
|
||||
// variables of SurfaceTexture objects. It must be locked whenever the
|
||||
// member variables are accessed.
|
||||
Mutex mMutex;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_GUI_SURFACETEXTURE_H
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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_SURFACETEXTURECLIENT_H
|
||||
#define ANDROID_GUI_SURFACETEXTURECLIENT_H
|
||||
|
||||
#include <gui/ISurfaceTexture.h>
|
||||
#include <gui/SurfaceTexture.h>
|
||||
|
||||
#include <ui/egl/android_natives.h>
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class SurfaceTextureClient
|
||||
: public EGLNativeBase<ANativeWindow, SurfaceTextureClient, RefBase>
|
||||
{
|
||||
public:
|
||||
SurfaceTextureClient(const sp<ISurfaceTexture>& surfaceTexture);
|
||||
|
||||
private:
|
||||
|
||||
// can't be copied
|
||||
SurfaceTextureClient& operator = (const SurfaceTextureClient& rhs);
|
||||
SurfaceTextureClient(const SurfaceTextureClient& rhs);
|
||||
|
||||
// ANativeWindow hooks
|
||||
static int setSwapInterval(ANativeWindow* window, int interval);
|
||||
static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer);
|
||||
static int cancelBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
|
||||
static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
|
||||
static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
|
||||
static int query(ANativeWindow* window, int what, int* value);
|
||||
static int perform(ANativeWindow* window, int operation, ...);
|
||||
|
||||
int setSwapInterval(int interval);
|
||||
int dequeueBuffer(android_native_buffer_t** buffer);
|
||||
int lockBuffer(android_native_buffer_t* buffer);
|
||||
int queueBuffer(android_native_buffer_t* buffer);
|
||||
int cancelBuffer(android_native_buffer_t* buffer);
|
||||
int query(int what, int* value);
|
||||
int perform(int operation, va_list args);
|
||||
|
||||
int dispatchSetUsage(va_list args);
|
||||
int dispatchConnect(va_list args);
|
||||
int dispatchDisconnect(va_list args);
|
||||
int dispatchSetCrop(va_list args);
|
||||
int dispatchSetBufferCount(va_list args);
|
||||
int dispatchSetBuffersGeometry(va_list args);
|
||||
int dispatchSetBuffersTransform(va_list args);
|
||||
|
||||
int connect(int api);
|
||||
int disconnect(int api);
|
||||
int setUsage(uint32_t reqUsage);
|
||||
int setCrop(Rect const* rect);
|
||||
int setBufferCount(int bufferCount);
|
||||
int setBuffersGeometry(int w, int h, int format);
|
||||
int setBuffersTransform(int transform);
|
||||
|
||||
void freeAllBuffers();
|
||||
|
||||
enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS };
|
||||
enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS };
|
||||
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
|
||||
|
||||
// mSurfaceTexture is the interface to the surface texture server. All
|
||||
// operations on the surface texture client ultimately translate into
|
||||
// interactions with the server using this interface.
|
||||
sp<ISurfaceTexture> mSurfaceTexture;
|
||||
|
||||
// mSlots stores the buffers that have been allocated for each buffer slot.
|
||||
// It is initialized to null pointers, and gets filled in with the result of
|
||||
// ISurfaceTexture::requestBuffer when the client dequeues a buffer from a
|
||||
// slot that has not yet been used. The buffer allocated to a slot will also
|
||||
// be replaced if the requested buffer usage or geometry differs from that
|
||||
// of the buffer allocated to a slot.
|
||||
sp<GraphicBuffer> mSlots[NUM_BUFFER_SLOTS];
|
||||
|
||||
// mReqWidth is the buffer width that will be requested at the next dequeue
|
||||
// operation. It is initialized to 1.
|
||||
uint32_t mReqWidth;
|
||||
|
||||
// mReqHeight is the buffer height that will be requested at the next deuque
|
||||
// operation. It is initialized to 1.
|
||||
uint32_t mReqHeight;
|
||||
|
||||
// mReqFormat is the buffer pixel format that will be requested at the next
|
||||
// deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
|
||||
uint32_t mReqFormat;
|
||||
|
||||
// mReqUsage is the set of buffer usage flags that will be requested
|
||||
// at the next deuque operation. It is initialized to 0.
|
||||
uint32_t mReqUsage;
|
||||
|
||||
// mMutex is the mutex used to prevent concurrent access to the member
|
||||
// variables of SurfaceTexture objects. It must be locked whenever the
|
||||
// member variables are accessed.
|
||||
Mutex mMutex;
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_GUI_SURFACETEXTURECLIENT_H
|
|
@ -121,6 +121,7 @@ private:
|
|||
friend class Surface;
|
||||
friend class BpSurface;
|
||||
friend class BnSurface;
|
||||
friend class SurfaceTextureClient;
|
||||
friend class LightRefBase<GraphicBuffer>;
|
||||
GraphicBuffer(const GraphicBuffer& rhs);
|
||||
GraphicBuffer& operator = (const GraphicBuffer& rhs);
|
||||
|
|
|
@ -4,17 +4,25 @@ include $(CLEAR_VARS)
|
|||
LOCAL_SRC_FILES:= \
|
||||
ISensorEventConnection.cpp \
|
||||
ISensorServer.cpp \
|
||||
ISurfaceTexture.cpp \
|
||||
Sensor.cpp \
|
||||
SensorChannel.cpp \
|
||||
SensorEventQueue.cpp \
|
||||
SensorManager.cpp
|
||||
SensorManager.cpp \
|
||||
SurfaceTexture.cpp \
|
||||
SurfaceTextureClient.cpp
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcutils \
|
||||
libutils \
|
||||
libbinder \
|
||||
libhardware \
|
||||
libhardware_legacy
|
||||
libhardware_legacy \
|
||||
libui \
|
||||
libEGL \
|
||||
libGLESv2 \
|
||||
libsurfaceflinger_client
|
||||
|
||||
|
||||
LOCAL_MODULE:= libgui
|
||||
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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 <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/Timers.h>
|
||||
|
||||
#include <binder/Parcel.h>
|
||||
#include <binder/IInterface.h>
|
||||
|
||||
#include <gui/ISurfaceTexture.h>
|
||||
|
||||
namespace android {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
enum {
|
||||
REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
|
||||
SET_BUFFER_COUNT,
|
||||
DEQUEUE_BUFFER,
|
||||
QUEUE_BUFFER,
|
||||
CANCEL_BUFFER,
|
||||
SET_CROP,
|
||||
SET_TRANSFORM,
|
||||
};
|
||||
|
||||
|
||||
class BpSurfaceTexture : public BpInterface<ISurfaceTexture>
|
||||
{
|
||||
public:
|
||||
BpSurfaceTexture(const sp<IBinder>& impl)
|
||||
: BpInterface<ISurfaceTexture>(impl)
|
||||
{
|
||||
}
|
||||
|
||||
virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
|
||||
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
|
||||
data.writeInt32(bufferIdx);
|
||||
data.writeInt32(w);
|
||||
data.writeInt32(h);
|
||||
data.writeInt32(format);
|
||||
data.writeInt32(usage);
|
||||
remote()->transact(REQUEST_BUFFER, data, &reply);
|
||||
sp<GraphicBuffer> buffer;
|
||||
bool nonNull = reply.readInt32();
|
||||
if (nonNull) {
|
||||
buffer = new GraphicBuffer();
|
||||
reply.read(*buffer);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
virtual status_t setBufferCount(int bufferCount)
|
||||
{
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
|
||||
data.writeInt32(bufferCount);
|
||||
remote()->transact(SET_BUFFER_COUNT, data, &reply);
|
||||
status_t err = reply.readInt32();
|
||||
return err;
|
||||
}
|
||||
|
||||
virtual status_t dequeueBuffer(int *buf) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
|
||||
remote()->transact(DEQUEUE_BUFFER, data, &reply);
|
||||
*buf = reply.readInt32();
|
||||
int result = reply.readInt32();
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual status_t queueBuffer(int buf) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
|
||||
data.writeInt32(buf);
|
||||
remote()->transact(QUEUE_BUFFER, data, &reply);
|
||||
status_t result = reply.readInt32();
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual void cancelBuffer(int buf) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
|
||||
data.writeInt32(buf);
|
||||
remote()->transact(CANCEL_BUFFER, data, &reply);
|
||||
}
|
||||
|
||||
virtual status_t setCrop(const Rect& reg) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
|
||||
data.writeFloat(reg.left);
|
||||
data.writeFloat(reg.top);
|
||||
data.writeFloat(reg.right);
|
||||
data.writeFloat(reg.bottom);
|
||||
remote()->transact(SET_CROP, data, &reply);
|
||||
status_t result = reply.readInt32();
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual status_t setTransform(uint32_t transform) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
|
||||
data.writeInt32(transform);
|
||||
remote()->transact(SET_TRANSFORM, data, &reply);
|
||||
status_t result = reply.readInt32();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture");
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
status_t BnSurfaceTexture::onTransact(
|
||||
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
|
||||
{
|
||||
switch(code) {
|
||||
case REQUEST_BUFFER: {
|
||||
CHECK_INTERFACE(ISurfaceTexture, data, reply);
|
||||
int bufferIdx = data.readInt32();
|
||||
uint32_t w = data.readInt32();
|
||||
uint32_t h = data.readInt32();
|
||||
uint32_t format = data.readInt32();
|
||||
uint32_t usage = data.readInt32();
|
||||
sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format,
|
||||
usage));
|
||||
reply->writeInt32(buffer != 0);
|
||||
if (buffer != 0) {
|
||||
reply->write(*buffer);
|
||||
}
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case SET_BUFFER_COUNT: {
|
||||
CHECK_INTERFACE(ISurfaceTexture, data, reply);
|
||||
int bufferCount = data.readInt32();
|
||||
int result = setBufferCount(bufferCount);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case DEQUEUE_BUFFER: {
|
||||
CHECK_INTERFACE(ISurfaceTexture, data, reply);
|
||||
int buf;
|
||||
int result = dequeueBuffer(&buf);
|
||||
reply->writeInt32(buf);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case QUEUE_BUFFER: {
|
||||
CHECK_INTERFACE(ISurfaceTexture, data, reply);
|
||||
int buf = data.readInt32();
|
||||
status_t result = queueBuffer(buf);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case CANCEL_BUFFER: {
|
||||
CHECK_INTERFACE(ISurfaceTexture, data, reply);
|
||||
int buf = data.readInt32();
|
||||
cancelBuffer(buf);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case SET_CROP: {
|
||||
Rect reg;
|
||||
CHECK_INTERFACE(ISurfaceTexture, data, reply);
|
||||
reg.left = data.readFloat();
|
||||
reg.top = data.readFloat();
|
||||
reg.right = data.readFloat();
|
||||
reg.bottom = data.readFloat();
|
||||
status_t result = setCrop(reg);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case SET_TRANSFORM: {
|
||||
Rect reg;
|
||||
CHECK_INTERFACE(ISurfaceTexture, data, reply);
|
||||
uint32_t transform = data.readInt32();
|
||||
status_t result = setTransform(transform);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
}
|
||||
return BBinder::onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
}; // namespace android
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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_TAG "SurfaceTexture"
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
#include <gui/SurfaceTexture.h>
|
||||
|
||||
#include <surfaceflinger/ISurfaceComposer.h>
|
||||
#include <surfaceflinger/SurfaceComposerClient.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
SurfaceTexture::SurfaceTexture(GLuint tex) :
|
||||
mBufferCount(MIN_BUFFER_SLOTS), mCurrentTexture(INVALID_BUFFER_SLOT),
|
||||
mLastQueued(INVALID_BUFFER_SLOT), mTexName(tex) {
|
||||
}
|
||||
|
||||
SurfaceTexture::~SurfaceTexture() {
|
||||
freeAllBuffers();
|
||||
}
|
||||
|
||||
status_t SurfaceTexture::setBufferCount(int bufferCount) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
freeAllBuffers();
|
||||
mBufferCount = bufferCount;
|
||||
return OK;
|
||||
}
|
||||
|
||||
sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
|
||||
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
if (buf < 0 || mBufferCount <= buf) {
|
||||
LOGE("requestBuffer: slot index out of range [0, %d]: %d",
|
||||
mBufferCount, buf);
|
||||
return 0;
|
||||
}
|
||||
usage |= GraphicBuffer::USAGE_HW_TEXTURE;
|
||||
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
|
||||
sp<GraphicBuffer> graphicBuffer(composer->createGraphicBuffer(w, h,
|
||||
format, usage));
|
||||
if (graphicBuffer == 0) {
|
||||
LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed");
|
||||
} else {
|
||||
mSlots[buf].mGraphicBuffer = graphicBuffer;
|
||||
if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage);
|
||||
mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
|
||||
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
|
||||
}
|
||||
}
|
||||
return graphicBuffer;
|
||||
}
|
||||
|
||||
status_t SurfaceTexture::dequeueBuffer(int *buf) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
int found = INVALID_BUFFER_SLOT;
|
||||
for (int i = 0; i < mBufferCount; i++) {
|
||||
if (!mSlots[i].mOwnedByClient && i != mCurrentTexture) {
|
||||
mSlots[i].mOwnedByClient = true;
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == INVALID_BUFFER_SLOT) {
|
||||
return -EBUSY;
|
||||
}
|
||||
*buf = found;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t SurfaceTexture::queueBuffer(int buf) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
if (buf < 0 || mBufferCount <= buf) {
|
||||
LOGE("queueBuffer: slot index out of range [0, %d]: %d",
|
||||
mBufferCount, buf);
|
||||
return -EINVAL;
|
||||
} else if (!mSlots[buf].mOwnedByClient) {
|
||||
LOGE("queueBuffer: slot %d is not owned by the client", buf);
|
||||
return -EINVAL;
|
||||
} else if (mSlots[buf].mGraphicBuffer == 0) {
|
||||
LOGE("queueBuffer: slot %d was enqueued without requesting a buffer",
|
||||
buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
mSlots[buf].mOwnedByClient = false;
|
||||
mLastQueued = buf;
|
||||
return OK;
|
||||
}
|
||||
|
||||
void SurfaceTexture::cancelBuffer(int buf) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
if (buf < 0 || mBufferCount <= buf) {
|
||||
LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount,
|
||||
buf);
|
||||
return;
|
||||
} else if (!mSlots[buf].mOwnedByClient) {
|
||||
LOGE("cancelBuffer: slot %d is not owned by the client", buf);
|
||||
return;
|
||||
}
|
||||
mSlots[buf].mOwnedByClient = false;
|
||||
}
|
||||
|
||||
status_t SurfaceTexture::setCrop(const Rect& reg) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
// XXX: How should we handle crops?
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t SurfaceTexture::setTransform(uint32_t transform) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
// XXX: How should we handle transforms?
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t SurfaceTexture::updateTexImage() {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
// We always bind the texture even if we don't update its contents.
|
||||
glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName);
|
||||
|
||||
// Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT,
|
||||
// so this check will fail until a buffer gets queued.
|
||||
if (mCurrentTexture != mLastQueued) {
|
||||
// XXX: Figure out the right target.
|
||||
mCurrentTexture = mLastQueued;
|
||||
EGLImageKHR image = mSlots[mCurrentTexture].mEglImage;
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
EGLDisplay dpy = eglGetCurrentDisplay();
|
||||
sp<GraphicBuffer> graphicBuffer = mSlots[mCurrentTexture].mGraphicBuffer;
|
||||
image = createImage(dpy, graphicBuffer);
|
||||
mSlots[mCurrentTexture].mEglImage = image;
|
||||
mSlots[mCurrentTexture].mEglDisplay = dpy;
|
||||
}
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image);
|
||||
GLint error = glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
LOGE("error binding external texture image %p (slot %d): %#04x",
|
||||
image, mCurrentTexture, error);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void SurfaceTexture::freeAllBuffers() {
|
||||
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
|
||||
mSlots[i].mGraphicBuffer = 0;
|
||||
mSlots[i].mOwnedByClient = false;
|
||||
if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
|
||||
mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
|
||||
mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
|
||||
const sp<GraphicBuffer>& graphicBuffer) {
|
||||
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
|
||||
EGLint attrs[] = {
|
||||
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
||||
EGL_NONE,
|
||||
};
|
||||
EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
|
||||
EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
|
||||
EGLint error = eglGetError();
|
||||
if (error != EGL_SUCCESS) {
|
||||
LOGE("error creating EGLImage: %#x", error);
|
||||
} else if (image == EGL_NO_IMAGE_KHR) {
|
||||
LOGE("no error reported, but no image was returned by "
|
||||
"eglCreateImageKHR");
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
}; // namespace android
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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_TAG "SurfaceTextureClient"
|
||||
|
||||
#include <gui/SurfaceTextureClient.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
SurfaceTextureClient::SurfaceTextureClient(
|
||||
const sp<ISurfaceTexture>& surfaceTexture):
|
||||
mSurfaceTexture(surfaceTexture), mReqWidth(1), mReqHeight(1),
|
||||
mReqFormat(DEFAULT_FORMAT), mReqUsage(0), mMutex() {
|
||||
// Initialize the ANativeWindow function pointers.
|
||||
ANativeWindow::setSwapInterval = setSwapInterval;
|
||||
ANativeWindow::dequeueBuffer = dequeueBuffer;
|
||||
ANativeWindow::cancelBuffer = cancelBuffer;
|
||||
ANativeWindow::lockBuffer = lockBuffer;
|
||||
ANativeWindow::queueBuffer = queueBuffer;
|
||||
ANativeWindow::query = query;
|
||||
ANativeWindow::perform = perform;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::setSwapInterval(ANativeWindow* window, int interval) {
|
||||
SurfaceTextureClient* c = getSelf(window);
|
||||
return c->setSwapInterval(interval);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::dequeueBuffer(ANativeWindow* window,
|
||||
android_native_buffer_t** buffer) {
|
||||
SurfaceTextureClient* c = getSelf(window);
|
||||
return c->dequeueBuffer(buffer);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::cancelBuffer(ANativeWindow* window,
|
||||
android_native_buffer_t* buffer) {
|
||||
SurfaceTextureClient* c = getSelf(window);
|
||||
return c->cancelBuffer(buffer);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::lockBuffer(ANativeWindow* window,
|
||||
android_native_buffer_t* buffer) {
|
||||
SurfaceTextureClient* c = getSelf(window);
|
||||
return c->lockBuffer(buffer);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::queueBuffer(ANativeWindow* window,
|
||||
android_native_buffer_t* buffer) {
|
||||
SurfaceTextureClient* c = getSelf(window);
|
||||
return c->queueBuffer(buffer);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::query(ANativeWindow* window, int what, int* value) {
|
||||
SurfaceTextureClient* c = getSelf(window);
|
||||
return c->query(what, value);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::perform(ANativeWindow* window, int operation, ...) {
|
||||
va_list args;
|
||||
va_start(args, operation);
|
||||
SurfaceTextureClient* c = getSelf(window);
|
||||
return c->perform(operation, args);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::setSwapInterval(int interval) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
int buf = -1;
|
||||
status_t err = mSurfaceTexture->dequeueBuffer(&buf);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
sp<GraphicBuffer>& gbuf(mSlots[buf]);
|
||||
if (gbuf == 0 || gbuf->getWidth() != mReqWidth ||
|
||||
gbuf->getHeight() != mReqHeight ||
|
||||
uint32_t(gbuf->getPixelFormat()) != mReqFormat ||
|
||||
(gbuf->getUsage() & mReqUsage) != mReqUsage) {
|
||||
gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight,
|
||||
mReqFormat, mReqUsage);
|
||||
if (gbuf == 0) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
}
|
||||
*buffer = gbuf.get();
|
||||
return OK;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
|
||||
if (mSlots[i].get() == buffer) {
|
||||
mSurfaceTexture->cancelBuffer(i);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::lockBuffer(android_native_buffer_t* buffer) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
|
||||
if (mSlots[i].get() == GraphicBuffer::getSelf(buffer)) {
|
||||
return mSurfaceTexture->queueBuffer(i);
|
||||
}
|
||||
}
|
||||
LOGE("queueBuffer: unknown buffer queued");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::query(int what, int* value) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
// XXX: Implement this!
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::perform(int operation, va_list args)
|
||||
{
|
||||
int res = NO_ERROR;
|
||||
switch (operation) {
|
||||
case NATIVE_WINDOW_CONNECT:
|
||||
res = dispatchConnect(args);
|
||||
break;
|
||||
case NATIVE_WINDOW_DISCONNECT:
|
||||
res = dispatchDisconnect(args);
|
||||
break;
|
||||
case NATIVE_WINDOW_SET_USAGE:
|
||||
res = dispatchSetUsage(args);
|
||||
break;
|
||||
case NATIVE_WINDOW_SET_CROP:
|
||||
res = dispatchSetCrop(args);
|
||||
break;
|
||||
case NATIVE_WINDOW_SET_BUFFER_COUNT:
|
||||
res = dispatchSetBufferCount(args);
|
||||
break;
|
||||
case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
|
||||
res = dispatchSetBuffersGeometry(args);
|
||||
break;
|
||||
case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
|
||||
res = dispatchSetBuffersTransform(args);
|
||||
break;
|
||||
default:
|
||||
res = NAME_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::dispatchConnect(va_list args) {
|
||||
int api = va_arg(args, int);
|
||||
return connect(api);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::dispatchDisconnect(va_list args) {
|
||||
int api = va_arg(args, int);
|
||||
return disconnect(api);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::dispatchSetUsage(va_list args) {
|
||||
int usage = va_arg(args, int);
|
||||
return setUsage(usage);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::dispatchSetCrop(va_list args) {
|
||||
android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
|
||||
return setCrop(reinterpret_cast<Rect const*>(rect));
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::dispatchSetBufferCount(va_list args) {
|
||||
size_t bufferCount = va_arg(args, size_t);
|
||||
return setBufferCount(bufferCount);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::dispatchSetBuffersGeometry(va_list args) {
|
||||
int w = va_arg(args, int);
|
||||
int h = va_arg(args, int);
|
||||
int f = va_arg(args, int);
|
||||
return setBuffersGeometry(w, h, f);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::dispatchSetBuffersTransform(va_list args) {
|
||||
int transform = va_arg(args, int);
|
||||
return setBuffersTransform(transform);
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::connect(int api) {
|
||||
// XXX: Implement this!
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::disconnect(int api) {
|
||||
// XXX: Implement this!
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::setUsage(uint32_t reqUsage)
|
||||
{
|
||||
Mutex::Autolock lock(mMutex);
|
||||
mReqUsage = reqUsage;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::setCrop(Rect const* rect)
|
||||
{
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
// empty/invalid rects are not allowed
|
||||
if (rect->isEmpty())
|
||||
return BAD_VALUE;
|
||||
|
||||
status_t err = mSurfaceTexture->setCrop(*rect);
|
||||
LOGE_IF(err, "ISurfaceTexture::setCrop(...) returned %s",
|
||||
strerror(-err));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::setBufferCount(int bufferCount)
|
||||
{
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
status_t err = mSurfaceTexture->setBufferCount(bufferCount);
|
||||
LOGE_IF(err, "ISurfaceTexture::setBufferCount(%d) returned %s",
|
||||
bufferCount, strerror(-err));
|
||||
|
||||
if (err == NO_ERROR) {
|
||||
freeAllBuffers();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::setBuffersGeometry(int w, int h, int format)
|
||||
{
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
if (w<0 || h<0 || format<0)
|
||||
return BAD_VALUE;
|
||||
|
||||
if ((w && !h) || (!w && h))
|
||||
return BAD_VALUE;
|
||||
|
||||
mReqWidth = w;
|
||||
mReqHeight = h;
|
||||
mReqFormat = format;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
int SurfaceTextureClient::setBuffersTransform(int transform)
|
||||
{
|
||||
Mutex::Autolock lock(mMutex);
|
||||
status_t err = mSurfaceTexture->setTransform(transform);
|
||||
return err;
|
||||
}
|
||||
|
||||
void SurfaceTextureClient::freeAllBuffers() {
|
||||
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
|
||||
mSlots[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace android
|
Loading…
Reference in New Issue