screenrecord.cpp revision e6d5794b2173ffe4e7509203a91778b19eafcebf
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#include <utils/Timers.h>
25
26#include <gui/Surface.h>
27#include <gui/SurfaceComposerClient.h>
28#include <gui/ISurfaceComposer.h>
29#include <ui/DisplayInfo.h>
30#include <media/openmax/OMX_IVCommon.h>
31#include <media/stagefright/foundation/ABuffer.h>
32#include <media/stagefright/foundation/ADebug.h>
33#include <media/stagefright/foundation/AMessage.h>
34#include <media/stagefright/MediaCodec.h>
35#include <media/stagefright/MediaErrors.h>
36#include <media/stagefright/MediaMuxer.h>
37#include <media/ICrypto.h>
38
39#include <stdlib.h>
40#include <unistd.h>
41#include <string.h>
42#include <stdio.h>
43#include <fcntl.h>
44#include <signal.h>
45#include <getopt.h>
46#include <sys/wait.h>
47
48using namespace android;
49
50static const uint32_t kMinBitRate = 100000;         // 0.1Mbps
51static const uint32_t kMaxBitRate = 100 * 1000000;  // 100Mbps
52static const uint32_t kMaxTimeLimitSec = 180;       // 3 minutes
53static const uint32_t kFallbackWidth = 1280;        // 720p
54static const uint32_t kFallbackHeight = 720;
55
56// Command-line parameters.
57static bool gVerbose = false;               // chatty on stdout
58static bool gRotate = false;                // rotate 90 degrees
59static bool gSizeSpecified = false;         // was size explicitly requested?
60static uint32_t gVideoWidth = 0;            // default width+height
61static uint32_t gVideoHeight = 0;
62static uint32_t gBitRate = 4000000;         // 4Mbps
63static uint32_t gTimeLimitSec = kMaxTimeLimitSec;
64
65// Set by signal handler to stop recording.
66static bool gStopRequested;
67
68// Previous signal handler state, restored after first hit.
69static struct sigaction gOrigSigactionINT;
70static struct sigaction gOrigSigactionHUP;
71
72
73/*
74 * Catch keyboard interrupt signals.  On receipt, the "stop requested"
75 * flag is raised, and the original handler is restored (so that, if
76 * we get stuck finishing, a second Ctrl-C will kill the process).
77 */
78static void signalCatcher(int signum)
79{
80    gStopRequested = true;
81    switch (signum) {
82    case SIGINT:
83    case SIGHUP:
84        sigaction(SIGINT, &gOrigSigactionINT, NULL);
85        sigaction(SIGHUP, &gOrigSigactionHUP, NULL);
86        break;
87    default:
88        abort();
89        break;
90    }
91}
92
93/*
94 * Configures signal handlers.  The previous handlers are saved.
95 *
96 * If the command is run from an interactive adb shell, we get SIGINT
97 * when Ctrl-C is hit.  If we're run from the host, the local adb process
98 * gets the signal, and we get a SIGHUP when the terminal disconnects.
99 */
100static status_t configureSignals()
101{
102    struct sigaction act;
103    memset(&act, 0, sizeof(act));
104    act.sa_handler = signalCatcher;
105    if (sigaction(SIGINT, &act, &gOrigSigactionINT) != 0) {
106        status_t err = -errno;
107        fprintf(stderr, "Unable to configure SIGINT handler: %s\n",
108                strerror(errno));
109        return err;
110    }
111    if (sigaction(SIGHUP, &act, &gOrigSigactionHUP) != 0) {
112        status_t err = -errno;
113        fprintf(stderr, "Unable to configure SIGHUP handler: %s\n",
114                strerror(errno));
115        return err;
116    }
117    return NO_ERROR;
118}
119
120/*
121 * Returns "true" if the device is rotated 90 degrees.
122 */
123static bool isDeviceRotated(int orientation) {
124    return orientation != DISPLAY_ORIENTATION_0 &&
125            orientation != DISPLAY_ORIENTATION_180;
126}
127
128/*
129 * Configures and starts the MediaCodec encoder.  Obtains an input surface
130 * from the codec.
131 */
132static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec,
133        sp<IGraphicBufferProducer>* pBufferProducer) {
134    status_t err;
135
136    if (gVerbose) {
137        printf("Configuring recorder for %dx%d video at %.2fMbps\n",
138                gVideoWidth, gVideoHeight, gBitRate / 1000000.0);
139    }
140
141    sp<AMessage> format = new AMessage;
142    format->setInt32("width", gVideoWidth);
143    format->setInt32("height", gVideoHeight);
144    format->setString("mime", "video/avc");
145    format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
146    format->setInt32("bitrate", gBitRate);
147    format->setFloat("frame-rate", displayFps);
148    format->setInt32("i-frame-interval", 10);
149
150    sp<ALooper> looper = new ALooper;
151    looper->setName("screenrecord_looper");
152    looper->start();
153    ALOGV("Creating codec");
154    sp<MediaCodec> codec = MediaCodec::CreateByType(looper, "video/avc", true);
155    if (codec == NULL) {
156        fprintf(stderr, "ERROR: unable to create video/avc codec instance\n");
157        return UNKNOWN_ERROR;
158    }
159    err = codec->configure(format, NULL, NULL,
160            MediaCodec::CONFIGURE_FLAG_ENCODE);
161    if (err != NO_ERROR) {
162        codec->release();
163        codec.clear();
164
165        fprintf(stderr, "ERROR: unable to configure codec (err=%d)\n", err);
166        return err;
167    }
168
169    ALOGV("Creating buffer producer");
170    sp<IGraphicBufferProducer> bufferProducer;
171    err = codec->createInputSurface(&bufferProducer);
172    if (err != NO_ERROR) {
173        codec->release();
174        codec.clear();
175
176        fprintf(stderr,
177            "ERROR: unable to create encoder input surface (err=%d)\n", err);
178        return err;
179    }
180
181    ALOGV("Starting codec");
182    err = codec->start();
183    if (err != NO_ERROR) {
184        codec->release();
185        codec.clear();
186
187        fprintf(stderr, "ERROR: unable to start codec (err=%d)\n", err);
188        return err;
189    }
190
191    ALOGV("Codec prepared");
192    *pCodec = codec;
193    *pBufferProducer = bufferProducer;
194    return 0;
195}
196
197/*
198 * Configures the virtual display.  When this completes, virtual display
199 * frames will start being sent to the encoder's surface.
200 */
201static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo,
202        const sp<IGraphicBufferProducer>& bufferProducer,
203        sp<IBinder>* pDisplayHandle) {
204    status_t err;
205
206    // Set the region of the layer stack we're interested in, which in our
207    // case is "all of it".  If the app is rotated (so that the width of the
208    // app is based on the height of the display), reverse width/height.
209    bool deviceRotated = isDeviceRotated(mainDpyInfo.orientation);
210    uint32_t sourceWidth, sourceHeight;
211    if (!deviceRotated) {
212        sourceWidth = mainDpyInfo.w;
213        sourceHeight = mainDpyInfo.h;
214    } else {
215        ALOGV("using rotated width/height");
216        sourceHeight = mainDpyInfo.w;
217        sourceWidth = mainDpyInfo.h;
218    }
219    Rect layerStackRect(sourceWidth, sourceHeight);
220
221    // We need to preserve the aspect ratio of the display.
222    float displayAspect = (float) sourceHeight / (float) sourceWidth;
223
224
225    // Set the way we map the output onto the display surface (which will
226    // be e.g. 1280x720 for a 720p video).  The rect is interpreted
227    // post-rotation, so if the display is rotated 90 degrees we need to
228    // "pre-rotate" it by flipping width/height, so that the orientation
229    // adjustment changes it back.
230    //
231    // We might want to encode a portrait display as landscape to use more
232    // of the screen real estate.  (If players respect a 90-degree rotation
233    // hint, we can essentially get a 720x1280 video instead of 1280x720.)
234    // In that case, we swap the configured video width/height and then
235    // supply a rotation value to the display projection.
236    uint32_t videoWidth, videoHeight;
237    uint32_t outWidth, outHeight;
238    if (!gRotate) {
239        videoWidth = gVideoWidth;
240        videoHeight = gVideoHeight;
241    } else {
242        videoWidth = gVideoHeight;
243        videoHeight = gVideoWidth;
244    }
245    if (videoHeight > (uint32_t)(videoWidth * displayAspect)) {
246        // limited by narrow width; reduce height
247        outWidth = videoWidth;
248        outHeight = (uint32_t)(videoWidth * displayAspect);
249    } else {
250        // limited by short height; restrict width
251        outHeight = videoHeight;
252        outWidth = (uint32_t)(videoHeight / displayAspect);
253    }
254    uint32_t offX, offY;
255    offX = (videoWidth - outWidth) / 2;
256    offY = (videoHeight - outHeight) / 2;
257    Rect displayRect(offX, offY, offX + outWidth, offY + outHeight);
258
259    if (gVerbose) {
260        if (gRotate) {
261            printf("Rotated content area is %ux%u at offset x=%d y=%d\n",
262                    outHeight, outWidth, offY, offX);
263        } else {
264            printf("Content area is %ux%u at offset x=%d y=%d\n",
265                    outWidth, outHeight, offX, offY);
266        }
267    }
268
269
270    sp<IBinder> dpy = SurfaceComposerClient::createDisplay(
271            String8("ScreenRecorder"), false /* secure */);
272
273    SurfaceComposerClient::openGlobalTransaction();
274    SurfaceComposerClient::setDisplaySurface(dpy, bufferProducer);
275    SurfaceComposerClient::setDisplayProjection(dpy,
276            gRotate ? DISPLAY_ORIENTATION_90 : DISPLAY_ORIENTATION_0,
277            layerStackRect, displayRect);
278    SurfaceComposerClient::setDisplayLayerStack(dpy, 0);    // default stack
279    SurfaceComposerClient::closeGlobalTransaction();
280
281    *pDisplayHandle = dpy;
282
283    return NO_ERROR;
284}
285
286/*
287 * Runs the MediaCodec encoder, sending the output to the MediaMuxer.  The
288 * input frames are coming from the virtual display as fast as SurfaceFlinger
289 * wants to send them.
290 *
291 * The muxer must *not* have been started before calling.
292 */
293static status_t runEncoder(const sp<MediaCodec>& encoder,
294        const sp<MediaMuxer>& muxer) {
295    static int kTimeout = 250000;   // be responsive on signal
296    status_t err;
297    ssize_t trackIdx = -1;
298    uint32_t debugNumFrames = 0;
299    int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC);
300    int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec);
301
302    Vector<sp<ABuffer> > buffers;
303    err = encoder->getOutputBuffers(&buffers);
304    if (err != NO_ERROR) {
305        fprintf(stderr, "Unable to get output buffers (err=%d)\n", err);
306        return err;
307    }
308
309    // This is set by the signal handler.
310    gStopRequested = false;
311
312    // Run until we're signaled.
313    while (!gStopRequested) {
314        size_t bufIndex, offset, size;
315        int64_t ptsUsec;
316        uint32_t flags;
317
318        if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) {
319            if (gVerbose) {
320                printf("Time limit reached\n");
321            }
322            break;
323        }
324
325        ALOGV("Calling dequeueOutputBuffer");
326        err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec,
327                &flags, kTimeout);
328        ALOGV("dequeueOutputBuffer returned %d", err);
329        switch (err) {
330        case NO_ERROR:
331            // got a buffer
332            if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) {
333                // ignore this -- we passed the CSD into MediaMuxer when
334                // we got the format change notification
335                ALOGV("Got codec config buffer (%u bytes); ignoring", size);
336                size = 0;
337            }
338            if (size != 0) {
339                ALOGV("Got data in buffer %d, size=%d, pts=%lld",
340                        bufIndex, size, ptsUsec);
341                CHECK(trackIdx != -1);
342
343                // If the virtual display isn't providing us with timestamps,
344                // use the current time.
345                if (ptsUsec == 0) {
346                    ptsUsec = systemTime(SYSTEM_TIME_MONOTONIC) / 1000;
347                }
348
349                // The MediaMuxer docs are unclear, but it appears that we
350                // need to pass either the full set of BufferInfo flags, or
351                // (flags & BUFFER_FLAG_SYNCFRAME).
352                err = muxer->writeSampleData(buffers[bufIndex], trackIdx,
353                        ptsUsec, flags);
354                if (err != NO_ERROR) {
355                    fprintf(stderr, "Failed writing data to muxer (err=%d)\n",
356                            err);
357                    return err;
358                }
359                debugNumFrames++;
360            }
361            err = encoder->releaseOutputBuffer(bufIndex);
362            if (err != NO_ERROR) {
363                fprintf(stderr, "Unable to release output buffer (err=%d)\n",
364                        err);
365                return err;
366            }
367            if ((flags & MediaCodec::BUFFER_FLAG_EOS) != 0) {
368                // Not expecting EOS from SurfaceFlinger.  Go with it.
369                ALOGD("Received end-of-stream");
370                gStopRequested = false;
371            }
372            break;
373        case -EAGAIN:                       // INFO_TRY_AGAIN_LATER
374            ALOGV("Got -EAGAIN, looping");
375            break;
376        case INFO_FORMAT_CHANGED:           // INFO_OUTPUT_FORMAT_CHANGED
377            {
378                // format includes CSD, which we must provide to muxer
379                ALOGV("Encoder format changed");
380                sp<AMessage> newFormat;
381                encoder->getOutputFormat(&newFormat);
382                trackIdx = muxer->addTrack(newFormat);
383                ALOGV("Starting muxer");
384                err = muxer->start();
385                if (err != NO_ERROR) {
386                    fprintf(stderr, "Unable to start muxer (err=%d)\n", err);
387                    return err;
388                }
389            }
390            break;
391        case INFO_OUTPUT_BUFFERS_CHANGED:   // INFO_OUTPUT_BUFFERS_CHANGED
392            // not expected for an encoder; handle it anyway
393            ALOGV("Encoder buffers changed");
394            err = encoder->getOutputBuffers(&buffers);
395            if (err != NO_ERROR) {
396                fprintf(stderr,
397                        "Unable to get new output buffers (err=%d)\n", err);
398                return err;
399            }
400            break;
401        case INVALID_OPERATION:
402            fprintf(stderr, "Request for encoder buffer failed\n");
403            return err;
404        default:
405            fprintf(stderr,
406                    "Got weird result %d from dequeueOutputBuffer\n", err);
407            return err;
408        }
409    }
410
411    ALOGV("Encoder stopping (req=%d)", gStopRequested);
412    if (gVerbose) {
413        printf("Encoder stopping; recorded %u frames in %lld seconds\n",
414                debugNumFrames,
415                nanoseconds_to_seconds(systemTime(CLOCK_MONOTONIC) - startWhenNsec));
416    }
417    return NO_ERROR;
418}
419
420/*
421 * Main "do work" method.
422 *
423 * Configures codec, muxer, and virtual display, then starts moving bits
424 * around.
425 */
426static status_t recordScreen(const char* fileName) {
427    status_t err;
428
429    // Configure signal handler.
430    err = configureSignals();
431    if (err != NO_ERROR) return err;
432
433    // Start Binder thread pool.  MediaCodec needs to be able to receive
434    // messages from mediaserver.
435    sp<ProcessState> self = ProcessState::self();
436    self->startThreadPool();
437
438    // Get main display parameters.
439    sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(
440            ISurfaceComposer::eDisplayIdMain);
441    DisplayInfo mainDpyInfo;
442    err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
443    if (err != NO_ERROR) {
444        fprintf(stderr, "ERROR: unable to get display characteristics\n");
445        return err;
446    }
447    if (gVerbose) {
448        printf("Main display is %dx%d @%.2ffps (orientation=%u)\n",
449                mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps,
450                mainDpyInfo.orientation);
451    }
452
453    bool rotated = isDeviceRotated(mainDpyInfo.orientation);
454    if (gVideoWidth == 0) {
455        gVideoWidth = rotated ? mainDpyInfo.h : mainDpyInfo.w;
456    }
457    if (gVideoHeight == 0) {
458        gVideoHeight = rotated ? mainDpyInfo.w : mainDpyInfo.h;
459    }
460
461    // Configure and start the encoder.
462    sp<MediaCodec> encoder;
463    sp<IGraphicBufferProducer> bufferProducer;
464    err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);
465
466    if (err != NO_ERROR && !gSizeSpecified) {
467        if (gVideoWidth != kFallbackWidth && gVideoHeight != kFallbackHeight) {
468            ALOGV("Retrying with 720p");
469            fprintf(stderr, "WARNING: failed at %dx%d, retrying at 720p\n",
470                    gVideoWidth, gVideoHeight);
471            gVideoWidth = kFallbackWidth;
472            gVideoHeight = kFallbackHeight;
473            err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);
474        }
475    }
476    if (err != NO_ERROR) {
477        return err;
478    }
479
480    // Configure virtual display.
481    sp<IBinder> dpy;
482    err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy);
483    if (err != NO_ERROR) {
484        encoder->release();
485        encoder.clear();
486
487        return err;
488    }
489
490    // Configure, but do not start, muxer.
491    sp<MediaMuxer> muxer = new MediaMuxer(fileName,
492            MediaMuxer::OUTPUT_FORMAT_MPEG_4);
493    if (gRotate) {
494        muxer->setOrientationHint(90);
495    }
496
497    // Main encoder loop.
498    err = runEncoder(encoder, muxer);
499    if (err != NO_ERROR) {
500        encoder->release();
501        encoder.clear();
502
503        return err;
504    }
505
506    if (gVerbose) {
507        printf("Stopping encoder and muxer\n");
508    }
509
510    // Shut everything down, starting with the producer side.
511    bufferProducer = NULL;
512    SurfaceComposerClient::destroyDisplay(dpy);
513
514    encoder->stop();
515    muxer->stop();
516    encoder->release();
517
518    return 0;
519}
520
521/*
522 * Sends a broadcast to the media scanner to tell it about the new video.
523 *
524 * This is optional, but nice to have.
525 */
526static status_t notifyMediaScanner(const char* fileName) {
527    pid_t pid = fork();
528    if (pid < 0) {
529        int err = errno;
530        ALOGW("fork() failed: %s", strerror(err));
531        return -err;
532    } else if (pid > 0) {
533        // parent; wait for the child, mostly to make the verbose-mode output
534        // look right, but also to check for and log failures
535        int status;
536        pid_t actualPid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
537        if (actualPid != pid) {
538            ALOGW("waitpid() returned %d (errno=%d)", actualPid, errno);
539        } else if (status != 0) {
540            ALOGW("'am broadcast' exited with status=%d", status);
541        } else {
542            ALOGV("'am broadcast' exited successfully");
543        }
544    } else {
545        const char* kCommand = "/system/bin/am";
546
547        // child; we're single-threaded, so okay to alloc
548        String8 fileUrl("file://");
549        fileUrl.append(fileName);
550        const char* const argv[] = {
551                kCommand,
552                "broadcast",
553                "-a",
554                "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
555                "-d",
556                fileUrl.string(),
557                NULL
558        };
559        if (gVerbose) {
560            printf("Executing:");
561            for (int i = 0; argv[i] != NULL; i++) {
562                printf(" %s", argv[i]);
563            }
564            putchar('\n');
565        } else {
566            // non-verbose, suppress 'am' output
567            ALOGV("closing stdout/stderr in child");
568            int fd = open("/dev/null", O_WRONLY);
569            if (fd >= 0) {
570                dup2(fd, STDOUT_FILENO);
571                dup2(fd, STDERR_FILENO);
572                close(fd);
573            }
574        }
575        execv(kCommand, const_cast<char* const*>(argv));
576        ALOGE("execv(%s) failed: %s\n", kCommand, strerror(errno));
577        exit(1);
578    }
579    return NO_ERROR;
580}
581
582/*
583 * Parses a string of the form "1280x720".
584 *
585 * Returns true on success.
586 */
587static bool parseWidthHeight(const char* widthHeight, uint32_t* pWidth,
588        uint32_t* pHeight) {
589    long width, height;
590    char* end;
591
592    // Must specify base 10, or "0x0" gets parsed differently.
593    width = strtol(widthHeight, &end, 10);
594    if (end == widthHeight || *end != 'x' || *(end+1) == '\0') {
595        // invalid chars in width, or missing 'x', or missing height
596        return false;
597    }
598    height = strtol(end + 1, &end, 10);
599    if (*end != '\0') {
600        // invalid chars in height
601        return false;
602    }
603
604    *pWidth = width;
605    *pHeight = height;
606    return true;
607}
608
609/*
610 * Dumps usage on stderr.
611 */
612static void usage() {
613    fprintf(stderr,
614        "Usage: screenrecord [options] <filename>\n"
615        "\n"
616        "Records the device's display to a .mp4 file.\n"
617        "\n"
618        "Options:\n"
619        "--size WIDTHxHEIGHT\n"
620        "    Set the video size, e.g. \"1280x720\".  Default is the device's main\n"
621        "    display resolution (if supported), 1280x720 if not.  For best results,\n"
622        "    use a size supported by the AVC encoder.\n"
623        "--bit-rate RATE\n"
624        "    Set the video bit rate, in megabits per second.  Default %dMbps.\n"
625        "--time-limit TIME\n"
626        "    Set the maximum recording time, in seconds.  Default / maximum is %d.\n"
627        "--rotate\n"
628        "    Rotate the output 90 degrees.\n"
629        "--verbose\n"
630        "    Display interesting information on stdout.\n"
631        "--help\n"
632        "    Show this message.\n"
633        "\n"
634        "Recording continues until Ctrl-C is hit or the time limit is reached.\n"
635        "\n",
636        gBitRate / 1000000, gTimeLimitSec
637        );
638}
639
640/*
641 * Parses args and kicks things off.
642 */
643int main(int argc, char* const argv[]) {
644    static const struct option longOptions[] = {
645        { "help",       no_argument,        NULL, 'h' },
646        { "verbose",    no_argument,        NULL, 'v' },
647        { "size",       required_argument,  NULL, 's' },
648        { "bit-rate",   required_argument,  NULL, 'b' },
649        { "time-limit", required_argument,  NULL, 't' },
650        { "rotate",     no_argument,        NULL, 'r' },
651        { NULL,         0,                  NULL, 0 }
652    };
653
654    while (true) {
655        int optionIndex = 0;
656        int ic = getopt_long(argc, argv, "", longOptions, &optionIndex);
657        if (ic == -1) {
658            break;
659        }
660
661        switch (ic) {
662        case 'h':
663            usage();
664            return 0;
665        case 'v':
666            gVerbose = true;
667            break;
668        case 's':
669            if (!parseWidthHeight(optarg, &gVideoWidth, &gVideoHeight)) {
670                fprintf(stderr, "Invalid size '%s', must be width x height\n",
671                        optarg);
672                return 2;
673            }
674            if (gVideoWidth == 0 || gVideoHeight == 0) {
675                fprintf(stderr,
676                    "Invalid size %ux%u, width and height may not be zero\n",
677                    gVideoWidth, gVideoHeight);
678                return 2;
679            }
680            gSizeSpecified = true;
681            break;
682        case 'b':
683            gBitRate = atoi(optarg);
684            if (gBitRate < kMinBitRate || gBitRate > kMaxBitRate) {
685                fprintf(stderr,
686                        "Bit rate %dbps outside acceptable range [%d,%d]\n",
687                        gBitRate, kMinBitRate, kMaxBitRate);
688                return 2;
689            }
690            break;
691        case 't':
692            gTimeLimitSec = atoi(optarg);
693            if (gTimeLimitSec == 0 || gTimeLimitSec > kMaxTimeLimitSec) {
694                fprintf(stderr,
695                        "Time limit %ds outside acceptable range [1,%d]\n",
696                        gTimeLimitSec, kMaxTimeLimitSec);
697                return 2;
698            }
699            break;
700        case 'r':
701            gRotate = true;
702            break;
703        default:
704            if (ic != '?') {
705                fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);
706            }
707            return 2;
708        }
709    }
710
711    if (optind != argc - 1) {
712        fprintf(stderr, "Must specify output file (see --help).\n");
713        return 2;
714    }
715
716    // MediaMuxer tries to create the file in the constructor, but we don't
717    // learn about the failure until muxer.start(), which returns a generic
718    // error code without logging anything.  We attempt to create the file
719    // now for better diagnostics.
720    const char* fileName = argv[optind];
721    int fd = open(fileName, O_CREAT | O_RDWR, 0644);
722    if (fd < 0) {
723        fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno));
724        return 1;
725    }
726    close(fd);
727
728    status_t err = recordScreen(fileName);
729    if (err == NO_ERROR) {
730        // Try to notify the media scanner.  Not fatal if this fails.
731        notifyMediaScanner(fileName);
732    }
733    ALOGD(err == NO_ERROR ? "success" : "failed");
734    return (int) err;
735}
736