• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     Mutex::Autolock _l(mMutex);
94     mState = STOPPING;
95     mEventCond.signal();
96     return NO_ERROR;
97 }
98 
threadLoop()99 bool 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 
setup_l()133 status_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, true, false);
176     mGlConsumer->setName(String8("virtual display"));
177     mGlConsumer->setDefaultBufferSize(width, height);
178     mProducer->setMaxDequeuedBufferCount(4);
179     mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
180 
181     mGlConsumer->setFrameAvailableListener(this);
182 
183     return NO_ERROR;
184 }
185 
186 
release_l()187 void 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 
processFrame_l()198 void 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 
206     if (mLastFrameNumber > 0) {
207         mTotalDroppedFrames += size_t(frameNumber - mLastFrameNumber) - 1;
208     }
209     mLastFrameNumber = frameNumber;
210 
211     mTextRenderer.setProportionalScale(35);
212 
213     if (false) {  // DEBUG - full blue background
214         glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
215         glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
216     }
217 
218     int width = mEglWindow.getWidth();
219     int height = mEglWindow.getHeight();
220     if (false) {  // DEBUG - draw inset
221         mExtTexProgram.blit(mExtTextureName, texMatrix,
222                 100, 100, width-200, height-200);
223     } else {
224         mExtTexProgram.blit(mExtTextureName, texMatrix,
225                 0, 0, width, height);
226     }
227 
228     glEnable(GL_BLEND);
229     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
230     if (false) {  // DEBUG - show entire font bitmap
231         mTexProgram.blit(mTextRenderer.getTextureName(), Program::kIdentity,
232                 100, 100, width-200, height-200);
233     }
234 
235     char textBuf[64];
236     getTimeString_l(monotonicNsec, textBuf, sizeof(textBuf));
237     String8 timeStr(String8::format("%s f=%" PRId64 " (%zd)",
238             textBuf, frameNumber, mTotalDroppedFrames));
239     mTextRenderer.drawString(mTexProgram, Program::kIdentity, 0, 0, timeStr);
240 
241     glDisable(GL_BLEND);
242 
243     if (false) {  // DEBUG - add red rectangle in lower-left corner
244         glEnable(GL_SCISSOR_TEST);
245         glScissor(0, 0, 200, 200);
246         glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
247         glClear(GL_COLOR_BUFFER_BIT);
248         glDisable(GL_SCISSOR_TEST);
249     }
250 
251     mEglWindow.presentationTime(monotonicNsec);
252     mEglWindow.swapBuffers();
253 }
254 
getTimeString_l(nsecs_t monotonicNsec,char * buf,size_t bufLen)255 void Overlay::getTimeString_l(nsecs_t monotonicNsec, char* buf, size_t bufLen) {
256     //const char* format = "%m-%d %T";    // matches log output
257     const char* format = "%T";
258     struct tm tm;
259 
260     if (mUseMonotonicTimestamps) {
261         snprintf(buf, bufLen, "%" PRId64, monotonicNsec);
262         return;
263     }
264 
265     // localtime/strftime is not the fastest way to do this, but a trivial
266     // benchmark suggests that the cost is negligible.
267     int64_t realTime = mStartRealtimeNsecs +
268             (monotonicNsec - mStartMonotonicNsecs);
269     time_t secs = (time_t) (realTime / 1000000000);
270     localtime_r(&secs, &tm);
271     strftime(buf, bufLen, format, &tm);
272 
273     int32_t msec = (int32_t) ((realTime % 1000000000) / 1000000);
274     char tmpBuf[5];
275     snprintf(tmpBuf, sizeof(tmpBuf), ".%03d", msec);
276     strlcat(buf, tmpBuf, bufLen);
277 }
278 
279 // Callback; executes on arbitrary thread.
onFrameAvailable(const BufferItem &)280 void Overlay::onFrameAvailable(const BufferItem& /* item */) {
281     ALOGV("Overlay::onFrameAvailable");
282     Mutex::Autolock _l(mMutex);
283     mFrameAvailable = true;
284     mEventCond.signal();
285 }
286 
287 
drawInfoPage(const sp<IGraphicBufferProducer> & outputSurface)288 /*static*/ status_t Overlay::drawInfoPage(
289         const sp<IGraphicBufferProducer>& outputSurface) {
290     status_t err;
291 
292     EglWindow window;
293     err = window.createWindow(outputSurface);
294     if (err != NO_ERROR) {
295         return err;
296     }
297     window.makeCurrent();
298 
299     int width = window.getWidth();
300     int height = window.getHeight();
301     glViewport(0, 0, width, height);
302     glDisable(GL_DEPTH_TEST);
303     glDisable(GL_CULL_FACE);
304 
305     // Shaders for rendering.
306     Program texProgram;
307     err = texProgram.setup(Program::PROGRAM_TEXTURE_2D);
308     if (err != NO_ERROR) {
309         return err;
310     }
311     TextRenderer textRenderer;
312     err = textRenderer.loadIntoTexture();
313     if (err != NO_ERROR) {
314         return err;
315     }
316     textRenderer.setScreenSize(width, height);
317 
318     doDrawInfoPage(window, texProgram, textRenderer);
319 
320     // Destroy the surface.  This causes a disconnect.
321     texProgram.release();
322     window.release();
323 
324     return NO_ERROR;
325 }
326 
doDrawInfoPage(const EglWindow & window,const Program & texProgram,TextRenderer & textRenderer)327 /*static*/ void Overlay::doDrawInfoPage(const EglWindow& window,
328         const Program& texProgram, TextRenderer& textRenderer) {
329     const nsecs_t holdTime = 250000000LL;
330 
331     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
332     glClear(GL_COLOR_BUFFER_BIT);
333 
334     int width = window.getWidth();
335     int height = window.getHeight();
336 
337     // Draw a thin border around the screen.  Some players, e.g. browser
338     // plugins, make it hard to see where the edges are when the device
339     // is using a black background, so this gives the viewer a frame of
340     // reference.
341     //
342     // This is a clumsy way to do it, but we're only doing it for one frame,
343     // and it's easier than actually drawing lines.
344     const int lineWidth = 4;
345     glEnable(GL_SCISSOR_TEST);
346     glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
347     glScissor(0, 0, width, lineWidth);
348     glClear(GL_COLOR_BUFFER_BIT);
349     glScissor(0, height - lineWidth, width, lineWidth);
350     glClear(GL_COLOR_BUFFER_BIT);
351     glScissor(0, 0, lineWidth, height);
352     glClear(GL_COLOR_BUFFER_BIT);
353     glScissor(width - lineWidth, 0, lineWidth, height);
354     glClear(GL_COLOR_BUFFER_BIT);
355     glDisable(GL_SCISSOR_TEST);
356 
357     //glEnable(GL_BLEND);
358     //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
359     textRenderer.setProportionalScale(30);
360 
361     float xpos = 0;
362     float ypos = 0;
363     ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos,
364             String8::format("Android screenrecord v%d.%d",
365                     kVersionMajor, kVersionMinor));
366 
367     // Show date/time
368     time_t now = time(0);
369     struct tm tm;
370     localtime_r(&now, &tm);
371     char timeBuf[64];
372     strftime(timeBuf, sizeof(timeBuf), "%a, %d %b %Y %T %z", &tm);
373     String8 header("Started ");
374     header += timeBuf;
375     ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, header);
376     ypos += 8 * textRenderer.getScale();    // slight padding
377 
378     // Show selected system property values
379     for (int i = 0; i < NELEM(kPropertyNames); i++) {
380         char valueBuf[PROPERTY_VALUE_MAX];
381 
382         property_get(kPropertyNames[i], valueBuf, "");
383         if (valueBuf[0] == '\0') {
384             continue;
385         }
386         String8 str(String8::format("%s: [%s]", kPropertyNames[i], valueBuf));
387         ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, str);
388     }
389     ypos += 8 * textRenderer.getScale();    // slight padding
390 
391     // Show GL info
392     String8 glStr("OpenGL: ");
393     glStr += (char*) glGetString(GL_VENDOR);
394     glStr += " / ";
395     glStr += (char*) glGetString(GL_RENDERER);
396     glStr += ", ";
397     glStr += (char*) glGetString(GL_VERSION);
398     ypos = textRenderer.drawWrappedString(texProgram, xpos, ypos, glStr);
399 
400     //glDisable(GL_BLEND);
401 
402     // Set a presentation time slightly in the past.  This will cause the
403     // player to hold the frame on screen.
404     window.presentationTime(systemTime(CLOCK_MONOTONIC) - holdTime);
405     window.swapBuffers();
406 }
407