Skip to content

Commit 031b55b

Browse files
committed
io.appium.settings: recording: Handle orientation and make resolution dynamic
Signed-off-by: sirmordred <[email protected]>
1 parent b2f96c7 commit 031b55b

File tree

2 files changed

+44
-30
lines changed

2 files changed

+44
-30
lines changed

app/src/main/java/io/appium/settings/RecorderService.java

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -89,29 +89,25 @@ private void startScreenRecord(MediaProjectionManager mediaProjectionManager,
8989
if (projection != null) {
9090
Log.v(TAG, "projection different from null");
9191
final DisplayMetrics metrics = getResources().getDisplayMetrics();
92-
int width = metrics.widthPixels;
93-
int height = metrics.heightPixels;
94-
float scale;
95-
final float scaleX;
96-
final float scaleY;
97-
if (width > height) {
98-
// Horizontal
99-
scaleX = width / 1920f;
100-
scaleY = height / 1080f;
101-
} else {
102-
// Vertical
103-
scaleX = width / 1080f;
104-
scaleY = height / 1920f;
92+
int rawWidth = metrics.widthPixels;
93+
int rawHeight = metrics.heightPixels;
94+
boolean isRotated = false;
95+
if (rawWidth > rawHeight) {
96+
// landscape mode
97+
isRotated = true;
98+
/* TODO we need to rotate frames that comes from virtual screen before writing to file via muxer,
99+
* for handling landscape mode properly, rotateYuvImages somehow fast and reliable
100+
* for now, just flip width and height to fake and force portrait mode instead of landscape
101+
* */
102+
rawWidth = metrics.heightPixels;
103+
rawHeight = metrics.widthPixels;
105104
}
106-
scale = Math.max(scaleX, scaleY);
107-
width = (int)(width / scale);
108-
height = (int)(height / scale);
109105
Log.v(TAG, String.format("startRecording:(%d,%d)(%d,%d)",
110-
metrics.widthPixels, metrics.heightPixels, width, height));
106+
metrics.widthPixels, metrics.heightPixels, rawWidth, rawHeight));
111107
String outputFilePath = intent.getStringExtra(ACTION_RECORDING_FILENAME);
112108
if (outputFilePath != null) {
113-
recorderThread = new RecorderThread(projection, outputFilePath, width, height,
114-
calcBitRate(width, height));
109+
recorderThread = new RecorderThread(projection, outputFilePath,
110+
rawWidth, rawHeight, isRotated);
115111
recorderThread.startRecording();
116112
} else {
117113
Log.i(TAG, "outputFilePath == null");
@@ -120,13 +116,6 @@ private void startScreenRecord(MediaProjectionManager mediaProjectionManager,
120116
}
121117
}
122118

123-
protected int calcBitRate(int width, int height) {
124-
final int bitrate = (int) (RecorderConstant.BITRATE_MULTIPLIER *
125-
RecorderConstant.VIDEO_CODEC_FRAME_RATE * width * height);
126-
Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f));
127-
return bitrate;
128-
}
129-
130119
/**
131120
* stop screen recording
132121
*/

app/src/main/java/io/appium/settings/RecorderThread.java

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)