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