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