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