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