Initial import of VNCFlinger
diff --git a/src/VirtualDisplay.cpp b/src/VirtualDisplay.cpp
new file mode 100644
index 0000000..369d271
--- /dev/null
+++ b/src/VirtualDisplay.cpp
@@ -0,0 +1,297 @@
+/*
+ * 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 <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.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) {
+ printf("Rotated content area is %ux%u at offset x=%d y=%d\n",
+ outHeight, outWidth, offY, offX);
+ } else {
+ printf("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, EventQueue *queue) {
+
+ Mutex::Autolock _l(mMutex);
+
+ mQueue = queue;
+
+ 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");
+ void* ptr = processFrame_l();
+ const Event ev(EVENT_BUFFER_READY, ptr);
+ mQueue->enqueue(ev);
+ }
+
+ ALOGV("VDS thread stopping");
+ release_l();
+ mState = STOPPED;
+ return false; // stop
+}
+
+status_t VirtualDisplay::setup_l() {
+ status_t err;
+
+ err = mEglWindow.createPbuffer(mWidth, mHeight);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ mEglWindow.makeCurrent();
+
+ glViewport(0, 0, mWidth, mHeight);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ // Shader for rendering the external texture.
+ err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ // Input side (buffers from virtual display).
+ glGenTextures(1, &mExtTextureName);
+ if (mExtTextureName == 0) {
+ ALOGE("glGenTextures failed: %#x", glGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ mBufSize = mWidth * mHeight * kGlBytesPerPixel;
+
+ // pixel buffer for image copy
+ mPBO = new GLuint[NUM_PBO];
+ glGenBuffers(NUM_PBO, mPBO);
+
+ for (unsigned int i = 0; i < NUM_PBO; i++) {
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[i]);
+ glBufferData(GL_PIXEL_PACK_BUFFER, mBufSize, 0, GL_DYNAMIC_READ);
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+ }
+
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&mProducer, &consumer);
+ mGlConsumer = new GLConsumer(consumer, mExtTextureName,
+ GL_TEXTURE_EXTERNAL_OES, true, false);
+ mGlConsumer->setName(String8("virtual display"));
+ mGlConsumer->setDefaultBufferSize(mWidth, mHeight);
+ mProducer->setMaxDequeuedBufferCount(4);
+ mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
+
+ mGlConsumer->setFrameAvailableListener(this);
+
+ ALOGD("VirtualDisplay::setup_l OK");
+ return NO_ERROR;
+}
+
+void* VirtualDisplay::processFrame_l() {
+ ALOGD("processFrame_l\n");
+
+ float texMatrix[16];
+ mGlConsumer->updateTexImage();
+ mGlConsumer->getTransformMatrix(texMatrix);
+
+ // The data is in an external texture, so we need to render it to the
+ // pbuffer to get access to RGB pixel data. We also want to flip it
+ // upside-down for easy conversion to a bitmap.
+ int width = mEglWindow.getWidth();
+ int height = mEglWindow.getHeight();
+ mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0, mWidth, mHeight, true);
+
+ GLenum glErr;
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[mIndex]);
+ glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ if ((glErr = glGetError()) != GL_NO_ERROR) {
+ ALOGE("glReadPixels failed: %#x", glErr);
+ return NULL;
+ }
+
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[mIndex]);
+ void* ptr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, mBufSize, GL_MAP_READ_BIT);
+ glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+
+ mIndex = (mIndex + 1) % NUM_PBO;
+ return ptr;
+}
+
+void VirtualDisplay::release_l() {
+ ALOGD("release_l");
+ mGlConsumer.clear();
+ mProducer.clear();
+ mExtTexProgram.release();
+ mEglWindow.release();
+ 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);
+}