diff --git a/Android.mk b/Android.mk index 2f0f786aa34a0f5631903e4620423883c3dc3cb5..2d9707638e7d30cc7392cf86b59a32bc1500cff5 100644 --- a/Android.mk +++ b/Android.mk @@ -4,7 +4,6 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ src/InputDevice.cpp \ - src/VirtualDisplay.cpp \ src/VNCFlinger.cpp \ src/main.cpp diff --git a/src/README b/src/README deleted file mode 100644 index e6cbb2b5ac9f258c93de42592b4469c3d24ecb65..0000000000000000000000000000000000000000 --- a/src/README +++ /dev/null @@ -1,2 +0,0 @@ -- The surfaceflinger method is present from version 2.3.X and should be supported by all devices. -- It connects with the surfaceflinger service through a Binder interface. diff --git a/src/VNCFlinger.cpp b/src/VNCFlinger.cpp index efd6cbc9f7c44459951a35daf31a0c8d9223343b..4775d0bdd5d5becd9e32c220e3f92859b2a3186a 100644 --- a/src/VNCFlinger.cpp +++ b/src/VNCFlinger.cpp @@ -12,14 +12,26 @@ #include "InputDevice.h" #include "VNCFlinger.h" -using namespace android; -Mutex VNCFlinger::sUpdateMutex; +using namespace android; status_t VNCFlinger::start() { - Mutex::Autolock _l(mMutex); + sp<ProcessState> self = ProcessState::self(); + self->startThreadPool(); + + status_t err = NO_ERROR; + + mMainDpy = SurfaceComposerClient::getBuiltInDisplay( + ISurfaceComposer::eDisplayIdMain); + err = SurfaceComposerClient::getDisplayInfo(mMainDpy, &mMainDpyInfo); + if (err != NO_ERROR) { + ALOGE("Failed to get display characteristics\n"); + return err; + } + mHeight = mMainDpyInfo.h; + mWidth = mMainDpyInfo.w; - status_t err = setup_l(); + err = createVNCServer(); if (err != NO_ERROR) { ALOGE("Failed to start VNCFlinger: err=%d", err); return err; @@ -29,33 +41,77 @@ status_t VNCFlinger::start() { rfbRunEventLoop(mVNCScreen, -1, true); - mCondition.wait(mMutex); + eventLoop(); - release_l(); return NO_ERROR; } -status_t VNCFlinger::setup_l() { +void VNCFlinger::eventLoop() { + mRunning = true; - status_t err = NO_ERROR; + Mutex::Autolock _l(mEventMutex); + while (mRunning) { + mEventCond.wait(mEventMutex); - mMainDpy = SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain); - err = SurfaceComposerClient::getDisplayInfo(mMainDpy, &mMainDpyInfo); - if (err != NO_ERROR) { - ALOGE("Unable to get display characteristics\n"); - return err; - } + // spurious wakeup? never. + if (mClientCount == 0) { + continue; + } - bool rotated = VirtualDisplay::isDeviceRotated(mMainDpyInfo.orientation); - if (mWidth == 0) { - mWidth = rotated ? mMainDpyInfo.h : mMainDpyInfo.w; - } - if (mHeight == 0) { - mHeight = rotated ? mMainDpyInfo.w : mMainDpyInfo.h; + // this is a new client, so fire everything up + status_t err = createVirtualDisplay(); + if (err != NO_ERROR) { + ALOGE("Failed to create virtual display: err=%d", err); + } + + // loop while clients are connected and process frames + // on the main thread when signalled + while (mClientCount > 0) { + mEventCond.wait(mEventMutex); + if (mFrameAvailable) { + processFrame(); + mFrameAvailable = false; + } + } + + destroyVirtualDisplay(); } +} + +status_t VNCFlinger::createVirtualDisplay() { + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&mProducer, &consumer); + mCpuConsumer = new CpuConsumer(consumer, 1); + mCpuConsumer->setName(String8("vds-to-cpu")); + mCpuConsumer->setDefaultBufferSize(mWidth, mHeight); + mProducer->setMaxDequeuedBufferCount(4); + + mListener = new FrameListener(this); + mCpuConsumer->setFrameAvailableListener(mListener); - ALOGD("Display dimensions: %dx%d rotated=%d", mWidth, mHeight, rotated); + mDpy = SurfaceComposerClient::createDisplay( + String8("VNC-VirtualDisplay"), false /*secure*/); + + SurfaceComposerClient::openGlobalTransaction(); + SurfaceComposerClient::setDisplaySurface(mDpy, mProducer); + //setDisplayProjection(mDpy, mainDpyInfo); + SurfaceComposerClient::setDisplayLayerStack(mDpy, 0); // default stack + SurfaceComposerClient::closeGlobalTransaction(); + + ALOGV("Virtual display created"); + return NO_ERROR; +} + +status_t VNCFlinger::destroyVirtualDisplay() { + mCpuConsumer.clear(); + mProducer.clear(); + SurfaceComposerClient::destroyDisplay(mDpy); + return NO_ERROR; +} + +status_t VNCFlinger::createVNCServer() { + + status_t err = NO_ERROR; rfbLog = VNCFlinger::rfbLogger; rfbErr = VNCFlinger::rfbLogger; @@ -88,32 +144,27 @@ status_t VNCFlinger::setup_l() { /* Mark as dirty since we haven't sent any updates at all yet. */ rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight); - - mVirtualDisplay = new VirtualDisplay(mVNCScreen, &sUpdateMutex); - return err; } -void VNCFlinger::release_l() { - mVirtualDisplay.clear(); +status_t VNCFlinger::stop() { + Mutex::Autolock _L(mEventMutex); - ALOGD("VNCFlinger released"); -} + mClientCount = 0; + mRunning = false; -status_t VNCFlinger::stop() { - Mutex::Autolock _l(mMutex); - mCondition.signal(); + mEventCond.signal(); return NO_ERROR; } size_t VNCFlinger::addClient() { - Mutex::Autolock _l(mMutex); + Mutex::Autolock _l(mEventMutex); if (mClientCount == 0) { + mClientCount++; InputDevice::start(mWidth, mHeight); - mVirtualDisplay->start(mMainDpyInfo); + mEventCond.signal(); } - mClientCount++; ALOGI("Client connected (%zu)", mClientCount); @@ -121,12 +172,12 @@ size_t VNCFlinger::addClient() { } size_t VNCFlinger::removeClient() { - Mutex::Autolock _l(mMutex); + Mutex::Autolock _l(mEventMutex); if (mClientCount > 0) { mClientCount--; if (mClientCount == 0) { - mVirtualDisplay->stop(); InputDevice::stop(); + mEventCond.signal(); } } @@ -150,23 +201,18 @@ enum rfbNewClientAction VNCFlinger::onNewClient(rfbClientPtr cl) { return RFB_CLIENT_ACCEPT; } -void VNCFlinger::onFrameStart(rfbClientPtr /* cl */) { - sUpdateMutex.lock(); +void VNCFlinger::onFrameStart(rfbClientPtr cl) { + VNCFlinger *vf = (VNCFlinger *)cl->screen->screenData; + vf->mUpdateMutex.lock(); ALOGV("frame start"); } -void VNCFlinger::onFrameDone(rfbClientPtr /* cl */, int status) { - sUpdateMutex.unlock(); +void VNCFlinger::onFrameDone(rfbClientPtr cl, int status) { + VNCFlinger *vf = (VNCFlinger *)cl->screen->screenData; + vf->mUpdateMutex.unlock(); ALOGV("frame done! %d", status); } -void VNCFlinger::markFrame(void* frame, size_t stride) { - Mutex::Autolock _l(sUpdateMutex); - mVNCScreen->frameBuffer = (char *)frame; - mVNCScreen->paddedWidthInBytes = stride * 4; - rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight); -} - void VNCFlinger::rfbLogger(const char *format, ...) { va_list args; char buf[256]; @@ -176,3 +222,51 @@ void VNCFlinger::rfbLogger(const char *format, ...) { ALOGI("%s", buf); va_end(args); } + +void VNCFlinger::FrameListener::onFrameAvailable(const BufferItem& item) { + Mutex::Autolock _l(mVNC->mEventMutex); + mVNC->mFrameAvailable = true; + mVNC->mEventCond.signal(); + ALOGV("onFrameAvailable: mTimestamp=%ld mFrameNumber=%ld", + item.mTimestamp, item.mFrameNumber); +} + +void VNCFlinger::processFrame() { + ALOGV("processFrame\n"); + + // Take the update mutex. This ensures that we don't dequeue + // a new buffer and blow away the one being sent to a client. + // The BufferQueue is self-regulating and will drop frames + // automatically for us. + Mutex::Autolock _l(mUpdateMutex); + + CpuConsumer::LockedBuffer imgBuffer; + status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer); + if (res != OK) { + ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res); + return; + } + + ALOGV("processFrame: ptr: %p format: %x (%dx%d, stride=%d)", + imgBuffer.data, imgBuffer.format, imgBuffer.width, + imgBuffer.height, imgBuffer.stride); + + void* vncbuf = mVNCScreen->frameBuffer; + void* imgbuf = imgBuffer.data; + + // Copy the frame to the server's buffer + if (imgBuffer.stride > mWidth) { + // Image has larger stride, so we need to copy row by row + for (size_t y = 0; y < mHeight; y++) { + memcpy(vncbuf, imgbuf, mWidth * 4); + vncbuf = (void *)((char *)vncbuf + mWidth * 4); + imgbuf = (void *)((char *)imgbuf + imgBuffer.stride * 4); + } + } else { + memcpy(vncbuf, imgbuf, mWidth * mHeight * 4); + } + + rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight); + + mCpuConsumer->unlockBuffer(imgBuffer); +} diff --git a/src/VNCFlinger.h b/src/VNCFlinger.h index 8c026b9c562cce4225e3b28338f661c53d350da8..4535fb670bf82bb699e1fe30ce4acacd8f961985 100644 --- a/src/VNCFlinger.h +++ b/src/VNCFlinger.h @@ -1,8 +1,7 @@ #ifndef VNCFLINGER_H #define VNCFLINGER_H -#include "VirtualDisplay.h" - +#include <gui/CpuConsumer.h> #include <ui/DisplayInfo.h> #include "rfb/rfb.h" @@ -27,39 +26,67 @@ public: virtual size_t addClient(); virtual size_t removeClient(); - virtual void markFrame(void* frame, size_t stride); private: - virtual status_t setup_l(); - virtual void release_l(); + class FrameListener : public CpuConsumer::FrameAvailableListener { + public: + FrameListener(VNCFlinger *vnc) : mVNC(vnc) {} + + virtual void onFrameAvailable(const BufferItem& item); + + private: + FrameListener(FrameListener&) {} + VNCFlinger *mVNC; + }; + + virtual void eventLoop(); + virtual status_t createVirtualDisplay(); + virtual status_t destroyVirtualDisplay(); + virtual status_t createVNCServer(); + + virtual void processFrame(); + + // vncserver callbacks static ClientGoneHookPtr onClientGone(rfbClientPtr cl); static enum rfbNewClientAction onNewClient(rfbClientPtr cl); static void onFrameStart(rfbClientPtr cl); static void onFrameDone(rfbClientPtr cl, int result); static void rfbLogger(const char *format, ...); - Condition mCondition; + bool mRunning; + bool mFrameAvailable; + + Mutex mEventMutex; + Mutex mUpdateMutex; + + Condition mEventCond; rfbScreenInfoPtr mVNCScreen; uint8_t *mVNCBuf; uint32_t mWidth, mHeight; - bool mRotate; sp<IBinder> mMainDpy; DisplayInfo mMainDpyInfo; - - Mutex mMutex; - static Mutex sUpdateMutex; - - sp<VirtualDisplay> mVirtualDisplay; int mArgc; char **mArgv; size_t mClientCount; + + sp<FrameListener> mListener; + + // Producer side of queue, passed into the virtual display. + sp<IGraphicBufferProducer> mProducer; + + // This receives frames from the virtual display and makes them available + sp<CpuConsumer> mCpuConsumer; + + // The virtual display instance + sp<IBinder> mDpy; + }; }; diff --git a/src/VirtualDisplay.cpp b/src/VirtualDisplay.cpp deleted file mode 100644 index 27cc00e195880173d797f3397d8b63648be92ba9..0000000000000000000000000000000000000000 --- a/src/VirtualDisplay.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "VNC-VirtualDisplay" -//#define LOG_NDEBUG 0 -#include <utils/Log.h> - -#include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> - -#include <gui/SurfaceComposerClient.h> - -#include <GLES3/gl3.h> -#include <GLES3/gl3ext.h> -#include <GLES2/gl2ext.h> - -#include <ui/Rect.h> - -#include "VirtualDisplay.h" - -using namespace android; - -static const int kGlBytesPerPixel = 4; // GL_RGBA - - -/* - * Returns "true" if the device is rotated 90 degrees. - */ -bool VirtualDisplay::isDeviceRotated(int orientation) { - return orientation != DISPLAY_ORIENTATION_0 && - orientation != DISPLAY_ORIENTATION_180; -} - -/* - * Sets the display projection, based on the display dimensions, video size, - * and device orientation. - */ -status_t VirtualDisplay::setDisplayProjection(const sp<IBinder>& dpy, - const DisplayInfo& mMainDpyInfo) { - status_t err; - - // Set the region of the layer stack we're interested in, which in our - // case is "all of it". If the app is rotated (so that the width of the - // app is based on the height of the display), reverse width/height. - bool deviceRotated = isDeviceRotated(mMainDpyInfo.orientation); - uint32_t sourceWidth, sourceHeight; - if (!deviceRotated) { - sourceWidth = mMainDpyInfo.w; - sourceHeight = mMainDpyInfo.h; - } else { - ALOGV("using rotated width/height"); - sourceHeight = mMainDpyInfo.w; - sourceWidth = mMainDpyInfo.h; - } - Rect layerStackRect(sourceWidth, sourceHeight); - - // We need to preserve the aspect ratio of the display. - float displayAspect = (float) sourceHeight / (float) sourceWidth; - - - // Set the way we map the output onto the display surface (which will - // be e.g. 1280x720 for a 720p video). The rect is interpreted - // post-rotation, so if the display is rotated 90 degrees we need to - // "pre-rotate" it by flipping width/height, so that the orientation - // adjustment changes it back. - // - // We might want to encode a portrait display as landscape to use more - // of the screen real estate. (If players respect a 90-degree rotation - // hint, we can essentially get a 720x1280 video instead of 1280x720.) - // In that case, we swap the configured video width/height and then - // supply a rotation value to the display projection. - uint32_t videoWidth, videoHeight; - uint32_t outWidth, outHeight; - if (!mRotate) { - videoWidth = mWidth; - videoHeight = mHeight; - } else { - videoWidth = mHeight; - videoHeight = mWidth; - } - if (videoHeight > (uint32_t)(videoWidth * displayAspect)) { - // limited by narrow width; reduce height - outWidth = videoWidth; - outHeight = (uint32_t)(videoWidth * displayAspect); - } else { - // limited by short height; restrict width - outHeight = videoHeight; - outWidth = (uint32_t)(videoHeight / displayAspect); - } - uint32_t offX, offY; - offX = (videoWidth - outWidth) / 2; - offY = (videoHeight - outHeight) / 2; - Rect displayRect(offX, offY, offX + outWidth, offY + outHeight); - - if (mRotate) { - ALOGV("Rotated content area is %ux%u at offset x=%d y=%d\n", - outHeight, outWidth, offY, offX); - } else { - ALOGV("Content area is %ux%u at offset x=%d y=%d\n", - outWidth, outHeight, offX, offY); - } - - SurfaceComposerClient::setDisplayProjection(dpy, - mRotate ? DISPLAY_ORIENTATION_90 : DISPLAY_ORIENTATION_0, - layerStackRect, displayRect); - return NO_ERROR; -} - -status_t VirtualDisplay::start(const DisplayInfo& mainDpyInfo) { - - Mutex::Autolock _l(mMutex); - - ALOGV("Orientation: %d", mainDpyInfo.orientation); - mRotate = isDeviceRotated(mainDpyInfo.orientation); - mWidth = mRotate ? mainDpyInfo.h : mainDpyInfo.w; - mHeight = mRotate ? mainDpyInfo.w : mainDpyInfo.h; - - sp<ProcessState> self = ProcessState::self(); - self->startThreadPool(); - - run("vnc-virtualdisplay"); - - mState = INIT; - while (mState == INIT) { - mStartCond.wait(mMutex); - } - - if (mThreadResult != NO_ERROR) { - ALOGE("Failed to start VDS thread: err=%d", mThreadResult); - return mThreadResult; - } - assert(mState == RUNNING); - - mDpy = SurfaceComposerClient::createDisplay( - String8("VNCFlinger"), false /*secure*/); - - SurfaceComposerClient::openGlobalTransaction(); - SurfaceComposerClient::setDisplaySurface(mDpy, mProducer); - setDisplayProjection(mDpy, mainDpyInfo); - SurfaceComposerClient::setDisplayLayerStack(mDpy, 0); // default stack - SurfaceComposerClient::closeGlobalTransaction(); - - ALOGV("VirtualDisplay::start successful"); - return NO_ERROR; -} - -status_t VirtualDisplay::stop() { - Mutex::Autolock _l(mMutex); - mState = STOPPING; - mEventCond.signal(); - return NO_ERROR; -} - -bool VirtualDisplay::threadLoop() { - Mutex::Autolock _l(mMutex); - - mThreadResult = setup_l(); - - if (mThreadResult != NO_ERROR) { - ALOGW("Aborting VDS thread"); - mState = STOPPED; - release_l(); - mStartCond.broadcast(); - return false; - } - - ALOGV("VDS thread running"); - mState = RUNNING; - mStartCond.broadcast(); - - while (mState == RUNNING) { - mEventCond.wait(mMutex); - ALOGD("Awake, frame available"); - processFrame_l(); - } - - ALOGV("VDS thread stopping"); - release_l(); - mState = STOPPED; - return false; // stop -} - -status_t VirtualDisplay::setup_l() { - status_t err; - - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&mProducer, &consumer); - mCpuConsumer = new CpuConsumer(consumer, 1); - mCpuConsumer->setName(String8("vds-to-cpu")); - mCpuConsumer->setDefaultBufferSize(mWidth, mHeight); - mProducer->setMaxDequeuedBufferCount(4); - - mCpuConsumer->setFrameAvailableListener(this); - - ALOGD("VirtualDisplay::setup_l OK"); - return NO_ERROR; -} - -void* VirtualDisplay::processFrame_l() { - ALOGD("processFrame_l\n"); - - mUpdateMutex->lock(); - - CpuConsumer::LockedBuffer imgBuffer; - status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer); - if (res != OK) { - ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res); - return nullptr; - } - - ALOGV("imgBuffer ptr: %p format: %x (%dx%d, stride=%d)", imgBuffer.data, imgBuffer.format, imgBuffer.width, imgBuffer.height, imgBuffer.stride); - - void* vncbuf = mVNCScreen->frameBuffer; - void* imgbuf = imgBuffer.data; - - for (size_t y = 0; y < mHeight; y++) { - memcpy(vncbuf, imgbuf, mWidth * 4); - vncbuf = (void *)((char *)vncbuf + mWidth * 4); - imgbuf = (void *)((char *)imgbuf + imgBuffer.stride * 4); - } - ALOGD("buf copied"); - - mVNCScreen->frameBuffer = (char *)imgBuffer.data; - mVNCScreen->paddedWidthInBytes = imgBuffer.stride * 4; - rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight); - - - mCpuConsumer->unlockBuffer(imgBuffer); - mUpdateMutex->unlock(); - - return nullptr; -} - -void VirtualDisplay::release_l() { - ALOGD("release_l"); - mCpuConsumer.clear(); - mProducer.clear(); - SurfaceComposerClient::destroyDisplay(mDpy); -} - -// Callback; executes on arbitrary thread. -void VirtualDisplay::onFrameAvailable(const BufferItem& item) { - Mutex::Autolock _l(mMutex); - mEventCond.signal(); - ALOGD("mTimestamp=%ld mFrameNumber=%ld", item.mTimestamp, item.mFrameNumber); -} diff --git a/src/VirtualDisplay.h b/src/VirtualDisplay.h deleted file mode 100644 index 57c2c928bcb7aac8ef8233338c1205cbad3b3cd9..0000000000000000000000000000000000000000 --- a/src/VirtualDisplay.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 VDS_H -#define VDS_H - -#include <gui/BufferQueue.h> -#include <gui/CpuConsumer.h> -#include <gui/IGraphicBufferProducer.h> -#include <ui/DisplayInfo.h> -#include <utils/Thread.h> - -#include <rfb/rfb.h> - -#define NUM_PBO 2 - -namespace android { - -/* - * Support for "frames" output format. - */ -class VirtualDisplay : public CpuConsumer::FrameAvailableListener, Thread { -public: - VirtualDisplay(rfbScreenInfoPtr vncScreen, Mutex *updateMutex) : Thread(false), - mVNCScreen(vncScreen), - mUpdateMutex(updateMutex), - mThreadResult(UNKNOWN_ERROR), - mState(UNINITIALIZED) - {} - - // Create an "input surface", similar in purpose to a MediaCodec input - // surface, that the virtual display can send buffers to. Also configures - // EGL with a pbuffer surface on the current thread. - virtual status_t start(const DisplayInfo& mainDpyInfo); - - virtual status_t stop(); - - static bool isDeviceRotated(int orientation); - -private: - VirtualDisplay(const VirtualDisplay&); - VirtualDisplay& operator=(const VirtualDisplay&); - - // Destruction via RefBase. - virtual ~VirtualDisplay() { - assert(mState == UNINITIALIZED || mState == STOPPED); - } - - virtual status_t setDisplayProjection(const sp<IBinder>& dpy, - const DisplayInfo& mainDpyInfo); - - // (overrides GLConsumer::FrameAvailableListener method) - virtual void onFrameAvailable(const BufferItem& item); - - // (overrides Thread method) - virtual bool threadLoop(); - - // One-time setup (essentially object construction on the overlay thread). - status_t setup_l(); - - // Release all resources held. - void release_l(); - - // Process a frame received from the virtual display. - void* processFrame_l(); - - rfbScreenInfoPtr mVNCScreen; - Mutex *mUpdateMutex; - - uint32_t mHeight, mWidth; - bool mRotate; - - // Used to wait for the FrameAvailableListener callback. - Mutex mMutex; - - // Initialization gate. - Condition mStartCond; - - // Thread status, mostly useful during startup. - status_t mThreadResult; - - // Overlay thread state. States advance from left to right; object may - // not be restarted. - enum { UNINITIALIZED, INIT, RUNNING, STOPPING, STOPPED } mState; - - // Event notification. Overlay thread sleeps on this until a frame - // arrives or it's time to shut down. - Condition mEventCond; - - // Producer side of queue, passed into the virtual display. - // The consumer end feeds into our GLConsumer. - sp<IGraphicBufferProducer> mProducer; - - // This receives frames from the virtual display and makes them available - sp<CpuConsumer> mCpuConsumer; - - sp<IBinder> mDpy; -}; - -}; // namespace android - -#endif /* VDS_H */