• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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