Home | History | Annotate | Download | only in screenrecord
      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 
     26 using namespace android;
     27 
     28 static const bool kShowTiming = false;      // set to "true" for debugging
     29 static const int kGlBytesPerPixel = 4;      // GL_RGBA
     30 static const int kOutBytesPerPixel = 3;     // RGB only
     31 
     32 inline 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 
     43 status_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 
     90 status_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 
    193 void 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.
    209 void FrameOutput::onFrameAvailable(const BufferItem& /* item */) {
    210     Mutex::Autolock _l(mMutex);
    211     mFrameAvailable = true;
    212     mEventCond.signal();
    213 }
    214