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