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