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        // 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 */
530static 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 */
591static 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 */
616static 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 */
647int 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