xref: /aosp_15_r20/frameworks/av/cmds/screenrecord/Overlay.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright 2013 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 #include <assert.h>
18 #include <inttypes.h>
19 #include <stdlib.h>
20 
21 #define LOG_TAG "ScreenRecord"
22 //#define LOG_NDEBUG 0
23 #include <utils/Log.h>
24 
25 #include <gui/BufferQueue.h>
26 #include <gui/Surface.h>
27 #include <cutils/properties.h>
28 #include <utils/misc.h>
29 
30 #include <GLES2/gl2.h>
31 #include <GLES2/gl2ext.h>
32 
33 #include "screenrecord.h"
34 #include "Overlay.h"
35 #include "TextRenderer.h"
36 
37 using namespace android;
38 
39 // System properties to look up and display on the info screen.
40 const char* Overlay::kPropertyNames[] = {
41         "ro.build.description",
42         // includes ro.build.id, ro.build.product, ro.build.tags, ro.build.type,
43         // and ro.build.version.release
44         "ro.product.manufacturer",
45         "ro.product.model",
46         "ro.board.platform",
47         "ro.revision",
48         "dalvik.vm.heapgrowthlimit",
49         "dalvik.vm.heapsize",
50         "persist.sys.dalvik.vm.lib.2",
51         //"ro.product.cpu.abi",
52         //"ro.bootloader",
53         //"this-never-appears!",
54 };
55 
56 
start(const sp<IGraphicBufferProducer> & outputSurface,sp<IGraphicBufferProducer> * pBufferProducer)57 status_t Overlay::start(const sp<IGraphicBufferProducer>& outputSurface,
58         sp<IGraphicBufferProducer>* pBufferProducer) {
59     ALOGV("Overlay::start");
60     mOutputSurface = outputSurface;
61 
62     // Grab the current monotonic time and the current wall-clock time so we
63     // can map one to the other.  This allows the overlay counter to advance
64     // by the exact delay between frames, but if the wall clock gets adjusted
65     // we won't track it, which means we'll gradually go out of sync with the
66     // times in logcat.
67     mStartMonotonicNsecs = systemTime(CLOCK_MONOTONIC);
68     mStartRealtimeNsecs = systemTime(CLOCK_REALTIME);
69 
70     Mutex::Autolock _l(mMutex);
71 
72     // Start the thread.  Traffic begins immediately.
73     run("overlay");
74 
75     mState = INIT;
76     while (mState == INIT) {
77         mStartCond.wait(mMutex);
78     }
79 
80     if (mThreadResult != NO_ERROR) {
81         ALOGE("Failed to start overlay thread: err=%d", mThreadResult);
82         return mThreadResult;
83     }
84     assert(mState == RUNNING);
85 
86     ALOGV("Overlay::start successful");
87     *pBufferProducer = mProducer;
88     return NO_ERROR;
89 }
90 
stop()91 status_t Overlay::stop() {
92     ALOGV("Overlay::stop");
93     {
94         Mutex::Autolock _l(mMutex);
95         mState = STOPPING;
96         mEventCond.signal();
97     }
98     join();
99     return NO_ERROR;
100 }
101 
threadLoop()102 bool Overlay::threadLoop() {
103     Mutex::Autolock _l(mMutex);
104 
105     mThreadResult = setup_l();
106 
107     if (mThreadResult != NO_ERROR) {
108         ALOGW("Aborting overlay thread");
109         mState = STOPPED;
110         release_l();
111         mStartCond.broadcast();
112         return false;
113     }
114 
115     ALOGV("Overlay thread running");
116     mState = RUNNING;
117     mStartCond.broadcast();
118 
119     while (mState == RUNNING) {
120         mEventCond.wait(mMutex);
121         if (mFrameAvailable) {
122             ALOGV("Awake, frame available");
123             processFrame_l();
124             mFrameAvailable = false;
125         } else {
126             ALOGV("Awake, frame not available");
127         }
128     }
129 
130     ALOGV("Overlay thread stopping");
131     release_l();
132     mState = STOPPED;
133     return false;       // stop
134 }
135 
setup_l()136 status_t Overlay::setup_l() {
137     status_t err;
138 
139     err = mEglWindow.createWindow(mOutputSurface);
140     if (err != NO_ERROR) {
141         return err;
142     }
143     mEglWindow.makeCurrent();
144 
145     int width = mEglWindow.getWidth();
146     int height = mEglWindow.getHeight();
147 
148     glViewport(0, 0, width, height);
149     glDisable(GL_DEPTH_TEST);
150     glDisable(GL_CULL_FACE);
151 
152     // Shaders for rendering from different types of textures.
153     err = mTexProgram.setup(Program::PROGRAM_TEXTURE_2D);
154     if (err != NO_ERROR) {
155         return err;
156     }
157     err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE);
158     if (err != NO_ERROR) {
159         return err;
160     }
161 
162     err = mTextRenderer.loadIntoTexture();
163     if (err != NO_ERROR) {
164         return err;
165     }
166     mTextRenderer.setScreenSize(width, height);
167 
168     // Input side (buffers from virtual display).
169     glGenTextures(1, &mExtTextureName);
170     if (mExtTextureName == 0) {
171         ALOGE("glGenTextures failed: %#x", glGetError());
172         return UNKNOWN_ERROR;
173     }
174 
175 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
176     mGlConsumer = new GLConsumer(mExtTextureName, GL_TEXTURE_EXTERNAL_OES, /*useFenceSync=*/true,
177                                  /*isControlledByApp=*/false);
178     mProducer = mGlConsumer->getSurface()->getIGraphicBufferProducer();
179 #else
180     sp<IGraphicBufferConsumer> consumer;
181     BufferQueue::createBufferQueue(&mProducer, &consumer);
182     mGlConsumer = new GLConsumer(consumer, mExtTextureName,
183                 GL_TEXTURE_EXTERNAL_OES, true, false);
184 #endif  // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
185     mGlConsumer->setName(String8("virtual display"));
186     mGlConsumer->setDefaultBufferSize(width, height);
187     mProducer->setMaxDequeuedBufferCount(4);
188     mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
189 
190     mGlConsumer->setFrameAvailableListener(this);
191 
192     return NO_ERROR;
193 }
194 
195 
release_l()196 void Overlay::release_l() {
197     ALOGV("Overlay::release_l");
198     mOutputSurface.clear();
199     mGlConsumer.clear();
200     mProducer.clear();
201 
202     mTexProgram.release();
203     mExtTexProgram.release();
204     mEglWindow.release();
205 }
206 
processFrame_l()207 void Overlay::processFrame_l() {
208     float texMatrix[16];
209 
210     mGlConsumer->updateTexImage();
211     mGlConsumer->getTransformMatrix(texMatrix);
212     nsecs_t monotonicNsec = mGlConsumer->getTimestamp();
213     nsecs_t frameNumber = mGlConsumer->getFrameNumber();
214 
215     if (mLastFrameNumber > 0) {
216         mTotalDroppedFrames += size_t(frameNumber - mLastFrameNumber) - 1;
217     }
218     mLastFrameNumber = frameNumber;
219 
220     mTextRenderer.setProportionalScale(35);
221 
222     if (false) {  // DEBUG - full blue background
223         glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
224         glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
225     }
226 
227     int width = mEglWindow.getWidth();
228     int height = mEglWindow.getHeight();
229     if (false) {  // DEBUG - draw inset
230         mExtTexProgram.blit(mExtTextureName, texMatrix,
231                 100, 100, width-200, height-200);
232     } else {
233         mExtTexProgram.blit(mExtTextureName, texMatrix,
234                 0, 0, width, height);
235     }
236 
237     glEnable(GL_BLEND);
238     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
239     if (false) {  // DEBUG - show entire font bitmap
240         mTexProgram.blit(mTextRenderer.getTextureName(), Program::kIdentity,
241                 100, 100, width-200, height-200);
242     }
243 
244     char textBuf[64];
245     getTimeString_l(monotonicNsec, textBuf, sizeof(textBuf));
246     String8 timeStr(String8::format("%s f=%" PRId64 " (%zd)",
247             textBuf, frameNumber, mTotalDroppedFrames));
248     mTextRenderer.drawString(mTexProgram, Program::kIdentity, 0, 0, timeStr);
249 
250     glDisable(GL_BLEND);
251 
252     if (false) {  // DEBUG - add red rectangle in lower-left corner
253         glEnable(GL_SCISSOR_TEST);
254         glScissor(0, 0, 200, 200);
255         glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
256         glClear(GL_COLOR_BUFFER_BIT);
257         glDisable(GL_SCISSOR_TEST);
258     }
259 
260     mEglWindow.presentationTime(monotonicNsec);
261     mEglWindow.swapBuffers();
262 }
263 
getTimeString_l(nsecs_t monotonicNsec,char * buf,size_t bufLen)264 void Overlay::getTimeString_l(nsecs_t monotonicNsec, char* buf, size_t bufLen) {
265     //const char* format = "%m-%d %T";    // matches log output
266     const char* format = "%T";
267     struct tm tm;
268 
269     if (mUseMonotonicTimestamps) {
270         snprintf(buf, bufLen, "%" PRId64, monotonicNsec);
271         return;
272     }
273 
274     // localtime/strftime is not the fastest way to do this, but a trivial
275     // benchmark suggests that the cost is negligible.
276     int64_t realTime = mStartRealtimeNsecs +
277             (monotonicNsec - mStartMonotonicNsecs);
278     time_t secs = (time_t) (realTime / 1000000000);
279     localtime_r(&secs, &tm);
280     strftime(buf, bufLen, format, &tm);
281 
282     int32_t msec = (int32_t) ((realTime % 1000000000) / 1000000);
283     char tmpBuf[5];
284     snprintf(tmpBuf, sizeof(tmpBuf), ".%03d", msec);
285     strlcat(buf, tmpBuf, bufLen);
286 }
287 
288 // Callback; executes on arbitrary thread.
onFrameAvailable(const BufferItem &)289 void Overlay::onFrameAvailable(const BufferItem& /* item */) {
290     ALOGV("Overlay::onFrameAvailable");
291     Mutex::Autolock _l(mMutex);
292     mFrameAvailable = true;
293     mEventCond.signal();
294 }
295 
296 
drawInfoPage(const sp<IGraphicBufferProducer> & outputSurface)297 /*static*/ status_t Overlay::drawInfoPage(
298         const sp<IGraphicBufferProducer>& outputSurface) {
299     status_t err;
300 
301     EglWindow window;
302     err = window.createWindow(outputSurface);
303     if (err != NO_ERROR) {
304         return err;
305     }
306     window.makeCurrent();
307 
308     int width = window.getWidth();
309     int height = window.getHeight();
310     glViewport(0, 0, width, height);
311     glDisable(GL_DEPTH_TEST);
312     glDisable(GL_CULL_FACE);
313 
314     // Shaders for rendering.
315     Program texProgram;
316     err = texProgram.setup(Program::PROGRAM_TEXTURE_2D);
317     if (err != NO_ERROR) {
318         return err;
319     }
320     TextRenderer textRenderer;
321     err = textRenderer.loadIntoTexture();
322     if (err != NO_ERROR) {
323         return err;
324     }
325     textRenderer.setScreenSize(width, height);
326 
327     doDrawInfoPage(window, texProgram, textRenderer);
328 
329     // Destroy the surface.  This causes a disconnect.
330     texProgram.release();
331     window.release();
332 
333     return NO_ERROR;
334 }
335 
doDrawInfoPage(const EglWindow & window,const Program & texProgram,TextRenderer & textRenderer)336 /*static*/ void Overlay::doDrawInfoPage(const EglWindow& window,
337         const Program& texProgram, TextRenderer& textRenderer) {
338     const nsecs_t holdTime = 250000000LL;
339 
340     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
341     glClear(GL_COLOR_BUFFER_BIT);
342 
343     int width = window.getWidth();
344     int height = window.getHeight();
345 
346     // Draw a thin border around the screen.  Some players, e.g. browser
347     // plugins, make it hard to see where the edges are when the device
348     // is using a black background, so this gives the viewer a frame of
349     // reference.
350     //
351     // This is a clumsy way to do it, but we're only doing it for one frame,
352     // and it's easier than actually drawing lines.
353     const int lineWidth = 4;
354     glEnable(GL_SCISSOR_TEST);
355     glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
356     glScissor(0, 0, width, lineWidth);
357     glClear(GL_COLOR_BUFFER_BIT);
358     glScissor(0, height - lineWidth, width, lineWidth);
359     glClear(GL_COLOR_BUFFER_BIT);
360     glScissor(0, 0, lineWidth, height);
361     glClear(GL_COLOR_BUFFER_BIT);
362     glScissor(width - lineWidth, 0, lineWidth, height);
363     glClear(GL_COLOR_BUFFER_BIT);
364     glDisable(GL_SCISSOR_TEST);
365 
366     //glEnable(GL_BLEND);
367     //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
368     textRenderer.setProportionalScale(30);
369 
370     float xpos = 0;
371     float ypos = 0;
372     ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos,
373             String8::format("Android screenrecord v%d.%d",
374                     kVersionMajor, kVersionMinor));
375 
376     // Show date/time
377     time_t now = time(0);
378     struct tm tm;
379     localtime_r(&now, &tm);
380     char timeBuf[64];
381     strftime(timeBuf, sizeof(timeBuf), "%a, %d %b %Y %T %z", &tm);
382     String8 header("Started ");
383     header += timeBuf;
384     ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, header);
385     ypos += 8 * textRenderer.getScale();    // slight padding
386 
387     // Show selected system property values
388     for (int i = 0; i < NELEM(kPropertyNames); i++) {
389         char valueBuf[PROPERTY_VALUE_MAX];
390 
391         property_get(kPropertyNames[i], valueBuf, "");
392         if (valueBuf[0] == '\0') {
393             continue;
394         }
395         String8 str(String8::format("%s: [%s]", kPropertyNames[i], valueBuf));
396         ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, str);
397     }
398     ypos += 8 * textRenderer.getScale();    // slight padding
399 
400     // Show GL info
401     String8 glStr("OpenGL: ");
402     glStr += (char*) glGetString(GL_VENDOR);
403     glStr += " / ";
404     glStr += (char*) glGetString(GL_RENDERER);
405     glStr += ", ";
406     glStr += (char*) glGetString(GL_VERSION);
407     ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, glStr);
408 
409     //glDisable(GL_BLEND);
410 
411     // Set a presentation time slightly in the past.  This will cause the
412     // player to hold the frame on screen.
413     window.presentationTime(systemTime(CLOCK_MONOTONIC) - holdTime);
414     window.swapBuffers();
415 }
416