@ -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 & mainDp yInfo) {
const DisplayInfo & displa yInfo) {
// 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 , mainDp yInfo. viewportH ) ;
Rect layerStackRect ( displayInfo. viewportW , displa yInfo. 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 ) mainDp yInfo. viewportW ;
float displayAspect = ( float ) displayInfo. viewportH / ( float ) displa yInfo. 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 & mainDp yInfo,
static status_t prepareVirtualDisplay ( const DisplayInfo & displa yInfo,
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 , mainDp yInfo) ;
setDisplayProjection ( t , dpy , displa yInfo) ;
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 > & mainDp y,
const sp < MediaMuxer > & muxer , FILE * rawFp , const sp < IBinder > & displa y,
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 mainDp yInfo;
DisplayInfo displa yInfo;
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 ( mainDp y,
err = SurfaceComposerClient : : getDisplayInfo ( displa y,
& mainDp yInfo) ;
& displa yInfo) ;
if ( err ! = NO_ERROR ) {
if ( err ! = NO_ERROR ) {
ALOGW ( " getDisplayInfo(main) failed: %d " , err ) ;
ALOGW ( " getDisplayInfo(main) failed: %d " , err ) ;
} else if ( orientation ! = mainDp yInfo. orientation ) {
} else if ( orientation ! = displa yInfo. orientation ) {
ALOGD ( " orientation changed, now %d " , mainDp yInfo. orientation ) ;
ALOGD ( " orientation changed, now %d " , displa yInfo. orientation ) ;
SurfaceComposerClient : : Transaction t ;
SurfaceComposerClient : : Transaction t ;
setDisplayProjection ( t , virtualDpy , mainDp yInfo) ;
setDisplayProjection ( t , virtualDpy , displa yInfo) ;
t . apply ( ) ;
t . apply ( ) ;
orientation = mainDp yInfo. orientation ;
orientation = displa yInfo. 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 mainDp yInfo;
DisplayInfo displa yInfo;
err = SurfaceComposerClient : : getDisplayInfo ( mainDpy, & mainDp yInfo) ;
err = SurfaceComposerClient : : getDisplayInfo ( display, & displa yInfo) ;
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 , mainDp yInfo. fps ,
displayInfo. viewportW , displayInfo . viewportH , displa yInfo. 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 ( mainDp yInfo. viewportW ) ;
gVideoWidth = floorToEven ( displa yInfo. viewportW ) ;
}
}
if ( gVideoHeight = = 0 ) {
if ( gVideoHeight = = 0 ) {
gVideoHeight = floorToEven ( mainDp yInfo. viewportH ) ;
gVideoHeight = floorToEven ( displa yInfo. 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 ( mainDp yInfo. fps , & encoder , & encoderInputSurface ) ;
err = prepareEncoder ( displa yInfo. 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 ( mainDp yInfo. fps , & encoder ,
err = prepareEncoder ( displa yInfo. 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 ( mainDp yInfo, bufferProducer , & dpy ) ;
err = prepareVirtualDisplay ( displa yInfo, 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 , mainDp y, dpy ,
err = runEncoder ( encoder , muxer , rawFp , displa y, dpy ,
mainDp yInfo. orientation ) ;
displa yInfo. 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 ) ;