xref: /aosp_15_r20/frameworks/av/cmds/screenrecord/FrameOutput.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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