@ -37,6 +37,7 @@
# include <binder/IPCThreadState.h>
# include <binder/IPCThreadState.h>
# include <utils/Errors.h>
# include <utils/Errors.h>
# include <utils/SystemClock.h>
# include <utils/Timers.h>
# include <utils/Timers.h>
# include <utils/Trace.h>
# include <utils/Trace.h>
@ -95,6 +96,8 @@ static const uint32_t kMaxTimeLimitSec = 180; // 3 minutes
static const uint32_t kFallbackWidth = 1280 ; // 720p
static const uint32_t kFallbackWidth = 1280 ; // 720p
static const uint32_t kFallbackHeight = 720 ;
static const uint32_t kFallbackHeight = 720 ;
static const char * kMimeTypeAvc = " video/avc " ;
static const char * kMimeTypeAvc = " video/avc " ;
static const char * kMimeTypeApplicationOctetstream = " application/octet-stream " ;
static const char * kWinscopeMagicString = " #VV1NSC0PET1ME!# " ;
// Command-line parameters.
// Command-line parameters.
static bool gVerbose = false ; // chatty on stdout
static bool gVerbose = false ; // chatty on stdout
@ -349,6 +352,50 @@ static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo,
return NO_ERROR ;
return NO_ERROR ;
}
}
/*
* Writes an unsigned integer byte - by - byte in little endian order regardless
* of the platform endianness .
*/
template < typename UINT >
static void writeValueLE ( UINT value , uint8_t * buffer ) {
for ( int i = 0 ; i < sizeof ( UINT ) ; + + i ) {
buffer [ i ] = static_cast < uint8_t > ( value ) ;
value > > = 8 ;
}
}
/*
* Saves frames presentation time relative to the elapsed realtime clock in microseconds
* preceded by a Winscope magic string and frame count to a metadata track .
* This metadata is used by the Winscope tool to sync video with SurfaceFlinger
* and WindowManager traces .
*
* The metadata is written as a binary array as follows :
* - winscope magic string ( kWinscopeMagicString constant ) , without trailing null char ,
* - the number of recorded frames ( as little endian uint32 ) ,
* - for every frame its presentation time relative to the elapsed realtime clock in microseconds
* ( as little endian uint64 ) .
*/
static status_t writeWinscopeMetadata ( const Vector < int64_t > & timestamps ,
const ssize_t metaTrackIdx , const sp < MediaMuxer > & muxer ) {
ALOGV ( " Writing metadata " ) ;
int64_t systemTimeToElapsedTimeOffsetMicros = ( android : : elapsedRealtimeNano ( )
- systemTime ( SYSTEM_TIME_MONOTONIC ) ) / 1000 ;
sp < ABuffer > buffer = new ABuffer ( timestamps . size ( ) * sizeof ( int64_t )
+ sizeof ( uint32_t ) + strlen ( kWinscopeMagicString ) ) ;
uint8_t * pos = buffer - > data ( ) ;
strcpy ( reinterpret_cast < char * > ( pos ) , kWinscopeMagicString ) ;
pos + = strlen ( kWinscopeMagicString ) ;
writeValueLE < uint32_t > ( timestamps . size ( ) , pos ) ;
pos + = sizeof ( uint32_t ) ;
for ( size_t idx = 0 ; idx < timestamps . size ( ) ; + + idx ) {
writeValueLE < uint64_t > ( static_cast < uint64_t > ( timestamps [ idx ]
+ systemTimeToElapsedTimeOffsetMicros ) , pos ) ;
pos + = sizeof ( uint64_t ) ;
}
return muxer - > writeSampleData ( buffer , metaTrackIdx , timestamps [ 0 ] , 0 ) ;
}
/*
/*
* Runs the MediaCodec encoder , sending the output to the MediaMuxer . The
* Runs the MediaCodec encoder , sending the output to the MediaMuxer . The
* input frames are coming from the virtual display as fast as SurfaceFlinger
* input frames are coming from the virtual display as fast as SurfaceFlinger
@ -364,10 +411,12 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
static int kTimeout = 250000 ; // be responsive on signal
static int kTimeout = 250000 ; // be responsive on signal
status_t err ;
status_t err ;
ssize_t trackIdx = - 1 ;
ssize_t trackIdx = - 1 ;
ssize_t metaTrackIdx = - 1 ;
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 mainDpyInfo ;
Vector < int64_t > timestamps ;
assert ( ( rawFp = = NULL & & muxer ! = NULL ) | | ( rawFp ! = NULL & & muxer = = NULL ) ) ;
assert ( ( rawFp = = NULL & & muxer ! = NULL ) | | ( rawFp ! = NULL & & muxer = = NULL ) ) ;
@ -465,6 +514,9 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
" Failed writing data to muxer (err=%d) \n " , err ) ;
" Failed writing data to muxer (err=%d) \n " , err ) ;
return err ;
return err ;
}
}
if ( gOutputFormat = = FORMAT_MP4 ) {
timestamps . add ( ptsUsec ) ;
}
}
}
debugNumFrames + + ;
debugNumFrames + + ;
}
}
@ -491,6 +543,11 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
encoder - > getOutputFormat ( & newFormat ) ;
encoder - > getOutputFormat ( & newFormat ) ;
if ( muxer ! = NULL ) {
if ( muxer ! = NULL ) {
trackIdx = muxer - > addTrack ( newFormat ) ;
trackIdx = muxer - > addTrack ( newFormat ) ;
if ( gOutputFormat = = FORMAT_MP4 ) {
sp < AMessage > metaFormat = new AMessage ;
metaFormat - > setString ( KEY_MIME , kMimeTypeApplicationOctetstream ) ;
metaTrackIdx = muxer - > addTrack ( metaFormat ) ;
}
ALOGV ( " Starting muxer " ) ;
ALOGV ( " Starting muxer " ) ;
err = muxer - > start ( ) ;
err = muxer - > start ( ) ;
if ( err ! = NO_ERROR ) {
if ( err ! = NO_ERROR ) {
@ -527,6 +584,13 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
systemTime ( CLOCK_MONOTONIC ) - startWhenNsec ) ) ;
systemTime ( CLOCK_MONOTONIC ) - startWhenNsec ) ) ;
fflush ( stdout ) ;
fflush ( stdout ) ;
}
}
if ( metaTrackIdx > = 0 & & ! timestamps . isEmpty ( ) ) {
err = writeWinscopeMetadata ( timestamps , metaTrackIdx , muxer ) ;
if ( err ! = NO_ERROR ) {
fprintf ( stderr , " Failed writing metadata to muxer (err=%d) \n " , err ) ;
return err ;
}
}
return NO_ERROR ;
return NO_ERROR ;
}
}