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