• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
signalCatcher(int signum)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  */
configureSignals()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  */
isDeviceRotated(int orientation)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  */
prepareEncoder(float displayFps,sp<MediaCodec> * pCodec,sp<IGraphicBufferProducer> * pBufferProducer)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  */
prepareVirtualDisplay(const DisplayInfo & mainDpyInfo,const sp<IGraphicBufferProducer> & bufferProducer,sp<IBinder> * pDisplayHandle)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  */
runEncoder(const sp<MediaCodec> & encoder,const sp<MediaMuxer> & muxer)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  */
recordScreen(const char * fileName)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  */
notifyMediaScanner(const char * fileName)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  */
parseWidthHeight(const char * widthHeight,uint32_t * pWidth,uint32_t * pHeight)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  */
usage()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  */
main(int argc,char * const argv[])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