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