vnc: Simplify everything
Previous design was using extra threads needlessly and had some
hacky data sharing going on. Slim this down to just two classes
and utilize threads appropriately.
diff --git a/src/VNCFlinger.cpp b/src/VNCFlinger.cpp
index efd6cbc..4775d0b 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;
-
status_t VNCFlinger::start() {
- Mutex::Autolock _l(mMutex);
+ sp<ProcessState> self = ProcessState::self();
+ self->startThreadPool();
- status_t err = setup_l();
+ 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;
+
+ err = createVNCServer();
if (err != NO_ERROR) {
ALOGE("Failed to start VNCFlinger: err=%d", err);
return err;
@@ -29,34 +41,78 @@
rfbRunEventLoop(mVNCScreen, -1, true);
- mCondition.wait(mMutex);
+ eventLoop();
- release_l();
return NO_ERROR;
}
-status_t VNCFlinger::setup_l() {
+void VNCFlinger::eventLoop() {
+ mRunning = true;
+
+ Mutex::Autolock _l(mEventMutex);
+ while (mRunning) {
+ mEventCond.wait(mEventMutex);
+
+ // spurious wakeup? never.
+ if (mClientCount == 0) {
+ continue;
+ }
+
+ // 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);
+
+ 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;
- mMainDpy = SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain);
- err = SurfaceComposerClient::getDisplayInfo(mMainDpy, &mMainDpyInfo);
- if (err != NO_ERROR) {
- ALOGE("Unable to get display characteristics\n");
- return err;
- }
-
- bool rotated = VirtualDisplay::isDeviceRotated(mMainDpyInfo.orientation);
- if (mWidth == 0) {
- mWidth = rotated ? mMainDpyInfo.h : mMainDpyInfo.w;
- }
- if (mHeight == 0) {
- mHeight = rotated ? mMainDpyInfo.w : mMainDpyInfo.h;
- }
-
- ALOGD("Display dimensions: %dx%d rotated=%d", mWidth, mHeight, rotated);
-
rfbLog = VNCFlinger::rfbLogger;
rfbErr = VNCFlinger::rfbLogger;
@@ -88,32 +144,27 @@
/* 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();
-
- ALOGD("VNCFlinger released");
-}
-
status_t VNCFlinger::stop() {
- Mutex::Autolock _l(mMutex);
- mCondition.signal();
+ Mutex::Autolock _L(mEventMutex);
+
+ mClientCount = 0;
+ mRunning = false;
+
+ 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::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 @@
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 @@
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);
+}