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