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 
setValueLE(uint8_t * buf,uint32_t value)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 
createInputSurface(int width,int height,sp<IGraphicBufferProducer> * pBufferProducer)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 
copyFrame(FILE * fp,long timeoutUsec,bool rawFrames)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 
reduceRgbaToRgb(uint8_t * buf,unsigned int pixelCount)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.
onFrameAvailable(const BufferItem &)209 void FrameOutput::onFrameAvailable(const BufferItem& /* item */) {
210     Mutex::Autolock _l(mMutex);
211     mFrameAvailable = true;
212     mEventCond.signal();
213 }
214