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