FrameOutput.cpp revision 587c6fefcd3c1d05c608ff511cf3534bc765256e
1/* 2 * Copyright 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "ScreenRecord" 18//#define LOG_NDEBUG 0 19#include <utils/Log.h> 20 21#include <GLES2/gl2.h> 22#include <GLES2/gl2ext.h> 23 24#include "FrameOutput.h" 25 26using namespace android; 27 28static const bool kShowTiming = false; // set to "true" for debugging 29static const int kGlBytesPerPixel = 4; // GL_RGBA 30static const int kOutBytesPerPixel = 3; // RGB only 31 32inline void FrameOutput::setValueLE(uint8_t* buf, uint32_t value) { 33 // Since we're running on an Android device, we're (almost) guaranteed 34 // to be little-endian, and (almost) guaranteed that unaligned 32-bit 35 // writes will work without any performance penalty... but do it 36 // byte-by-byte anyway. 37 buf[0] = (uint8_t) value; 38 buf[1] = (uint8_t) (value >> 8); 39 buf[2] = (uint8_t) (value >> 16); 40 buf[3] = (uint8_t) (value >> 24); 41} 42 43status_t FrameOutput::createInputSurface(int width, int height, 44 sp<IGraphicBufferProducer>* pBufferProducer) { 45 status_t err; 46 47 err = mEglWindow.createPbuffer(width, height); 48 if (err != NO_ERROR) { 49 return err; 50 } 51 mEglWindow.makeCurrent(); 52 53 glViewport(0, 0, width, height); 54 glDisable(GL_DEPTH_TEST); 55 glDisable(GL_CULL_FACE); 56 57 // Shader for rendering the external texture. 58 err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE); 59 if (err != NO_ERROR) { 60 return err; 61 } 62 63 // Input side (buffers from virtual display). 64 glGenTextures(1, &mExtTextureName); 65 if (mExtTextureName == 0) { 66 ALOGE("glGenTextures failed: %#x", glGetError()); 67 return UNKNOWN_ERROR; 68 } 69 70 mBufferQueue = new BufferQueue(/*new GraphicBufferAlloc()*/); 71 mGlConsumer = new GLConsumer(mBufferQueue, mExtTextureName, 72 GL_TEXTURE_EXTERNAL_OES); 73 mGlConsumer->setName(String8("virtual display")); 74 mGlConsumer->setDefaultBufferSize(width, height); 75 mGlConsumer->setDefaultMaxBufferCount(5); 76 mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE); 77 78 mGlConsumer->setFrameAvailableListener(this); 79 80 mPixelBuf = new uint8_t[width * height * kGlBytesPerPixel]; 81 82 *pBufferProducer = mBufferQueue; 83 84 ALOGD("FrameOutput::createInputSurface OK"); 85 return NO_ERROR; 86} 87 88status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec) { 89 Mutex::Autolock _l(mMutex); 90 ALOGV("copyFrame %ld\n", timeoutUsec); 91 92 if (!mFrameAvailable) { 93 nsecs_t timeoutNsec = (nsecs_t)timeoutUsec * 1000; 94 int cc = mEventCond.waitRelative(mMutex, timeoutNsec); 95 if (cc == -ETIMEDOUT) { 96 ALOGV("cond wait timed out"); 97 return ETIMEDOUT; 98 } else if (cc != 0) { 99 ALOGW("cond wait returned error %d", cc); 100 return cc; 101 } 102 } 103 if (!mFrameAvailable) { 104 // This happens when Ctrl-C is hit. Apparently POSIX says that the 105 // pthread wait call doesn't return EINTR, treating this instead as 106 // an instance of a "spurious wakeup". We didn't get a frame, so 107 // we just treat it as a timeout. 108 return ETIMEDOUT; 109 } 110 111 // A frame is available. Clear the flag for the next round. 112 mFrameAvailable = false; 113 114 float texMatrix[16]; 115 mGlConsumer->updateTexImage(); 116 mGlConsumer->getTransformMatrix(texMatrix); 117 118 // The data is in an external texture, so we need to render it to the 119 // pbuffer to get access to RGB pixel data. We also want to flip it 120 // upside-down for easy conversion to a bitmap. 121 int width = mEglWindow.getWidth(); 122 int height = mEglWindow.getHeight(); 123 status_t err = mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0, 124 width, height, true); 125 if (err != NO_ERROR) { 126 return err; 127 } 128 129 // GLES only guarantees that glReadPixels() will work with GL_RGBA, so we 130 // need to get 4 bytes/pixel and reduce it. Depending on the size of the 131 // screen and the device capabilities, this can take a while. 132 int64_t startWhenNsec, pixWhenNsec, endWhenNsec; 133 if (kShowTiming) { 134 startWhenNsec = systemTime(CLOCK_MONOTONIC); 135 } 136 GLenum glErr; 137 glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, mPixelBuf); 138 if ((glErr = glGetError()) != GL_NO_ERROR) { 139 ALOGE("glReadPixels failed: %#x", glErr); 140 return UNKNOWN_ERROR; 141 } 142 if (kShowTiming) { 143 pixWhenNsec = systemTime(CLOCK_MONOTONIC); 144 } 145 reduceRgbaToRgb(mPixelBuf, width * height); 146 if (kShowTiming) { 147 endWhenNsec = systemTime(CLOCK_MONOTONIC); 148 ALOGD("got pixels (get=%.3f ms, reduce=%.3fms)", 149 (pixWhenNsec - startWhenNsec) / 1000000.0, 150 (endWhenNsec - pixWhenNsec) / 1000000.0); 151 } 152 153 // Fill out the header. 154 size_t headerLen = sizeof(uint32_t) * 5; 155 size_t rgbDataLen = width * height * kOutBytesPerPixel; 156 size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen; 157 uint8_t header[headerLen]; 158 setValueLE(&header[0], packetLen); 159 setValueLE(&header[4], width); 160 setValueLE(&header[8], height); 161 setValueLE(&header[12], width * kOutBytesPerPixel); 162 setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888); 163 164 // Currently using buffered I/O rather than writev(). Not expecting it 165 // to make much of a difference, but it might be worth a test for larger 166 // frame sizes. 167 if (kShowTiming) { 168 startWhenNsec = systemTime(CLOCK_MONOTONIC); 169 } 170 fwrite(header, 1, headerLen, fp); 171 fwrite(mPixelBuf, 1, rgbDataLen, fp); 172 fflush(fp); 173 if (kShowTiming) { 174 endWhenNsec = systemTime(CLOCK_MONOTONIC); 175 ALOGD("wrote pixels (%.3f ms)", 176 (endWhenNsec - startWhenNsec) / 1000000.0); 177 } 178 179 if (ferror(fp)) { 180 // errno may not be useful; log it anyway 181 ALOGE("write failed (errno=%d)", errno); 182 return UNKNOWN_ERROR; 183 } 184 185 return NO_ERROR; 186} 187 188void FrameOutput::reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount) { 189 // Convert RGBA to RGB. 190 // 191 // Unaligned 32-bit accesses are allowed on ARM, so we could do this 192 // with 32-bit copies advancing at different rates (taking care at the 193 // end to not go one byte over). 194 const uint8_t* readPtr = buf; 195 for (unsigned int i = 0; i < pixelCount; i++) { 196 *buf++ = *readPtr++; 197 *buf++ = *readPtr++; 198 *buf++ = *readPtr++; 199 readPtr++; 200 } 201} 202 203// Callback; executes on arbitrary thread. 204void FrameOutput::onFrameAvailable() { 205 Mutex::Autolock _l(mMutex); 206 mFrameAvailable = true; 207 mEventCond.signal(); 208} 209