screenrecord.cpp revision 441e847feb0e055ecb004802802cea07782ab228
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 ATRACE_TAG ATRACE_TAG_GRAPHICS 19//#define LOG_NDEBUG 0 20#include <utils/Log.h> 21 22#include <binder/IPCThreadState.h> 23#include <utils/Errors.h> 24#include <utils/Timers.h> 25#include <utils/Trace.h> 26 27#include <gui/Surface.h> 28#include <gui/SurfaceComposerClient.h> 29#include <gui/ISurfaceComposer.h> 30#include <ui/DisplayInfo.h> 31#include <media/openmax/OMX_IVCommon.h> 32#include <media/stagefright/foundation/ABuffer.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 <ctype.h> 44#include <fcntl.h> 45#include <signal.h> 46#include <getopt.h> 47#include <sys/wait.h> 48 49#include "screenrecord.h" 50#include "Overlay.h" 51 52using namespace android; 53 54static const uint32_t kMinBitRate = 100000; // 0.1Mbps 55static const uint32_t kMaxBitRate = 200 * 1000000; // 200Mbps 56static const uint32_t kMaxTimeLimitSec = 180; // 3 minutes 57static const uint32_t kFallbackWidth = 1280; // 720p 58static const uint32_t kFallbackHeight = 720; 59 60// Command-line parameters. 61static bool gVerbose = false; // chatty on stdout 62static bool gRotate = false; // rotate 90 degrees 63static bool gSizeSpecified = false; // was size explicitly requested? 64static bool gWantInfoScreen = false; // do we want initial info screen? 65static bool gWantFrameTime = false; // do we want times on each frame? 66static uint32_t gVideoWidth = 0; // default width+height 67static uint32_t gVideoHeight = 0; 68static uint32_t gBitRate = 4000000; // 4Mbps 69static uint32_t gTimeLimitSec = kMaxTimeLimitSec; 70 71// Set by signal handler to stop recording. 72static volatile bool gStopRequested; 73 74// Previous signal handler state, restored after first hit. 75static struct sigaction gOrigSigactionINT; 76static struct sigaction gOrigSigactionHUP; 77 78 79/* 80 * Catch keyboard interrupt signals. On receipt, the "stop requested" 81 * flag is raised, and the original handler is restored (so that, if 82 * we get stuck finishing, a second Ctrl-C will kill the process). 83 */ 84static void signalCatcher(int signum) 85{ 86 gStopRequested = true; 87 switch (signum) { 88 case SIGINT: 89 case SIGHUP: 90 sigaction(SIGINT, &gOrigSigactionINT, NULL); 91 sigaction(SIGHUP, &gOrigSigactionHUP, NULL); 92 break; 93 default: 94 abort(); 95 break; 96 } 97} 98 99/* 100 * Configures signal handlers. The previous handlers are saved. 101 * 102 * If the command is run from an interactive adb shell, we get SIGINT 103 * when Ctrl-C is hit. If we're run from the host, the local adb process 104 * gets the signal, and we get a SIGHUP when the terminal disconnects. 105 */ 106static status_t configureSignals() { 107 struct sigaction act; 108 memset(&act, 0, sizeof(act)); 109 act.sa_handler = signalCatcher; 110 if (sigaction(SIGINT, &act, &gOrigSigactionINT) != 0) { 111 status_t err = -errno; 112 fprintf(stderr, "Unable to configure SIGINT handler: %s\n", 113 strerror(errno)); 114 return err; 115 } 116 if (sigaction(SIGHUP, &act, &gOrigSigactionHUP) != 0) { 117 status_t err = -errno; 118 fprintf(stderr, "Unable to configure SIGHUP handler: %s\n", 119 strerror(errno)); 120 return err; 121 } 122 return NO_ERROR; 123} 124 125/* 126 * Returns "true" if the device is rotated 90 degrees. 127 */ 128static bool isDeviceRotated(int orientation) { 129 return orientation != DISPLAY_ORIENTATION_0 && 130 orientation != DISPLAY_ORIENTATION_180; 131} 132 133/* 134 * Configures and starts the MediaCodec encoder. Obtains an input surface 135 * from the codec. 136 */ 137static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec, 138 sp<IGraphicBufferProducer>* pBufferProducer) { 139 status_t err; 140 141 if (gVerbose) { 142 printf("Configuring recorder for %dx%d video at %.2fMbps\n", 143 gVideoWidth, gVideoHeight, gBitRate / 1000000.0); 144 } 145 146 sp<AMessage> format = new AMessage; 147 format->setInt32("width", gVideoWidth); 148 format->setInt32("height", gVideoHeight); 149 format->setString("mime", "video/avc"); 150 format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque); 151 format->setInt32("bitrate", gBitRate); 152 format->setFloat("frame-rate", displayFps); 153 format->setInt32("i-frame-interval", 10); 154 155 sp<ALooper> looper = new ALooper; 156 looper->setName("screenrecord_looper"); 157 looper->start(); 158 ALOGV("Creating codec"); 159 sp<MediaCodec> codec = MediaCodec::CreateByType(looper, "video/avc", true); 160 if (codec == NULL) { 161 fprintf(stderr, "ERROR: unable to create video/avc codec instance\n"); 162 return UNKNOWN_ERROR; 163 } 164 165 err = codec->configure(format, NULL, NULL, 166 MediaCodec::CONFIGURE_FLAG_ENCODE); 167 if (err != NO_ERROR) { 168 fprintf(stderr, "ERROR: unable to configure codec (err=%d)\n", err); 169 codec->release(); 170 return err; 171 } 172 173 ALOGV("Creating encoder input surface"); 174 sp<IGraphicBufferProducer> bufferProducer; 175 err = codec->createInputSurface(&bufferProducer); 176 if (err != NO_ERROR) { 177 fprintf(stderr, 178 "ERROR: unable to create encoder input surface (err=%d)\n", err); 179 codec->release(); 180 return err; 181 } 182 183 ALOGV("Starting codec"); 184 err = codec->start(); 185 if (err != NO_ERROR) { 186 fprintf(stderr, "ERROR: unable to start codec (err=%d)\n", err); 187 codec->release(); 188 return err; 189 } 190 191 ALOGV("Codec prepared"); 192 *pCodec = codec; 193 *pBufferProducer = bufferProducer; 194 return 0; 195} 196 197/* 198 * Sets the display projection, based on the display dimensions, video size, 199 * and device orientation. 200 */ 201static status_t setDisplayProjection(const sp<IBinder>& dpy, 202 const DisplayInfo& mainDpyInfo) { 203 status_t err; 204 205 // Set the region of the layer stack we're interested in, which in our 206 // case is "all of it". If the app is rotated (so that the width of the 207 // app is based on the height of the display), reverse width/height. 208 bool deviceRotated = isDeviceRotated(mainDpyInfo.orientation); 209 uint32_t sourceWidth, sourceHeight; 210 if (!deviceRotated) { 211 sourceWidth = mainDpyInfo.w; 212 sourceHeight = mainDpyInfo.h; 213 } else { 214 ALOGV("using rotated width/height"); 215 sourceHeight = mainDpyInfo.w; 216 sourceWidth = mainDpyInfo.h; 217 } 218 Rect layerStackRect(sourceWidth, sourceHeight); 219 220 // We need to preserve the aspect ratio of the display. 221 float displayAspect = (float) sourceHeight / (float) sourceWidth; 222 223 224 // Set the way we map the output onto the display surface (which will 225 // be e.g. 1280x720 for a 720p video). The rect is interpreted 226 // post-rotation, so if the display is rotated 90 degrees we need to 227 // "pre-rotate" it by flipping width/height, so that the orientation 228 // adjustment changes it back. 229 // 230 // We might want to encode a portrait display as landscape to use more 231 // of the screen real estate. (If players respect a 90-degree rotation 232 // hint, we can essentially get a 720x1280 video instead of 1280x720.) 233 // In that case, we swap the configured video width/height and then 234 // supply a rotation value to the display projection. 235 uint32_t videoWidth, videoHeight; 236 uint32_t outWidth, outHeight; 237 if (!gRotate) { 238 videoWidth = gVideoWidth; 239 videoHeight = gVideoHeight; 240 } else { 241 videoWidth = gVideoHeight; 242 videoHeight = gVideoWidth; 243 } 244 if (videoHeight > (uint32_t)(videoWidth * displayAspect)) { 245 // limited by narrow width; reduce height 246 outWidth = videoWidth; 247 outHeight = (uint32_t)(videoWidth * displayAspect); 248 } else { 249 // limited by short height; restrict width 250 outHeight = videoHeight; 251 outWidth = (uint32_t)(videoHeight / displayAspect); 252 } 253 uint32_t offX, offY; 254 offX = (videoWidth - outWidth) / 2; 255 offY = (videoHeight - outHeight) / 2; 256 Rect displayRect(offX, offY, offX + outWidth, offY + outHeight); 257 258 if (gVerbose) { 259 if (gRotate) { 260 printf("Rotated content area is %ux%u at offset x=%d y=%d\n", 261 outHeight, outWidth, offY, offX); 262 } else { 263 printf("Content area is %ux%u at offset x=%d y=%d\n", 264 outWidth, outHeight, offX, offY); 265 } 266 } 267 268 SurfaceComposerClient::setDisplayProjection(dpy, 269 gRotate ? DISPLAY_ORIENTATION_90 : DISPLAY_ORIENTATION_0, 270 layerStackRect, displayRect); 271 return NO_ERROR; 272} 273 274/* 275 * Configures the virtual display. When this completes, virtual display 276 * frames will start arriving from the buffer producer. 277 */ 278static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo, 279 const sp<IGraphicBufferProducer>& bufferProducer, 280 sp<IBinder>* pDisplayHandle) { 281 sp<IBinder> dpy = SurfaceComposerClient::createDisplay( 282 String8("ScreenRecorder"), false /*secure*/); 283 284 SurfaceComposerClient::openGlobalTransaction(); 285 SurfaceComposerClient::setDisplaySurface(dpy, bufferProducer); 286 setDisplayProjection(dpy, mainDpyInfo); 287 SurfaceComposerClient::setDisplayLayerStack(dpy, 0); // default stack 288 SurfaceComposerClient::closeGlobalTransaction(); 289 290 *pDisplayHandle = dpy; 291 292 return NO_ERROR; 293} 294 295/* 296 * Runs the MediaCodec encoder, sending the output to the MediaMuxer. The 297 * input frames are coming from the virtual display as fast as SurfaceFlinger 298 * wants to send them. 299 * 300 * The muxer must *not* have been started before calling. 301 */ 302static status_t runEncoder(const sp<MediaCodec>& encoder, 303 const sp<MediaMuxer>& muxer, const sp<IBinder>& mainDpy, 304 const sp<IBinder>& virtualDpy, uint8_t orientation) { 305 static int kTimeout = 250000; // be responsive on signal 306 status_t err; 307 ssize_t trackIdx = -1; 308 uint32_t debugNumFrames = 0; 309 int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC); 310 int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec); 311 DisplayInfo mainDpyInfo; 312 313 Vector<sp<ABuffer> > buffers; 314 err = encoder->getOutputBuffers(&buffers); 315 if (err != NO_ERROR) { 316 fprintf(stderr, "Unable to get output buffers (err=%d)\n", err); 317 return err; 318 } 319 320 // This is set by the signal handler. 321 gStopRequested = false; 322 323 // Run until we're signaled. 324 while (!gStopRequested) { 325 size_t bufIndex, offset, size; 326 int64_t ptsUsec; 327 uint32_t flags; 328 329 if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) { 330 if (gVerbose) { 331 printf("Time limit reached\n"); 332 } 333 break; 334 } 335 336 ALOGV("Calling dequeueOutputBuffer"); 337 err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec, 338 &flags, kTimeout); 339 ALOGV("dequeueOutputBuffer returned %d", err); 340 switch (err) { 341 case NO_ERROR: 342 // got a buffer 343 if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) { 344 // ignore this -- we passed the CSD into MediaMuxer when 345 // we got the format change notification 346 ALOGV("Got codec config buffer (%u bytes); ignoring", size); 347 size = 0; 348 } 349 if (size != 0) { 350 ALOGV("Got data in buffer %d, size=%d, pts=%lld", 351 bufIndex, size, ptsUsec); 352 assert(trackIdx != -1); 353 354 { // scope 355 ATRACE_NAME("orientation"); 356 // Check orientation, update if it has changed. 357 // 358 // Polling for changes is inefficient and wrong, but the 359 // useful stuff is hard to get at without a Dalvik VM. 360 err = SurfaceComposerClient::getDisplayInfo(mainDpy, 361 &mainDpyInfo); 362 if (err != NO_ERROR) { 363 ALOGW("getDisplayInfo(main) failed: %d", err); 364 } else if (orientation != mainDpyInfo.orientation) { 365 ALOGD("orientation changed, now %d", mainDpyInfo.orientation); 366 SurfaceComposerClient::openGlobalTransaction(); 367 setDisplayProjection(virtualDpy, mainDpyInfo); 368 SurfaceComposerClient::closeGlobalTransaction(); 369 orientation = mainDpyInfo.orientation; 370 } 371 } 372 373 // If the virtual display isn't providing us with timestamps, 374 // use the current time. This isn't great -- we could get 375 // decoded data in clusters -- but we're not expecting 376 // to hit this anyway. 377 if (ptsUsec == 0) { 378 ptsUsec = systemTime(SYSTEM_TIME_MONOTONIC) / 1000; 379 } 380 381 // The MediaMuxer docs are unclear, but it appears that we 382 // need to pass either the full set of BufferInfo flags, or 383 // (flags & BUFFER_FLAG_SYNCFRAME). 384 // 385 // If this blocks for too long we could drop frames. We may 386 // want to queue these up and do them on a different thread. 387 { // scope 388 ATRACE_NAME("write sample"); 389 err = muxer->writeSampleData(buffers[bufIndex], trackIdx, 390 ptsUsec, flags); 391 if (err != NO_ERROR) { 392 fprintf(stderr, 393 "Failed writing data to muxer (err=%d)\n", err); 394 return err; 395 } 396 } 397 debugNumFrames++; 398 } 399 err = encoder->releaseOutputBuffer(bufIndex); 400 if (err != NO_ERROR) { 401 fprintf(stderr, "Unable to release output buffer (err=%d)\n", 402 err); 403 return err; 404 } 405 if ((flags & MediaCodec::BUFFER_FLAG_EOS) != 0) { 406 // Not expecting EOS from SurfaceFlinger. Go with it. 407 ALOGI("Received end-of-stream"); 408 gStopRequested = true; 409 } 410 break; 411 case -EAGAIN: // INFO_TRY_AGAIN_LATER 412 ALOGV("Got -EAGAIN, looping"); 413 break; 414 case INFO_FORMAT_CHANGED: // INFO_OUTPUT_FORMAT_CHANGED 415 { 416 // Format includes CSD, which we must provide to muxer. 417 ALOGV("Encoder format changed"); 418 sp<AMessage> newFormat; 419 encoder->getOutputFormat(&newFormat); 420 trackIdx = muxer->addTrack(newFormat); 421 ALOGV("Starting muxer"); 422 err = muxer->start(); 423 if (err != NO_ERROR) { 424 fprintf(stderr, "Unable to start muxer (err=%d)\n", err); 425 return err; 426 } 427 } 428 break; 429 case INFO_OUTPUT_BUFFERS_CHANGED: // INFO_OUTPUT_BUFFERS_CHANGED 430 // Not expected for an encoder; handle it anyway. 431 ALOGV("Encoder buffers changed"); 432 err = encoder->getOutputBuffers(&buffers); 433 if (err != NO_ERROR) { 434 fprintf(stderr, 435 "Unable to get new output buffers (err=%d)\n", err); 436 return err; 437 } 438 break; 439 case INVALID_OPERATION: 440 ALOGW("dequeueOutputBuffer returned INVALID_OPERATION"); 441 return err; 442 default: 443 fprintf(stderr, 444 "Got weird result %d from dequeueOutputBuffer\n", err); 445 return err; 446 } 447 } 448 449 ALOGV("Encoder stopping (req=%d)", gStopRequested); 450 if (gVerbose) { 451 printf("Encoder stopping; recorded %u frames in %lld seconds\n", 452 debugNumFrames, nanoseconds_to_seconds( 453 systemTime(CLOCK_MONOTONIC) - startWhenNsec)); 454 } 455 return NO_ERROR; 456} 457 458/* 459 * Main "do work" method. 460 * 461 * Configures codec, muxer, and virtual display, then starts moving bits 462 * around. 463 */ 464static status_t recordScreen(const char* fileName) { 465 status_t err; 466 467 // Configure signal handler. 468 err = configureSignals(); 469 if (err != NO_ERROR) return err; 470 471 // Start Binder thread pool. MediaCodec needs to be able to receive 472 // messages from mediaserver. 473 sp<ProcessState> self = ProcessState::self(); 474 self->startThreadPool(); 475 476 // Get main display parameters. 477 sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay( 478 ISurfaceComposer::eDisplayIdMain); 479 DisplayInfo mainDpyInfo; 480 err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo); 481 if (err != NO_ERROR) { 482 fprintf(stderr, "ERROR: unable to get display characteristics\n"); 483 return err; 484 } 485 if (gVerbose) { 486 printf("Main display is %dx%d @%.2ffps (orientation=%u)\n", 487 mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps, 488 mainDpyInfo.orientation); 489 } 490 491 bool rotated = isDeviceRotated(mainDpyInfo.orientation); 492 if (gVideoWidth == 0) { 493 gVideoWidth = rotated ? mainDpyInfo.h : mainDpyInfo.w; 494 } 495 if (gVideoHeight == 0) { 496 gVideoHeight = rotated ? mainDpyInfo.w : mainDpyInfo.h; 497 } 498 499 // Configure and start the encoder. 500 sp<MediaCodec> encoder; 501 sp<IGraphicBufferProducer> encoderInputSurface; 502 err = prepareEncoder(mainDpyInfo.fps, &encoder, &encoderInputSurface); 503 504 if (err != NO_ERROR && !gSizeSpecified) { 505 // fallback is defined for landscape; swap if we're in portrait 506 bool needSwap = gVideoWidth < gVideoHeight; 507 uint32_t newWidth = needSwap ? kFallbackHeight : kFallbackWidth; 508 uint32_t newHeight = needSwap ? kFallbackWidth : kFallbackHeight; 509 if (gVideoWidth != newWidth && gVideoHeight != newHeight) { 510 ALOGV("Retrying with 720p"); 511 fprintf(stderr, "WARNING: failed at %dx%d, retrying at %dx%d\n", 512 gVideoWidth, gVideoHeight, newWidth, newHeight); 513 gVideoWidth = newWidth; 514 gVideoHeight = newHeight; 515 err = prepareEncoder(mainDpyInfo.fps, &encoder, 516 &encoderInputSurface); 517 } 518 } 519 if (err != NO_ERROR) return err; 520 521 // From here on, we must explicitly release() the encoder before it goes 522 // out of scope, or we will get an assertion failure from stagefright 523 // later on in a different thread. 524 525 526 // Draw the "info" page by rendering a frame with GLES and sending 527 // it directly to the encoder. 528 // TODO: consider displaying this as a regular layer to avoid b/11697754 529 if (gWantInfoScreen) { 530 Overlay::drawInfoPage(encoderInputSurface); 531 } 532 533 // Configure optional overlay. 534 sp<IGraphicBufferProducer> bufferProducer; 535 sp<Overlay> overlay = new Overlay(); 536 if (gWantFrameTime) { 537 // Send virtual display frames to an external texture. 538 err = overlay->start(encoderInputSurface, &bufferProducer); 539 if (err != NO_ERROR) { 540 encoder->release(); 541 return err; 542 } 543 if (gVerbose) { 544 printf("Bugreport overlay created\n"); 545 } 546 } else { 547 // Use the encoder's input surface as the virtual display surface. 548 bufferProducer = encoderInputSurface; 549 } 550 551 // Configure virtual display. 552 sp<IBinder> dpy; 553 err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy); 554 if (err != NO_ERROR) { 555 encoder->release(); 556 return err; 557 } 558 559 // Configure muxer. We have to wait for the CSD blob from the encoder 560 // before we can start it. 561 sp<MediaMuxer> muxer = new MediaMuxer(fileName, 562 MediaMuxer::OUTPUT_FORMAT_MPEG_4); 563 if (gRotate) { 564 muxer->setOrientationHint(90); // TODO: does this do anything? 565 } 566 567 // Main encoder loop. 568 err = runEncoder(encoder, muxer, mainDpy, dpy, mainDpyInfo.orientation); 569 if (err != NO_ERROR) { 570 fprintf(stderr, "Encoder failed (err=%d)\n", err); 571 // fall through to cleanup 572 } 573 574 if (gVerbose) { 575 printf("Stopping encoder and muxer\n"); 576 } 577 578 // Shut everything down, starting with the producer side. 579 encoderInputSurface = NULL; 580 SurfaceComposerClient::destroyDisplay(dpy); 581 overlay->stop(); 582 encoder->stop(); 583 // If we don't stop muxer explicitly, i.e. let the destructor run, 584 // it may hang (b/11050628). 585 muxer->stop(); 586 encoder->release(); 587 588 return err; 589} 590 591/* 592 * Sends a broadcast to the media scanner to tell it about the new video. 593 * 594 * This is optional, but nice to have. 595 */ 596static status_t notifyMediaScanner(const char* fileName) { 597 // need to do allocations before the fork() 598 String8 fileUrl("file://"); 599 fileUrl.append(fileName); 600 601 const char* kCommand = "/system/bin/am"; 602 const char* const argv[] = { 603 kCommand, 604 "broadcast", 605 "-a", 606 "android.intent.action.MEDIA_SCANNER_SCAN_FILE", 607 "-d", 608 fileUrl.string(), 609 NULL 610 }; 611 if (gVerbose) { 612 printf("Executing:"); 613 for (int i = 0; argv[i] != NULL; i++) { 614 printf(" %s", argv[i]); 615 } 616 putchar('\n'); 617 } 618 619 pid_t pid = fork(); 620 if (pid < 0) { 621 int err = errno; 622 ALOGW("fork() failed: %s", strerror(err)); 623 return -err; 624 } else if (pid > 0) { 625 // parent; wait for the child, mostly to make the verbose-mode output 626 // look right, but also to check for and log failures 627 int status; 628 pid_t actualPid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); 629 if (actualPid != pid) { 630 ALOGW("waitpid(%d) returned %d (errno=%d)", pid, actualPid, errno); 631 } else if (status != 0) { 632 ALOGW("'am broadcast' exited with status=%d", status); 633 } else { 634 ALOGV("'am broadcast' exited successfully"); 635 } 636 } else { 637 if (!gVerbose) { 638 // non-verbose, suppress 'am' output 639 ALOGV("closing stdout/stderr in child"); 640 int fd = open("/dev/null", O_WRONLY); 641 if (fd >= 0) { 642 dup2(fd, STDOUT_FILENO); 643 dup2(fd, STDERR_FILENO); 644 close(fd); 645 } 646 } 647 execv(kCommand, const_cast<char* const*>(argv)); 648 ALOGE("execv(%s) failed: %s\n", kCommand, strerror(errno)); 649 exit(1); 650 } 651 return NO_ERROR; 652} 653 654/* 655 * Parses a string of the form "1280x720". 656 * 657 * Returns true on success. 658 */ 659static bool parseWidthHeight(const char* widthHeight, uint32_t* pWidth, 660 uint32_t* pHeight) { 661 long width, height; 662 char* end; 663 664 // Must specify base 10, or "0x0" gets parsed differently. 665 width = strtol(widthHeight, &end, 10); 666 if (end == widthHeight || *end != 'x' || *(end+1) == '\0') { 667 // invalid chars in width, or missing 'x', or missing height 668 return false; 669 } 670 height = strtol(end + 1, &end, 10); 671 if (*end != '\0') { 672 // invalid chars in height 673 return false; 674 } 675 676 *pWidth = width; 677 *pHeight = height; 678 return true; 679} 680 681/* 682 * Accepts a string with a bare number ("4000000") or with a single-character 683 * unit ("4m"). 684 * 685 * Returns an error if parsing fails. 686 */ 687static status_t parseValueWithUnit(const char* str, uint32_t* pValue) { 688 long value; 689 char* endptr; 690 691 value = strtol(str, &endptr, 10); 692 if (*endptr == '\0') { 693 // bare number 694 *pValue = value; 695 return NO_ERROR; 696 } else if (toupper(*endptr) == 'M' && *(endptr+1) == '\0') { 697 *pValue = value * 1000000; // check for overflow? 698 return NO_ERROR; 699 } else { 700 fprintf(stderr, "Unrecognized value: %s\n", str); 701 return UNKNOWN_ERROR; 702 } 703} 704 705/* 706 * Dumps usage on stderr. 707 */ 708static void usage() { 709 fprintf(stderr, 710 "Usage: screenrecord [options] <filename>\n" 711 "\n" 712 "Android screenrecord v%d.%d. Records the device's display to a .mp4 file.\n" 713 "\n" 714 "Options:\n" 715 "--size WIDTHxHEIGHT\n" 716 " Set the video size, e.g. \"1280x720\". Default is the device's main\n" 717 " display resolution (if supported), 1280x720 if not. For best results,\n" 718 " use a size supported by the AVC encoder.\n" 719 "--bit-rate RATE\n" 720 " Set the video bit rate, in megabits per second. Value may be specified\n" 721 " in bits or megabits, e.g. '4000000' is equivalent to '4M'. Default %dMbps.\n" 722 "--bugreport\n" 723 " Add additional information, such as a timestamp overlay, that is helpful\n" 724 " in videos captured to illustrate bugs.\n" 725 "--time-limit TIME\n" 726 " Set the maximum recording time, in seconds. Default / maximum is %d.\n" 727 "--verbose\n" 728 " Display interesting information on stdout.\n" 729 "--help\n" 730 " Show this message.\n" 731 "\n" 732 "Recording continues until Ctrl-C is hit or the time limit is reached.\n" 733 "\n", 734 kVersionMajor, kVersionMinor, gBitRate / 1000000, gTimeLimitSec 735 ); 736} 737 738/* 739 * Parses args and kicks things off. 740 */ 741int main(int argc, char* const argv[]) { 742 static const struct option longOptions[] = { 743 { "help", no_argument, NULL, 'h' }, 744 { "verbose", no_argument, NULL, 'v' }, 745 { "size", required_argument, NULL, 's' }, 746 { "bit-rate", required_argument, NULL, 'b' }, 747 { "time-limit", required_argument, NULL, 't' }, 748 { "show-device-info", no_argument, NULL, 'i' }, 749 { "show-frame-time", no_argument, NULL, 'f' }, 750 { "bugreport", no_argument, NULL, 'u' }, 751 { "rotate", no_argument, NULL, 'r' }, 752 { NULL, 0, NULL, 0 } 753 }; 754 755 while (true) { 756 int optionIndex = 0; 757 int ic = getopt_long(argc, argv, "", longOptions, &optionIndex); 758 if (ic == -1) { 759 break; 760 } 761 762 switch (ic) { 763 case 'h': 764 usage(); 765 return 0; 766 case 'v': 767 gVerbose = true; 768 break; 769 case 's': 770 if (!parseWidthHeight(optarg, &gVideoWidth, &gVideoHeight)) { 771 fprintf(stderr, "Invalid size '%s', must be width x height\n", 772 optarg); 773 return 2; 774 } 775 if (gVideoWidth == 0 || gVideoHeight == 0) { 776 fprintf(stderr, 777 "Invalid size %ux%u, width and height may not be zero\n", 778 gVideoWidth, gVideoHeight); 779 return 2; 780 } 781 gSizeSpecified = true; 782 break; 783 case 'b': 784 if (parseValueWithUnit(optarg, &gBitRate) != NO_ERROR) { 785 return 2; 786 } 787 if (gBitRate < kMinBitRate || gBitRate > kMaxBitRate) { 788 fprintf(stderr, 789 "Bit rate %dbps outside acceptable range [%d,%d]\n", 790 gBitRate, kMinBitRate, kMaxBitRate); 791 return 2; 792 } 793 break; 794 case 't': 795 gTimeLimitSec = atoi(optarg); 796 if (gTimeLimitSec == 0 || gTimeLimitSec > kMaxTimeLimitSec) { 797 fprintf(stderr, 798 "Time limit %ds outside acceptable range [1,%d]\n", 799 gTimeLimitSec, kMaxTimeLimitSec); 800 return 2; 801 } 802 break; 803 case 'i': 804 gWantInfoScreen = true; 805 break; 806 case 'f': 807 gWantFrameTime = true; 808 break; 809 case 'u': 810 gWantInfoScreen = true; 811 gWantFrameTime = true; 812 break; 813 case 'r': 814 // experimental feature 815 gRotate = true; 816 break; 817 default: 818 if (ic != '?') { 819 fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic); 820 } 821 return 2; 822 } 823 } 824 825 if (optind != argc - 1) { 826 fprintf(stderr, "Must specify output file (see --help).\n"); 827 return 2; 828 } 829 830 // MediaMuxer tries to create the file in the constructor, but we don't 831 // learn about the failure until muxer.start(), which returns a generic 832 // error code without logging anything. We attempt to create the file 833 // now for better diagnostics. 834 const char* fileName = argv[optind]; 835 int fd = open(fileName, O_CREAT | O_RDWR, 0644); 836 if (fd < 0) { 837 fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno)); 838 return 1; 839 } 840 close(fd); 841 842 status_t err = recordScreen(fileName); 843 if (err == NO_ERROR) { 844 // Try to notify the media scanner. Not fatal if this fails. 845 notifyMediaScanner(fileName); 846 } 847 ALOGD(err == NO_ERROR ? "success" : "failed"); 848 return (int) err; 849} 850