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, true, false); 75 mGlConsumer->setName(String8("virtual display")); 76 mGlConsumer->setDefaultBufferSize(width, height); 77 producer->setMaxDequeuedBufferCount(4); 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, bool rawFrames) { 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 size_t rgbDataLen = width * height * kOutBytesPerPixel; 156 157 if (!rawFrames) { 158 // Fill out the header. 159 size_t headerLen = sizeof(uint32_t) * 5; 160 size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen; 161 uint8_t header[headerLen]; 162 setValueLE(&header[0], packetLen); 163 setValueLE(&header[4], width); 164 setValueLE(&header[8], height); 165 setValueLE(&header[12], width * kOutBytesPerPixel); 166 setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888); 167 fwrite(header, 1, headerLen, fp); 168 } 169 170 // Currently using buffered I/O rather than writev(). Not expecting it 171 // to make much of a difference, but it might be worth a test for larger 172 // frame sizes. 173 if (kShowTiming) { 174 startWhenNsec = systemTime(CLOCK_MONOTONIC); 175 } 176 fwrite(mPixelBuf, 1, rgbDataLen, fp); 177 fflush(fp); 178 if (kShowTiming) { 179 endWhenNsec = systemTime(CLOCK_MONOTONIC); 180 ALOGD("wrote pixels (%.3f ms)", 181 (endWhenNsec - startWhenNsec) / 1000000.0); 182 } 183 184 if (ferror(fp)) { 185 // errno may not be useful; log it anyway 186 ALOGE("write failed (errno=%d)", errno); 187 return UNKNOWN_ERROR; 188 } 189 190 return NO_ERROR; 191} 192 193void FrameOutput::reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount) { 194 // Convert RGBA to RGB. 195 // 196 // Unaligned 32-bit accesses are allowed on ARM, so we could do this 197 // with 32-bit copies advancing at different rates (taking care at the 198 // end to not go one byte over). 199 const uint8_t* readPtr = buf; 200 for (unsigned int i = 0; i < pixelCount; i++) { 201 *buf++ = *readPtr++; 202 *buf++ = *readPtr++; 203 *buf++ = *readPtr++; 204 readPtr++; 205 } 206} 207 208// Callback; executes on arbitrary thread. 209void FrameOutput::onFrameAvailable(const BufferItem& /* item */) { 210 Mutex::Autolock _l(mMutex); 211 mFrameAvailable = true; 212 mEventCond.signal(); 213} 214