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