FrameOutput.cpp revision b278f5e70c001391779525fb4d3b024503ba9466
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 sp<IGraphicBufferProducer> producer; 71 sp<IGraphicBufferConsumer> consumer; 72 BufferQueue::createBufferQueue(&producer, &consumer); 73 mGlConsumer = new GLConsumer(consumer, mExtTextureName, 74 GL_TEXTURE_EXTERNAL_OES); 75 mGlConsumer->setName(String8("virtual display")); 76 mGlConsumer->setDefaultBufferSize(width, height); 77 mGlConsumer->setDefaultMaxBufferCount(5); 78 mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE); 79 80 mGlConsumer->setFrameAvailableListener(this); 81 82 mPixelBuf = new uint8_t[width * height * kGlBytesPerPixel]; 83 84 *pBufferProducer = producer; 85 86 ALOGD("FrameOutput::createInputSurface OK"); 87 return NO_ERROR; 88} 89 90status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec) { 91 Mutex::Autolock _l(mMutex); 92 ALOGV("copyFrame %ld\n", timeoutUsec); 93 94 if (!mFrameAvailable) { 95 nsecs_t timeoutNsec = (nsecs_t)timeoutUsec * 1000; 96 int cc = mEventCond.waitRelative(mMutex, timeoutNsec); 97 if (cc == -ETIMEDOUT) { 98 ALOGV("cond wait timed out"); 99 return ETIMEDOUT; 100 } else if (cc != 0) { 101 ALOGW("cond wait returned error %d", cc); 102 return cc; 103 } 104 } 105 if (!mFrameAvailable) { 106 // This happens when Ctrl-C is hit. Apparently POSIX says that the 107 // pthread wait call doesn't return EINTR, treating this instead as 108 // an instance of a "spurious wakeup". We didn't get a frame, so 109 // we just treat it as a timeout. 110 return ETIMEDOUT; 111 } 112 113 // A frame is available. Clear the flag for the next round. 114 mFrameAvailable = false; 115 116 float texMatrix[16]; 117 mGlConsumer->updateTexImage(); 118 mGlConsumer->getTransformMatrix(texMatrix); 119 120 // The data is in an external texture, so we need to render it to the 121 // pbuffer to get access to RGB pixel data. We also want to flip it 122 // upside-down for easy conversion to a bitmap. 123 int width = mEglWindow.getWidth(); 124 int height = mEglWindow.getHeight(); 125 status_t err = mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0, 126 width, height, true); 127 if (err != NO_ERROR) { 128 return err; 129 } 130 131 // GLES only guarantees that glReadPixels() will work with GL_RGBA, so we 132 // need to get 4 bytes/pixel and reduce it. Depending on the size of the 133 // screen and the device capabilities, this can take a while. 134 int64_t startWhenNsec, pixWhenNsec, endWhenNsec; 135 if (kShowTiming) { 136 startWhenNsec = systemTime(CLOCK_MONOTONIC); 137 } 138 GLenum glErr; 139 glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, mPixelBuf); 140 if ((glErr = glGetError()) != GL_NO_ERROR) { 141 ALOGE("glReadPixels failed: %#x", glErr); 142 return UNKNOWN_ERROR; 143 } 144 if (kShowTiming) { 145 pixWhenNsec = systemTime(CLOCK_MONOTONIC); 146 } 147 reduceRgbaToRgb(mPixelBuf, width * height); 148 if (kShowTiming) { 149 endWhenNsec = systemTime(CLOCK_MONOTONIC); 150 ALOGD("got pixels (get=%.3f ms, reduce=%.3fms)", 151 (pixWhenNsec - startWhenNsec) / 1000000.0, 152 (endWhenNsec - pixWhenNsec) / 1000000.0); 153 } 154 155 // Fill out the header. 156 size_t headerLen = sizeof(uint32_t) * 5; 157 size_t rgbDataLen = width * height * kOutBytesPerPixel; 158 size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen; 159 uint8_t header[headerLen]; 160 setValueLE(&header[0], packetLen); 161 setValueLE(&header[4], width); 162 setValueLE(&header[8], height); 163 setValueLE(&header[12], width * kOutBytesPerPixel); 164 setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888); 165 166 // Currently using buffered I/O rather than writev(). Not expecting it 167 // to make much of a difference, but it might be worth a test for larger 168 // frame sizes. 169 if (kShowTiming) { 170 startWhenNsec = systemTime(CLOCK_MONOTONIC); 171 } 172 fwrite(header, 1, headerLen, fp); 173 fwrite(mPixelBuf, 1, rgbDataLen, fp); 174 fflush(fp); 175 if (kShowTiming) { 176 endWhenNsec = systemTime(CLOCK_MONOTONIC); 177 ALOGD("wrote pixels (%.3f ms)", 178 (endWhenNsec - startWhenNsec) / 1000000.0); 179 } 180 181 if (ferror(fp)) { 182 // errno may not be useful; log it anyway 183 ALOGE("write failed (errno=%d)", errno); 184 return UNKNOWN_ERROR; 185 } 186 187 return NO_ERROR; 188} 189 190void FrameOutput::reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount) { 191 // Convert RGBA to RGB. 192 // 193 // Unaligned 32-bit accesses are allowed on ARM, so we could do this 194 // with 32-bit copies advancing at different rates (taking care at the 195 // end to not go one byte over). 196 const uint8_t* readPtr = buf; 197 for (unsigned int i = 0; i < pixelCount; i++) { 198 *buf++ = *readPtr++; 199 *buf++ = *readPtr++; 200 *buf++ = *readPtr++; 201 readPtr++; 202 } 203} 204 205// Callback; executes on arbitrary thread. 206void FrameOutput::onFrameAvailable() { 207 Mutex::Autolock _l(mMutex); 208 mFrameAvailable = true; 209 mEventCond.signal(); 210} 211