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