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