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