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