@@ -50,6 +50,7 @@ public class RecorderThread implements Runnable {
5050 private int videoBitrate ;
5151 private int sampleRate ;
5252 private Thread recordingThread ;
53+ private boolean isRotated ;
5354
5455 private volatile boolean stopped = false ;
5556 private volatile boolean audioStopped ;
@@ -73,14 +74,14 @@ public void onStopped() {
7374 };
7475
7576 public RecorderThread (MediaProjection mediaProjection , String outputFilePath ,
76- int videoWidth , int videoHeight , int videoBitrate ) {
77+ int videoWidth , int videoHeight , boolean isRotated ) {
7778 this .mediaProjection = mediaProjection ;
7879 handler = new Handler ();
7980 this .outputFilePath = outputFilePath ;
8081 this .videoWidth = videoWidth ;
8182 this .videoHeight = videoHeight ;
82- this .videoBitrate = videoBitrate ;
8383 this .sampleRate = RecorderConstant .AUDIO_CODEC_SAMPLE_RATE ;
84+ this .isRotated = isRotated ;
8485 videoMime = MediaFormat .MIMETYPE_VIDEO_AVC ;
8586 }
8687
@@ -98,8 +99,20 @@ public boolean isRecordingContinue() {
9899 return stopped ;
99100 }
100101
102+ @ RequiresApi (api = Build .VERSION_CODES .LOLLIPOP )
101103 private void setupVideoCodec () throws IOException {
102- // Encoded video resolution matches virtual display.
104+ // Encoded video resolution MUST match virtual display.
105+ videoEncoder = MediaCodec .createEncoderByType (videoMime );
106+
107+ /* Clamp raw width/height of device screen with video encoder's capabilities
108+ * to avoid crash.
109+ * */
110+ MediaCodecInfo .VideoCapabilities videoEncoderCapabilities = videoEncoder .getCodecInfo ().
111+ getCapabilitiesForType (videoMime ).getVideoCapabilities ();
112+ this .videoWidth = videoEncoderCapabilities .getSupportedWidths ().clamp (this .videoWidth );
113+ this .videoHeight = videoEncoderCapabilities .getSupportedHeights ().clamp (this .videoHeight );
114+ this .videoBitrate = calcBitRate (videoWidth , videoHeight );
115+
103116 MediaFormat encoderFormat = MediaFormat .createVideoFormat (videoMime , videoWidth ,
104117 videoHeight );
105118 encoderFormat .setInteger (MediaFormat .KEY_COLOR_FORMAT ,
@@ -112,7 +125,6 @@ private void setupVideoCodec() throws IOException {
112125 encoderFormat .setInteger (MediaFormat .KEY_I_FRAME_INTERVAL ,
113126 RecorderConstant .AUDIO_CODEC_I_FRAME_INTERVAL );
114127
115- videoEncoder = MediaCodec .createEncoderByType (videoMime );
116128 videoEncoder .configure (encoderFormat , null , null ,
117129 MediaCodec .CONFIGURE_FLAG_ENCODE );
118130 surface = videoEncoder .createInputSurface ();
@@ -227,6 +239,13 @@ private long getPresentationTimeUs() {
227239 - startTimestampUs );
228240 }
229241
242+ private int calcBitRate (int width , int height ) {
243+ final int bitrate = (int ) (RecorderConstant .BITRATE_MULTIPLIER *
244+ RecorderConstant .VIDEO_CODEC_FRAME_RATE * width * height );
245+ Log .i (TAG , String .format ("bitrate=%5.2f[Mbps]" , bitrate / 1024f / 1024f ));
246+ return bitrate ;
247+ }
248+
230249 private void startMuxerIfSetUp () {
231250 if (audioTrackIndex >= 0 && videoTrackIndex >= 0 ) {
232251 muxer .start ();
@@ -247,6 +266,12 @@ public void run() {
247266 muxer = new MediaMuxer (this .outputFilePath ,
248267 MediaMuxer .OutputFormat .MUXER_OUTPUT_MPEG_4 );
249268
269+ // set output file orientation info if it is in landscape mode (default is 0==portrait)
270+ // note: this method must be run before muxer.start()
271+ if (isRotated ) {
272+ muxer .setOrientationHint (90 );
273+ }
274+
250275 startAudioRecord ();
251276
252277 MediaCodec .BufferInfo bufferInfo = new MediaCodec .BufferInfo ();
0 commit comments