• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     sp<Surface> surface;
176     std::tie(mGlConsumer, surface) =
177             GLConsumer::create(mExtTextureName, GL_TEXTURE_EXTERNAL_OES, /*useFenceSync=*/true,
178                                /*isControlledByApp=*/false);
179     mProducer = surface->getIGraphicBufferProducer();
180     mGlConsumer->setName(String8("virtual display"));
181     mGlConsumer->setDefaultBufferSize(width, height);
182     mProducer->setMaxDequeuedBufferCount(4);
183     mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
184 
185     mGlConsumer->setFrameAvailableListener(this);
186 
187     return NO_ERROR;
188 }
189 
190 
release_l()191 void Overlay::release_l() {
192     ALOGV("Overlay::release_l");
193     mOutputSurface.clear();
194     mGlConsumer.clear();
195     mProducer.clear();
196 
197     mTexProgram.release();
198     mExtTexProgram.release();
199     mEglWindow.release();
200 }
201 
processFrame_l()202 void Overlay::processFrame_l() {
203     float texMatrix[16];
204 
205     mGlConsumer->updateTexImage();
206     mGlConsumer->getTransformMatrix(texMatrix);
207     nsecs_t monotonicNsec = mGlConsumer->getTimestamp();
208     nsecs_t frameNumber = mGlConsumer->getFrameNumber();
209 
210     if (mLastFrameNumber > 0) {
211         mTotalDroppedFrames += size_t(frameNumber - mLastFrameNumber) - 1;
212     }
213     mLastFrameNumber = frameNumber;
214 
215     mTextRenderer.setProportionalScale(35);
216 
217     if (false) {  // DEBUG - full blue background
218         glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
219         glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
220     }
221 
222     int width = mEglWindow.getWidth();
223     int height = mEglWindow.getHeight();
224     if (false) {  // DEBUG - draw inset
225         mExtTexProgram.blit(mExtTextureName, texMatrix,
226                 100, 100, width-200, height-200);
227     } else {
228         mExtTexProgram.blit(mExtTextureName, texMatrix,
229                 0, 0, width, height);
230     }
231 
232     glEnable(GL_BLEND);
233     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
234     if (false) {  // DEBUG - show entire font bitmap
235         mTexProgram.blit(mTextRenderer.getTextureName(), Program::kIdentity,
236                 100, 100, width-200, height-200);
237     }
238 
239     char textBuf[64];
240     getTimeString_l(monotonicNsec, textBuf, sizeof(textBuf));
241     String8 timeStr(String8::format("%s f=%" PRId64 " (%zd)",
242             textBuf, frameNumber, mTotalDroppedFrames));
243     mTextRenderer.drawString(mTexProgram, Program::kIdentity, 0, 0, timeStr);
244 
245     glDisable(GL_BLEND);
246 
247     if (false) {  // DEBUG - add red rectangle in lower-left corner
248         glEnable(GL_SCISSOR_TEST);
249         glScissor(0, 0, 200, 200);
250         glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
251         glClear(GL_COLOR_BUFFER_BIT);
252         glDisable(GL_SCISSOR_TEST);
253     }
254 
255     mEglWindow.presentationTime(monotonicNsec);
256     mEglWindow.swapBuffers();
257 }
258 
getTimeString_l(nsecs_t monotonicNsec,char * buf,size_t bufLen)259 void Overlay::getTimeString_l(nsecs_t monotonicNsec, char* buf, size_t bufLen) {
260     //const char* format = "%m-%d %T";    // matches log output
261     const char* format = "%T";
262     struct tm tm;
263 
264     if (mUseMonotonicTimestamps) {
265         snprintf(buf, bufLen, "%" PRId64, monotonicNsec);
266         return;
267     }
268 
269     // localtime/strftime is not the fastest way to do this, but a trivial
270     // benchmark suggests that the cost is negligible.
271     int64_t realTime = mStartRealtimeNsecs +
272             (monotonicNsec - mStartMonotonicNsecs);
273     time_t secs = (time_t) (realTime / 1000000000);
274     localtime_r(&secs, &tm);
275     strftime(buf, bufLen, format, &tm);
276 
277     int32_t msec = (int32_t) ((realTime % 1000000000) / 1000000);
278     char tmpBuf[5];
279     snprintf(tmpBuf, sizeof(tmpBuf), ".%03d", msec);
280     strlcat(buf, tmpBuf, bufLen);
281 }
282 
283 // Callback; executes on arbitrary thread.
onFrameAvailable(const BufferItem &)284 void Overlay::onFrameAvailable(const BufferItem& /* item */) {
285     ALOGV("Overlay::onFrameAvailable");
286     Mutex::Autolock _l(mMutex);
287     mFrameAvailable = true;
288     mEventCond.signal();
289 }
290 
291 
drawInfoPage(const sp<IGraphicBufferProducer> & outputSurface)292 /*static*/ status_t Overlay::drawInfoPage(
293         const sp<IGraphicBufferProducer>& outputSurface) {
294     status_t err;
295 
296     EglWindow window;
297     err = window.createWindow(outputSurface);
298     if (err != NO_ERROR) {
299         return err;
300     }
301     window.makeCurrent();
302 
303     int width = window.getWidth();
304     int height = window.getHeight();
305     glViewport(0, 0, width, height);
306     glDisable(GL_DEPTH_TEST);
307     glDisable(GL_CULL_FACE);
308 
309     // Shaders for rendering.
310     Program texProgram;
311     err = texProgram.setup(Program::PROGRAM_TEXTURE_2D);
312     if (err != NO_ERROR) {
313         return err;
314     }
315     TextRenderer textRenderer;
316     err = textRenderer.loadIntoTexture();
317     if (err != NO_ERROR) {
318         return err;
319     }
320     textRenderer.setScreenSize(width, height);
321 
322     doDrawInfoPage(window, texProgram, textRenderer);
323 
324     // Destroy the surface.  This causes a disconnect.
325     texProgram.release();
326     window.release();
327 
328     return NO_ERROR;
329 }
330 
doDrawInfoPage(const EglWindow & window,const Program & texProgram,TextRenderer & textRenderer)331 /*static*/ void Overlay::doDrawInfoPage(const EglWindow& window,
332         const Program& texProgram, TextRenderer& textRenderer) {
333     const nsecs_t holdTime = 250000000LL;
334 
335     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
336     glClear(GL_COLOR_BUFFER_BIT);
337 
338     int width = window.getWidth();
339     int height = window.getHeight();
340 
341     // Draw a thin border around the screen.  Some players, e.g. browser
342     // plugins, make it hard to see where the edges are when the device
343     // is using a black background, so this gives the viewer a frame of
344     // reference.
345     //
346     // This is a clumsy way to do it, but we're only doing it for one frame,
347     // and it's easier than actually drawing lines.
348     const int lineWidth = 4;
349     glEnable(GL_SCISSOR_TEST);
350     glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
351     glScissor(0, 0, width, lineWidth);
352     glClear(GL_COLOR_BUFFER_BIT);
353     glScissor(0, height - lineWidth, width, lineWidth);
354     glClear(GL_COLOR_BUFFER_BIT);
355     glScissor(0, 0, lineWidth, height);
356     glClear(GL_COLOR_BUFFER_BIT);
357     glScissor(width - lineWidth, 0, lineWidth, height);
358     glClear(GL_COLOR_BUFFER_BIT);
359     glDisable(GL_SCISSOR_TEST);
360 
361     //glEnable(GL_BLEND);
362     //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
363     textRenderer.setProportionalScale(30);
364 
365     float xpos = 0;
366     float ypos = 0;
367     ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos,
368             String8::format("Android screenrecord v%d.%d",
369                     kVersionMajor, kVersionMinor));
370 
371     // Show date/time
372     time_t now = time(0);
373     struct tm tm;
374     localtime_r(&now, &tm);
375     char timeBuf[64];
376     strftime(timeBuf, sizeof(timeBuf), "%a, %d %b %Y %T %z", &tm);
377     String8 header("Started ");
378     header += timeBuf;
379     ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, header);
380     ypos += 8 * textRenderer.getScale();    // slight padding
381 
382     // Show selected system property values
383     for (int i = 0; i < NELEM(kPropertyNames); i++) {
384         char valueBuf[PROPERTY_VALUE_MAX];
385 
386         property_get(kPropertyNames[i], valueBuf, "");
387         if (valueBuf[0] == '\0') {
388             continue;
389         }
390         String8 str(String8::format("%s: [%s]", kPropertyNames[i], valueBuf));
391         ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, str);
392     }
393     ypos += 8 * textRenderer.getScale();    // slight padding
394 
395     // Show GL info
396     String8 glStr("OpenGL: ");
397     glStr += (char*) glGetString(GL_VENDOR);
398     glStr += " / ";
399     glStr += (char*) glGetString(GL_RENDERER);
400     glStr += ", ";
401     glStr += (char*) glGetString(GL_VERSION);
402     ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, glStr);
403 
404     //glDisable(GL_BLEND);
405 
406     // Set a presentation time slightly in the past.  This will cause the
407     // player to hold the frame on screen.
408     window.presentationTime(systemTime(CLOCK_MONOTONIC) - holdTime);
409     window.swapBuffers();
410 }
411