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