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 
start(const sp<IGraphicBufferProducer> & outputSurface,sp<IGraphicBufferProducer> * pBufferProducer)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 
stop()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 
threadLoop()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 
setup_l()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 
release_l()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 
processFrame_l()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 
getTimeString_l(nsecs_t monotonicNsec,char * buf,size_t bufLen)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.
onFrameAvailable(const BufferItem &)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 
drawInfoPage(const sp<IGraphicBufferProducer> & outputSurface)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 
doDrawInfoPage(const EglWindow & window,const Program & texProgram,TextRenderer & textRenderer)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