screenrecord.cpp revision 46052913f307b1561f1661bb776fa29c0775758c
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 25#include <gui/Surface.h> 26#include <gui/SurfaceComposerClient.h> 27#include <gui/ISurfaceComposer.h> 28#include <ui/DisplayInfo.h> 29#include <media/openmax/OMX_IVCommon.h> 30#include <media/stagefright/foundation/ABuffer.h> 31#include <media/stagefright/foundation/ADebug.h> 32#include <media/stagefright/foundation/AMessage.h> 33#include <media/stagefright/MediaCodec.h> 34#include <media/stagefright/MediaErrors.h> 35#include <media/stagefright/MediaMuxer.h> 36#include <media/ICrypto.h> 37 38#include <stdio.h> 39#include <fcntl.h> 40#include <signal.h> 41#include <getopt.h> 42 43using namespace android; 44 45// Command-line parameters. 46static bool gVerbose = false; // chatty on stdout 47static bool gRotate = false; // rotate 90 degrees 48static bool gSizeSpecified = false; // was size explicitly requested? 49static uint32_t gVideoWidth = 0; // default width+height 50static uint32_t gVideoHeight = 0; 51static uint32_t gBitRate = 4000000; // 4Mbps 52 53// Set by signal handler to stop recording. 54static bool gStopRequested; 55 56// Previous signal handler state, restored after first hit. 57static struct sigaction gOrigSigactionINT; 58static struct sigaction gOrigSigactionHUP; 59 60static const uint32_t kMinBitRate = 100000; // 0.1Mbps 61static const uint32_t kMaxBitRate = 100 * 1000000; // 100Mbps 62 63/* 64 * Catch keyboard interrupt signals. On receipt, the "stop requested" 65 * flag is raised, and the original handler is restored (so that, if 66 * we get stuck finishing, a second Ctrl-C will kill the process). 67 */ 68static void signalCatcher(int signum) 69{ 70 gStopRequested = true; 71 switch (signum) { 72 case SIGINT: 73 sigaction(SIGINT, &gOrigSigactionINT, NULL); 74 break; 75 case SIGHUP: 76 sigaction(SIGHUP, &gOrigSigactionHUP, NULL); 77 break; 78 default: 79 abort(); 80 break; 81 } 82} 83 84/* 85 * Configures signal handlers. The previous handlers are saved. 86 * 87 * If the command is run from an interactive adb shell, we get SIGINT 88 * when Ctrl-C is hit. If we're run from the host, the local adb process 89 * gets the signal, and we get a SIGHUP when the terminal disconnects. 90 */ 91static status_t configureSignals() 92{ 93 struct sigaction act; 94 memset(&act, 0, sizeof(act)); 95 act.sa_handler = signalCatcher; 96 if (sigaction(SIGINT, &act, &gOrigSigactionINT) != 0) { 97 status_t err = -errno; 98 fprintf(stderr, "Unable to configure SIGINT handler: %s\n", 99 strerror(errno)); 100 return err; 101 } 102 if (sigaction(SIGHUP, &act, &gOrigSigactionHUP) != 0) { 103 status_t err = -errno; 104 fprintf(stderr, "Unable to configure SIGHUP handler: %s\n", 105 strerror(errno)); 106 return err; 107 } 108 return NO_ERROR; 109} 110 111/* 112 * Returns "true" if the device is rotated 90 degrees. 113 */ 114static bool isDeviceRotated(int orientation) { 115 return orientation != DISPLAY_ORIENTATION_0 && 116 orientation != DISPLAY_ORIENTATION_180; 117} 118 119/* 120 * Configures and starts the MediaCodec encoder. Obtains an input surface 121 * from the codec. 122 */ 123static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec, 124 sp<IGraphicBufferProducer>* pBufferProducer) { 125 status_t err; 126 127 if (gVerbose) { 128 printf("Configuring recorder for %dx%d video at %.2fMbps\n", 129 gVideoWidth, gVideoHeight, gBitRate / 1000000.0); 130 } 131 132 sp<AMessage> format = new AMessage; 133 format->setInt32("width", gVideoWidth); 134 format->setInt32("height", gVideoHeight); 135 format->setString("mime", "video/avc"); 136 format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque); 137 format->setInt32("bitrate", gBitRate); 138 format->setFloat("frame-rate", displayFps); 139 format->setInt32("i-frame-interval", 10); 140 141 /// MediaCodec 142 sp<ALooper> looper = new ALooper; 143 looper->setName("screenrecord_looper"); 144 looper->start(); 145 ALOGV("Creating codec"); 146 sp<MediaCodec> codec = MediaCodec::CreateByType(looper, "video/avc", true); 147 err = codec->configure(format, NULL, NULL, 148 MediaCodec::CONFIGURE_FLAG_ENCODE); 149 if (err != NO_ERROR) { 150 fprintf(stderr, "ERROR: unable to configure codec (err=%d)\n", err); 151 return err; 152 } 153 154 ALOGV("Creating buffer producer"); 155 sp<IGraphicBufferProducer> bufferProducer; 156 err = codec->createInputSurface(&bufferProducer); 157 if (err != NO_ERROR) { 158 fprintf(stderr, 159 "ERROR: unable to create encoder input surface (err=%d)\n", err); 160 return err; 161 } 162 163 ALOGV("Starting codec"); 164 err = codec->start(); 165 if (err != NO_ERROR) { 166 fprintf(stderr, "ERROR: unable to start codec (err=%d)\n", err); 167 return err; 168 } 169 170 ALOGV("Codec prepared"); 171 *pCodec = codec; 172 *pBufferProducer = bufferProducer; 173 return 0; 174} 175 176/* 177 * Configures the virtual display. When this completes, virtual display 178 * frames will start being sent to the encoder's surface. 179 */ 180static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo, 181 const sp<IGraphicBufferProducer>& bufferProducer, 182 sp<IBinder>* pDisplayHandle) { 183 status_t err; 184 185 // Set the region of the layer stack we're interested in, which in our 186 // case is "all of it". If the app is rotated (so that the width of the 187 // app is based on the height of the display), reverse width/height. 188 bool deviceRotated = isDeviceRotated(mainDpyInfo.orientation); 189 uint32_t sourceWidth, sourceHeight; 190 if (!deviceRotated) { 191 sourceWidth = mainDpyInfo.w; 192 sourceHeight = mainDpyInfo.h; 193 } else { 194 ALOGV("using rotated width/height"); 195 sourceHeight = mainDpyInfo.w; 196 sourceWidth = mainDpyInfo.h; 197 } 198 Rect layerStackRect(sourceWidth, sourceHeight); 199 200 // We need to preserve the aspect ratio of the display. 201 float displayAspect = (float) sourceHeight / (float) sourceWidth; 202 203 204 // Set the way we map the output onto the display surface (which will 205 // be e.g. 1280x720 for a 720p video). The rect is interpreted 206 // post-rotation, so if the display is rotated 90 degrees we need to 207 // "pre-rotate" it by flipping width/height, so that the orientation 208 // adjustment changes it back. 209 // 210 // We might want to encode a portrait display as landscape to use more 211 // of the screen real estate. (If players respect a 90-degree rotation 212 // hint, we can essentially get a 720x1280 video instead of 1280x720.) 213 // In that case, we swap the configured video width/height and then 214 // supply a rotation value to the display projection. 215 uint32_t videoWidth, videoHeight; 216 uint32_t outWidth, outHeight; 217 if (!gRotate) { 218 videoWidth = gVideoWidth; 219 videoHeight = gVideoHeight; 220 } else { 221 videoWidth = gVideoHeight; 222 videoHeight = gVideoWidth; 223 } 224 if (videoHeight > (uint32_t)(videoWidth * displayAspect)) { 225 // limited by narrow width; reduce height 226 outWidth = videoWidth; 227 outHeight = (uint32_t)(videoWidth * displayAspect); 228 } else { 229 // limited by short height; restrict width 230 outHeight = videoHeight; 231 outWidth = (uint32_t)(videoHeight / displayAspect); 232 } 233 uint32_t offX, offY; 234 offX = (videoWidth - outWidth) / 2; 235 offY = (videoHeight - outHeight) / 2; 236 Rect displayRect(offX, offY, offX + outWidth, offY + outHeight); 237 238 if (gVerbose) { 239 if (gRotate) { 240 printf("Rotated content area is %ux%u at offset x=%d y=%d\n", 241 outHeight, outWidth, offY, offX); 242 } else { 243 printf("Content area is %ux%u at offset x=%d y=%d\n", 244 outWidth, outHeight, offX, offY); 245 } 246 } 247 248 249 sp<IBinder> dpy = SurfaceComposerClient::createDisplay( 250 String8("ScreenRecorder"), false /* secure */); 251 252 SurfaceComposerClient::openGlobalTransaction(); 253 SurfaceComposerClient::setDisplaySurface(dpy, bufferProducer); 254 SurfaceComposerClient::setDisplayProjection(dpy, 255 gRotate ? DISPLAY_ORIENTATION_90 : DISPLAY_ORIENTATION_0, 256 layerStackRect, displayRect); 257 SurfaceComposerClient::setDisplayLayerStack(dpy, 0); // default stack 258 SurfaceComposerClient::closeGlobalTransaction(); 259 260 *pDisplayHandle = dpy; 261 262 return NO_ERROR; 263} 264 265/* 266 * Runs the MediaCodec encoder, sending the output to the MediaMuxer. The 267 * input frames are coming from the virtual display as fast as SurfaceFlinger 268 * wants to send them. 269 * 270 * The muxer must *not* have been started before calling. 271 */ 272static status_t runEncoder(const sp<MediaCodec>& encoder, 273 const sp<MediaMuxer>& muxer) { 274 static int kTimeout = 250000; // be responsive on signal 275 status_t err; 276 ssize_t trackIdx = -1; 277 uint32_t debugNumFrames = 0; 278 time_t debugStartWhen = time(NULL); 279 280 Vector<sp<ABuffer> > buffers; 281 err = encoder->getOutputBuffers(&buffers); 282 if (err != NO_ERROR) { 283 fprintf(stderr, "Unable to get output buffers (err=%d)\n", err); 284 return err; 285 } 286 287 // This is set by the signal handler. 288 gStopRequested = false; 289 290 // Run until we're signaled. 291 while (!gStopRequested) { 292 size_t bufIndex, offset, size; 293 int64_t ptsUsec; 294 uint32_t flags; 295 ALOGV("Calling dequeueOutputBuffer"); 296 err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec, 297 &flags, kTimeout); 298 ALOGV("dequeueOutputBuffer returned %d", err); 299 switch (err) { 300 case NO_ERROR: 301 // got a buffer 302 if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) { 303 // ignore this -- we passed the CSD into MediaMuxer when 304 // we got the format change notification 305 ALOGV("Got codec config buffer (%u bytes); ignoring", size); 306 size = 0; 307 } 308 if (size != 0) { 309 ALOGV("Got data in buffer %d, size=%d, pts=%lld", 310 bufIndex, size, ptsUsec); 311 CHECK(trackIdx != -1); 312 313 // If the virtual display isn't providing us with timestamps, 314 // use the current time. 315 if (ptsUsec == 0) { 316 ptsUsec = systemTime(SYSTEM_TIME_MONOTONIC) / 1000; 317 } 318 319 // The MediaMuxer docs are unclear, but it appears that we 320 // need to pass either the full set of BufferInfo flags, or 321 // (flags & BUFFER_FLAG_SYNCFRAME). 322 err = muxer->writeSampleData(buffers[bufIndex], trackIdx, 323 ptsUsec, flags); 324 if (err != NO_ERROR) { 325 fprintf(stderr, "Failed writing data to muxer (err=%d)\n", 326 err); 327 return err; 328 } 329 debugNumFrames++; 330 } 331 err = encoder->releaseOutputBuffer(bufIndex); 332 if (err != NO_ERROR) { 333 fprintf(stderr, "Unable to release output buffer (err=%d)\n", 334 err); 335 return err; 336 } 337 if ((flags & MediaCodec::BUFFER_FLAG_EOS) != 0) { 338 // Not expecting EOS from SurfaceFlinger. Go with it. 339 ALOGD("Received end-of-stream"); 340 gStopRequested = false; 341 } 342 break; 343 case -EAGAIN: // INFO_TRY_AGAIN_LATER 344 // not expected with infinite timeout 345 ALOGV("Got -EAGAIN, looping"); 346 break; 347 case INFO_FORMAT_CHANGED: // INFO_OUTPUT_FORMAT_CHANGED 348 { 349 // format includes CSD, which we must provide to muxer 350 ALOGV("Encoder format changed"); 351 sp<AMessage> newFormat; 352 encoder->getOutputFormat(&newFormat); 353 trackIdx = muxer->addTrack(newFormat); 354 ALOGV("Starting muxer"); 355 err = muxer->start(); 356 if (err != NO_ERROR) { 357 fprintf(stderr, "Unable to start muxer (err=%d)\n", err); 358 return err; 359 } 360 } 361 break; 362 case INFO_OUTPUT_BUFFERS_CHANGED: // INFO_OUTPUT_BUFFERS_CHANGED 363 // not expected for an encoder; handle it anyway 364 ALOGV("Encoder buffers changed"); 365 err = encoder->getOutputBuffers(&buffers); 366 if (err != NO_ERROR) { 367 fprintf(stderr, 368 "Unable to get new output buffers (err=%d)\n", err); 369 } 370 break; 371 default: 372 ALOGW("Got weird result %d from dequeueOutputBuffer", err); 373 return err; 374 } 375 } 376 377 ALOGV("Encoder stopping (req=%d)", gStopRequested); 378 if (gVerbose) { 379 printf("Encoder stopping; recorded %u frames in %ld seconds\n", 380 debugNumFrames, time(NULL) - debugStartWhen); 381 } 382 return NO_ERROR; 383} 384 385/* 386 * Main "do work" method. 387 * 388 * Configures codec, muxer, and virtual display, then starts moving bits 389 * around. 390 */ 391static status_t recordScreen(const char* fileName) { 392 status_t err; 393 394 // Configure signal handler. 395 err = configureSignals(); 396 if (err != NO_ERROR) return err; 397 398 // Start Binder thread pool. MediaCodec needs to be able to receive 399 // messages from mediaserver. 400 sp<ProcessState> self = ProcessState::self(); 401 self->startThreadPool(); 402 403 // Get main display parameters. 404 sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay( 405 ISurfaceComposer::eDisplayIdMain); 406 DisplayInfo mainDpyInfo; 407 err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo); 408 if (err != NO_ERROR) { 409 fprintf(stderr, "ERROR: unable to get display characteristics\n"); 410 return err; 411 } 412 if (gVerbose) { 413 printf("Main display is %dx%d @%.2ffps (orientation=%u)\n", 414 mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps, 415 mainDpyInfo.orientation); 416 } 417 418 bool rotated = isDeviceRotated(mainDpyInfo.orientation); 419 if (gVideoWidth == 0) { 420 gVideoWidth = rotated ? mainDpyInfo.h : mainDpyInfo.w; 421 } 422 if (gVideoHeight == 0) { 423 gVideoHeight = rotated ? mainDpyInfo.w : mainDpyInfo.h; 424 } 425 426 // Configure and start the encoder. 427 sp<MediaCodec> encoder; 428 sp<IGraphicBufferProducer> bufferProducer; 429 err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer); 430 if (err != NO_ERROR && !gSizeSpecified) { 431 ALOGV("Retrying with 720p"); 432 if (gVideoWidth != 1280 && gVideoHeight != 720) { 433 fprintf(stderr, "WARNING: failed at %dx%d, retrying at 720p\n", 434 gVideoWidth, gVideoHeight); 435 gVideoWidth = 1280; 436 gVideoHeight = 720; 437 err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer); 438 } 439 } 440 if (err != NO_ERROR) { 441 return err; 442 } 443 444 // Configure virtual display. 445 sp<IBinder> dpy; 446 err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy); 447 if (err != NO_ERROR) return err; 448 449 // Configure, but do not start, muxer. 450 sp<MediaMuxer> muxer = new MediaMuxer(fileName, 451 MediaMuxer::OUTPUT_FORMAT_MPEG_4); 452 if (gRotate) { 453 muxer->setOrientationHint(90); 454 } 455 456 // Main encoder loop. 457 err = runEncoder(encoder, muxer); 458 if (err != NO_ERROR) return err; 459 460 if (gVerbose) { 461 printf("Stopping encoder and muxer\n"); 462 } 463 464 // Shut everything down. 465 // 466 // The virtual display will continue to produce frames until "dpy" 467 // goes out of scope (and something causes the Binder traffic to transmit; 468 // can be forced with IPCThreadState::self()->flushCommands()). This 469 // could cause SurfaceFlinger to get stuck trying to feed us, so we want 470 // to set a NULL Surface to make the virtual display "dormant". 471 bufferProducer = NULL; 472 SurfaceComposerClient::openGlobalTransaction(); 473 SurfaceComposerClient::setDisplaySurface(dpy, bufferProducer); 474 SurfaceComposerClient::closeGlobalTransaction(); 475 476 encoder->stop(); 477 muxer->stop(); 478 encoder->release(); 479 480 return 0; 481} 482 483/* 484 * Parses a string of the form "1280x720". 485 * 486 * Returns true on success. 487 */ 488static bool parseWidthHeight(const char* widthHeight, uint32_t* pWidth, 489 uint32_t* pHeight) { 490 long width, height; 491 char* end; 492 493 // Must specify base 10, or "0x0" gets parsed differently. 494 width = strtol(widthHeight, &end, 10); 495 if (end == widthHeight || *end != 'x' || *(end+1) == '\0') { 496 // invalid chars in width, or missing 'x', or missing height 497 return false; 498 } 499 height = strtol(end + 1, &end, 10); 500 if (*end != '\0') { 501 // invalid chars in height 502 return false; 503 } 504 505 *pWidth = width; 506 *pHeight = height; 507 return true; 508} 509 510/* 511 * Dumps usage on stderr. 512 */ 513static void usage() { 514 fprintf(stderr, 515 "Usage: screenrecord [options] <filename>\n" 516 "\n" 517 "Records the device's display to a .mp4 file.\n" 518 "\n" 519 "Options:\n" 520 "--size WIDTHxHEIGHT\n" 521 " Set the video size, e.g. \"1280x720\". For best results, use\n" 522 " a size supported by the AVC encoder.\n" 523 "--bit-rate RATE\n" 524 " Set the video bit rate, in megabits per second. Default 4Mbps.\n" 525 "--rotate\n" 526 " Rotate the output 90 degrees.\n" 527 "--verbose\n" 528 " Display interesting information on stdout.\n" 529 "--help\n" 530 " Show this message.\n" 531 "\n" 532 "Recording continues until Ctrl-C is hit.\n" 533 "\n" 534 ); 535} 536 537/* 538 * Parses args and kicks things off. 539 */ 540int main(int argc, char* const argv[]) { 541 static const struct option longOptions[] = { 542 { "help", no_argument, NULL, 'h' }, 543 { "verbose", no_argument, NULL, 'v' }, 544 { "size", required_argument, NULL, 's' }, 545 { "bit-rate", required_argument, NULL, 'b' }, 546 { "rotate", no_argument, NULL, 'r' }, 547 { NULL, 0, NULL, 0 } 548 }; 549 550 while (true) { 551 int optionIndex = 0; 552 int ic = getopt_long(argc, argv, "", longOptions, &optionIndex); 553 if (ic == -1) { 554 break; 555 } 556 557 switch (ic) { 558 case 'h': 559 usage(); 560 return 0; 561 case 'v': 562 gVerbose = true; 563 break; 564 case 's': 565 if (!parseWidthHeight(optarg, &gVideoWidth, &gVideoHeight)) { 566 fprintf(stderr, "Invalid size '%s', must be width x height\n", 567 optarg); 568 return 2; 569 } 570 if (gVideoWidth == 0 || gVideoHeight == 0) { 571 fprintf(stderr, 572 "Invalid size %ux%u, width and height may not be zero\n", 573 gVideoWidth, gVideoHeight); 574 return 2; 575 } 576 gSizeSpecified = true; 577 break; 578 case 'b': 579 gBitRate = atoi(optarg); 580 if (gBitRate < kMinBitRate || gBitRate > kMaxBitRate) { 581 fprintf(stderr, 582 "Bit rate %dbps outside acceptable range [%d,%d]\n", 583 gBitRate, kMinBitRate, kMaxBitRate); 584 return 2; 585 } 586 break; 587 case 'r': 588 gRotate = true; 589 break; 590 default: 591 if (ic != '?') { 592 fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic); 593 } 594 return 2; 595 } 596 } 597 598 if (optind != argc - 1) { 599 fprintf(stderr, "Must specify output file (see --help).\n"); 600 return 2; 601 } 602 603 // MediaMuxer tries to create the file in the constructor, but we don't 604 // learn about the failure until muxer.start(), which returns a generic 605 // error code without logging anything. We attempt to create the file 606 // now for better diagnostics. 607 const char* fileName = argv[optind]; 608 int fd = open(fileName, O_CREAT | O_RDWR, 0644); 609 if (fd < 0) { 610 fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno)); 611 return 1; 612 } 613 close(fd); 614 615 status_t err = recordScreen(fileName); 616 ALOGD(err == NO_ERROR ? "success" : "failed"); 617 return (int) err; 618} 619