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