Home | History | Annotate | Download | only in screenrecord
      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 
     48 using namespace android;
     49 
     50 static const uint32_t kMinBitRate = 100000;         // 0.1Mbps
     51 static const uint32_t kMaxBitRate = 100 * 1000000;  // 100Mbps
     52 static const uint32_t kMaxTimeLimitSec = 180;       // 3 minutes
     53 static const uint32_t kFallbackWidth = 1280;        // 720p
     54 static const uint32_t kFallbackHeight = 720;
     55 
     56 // Command-line parameters.
     57 static bool gVerbose = false;               // chatty on stdout
     58 static bool gRotate = false;                // rotate 90 degrees
     59 static bool gSizeSpecified = false;         // was size explicitly requested?
     60 static uint32_t gVideoWidth = 0;            // default width+height
     61 static uint32_t gVideoHeight = 0;
     62 static uint32_t gBitRate = 4000000;         // 4Mbps
     63 static uint32_t gTimeLimitSec = kMaxTimeLimitSec;
     64 
     65 // Set by signal handler to stop recording.
     66 static bool gStopRequested;
     67 
     68 // Previous signal handler state, restored after first hit.
     69 static struct sigaction gOrigSigactionINT;
     70 static 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  */
     78 static 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  */
    100 static 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  */
    123 static 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  */
    132 static 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  */
    201 static 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  */
    293 static 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  */
    426 static 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         // fallback is defined for landscape; swap if we're in portrait
    468         bool needSwap = gVideoWidth < gVideoHeight;
    469         uint32_t newWidth = needSwap ? kFallbackHeight : kFallbackWidth;
    470         uint32_t newHeight = needSwap ? kFallbackWidth : kFallbackHeight;
    471         if (gVideoWidth != newWidth && gVideoHeight != newHeight) {
    472             ALOGV("Retrying with 720p");
    473             fprintf(stderr, "WARNING: failed at %dx%d, retrying at %dx%d\n",
    474                     gVideoWidth, gVideoHeight, newWidth, newHeight);
    475             gVideoWidth = newWidth;
    476             gVideoHeight = newHeight;
    477             err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);
    478         }
    479     }
    480     if (err != NO_ERROR) {
    481         return err;
    482     }
    483 
    484     // Configure virtual display.
    485     sp<IBinder> dpy;
    486     err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy);
    487     if (err != NO_ERROR) {
    488         encoder->release();
    489         encoder.clear();
    490 
    491         return err;
    492     }
    493 
    494     // Configure, but do not start, muxer.
    495     sp<MediaMuxer> muxer = new MediaMuxer(fileName,
    496             MediaMuxer::OUTPUT_FORMAT_MPEG_4);
    497     if (gRotate) {
    498         muxer->setOrientationHint(90);
    499     }
    500 
    501     // Main encoder loop.
    502     err = runEncoder(encoder, muxer);
    503     if (err != NO_ERROR) {
    504         encoder->release();
    505         encoder.clear();
    506 
    507         return err;
    508     }
    509 
    510     if (gVerbose) {
    511         printf("Stopping encoder and muxer\n");
    512     }
    513 
    514     // Shut everything down, starting with the producer side.
    515     bufferProducer = NULL;
    516     SurfaceComposerClient::destroyDisplay(dpy);
    517 
    518     encoder->stop();
    519     muxer->stop();
    520     encoder->release();
    521 
    522     return 0;
    523 }
    524 
    525 /*
    526  * Sends a broadcast to the media scanner to tell it about the new video.
    527  *
    528  * This is optional, but nice to have.
    529  */
    530 static status_t notifyMediaScanner(const char* fileName) {
    531     pid_t pid = fork();
    532     if (pid < 0) {
    533         int err = errno;
    534         ALOGW("fork() failed: %s", strerror(err));
    535         return -err;
    536     } else if (pid > 0) {
    537         // parent; wait for the child, mostly to make the verbose-mode output
    538         // look right, but also to check for and log failures
    539         int status;
    540         pid_t actualPid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
    541         if (actualPid != pid) {
    542             ALOGW("waitpid() returned %d (errno=%d)", actualPid, errno);
    543         } else if (status != 0) {
    544             ALOGW("'am broadcast' exited with status=%d", status);
    545         } else {
    546             ALOGV("'am broadcast' exited successfully");
    547         }
    548     } else {
    549         const char* kCommand = "/system/bin/am";
    550 
    551         // child; we're single-threaded, so okay to alloc
    552         String8 fileUrl("file://");
    553         fileUrl.append(fileName);
    554         const char* const argv[] = {
    555                 kCommand,
    556                 "broadcast",
    557                 "-a",
    558                 "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
    559                 "-d",
    560                 fileUrl.string(),
    561                 NULL
    562         };
    563         if (gVerbose) {
    564             printf("Executing:");
    565             for (int i = 0; argv[i] != NULL; i++) {
    566                 printf(" %s", argv[i]);
    567             }
    568             putchar('\n');
    569         } else {
    570             // non-verbose, suppress 'am' output
    571             ALOGV("closing stdout/stderr in child");
    572             int fd = open("/dev/null", O_WRONLY);
    573             if (fd >= 0) {
    574                 dup2(fd, STDOUT_FILENO);
    575                 dup2(fd, STDERR_FILENO);
    576                 close(fd);
    577             }
    578         }
    579         execv(kCommand, const_cast<char* const*>(argv));
    580         ALOGE("execv(%s) failed: %s\n", kCommand, strerror(errno));
    581         exit(1);
    582     }
    583     return NO_ERROR;
    584 }
    585 
    586 /*
    587  * Parses a string of the form "1280x720".
    588  *
    589  * Returns true on success.
    590  */
    591 static bool parseWidthHeight(const char* widthHeight, uint32_t* pWidth,
    592         uint32_t* pHeight) {
    593     long width, height;
    594     char* end;
    595 
    596     // Must specify base 10, or "0x0" gets parsed differently.
    597     width = strtol(widthHeight, &end, 10);
    598     if (end == widthHeight || *end != 'x' || *(end+1) == '\0') {
    599         // invalid chars in width, or missing 'x', or missing height
    600         return false;
    601     }
    602     height = strtol(end + 1, &end, 10);
    603     if (*end != '\0') {
    604         // invalid chars in height
    605         return false;
    606     }
    607 
    608     *pWidth = width;
    609     *pHeight = height;
    610     return true;
    611 }
    612 
    613 /*
    614  * Dumps usage on stderr.
    615  */
    616 static void usage() {
    617     fprintf(stderr,
    618         "Usage: screenrecord [options] <filename>\n"
    619         "\n"
    620         "Records the device's display to a .mp4 file.\n"
    621         "\n"
    622         "Options:\n"
    623         "--size WIDTHxHEIGHT\n"
    624         "    Set the video size, e.g. \"1280x720\".  Default is the device's main\n"
    625         "    display resolution (if supported), 1280x720 if not.  For best results,\n"
    626         "    use a size supported by the AVC encoder.\n"
    627         "--bit-rate RATE\n"
    628         "    Set the video bit rate, in megabits per second.  Default %dMbps.\n"
    629         "--time-limit TIME\n"
    630         "    Set the maximum recording time, in seconds.  Default / maximum is %d.\n"
    631         "--rotate\n"
    632         "    Rotate the output 90 degrees.\n"
    633         "--verbose\n"
    634         "    Display interesting information on stdout.\n"
    635         "--help\n"
    636         "    Show this message.\n"
    637         "\n"
    638         "Recording continues until Ctrl-C is hit or the time limit is reached.\n"
    639         "\n",
    640         gBitRate / 1000000, gTimeLimitSec
    641         );
    642 }
    643 
    644 /*
    645  * Parses args and kicks things off.
    646  */
    647 int main(int argc, char* const argv[]) {
    648     static const struct option longOptions[] = {
    649         { "help",       no_argument,        NULL, 'h' },
    650         { "verbose",    no_argument,        NULL, 'v' },
    651         { "size",       required_argument,  NULL, 's' },
    652         { "bit-rate",   required_argument,  NULL, 'b' },
    653         { "time-limit", required_argument,  NULL, 't' },
    654         { "rotate",     no_argument,        NULL, 'r' },
    655         { NULL,         0,                  NULL, 0 }
    656     };
    657 
    658     while (true) {
    659         int optionIndex = 0;
    660         int ic = getopt_long(argc, argv, "", longOptions, &optionIndex);
    661         if (ic == -1) {
    662             break;
    663         }
    664 
    665         switch (ic) {
    666         case 'h':
    667             usage();
    668             return 0;
    669         case 'v':
    670             gVerbose = true;
    671             break;
    672         case 's':
    673             if (!parseWidthHeight(optarg, &gVideoWidth, &gVideoHeight)) {
    674                 fprintf(stderr, "Invalid size '%s', must be width x height\n",
    675                         optarg);
    676                 return 2;
    677             }
    678             if (gVideoWidth == 0 || gVideoHeight == 0) {
    679                 fprintf(stderr,
    680                     "Invalid size %ux%u, width and height may not be zero\n",
    681                     gVideoWidth, gVideoHeight);
    682                 return 2;
    683             }
    684             gSizeSpecified = true;
    685             break;
    686         case 'b':
    687             gBitRate = atoi(optarg);
    688             if (gBitRate < kMinBitRate || gBitRate > kMaxBitRate) {
    689                 fprintf(stderr,
    690                         "Bit rate %dbps outside acceptable range [%d,%d]\n",
    691                         gBitRate, kMinBitRate, kMaxBitRate);
    692                 return 2;
    693             }
    694             break;
    695         case 't':
    696             gTimeLimitSec = atoi(optarg);
    697             if (gTimeLimitSec == 0 || gTimeLimitSec > kMaxTimeLimitSec) {
    698                 fprintf(stderr,
    699                         "Time limit %ds outside acceptable range [1,%d]\n",
    700                         gTimeLimitSec, kMaxTimeLimitSec);
    701                 return 2;
    702             }
    703             break;
    704         case 'r':
    705             gRotate = true;
    706             break;
    707         default:
    708             if (ic != '?') {
    709                 fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);
    710             }
    711             return 2;
    712         }
    713     }
    714 
    715     if (optind != argc - 1) {
    716         fprintf(stderr, "Must specify output file (see --help).\n");
    717         return 2;
    718     }
    719 
    720     // MediaMuxer tries to create the file in the constructor, but we don't
    721     // learn about the failure until muxer.start(), which returns a generic
    722     // error code without logging anything.  We attempt to create the file
    723     // now for better diagnostics.
    724     const char* fileName = argv[optind];
    725     int fd = open(fileName, O_CREAT | O_RDWR, 0644);
    726     if (fd < 0) {
    727         fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno));
    728         return 1;
    729     }
    730     close(fd);
    731 
    732     status_t err = recordScreen(fileName);
    733     if (err == NO_ERROR) {
    734         // Try to notify the media scanner.  Not fatal if this fails.
    735         notifyMediaScanner(fileName);
    736     }
    737     ALOGD(err == NO_ERROR ? "success" : "failed");
    738     return (int) err;
    739 }
    740