Add support to record external displays

Add an option --display-id to specify a pyhsical display id to record, bump version to 1.3. If we can not find the physical display from the id, error message will be printed out.
This CL depends on: ag/9366012

Bug: 136165419
Test: screenrecord --display-id 1 /data/local/tmp/test.mp4
Change-Id: I3b90d43024191d1e15563284836dc093862192e1
gugelfrei
Huihong Luo 5 years ago
parent a4be0c37e9
commit a7ccb190c5

@ -74,6 +74,7 @@ using android::MediaCodecBuffer;
using android::MediaMuxer; using android::MediaMuxer;
using android::Overlay; using android::Overlay;
using android::PersistentSurface; using android::PersistentSurface;
using android::PhysicalDisplayId;
using android::ProcessState; using android::ProcessState;
using android::Rect; using android::Rect;
using android::String8; using android::String8;
@ -116,7 +117,7 @@ static uint32_t gVideoHeight = 0;
static uint32_t gBitRate = 20000000; // 20Mbps static uint32_t gBitRate = 20000000; // 20Mbps
static uint32_t gTimeLimitSec = kMaxTimeLimitSec; static uint32_t gTimeLimitSec = kMaxTimeLimitSec;
static uint32_t gBframes = 0; static uint32_t gBframes = 0;
static PhysicalDisplayId gPhysicalDisplayId;
// Set by signal handler to stop recording. // Set by signal handler to stop recording.
static volatile bool gStopRequested = false; static volatile bool gStopRequested = false;
@ -269,14 +270,14 @@ static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec,
static status_t setDisplayProjection( static status_t setDisplayProjection(
SurfaceComposerClient::Transaction& t, SurfaceComposerClient::Transaction& t,
const sp<IBinder>& dpy, const sp<IBinder>& dpy,
const DisplayInfo& mainDpyInfo) { const DisplayInfo& displayInfo) {
// Set the region of the layer stack we're interested in, which in our // Set the region of the layer stack we're interested in, which in our
// case is "all of it". // case is "all of it".
Rect layerStackRect(mainDpyInfo.viewportW, mainDpyInfo.viewportH); Rect layerStackRect(displayInfo.viewportW, displayInfo.viewportH);
// We need to preserve the aspect ratio of the display. // We need to preserve the aspect ratio of the display.
float displayAspect = (float) mainDpyInfo.viewportH / (float) mainDpyInfo.viewportW; float displayAspect = (float) displayInfo.viewportH / (float) displayInfo.viewportW;
// Set the way we map the output onto the display surface (which will // Set the way we map the output onto the display surface (which will
@ -335,16 +336,15 @@ static status_t setDisplayProjection(
* Configures the virtual display. When this completes, virtual display * Configures the virtual display. When this completes, virtual display
* frames will start arriving from the buffer producer. * frames will start arriving from the buffer producer.
*/ */
static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo, static status_t prepareVirtualDisplay(const DisplayInfo& displayInfo,
const sp<IGraphicBufferProducer>& bufferProducer, const sp<IGraphicBufferProducer>& bufferProducer,
sp<IBinder>* pDisplayHandle) { sp<IBinder>* pDisplayHandle) {
sp<IBinder> dpy = SurfaceComposerClient::createDisplay( sp<IBinder> dpy = SurfaceComposerClient::createDisplay(
String8("ScreenRecorder"), false /*secure*/); String8("ScreenRecorder"), false /*secure*/);
SurfaceComposerClient::Transaction t; SurfaceComposerClient::Transaction t;
t.setDisplaySurface(dpy, bufferProducer); t.setDisplaySurface(dpy, bufferProducer);
setDisplayProjection(t, dpy, mainDpyInfo); setDisplayProjection(t, dpy, displayInfo);
t.setDisplayLayerStack(dpy, 0); // default stack t.setDisplayLayerStack(dpy, displayInfo.layerStack);
t.apply(); t.apply();
*pDisplayHandle = dpy; *pDisplayHandle = dpy;
@ -406,7 +406,7 @@ static status_t writeWinscopeMetadata(const Vector<int64_t>& timestamps,
* The muxer must *not* have been started before calling. * The muxer must *not* have been started before calling.
*/ */
static status_t runEncoder(const sp<MediaCodec>& encoder, static status_t runEncoder(const sp<MediaCodec>& encoder,
const sp<MediaMuxer>& muxer, FILE* rawFp, const sp<IBinder>& mainDpy, const sp<MediaMuxer>& muxer, FILE* rawFp, const sp<IBinder>& display,
const sp<IBinder>& virtualDpy, uint8_t orientation) { const sp<IBinder>& virtualDpy, uint8_t orientation) {
static int kTimeout = 250000; // be responsive on signal static int kTimeout = 250000; // be responsive on signal
status_t err; status_t err;
@ -415,7 +415,7 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
uint32_t debugNumFrames = 0; uint32_t debugNumFrames = 0;
int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC); int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC);
int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec); int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec);
DisplayInfo mainDpyInfo; DisplayInfo displayInfo;
Vector<int64_t> timestamps; Vector<int64_t> timestamps;
bool firstFrame = true; bool firstFrame = true;
@ -472,16 +472,16 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
// //
// Polling for changes is inefficient and wrong, but the // Polling for changes is inefficient and wrong, but the
// useful stuff is hard to get at without a Dalvik VM. // useful stuff is hard to get at without a Dalvik VM.
err = SurfaceComposerClient::getDisplayInfo(mainDpy, err = SurfaceComposerClient::getDisplayInfo(display,
&mainDpyInfo); &displayInfo);
if (err != NO_ERROR) { if (err != NO_ERROR) {
ALOGW("getDisplayInfo(main) failed: %d", err); ALOGW("getDisplayInfo(main) failed: %d", err);
} else if (orientation != mainDpyInfo.orientation) { } else if (orientation != displayInfo.orientation) {
ALOGD("orientation changed, now %d", mainDpyInfo.orientation); ALOGD("orientation changed, now %d", displayInfo.orientation);
SurfaceComposerClient::Transaction t; SurfaceComposerClient::Transaction t;
setDisplayProjection(t, virtualDpy, mainDpyInfo); setDisplayProjection(t, virtualDpy, displayInfo);
t.apply(); t.apply();
orientation = mainDpyInfo.orientation; orientation = displayInfo.orientation;
} }
} }
@ -661,32 +661,33 @@ static status_t recordScreen(const char* fileName) {
self->startThreadPool(); self->startThreadPool();
// Get main display parameters. // Get main display parameters.
const sp<IBinder> mainDpy = SurfaceComposerClient::getInternalDisplayToken(); sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(
if (mainDpy == nullptr) { gPhysicalDisplayId);
if (display == nullptr) {
fprintf(stderr, "ERROR: no display\n"); fprintf(stderr, "ERROR: no display\n");
return NAME_NOT_FOUND; return NAME_NOT_FOUND;
} }
DisplayInfo mainDpyInfo; DisplayInfo displayInfo;
err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo); err = SurfaceComposerClient::getDisplayInfo(display, &displayInfo);
if (err != NO_ERROR) { if (err != NO_ERROR) {
fprintf(stderr, "ERROR: unable to get display characteristics\n"); fprintf(stderr, "ERROR: unable to get display characteristics\n");
return err; return err;
} }
if (gVerbose) { if (gVerbose) {
printf("Main display is %dx%d @%.2ffps (orientation=%u)\n", printf("Display is %dx%d @%.2ffps (orientation=%u), layerStack=%u\n",
mainDpyInfo.viewportW, mainDpyInfo.viewportH, mainDpyInfo.fps, displayInfo.viewportW, displayInfo.viewportH, displayInfo.fps,
mainDpyInfo.orientation); displayInfo.orientation, displayInfo.layerStack);
fflush(stdout); fflush(stdout);
} }
// Encoder can't take odd number as config // Encoder can't take odd number as config
if (gVideoWidth == 0) { if (gVideoWidth == 0) {
gVideoWidth = floorToEven(mainDpyInfo.viewportW); gVideoWidth = floorToEven(displayInfo.viewportW);
} }
if (gVideoHeight == 0) { if (gVideoHeight == 0) {
gVideoHeight = floorToEven(mainDpyInfo.viewportH); gVideoHeight = floorToEven(displayInfo.viewportH);
} }
// Configure and start the encoder. // Configure and start the encoder.
@ -694,7 +695,7 @@ static status_t recordScreen(const char* fileName) {
sp<FrameOutput> frameOutput; sp<FrameOutput> frameOutput;
sp<IGraphicBufferProducer> encoderInputSurface; sp<IGraphicBufferProducer> encoderInputSurface;
if (gOutputFormat != FORMAT_FRAMES && gOutputFormat != FORMAT_RAW_FRAMES) { if (gOutputFormat != FORMAT_FRAMES && gOutputFormat != FORMAT_RAW_FRAMES) {
err = prepareEncoder(mainDpyInfo.fps, &encoder, &encoderInputSurface); err = prepareEncoder(displayInfo.fps, &encoder, &encoderInputSurface);
if (err != NO_ERROR && !gSizeSpecified) { if (err != NO_ERROR && !gSizeSpecified) {
// fallback is defined for landscape; swap if we're in portrait // fallback is defined for landscape; swap if we're in portrait
@ -707,7 +708,7 @@ static status_t recordScreen(const char* fileName) {
gVideoWidth, gVideoHeight, newWidth, newHeight); gVideoWidth, gVideoHeight, newWidth, newHeight);
gVideoWidth = newWidth; gVideoWidth = newWidth;
gVideoHeight = newHeight; gVideoHeight = newHeight;
err = prepareEncoder(mainDpyInfo.fps, &encoder, err = prepareEncoder(displayInfo.fps, &encoder,
&encoderInputSurface); &encoderInputSurface);
} }
} }
@ -755,7 +756,7 @@ static status_t recordScreen(const char* fileName) {
// Configure virtual display. // Configure virtual display.
sp<IBinder> dpy; sp<IBinder> dpy;
err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy); err = prepareVirtualDisplay(displayInfo, bufferProducer, &dpy);
if (err != NO_ERROR) { if (err != NO_ERROR) {
if (encoder != NULL) encoder->release(); if (encoder != NULL) encoder->release();
return err; return err;
@ -838,8 +839,8 @@ static status_t recordScreen(const char* fileName) {
} }
} else { } else {
// Main encoder loop. // Main encoder loop.
err = runEncoder(encoder, muxer, rawFp, mainDpy, dpy, err = runEncoder(encoder, muxer, rawFp, display, dpy,
mainDpyInfo.orientation); displayInfo.orientation);
if (err != NO_ERROR) { if (err != NO_ERROR) {
fprintf(stderr, "Encoder failed (err=%d)\n", err); fprintf(stderr, "Encoder failed (err=%d)\n", err);
// fall through to cleanup // fall through to cleanup
@ -1005,6 +1006,9 @@ static void usage() {
" in videos captured to illustrate bugs.\n" " in videos captured to illustrate bugs.\n"
"--time-limit TIME\n" "--time-limit TIME\n"
" Set the maximum recording time, in seconds. Default / maximum is %d.\n" " Set the maximum recording time, in seconds. Default / maximum is %d.\n"
"--display-id ID\n"
" specify the physical display ID to record. Default is the primary display.\n"
" see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
"--verbose\n" "--verbose\n"
" Display interesting information on stdout.\n" " Display interesting information on stdout.\n"
"--help\n" "--help\n"
@ -1036,9 +1040,18 @@ int main(int argc, char* const argv[]) {
{ "monotonic-time", no_argument, NULL, 'm' }, { "monotonic-time", no_argument, NULL, 'm' },
{ "persistent-surface", no_argument, NULL, 'p' }, { "persistent-surface", no_argument, NULL, 'p' },
{ "bframes", required_argument, NULL, 'B' }, { "bframes", required_argument, NULL, 'B' },
{ "display-id", required_argument, NULL, 'd' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
std::optional<PhysicalDisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
if (!displayId) {
fprintf(stderr, "Failed to get token for internal display\n");
return 1;
}
gPhysicalDisplayId = *displayId;
while (true) { while (true) {
int optionIndex = 0; int optionIndex = 0;
int ic = getopt_long(argc, argv, "", longOptions, &optionIndex); int ic = getopt_long(argc, argv, "", longOptions, &optionIndex);
@ -1133,6 +1146,18 @@ int main(int argc, char* const argv[]) {
return 2; return 2;
} }
break; break;
case 'd':
gPhysicalDisplayId = atoll(optarg);
if (gPhysicalDisplayId == 0) {
fprintf(stderr, "Please specify a valid physical display id\n");
return 2;
} else if (SurfaceComposerClient::
getPhysicalDisplayToken(gPhysicalDisplayId) == nullptr) {
fprintf(stderr, "Invalid physical display id: %"
ANDROID_PHYSICAL_DISPLAY_ID_FORMAT "\n", gPhysicalDisplayId);
return 2;
}
break;
default: default:
if (ic != '?') { if (ic != '?') {
fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic); fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);

@ -18,6 +18,6 @@
#define SCREENRECORD_SCREENRECORD_H #define SCREENRECORD_SCREENRECORD_H
#define kVersionMajor 1 #define kVersionMajor 1
#define kVersionMinor 2 #define kVersionMinor 3
#endif /*SCREENRECORD_SCREENRECORD_H*/ #endif /*SCREENRECORD_SCREENRECORD_H*/

Loading…
Cancel
Save