Do not expose frame rate in ScreenEncoder

The KEY_FRAME_RATE parameter value is necessary for the configuration of
the encoder, but its actual value does not impact the frame rate (only
resources used by the encoder).

Therefore, it's an internal detail and should not be exposed by the
ScreenEncoder class.
This commit is contained in:
Romain Vimont 2019-11-17 15:35:28 +01:00
parent 1b78a77962
commit fb976816f9

View file

@ -17,32 +17,27 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class ScreenEncoder implements Device.RotationListener { public class ScreenEncoder implements Device.RotationListener {
private static final int DEFAULT_FRAME_RATE = 60; // fps
private static final int DEFAULT_I_FRAME_INTERVAL = 10; // seconds private static final int DEFAULT_I_FRAME_INTERVAL = 10; // seconds
private static final int REPEAT_FRAME_DELAY_US = 100_000; // repeat after 100ms
private static final int REPEAT_FRAME_DELAY = 6; // repeat after 6 frames
private static final int MICROSECONDS_IN_ONE_SECOND = 1_000_000;
private static final int NO_PTS = -1; private static final int NO_PTS = -1;
private final AtomicBoolean rotationChanged = new AtomicBoolean(); private final AtomicBoolean rotationChanged = new AtomicBoolean();
private final ByteBuffer headerBuffer = ByteBuffer.allocate(12); private final ByteBuffer headerBuffer = ByteBuffer.allocate(12);
private int bitRate; private int bitRate;
private int frameRate;
private int iFrameInterval; private int iFrameInterval;
private boolean sendFrameMeta; private boolean sendFrameMeta;
private long ptsOrigin; private long ptsOrigin;
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int frameRate, int iFrameInterval) { public ScreenEncoder(boolean sendFrameMeta, int bitRate, int iFrameInterval) {
this.sendFrameMeta = sendFrameMeta; this.sendFrameMeta = sendFrameMeta;
this.bitRate = bitRate; this.bitRate = bitRate;
this.frameRate = frameRate;
this.iFrameInterval = iFrameInterval; this.iFrameInterval = iFrameInterval;
} }
public ScreenEncoder(boolean sendFrameMeta, int bitRate) { public ScreenEncoder(boolean sendFrameMeta, int bitRate) {
this(sendFrameMeta, bitRate, DEFAULT_FRAME_RATE, DEFAULT_I_FRAME_INTERVAL); this(sendFrameMeta, bitRate, DEFAULT_I_FRAME_INTERVAL);
} }
@Override @Override
@ -65,7 +60,7 @@ public class ScreenEncoder implements Device.RotationListener {
// <https://github.com/Genymobile/scrcpy/issues/921> // <https://github.com/Genymobile/scrcpy/issues/921>
Looper.prepareMainLooper(); Looper.prepareMainLooper();
MediaFormat format = createFormat(bitRate, frameRate, iFrameInterval); MediaFormat format = createFormat(bitRate, iFrameInterval);
device.setRotationListener(this); device.setRotationListener(this);
boolean alive; boolean alive;
try { try {
@ -148,15 +143,16 @@ public class ScreenEncoder implements Device.RotationListener {
return MediaCodec.createEncoderByType("video/avc"); return MediaCodec.createEncoderByType("video/avc");
} }
private static MediaFormat createFormat(int bitRate, int frameRate, int iFrameInterval) throws IOException { private static MediaFormat createFormat(int bitRate, int iFrameInterval) throws IOException {
MediaFormat format = new MediaFormat(); MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "video/avc"); format.setString(MediaFormat.KEY_MIME, "video/avc");
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); // must be present to configure the encoder, but does not impact the actual frame rate, which is variable
format.setInteger(MediaFormat.KEY_FRAME_RATE, 60);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);
// display the very first frame, and recover from bad quality when no new frames // display the very first frame, and recover from bad quality when no new frames
format.setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, MICROSECONDS_IN_ONE_SECOND * REPEAT_FRAME_DELAY / frameRate); // µs format.setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, REPEAT_FRAME_DELAY_US); // µs
return format; return format;
} }