screenrecord.cpp revision 1f5a90bc795475896044fcb1f74816c102851f06
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 <binder/IPCThreadState.h>
22#include <utils/Errors.h>
23#include <utils/Thread.h>
24
25#include <gui/Surface.h>
26#include <gui/SurfaceComposerClient.h>
27#include <gui/ISurfaceComposer.h>
28#include <ui/DisplayInfo.h>
29#include <media/openmax/OMX_IVCommon.h>
30#include <media/stagefright/foundation/ABuffer.h>
31#include <media/stagefright/foundation/ADebug.h>
32#include <media/stagefright/foundation/AMessage.h>
33#include <media/stagefright/MediaCodec.h>
34#include <media/stagefright/MediaErrors.h>
35#include <media/stagefright/MediaMuxer.h>
36#include <media/ICrypto.h>
37
38#include <stdio.h>
39#include <signal.h>
40#include <getopt.h>
41
42using namespace android;
43
44// Command-line parameters.
45static bool gVerbose = false;               // chatty on stdout
46static bool gRotate = false;                // rotate 90 degrees
47static uint32_t gVideoWidth = 1280;         // 720p
48static uint32_t gVideoHeight = 720;
49static uint32_t gBitRate = 4000000;         // 4Mbps
50
51// Set by signal handler to stop recording.
52static bool gStopRequested;
53
54// Previous signal handler state, restored after first hit.
55static struct sigaction gOrigSigactionINT;
56static struct sigaction gOrigSigactionHUP;
57
58static const uint32_t kMinBitRate = 100000;         // 0.1Mbps
59static const uint32_t kMaxBitRate = 100 * 1000000;  // 100Mbps
60
61/*
62 * Catch keyboard interrupt signals.  On receipt, the "stop requested"
63 * flag is raised, and the original handler is restored (so that, if
64 * we get stuck finishing, a second Ctrl-C will kill the process).
65 */
66static void signalCatcher(int signum)
67{
68    gStopRequested = true;
69    switch (signum) {
70    case SIGINT:
71        sigaction(SIGINT, &gOrigSigactionINT, NULL);
72        break;
73    case SIGHUP:
74        sigaction(SIGHUP, &gOrigSigactionHUP, NULL);
75        break;
76    default:
77        abort();
78        break;
79    }
80}
81
82/*
83 * Configures signal handlers.  The previous handlers are saved.
84 *
85 * If the command is run from an interactive adb shell, we get SIGINT
86 * when Ctrl-C is hit.  If we're run from the host, the local adb process
87 * gets the signal, and we get a SIGHUP when the terminal disconnects.
88 */
89static status_t configureSignals()
90{
91    struct sigaction act;
92    memset(&act, 0, sizeof(act));
93    act.sa_handler = signalCatcher;
94    if (sigaction(SIGINT, &act, &gOrigSigactionINT) != 0) {
95        status_t err = -errno;
96        fprintf(stderr, "Unable to configure SIGINT handler: %s\n",
97                strerror(errno));
98        return err;
99    }
100    if (sigaction(SIGHUP, &act, &gOrigSigactionHUP) != 0) {
101        status_t err = -errno;
102        fprintf(stderr, "Unable to configure SIGHUP handler: %s\n",
103                strerror(errno));
104        return err;
105    }
106    return NO_ERROR;
107}
108
109/*
110 * Configures and starts the MediaCodec encoder.  Obtains an input surface
111 * from the codec.
112 */
113static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec,
114        sp<IGraphicBufferProducer>* pBufferProducer) {
115    status_t err;
116
117    sp<AMessage> format = new AMessage;
118    format->setInt32("width", gVideoWidth);
119    format->setInt32("height", gVideoHeight);
120    format->setString("mime", "video/avc");
121    format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
122    format->setInt32("bitrate", gBitRate);
123    format->setFloat("frame-rate", displayFps);
124    format->setInt32("i-frame-interval", 10);
125
126    /// MediaCodec
127    sp<ALooper> looper = new ALooper;
128    looper->setName("screenrecord_looper");
129    looper->start();
130    ALOGV("Creating codec");
131    sp<MediaCodec> codec = MediaCodec::CreateByType(looper, "video/avc", true);
132    err = codec->configure(format, NULL, NULL,
133            MediaCodec::CONFIGURE_FLAG_ENCODE);
134    if (err != NO_ERROR) {
135        fprintf(stderr, "ERROR: unable to configure codec (err=%d)\n", err);
136        return err;
137    }
138
139    ALOGV("Creating buffer producer");
140    sp<IGraphicBufferProducer> bufferProducer;
141    err = codec->createInputSurface(&bufferProducer);
142    if (err != NO_ERROR) {
143        fprintf(stderr,
144            "ERROR: unable to create encoder input surface (err=%d)\n", err);
145        return err;
146    }
147
148    ALOGV("Starting codec");
149    err = codec->start();
150    if (err != NO_ERROR) {
151        fprintf(stderr, "ERROR: unable to start codec (err=%d)\n", err);
152        return err;
153    }
154
155    *pCodec = codec;
156    *pBufferProducer = bufferProducer;
157    return 0;
158}
159
160/*
161 * Configures the virtual display.  When this completes, virtual display
162 * frames will start being sent to the encoder's surface.
163 */
164static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo,
165        const sp<IGraphicBufferProducer>& bufferProducer,
166        sp<IBinder>* pDisplayHandle) {
167    status_t err;
168
169    // Set the region of the layer stack we're interested in, which in our
170    // case is "all of it".  If the app is rotated (so that the width of the
171    // app is based on the height of the display), reverse width/height.
172    bool deviceRotated = mainDpyInfo.orientation != DISPLAY_ORIENTATION_0 &&
173            mainDpyInfo.orientation != DISPLAY_ORIENTATION_180;
174    uint32_t sourceWidth, sourceHeight;
175    if (!deviceRotated) {
176        sourceWidth = mainDpyInfo.w;
177        sourceHeight = mainDpyInfo.h;
178    } else {
179        ALOGV("using rotated width/height");
180        sourceHeight = mainDpyInfo.w;
181        sourceWidth = mainDpyInfo.h;
182    }
183    Rect layerStackRect(sourceWidth, sourceHeight);
184
185    // We need to preserve the aspect ratio of the display.
186    float displayAspect = (float) sourceHeight / (float) sourceWidth;
187
188
189    // Set the way we map the output onto the display surface (which will
190    // be e.g. 1280x720 for a 720p video).  The rect is interpreted
191    // post-rotation, so if the display is rotated 90 degrees we need to
192    // "pre-rotate" it by flipping width/height, so that the orientation
193    // adjustment changes it back.
194    //
195    // We might want to encode a portrait display as landscape to use more
196    // of the screen real estate.  (If players respect a 90-degree rotation
197    // hint, we can essentially get a 720x1280 video instead of 1280x720.)
198    // In that case, we swap the configured video width/height and then
199    // supply a rotation value to the display projection.
200    uint32_t videoWidth, videoHeight;
201    uint32_t outWidth, outHeight;
202    if (!gRotate) {
203        videoWidth = gVideoWidth;
204        videoHeight = gVideoHeight;
205    } else {
206        videoWidth = gVideoHeight;
207        videoHeight = gVideoWidth;
208    }
209    if (videoHeight > (uint32_t)(videoWidth * displayAspect)) {
210        // limited by narrow width; reduce height
211        outWidth = videoWidth;
212        outHeight = (uint32_t)(videoWidth * displayAspect);
213    } else {
214        // limited by short height; restrict width
215        outHeight = videoHeight;
216        outWidth = (uint32_t)(videoHeight / displayAspect);
217    }
218    uint32_t offX, offY;
219    offX = (videoWidth - outWidth) / 2;
220    offY = (videoHeight - outHeight) / 2;
221    Rect displayRect(offX, offY, offX + outWidth, offY + outHeight);
222
223    if (gVerbose) {
224        if (gRotate) {
225            printf("Rotated content area is %ux%u at offset x=%d y=%d\n",
226                    outHeight, outWidth, offY, offX);
227        } else {
228            printf("Content area is %ux%u at offset x=%d y=%d\n",
229                    outWidth, outHeight, offX, offY);
230        }
231    }
232
233
234    sp<IBinder> dpy = SurfaceComposerClient::createDisplay(
235            String8("ScreenRecorder"), false /* secure */);
236
237    SurfaceComposerClient::openGlobalTransaction();
238    SurfaceComposerClient::setDisplaySurface(dpy, bufferProducer);
239    SurfaceComposerClient::setDisplayProjection(dpy,
240            gRotate ? DISPLAY_ORIENTATION_90 : DISPLAY_ORIENTATION_0,
241            layerStackRect, displayRect);
242    SurfaceComposerClient::setDisplayLayerStack(dpy, 0);    // default stack
243    SurfaceComposerClient::closeGlobalTransaction();
244
245    *pDisplayHandle = dpy;
246
247    return NO_ERROR;
248}
249
250/*
251 * Runs the MediaCodec encoder, sending the output to the MediaMuxer.  The
252 * input frames are coming from the virtual display as fast as SurfaceFlinger
253 * wants to send them.
254 *
255 * The muxer must *not* have been started before calling.
256 */
257static status_t runEncoder(const sp<MediaCodec>& encoder,
258        const sp<MediaMuxer>& muxer) {
259    static int kTimeout = 250000;   // be responsive on signal
260    status_t err;
261    ssize_t trackIdx = -1;
262    uint32_t debugNumFrames = 0;
263    time_t debugStartWhen = time(NULL);
264
265    Vector<sp<ABuffer> > buffers;
266    err = encoder->getOutputBuffers(&buffers);
267    if (err != NO_ERROR) {
268        fprintf(stderr, "Unable to get output buffers (err=%d)\n", err);
269        return err;
270    }
271
272    // This is set by the signal handler.
273    gStopRequested = false;
274
275    // Run until we're signaled.
276    while (!gStopRequested) {
277        size_t bufIndex, offset, size;
278        int64_t ptsUsec;
279        uint32_t flags;
280        ALOGV("Calling dequeueOutputBuffer");
281        err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec,
282                &flags, kTimeout);
283        ALOGV("dequeueOutputBuffer returned %d", err);
284        switch (err) {
285        case NO_ERROR:
286            // got a buffer
287            if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) {
288                // ignore this -- we passed the CSD into MediaMuxer when
289                // we got the format change notification
290                ALOGV("Got codec config buffer (%u bytes); ignoring", size);
291                size = 0;
292            }
293            if (size != 0) {
294                ALOGV("Got data in buffer %d, size=%d, pts=%lld",
295                        bufIndex, size, ptsUsec);
296                CHECK(trackIdx != -1);
297
298                // The MediaMuxer docs are unclear, but it appears that we
299                // need to pass either the full set of BufferInfo flags, or
300                // (flags & BUFFER_FLAG_SYNCFRAME).
301                err = muxer->writeSampleData(buffers[bufIndex], trackIdx,
302                        ptsUsec, flags);
303                if (err != NO_ERROR) {
304                    fprintf(stderr, "Failed writing data to muxer (err=%d)\n",
305                            err);
306                    return err;
307                }
308                debugNumFrames++;
309            }
310            err = encoder->releaseOutputBuffer(bufIndex);
311            if (err != NO_ERROR) {
312                fprintf(stderr, "Unable to release output buffer (err=%d)\n",
313                        err);
314                return err;
315            }
316            if ((flags & MediaCodec::BUFFER_FLAG_EOS) != 0) {
317                // Not expecting EOS from SurfaceFlinger.  Go with it.
318                ALOGD("Received end-of-stream");
319                gStopRequested = false;
320            }
321            break;
322        case -EAGAIN:                       // INFO_TRY_AGAIN_LATER
323            // not expected with infinite timeout
324            ALOGV("Got -EAGAIN, looping");
325            break;
326        case INFO_FORMAT_CHANGED:           // INFO_OUTPUT_FORMAT_CHANGED
327            {
328                // format includes CSD, which we must provide to muxer
329                ALOGV("Encoder format changed");
330                sp<AMessage> newFormat;
331                encoder->getOutputFormat(&newFormat);
332                trackIdx = muxer->addTrack(newFormat);
333                ALOGV("Starting muxer");
334                err = muxer->start();
335                if (err != NO_ERROR) {
336                    fprintf(stderr, "Unable to start muxer (err=%d)\n", err);
337                    return err;
338                }
339            }
340            break;
341        case INFO_OUTPUT_BUFFERS_CHANGED:   // INFO_OUTPUT_BUFFERS_CHANGED
342            // not expected for an encoder; handle it anyway
343            ALOGV("Encoder buffers changed");
344            err = encoder->getOutputBuffers(&buffers);
345            if (err != NO_ERROR) {
346                fprintf(stderr,
347                        "Unable to get new output buffers (err=%d)\n", err);
348            }
349            break;
350        default:
351            ALOGW("Got weird result %d from dequeueOutputBuffer", err);
352            return err;
353        }
354    }
355
356    ALOGV("Encoder stopping (req=%d)", gStopRequested);
357    if (gVerbose) {
358        printf("Encoder stopping; recorded %u frames in %ld seconds\n",
359                debugNumFrames, time(NULL) - debugStartWhen);
360    }
361    return NO_ERROR;
362}
363
364/*
365 * Main "do work" method.
366 *
367 * Configures codec, muxer, and virtual display, then starts moving bits
368 * around.
369 */
370static status_t recordScreen(const char* fileName) {
371    status_t err;
372
373    if (gVerbose) {
374        printf("Recording %dx%d video at %.2fMbps\n",
375                gVideoWidth, gVideoHeight, gBitRate / 1000000.0);
376    }
377
378    // Configure signal handler.
379    err = configureSignals();
380    if (err != NO_ERROR) return err;
381
382    // Start Binder thread pool.  MediaCodec needs to be able to receive
383    // messages from mediaserver.
384    sp<ProcessState> self = ProcessState::self();
385    self->startThreadPool();
386
387    // Get main display parameters.
388    sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(
389            ISurfaceComposer::eDisplayIdMain);
390    DisplayInfo mainDpyInfo;
391    err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
392    if (err != NO_ERROR) {
393        fprintf(stderr, "ERROR: unable to get display characteristics\n");
394        return err;
395    }
396    if (gVerbose) {
397        printf("Main display is %dx%d @%.2ffps (orientation=%u)\n",
398                mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps,
399                mainDpyInfo.orientation);
400    }
401
402    // Configure and start the encoder.
403    sp<MediaCodec> encoder;
404    sp<IGraphicBufferProducer> bufferProducer;
405    err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);
406    if (err != NO_ERROR) return err;
407
408    // Configure virtual display.
409    sp<IBinder> dpy;
410    err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy);
411    if (err != NO_ERROR) return err;
412
413    // Configure, but do not start, muxer.
414    sp<MediaMuxer> muxer = new MediaMuxer(fileName,
415            MediaMuxer::OUTPUT_FORMAT_MPEG_4);
416    if (gRotate) {
417        muxer->setOrientationHint(90);
418    }
419
420    // Main encoder loop.
421    err = runEncoder(encoder, muxer);
422    if (err != NO_ERROR) return err;
423
424    if (gVerbose) {
425        printf("Stopping encoder and muxer\n");
426    }
427
428    // Shut everything down.
429    //
430    // The virtual display will continue to produce frames until "dpy"
431    // goes out of scope (and something causes the Binder traffic to transmit;
432    // can be forced with IPCThreadState::self()->flushCommands()).  This
433    // could cause SurfaceFlinger to get stuck trying to feed us, so we want
434    // to set a NULL Surface to make the virtual display "dormant".
435    bufferProducer = NULL;
436    SurfaceComposerClient::openGlobalTransaction();
437    SurfaceComposerClient::setDisplaySurface(dpy, bufferProducer);
438    SurfaceComposerClient::closeGlobalTransaction();
439
440    encoder->stop();
441    muxer->stop();
442    encoder->release();
443
444    return 0;
445}
446
447/*
448 * Parses a string of the form "1280x720".
449 *
450 * Returns true on success.
451 */
452static bool parseWidthHeight(const char* widthHeight, uint32_t* pWidth,
453        uint32_t* pHeight) {
454    long width, height;
455    char* end;
456
457    // Must specify base 10, or "0x0" gets parsed differently.
458    width = strtol(widthHeight, &end, 10);
459    if (end == widthHeight || *end != 'x' || *(end+1) == '\0') {
460        // invalid chars in width, or missing 'x', or missing height
461        return false;
462    }
463    height = strtol(end + 1, &end, 10);
464    if (*end != '\0') {
465        // invalid chars in height
466        return false;
467    }
468
469    *pWidth = width;
470    *pHeight = height;
471    return true;
472}
473
474/*
475 * Dumps usage on stderr.
476 */
477static void usage() {
478    fprintf(stderr,
479        "Usage: screenrecord [options] <filename>\n"
480        "\n"
481        "Options:\n"
482        "--size WIDTHxHEIGHT\n"
483        "    Set the video size, e.g. \"1280x720\".  For best results, use\n"
484        "    a size supported by the AVC encoder.\n"
485        "--bit-rate RATE\n"
486        "    Set the video bit rate, in megabits per second.  Default 4Mbps.\n"
487        "--rotate\n"
488        "    Rotate the output 90 degrees.  Useful for filling the frame\n"
489        "    when in portrait mode.\n"
490        "--verbose\n"
491        "    Display interesting information on stdout.\n"
492        "--help\n"
493        "    Show this message.\n"
494        "\n"
495        "Recording continues until Ctrl-C is hit.\n"
496        "\n"
497        );
498}
499
500/*
501 * Parses args and kicks things off.
502 */
503int main(int argc, char* const argv[]) {
504    static const struct option longOptions[] = {
505        { "help",       no_argument,        NULL, 'h' },
506        { "verbose",    no_argument,        NULL, 'v' },
507        { "size",       required_argument,  NULL, 's' },
508        { "bit-rate",   required_argument,  NULL, 'b' },
509        { "rotate",     no_argument,        NULL, 'r' },
510        { NULL,         0,                  NULL, 0 }
511    };
512
513    while (true) {
514        int optionIndex = 0;
515        int ic = getopt_long(argc, argv, "", longOptions, &optionIndex);
516        if (ic == -1) {
517            break;
518        }
519
520        switch (ic) {
521        case 'h':
522            usage();
523            return 0;
524        case 'v':
525            gVerbose = true;
526            break;
527        case 's':
528            if (!parseWidthHeight(optarg, &gVideoWidth, &gVideoHeight)) {
529                fprintf(stderr, "Invalid size '%s', must be width x height\n",
530                        optarg);
531                return 2;
532            }
533            if (gVideoWidth == 0 || gVideoHeight == 0) {
534                fprintf(stderr,
535                    "Invalid size %ux%u, width and height may not be zero\n",
536                    gVideoWidth, gVideoHeight);
537                return 2;
538            }
539            break;
540        case 'b':
541            gBitRate = atoi(optarg);
542            if (gBitRate < kMinBitRate || gBitRate > kMaxBitRate) {
543                fprintf(stderr,
544                        "Bit rate %dbps outside acceptable range [%d,%d]\n",
545                        gBitRate, kMinBitRate, kMaxBitRate);
546                return 2;
547            }
548            break;
549        case 'r':
550            gRotate = true;
551            break;
552        default:
553            if (ic != '?') {
554                fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);
555            }
556            return 2;
557        }
558    }
559
560    if (optind != argc - 1) {
561        fprintf(stderr, "Must specify output file (see --help).\n");
562        return 2;
563    }
564
565    status_t err = recordScreen(argv[optind]);
566    ALOGD(err == NO_ERROR ? "success" : "failed");
567    return (int) err;
568}
569