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