allow requesting IDR frames through the control channel

This commit is contained in:
Andreas Lüdeke 2025-01-17 12:04:02 +01:00
commit 48b780e95b
No known key found for this signature in database
GPG key ID: 8904863CEE2E3CA0
5 changed files with 34 additions and 0 deletions

View file

@ -156,6 +156,7 @@ public final class Server {
if (controller != null) { if (controller != null) {
controller.setSurfaceCapture(surfaceCapture); controller.setSurfaceCapture(surfaceCapture);
controller.setSurfaceEncoder(surfaceEncoder);
} }
} }

View file

@ -25,6 +25,7 @@ public final class ControlMessage {
public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 15; public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 15;
public static final int TYPE_START_APP = 16; public static final int TYPE_START_APP = 16;
public static final int TYPE_RESET_VIDEO = 17; public static final int TYPE_RESET_VIDEO = 17;
public static final int TYPE_REQUEST_IDR = 18;
public static final long SEQUENCE_INVALID = 0; public static final long SEQUENCE_INVALID = 0;

View file

@ -56,6 +56,8 @@ public class ControlMessageReader {
return parseUhidDestroy(); return parseUhidDestroy();
case ControlMessage.TYPE_START_APP: case ControlMessage.TYPE_START_APP:
return parseStartApp(); return parseStartApp();
case ControlMessage.TYPE_REQUEST_IDR:
return ControlMessage.createEmpty(type);
default: default:
throw new ControlProtocolException("Unknown event type: " + type); throw new ControlProtocolException("Unknown event type: " + type);
} }

View file

@ -12,6 +12,7 @@ import com.genymobile.scrcpy.device.Size;
import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.LogUtils; import com.genymobile.scrcpy.util.LogUtils;
import com.genymobile.scrcpy.video.SurfaceCapture; import com.genymobile.scrcpy.video.SurfaceCapture;
import com.genymobile.scrcpy.video.SurfaceEncoder;
import com.genymobile.scrcpy.video.VirtualDisplayListener; import com.genymobile.scrcpy.video.VirtualDisplayListener;
import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.ClipboardManager;
import com.genymobile.scrcpy.wrappers.InputManager; import com.genymobile.scrcpy.wrappers.InputManager;
@ -99,6 +100,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
// Used for resetting video encoding on RESET_VIDEO message // Used for resetting video encoding on RESET_VIDEO message
private SurfaceCapture surfaceCapture; private SurfaceCapture surfaceCapture;
private SurfaceEncoder surfaceEncoder;
public Controller(ControlChannel controlChannel, CleanUp cleanUp, Options options) { public Controller(ControlChannel controlChannel, CleanUp cleanUp, Options options) {
this.displayId = options.getDisplayId(); this.displayId = options.getDisplayId();
@ -154,6 +156,10 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
this.surfaceCapture = surfaceCapture; this.surfaceCapture = surfaceCapture;
} }
public void setSurfaceEncoder(SurfaceEncoder surfaceEncoder) {
this.surfaceEncoder = surfaceEncoder;
}
private UhidManager getUhidManager() { private UhidManager getUhidManager() {
if (uhidManager == null) { if (uhidManager == null) {
uhidManager = new UhidManager(sender); uhidManager = new UhidManager(sender);
@ -307,6 +313,9 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
case ControlMessage.TYPE_RESET_VIDEO: case ControlMessage.TYPE_RESET_VIDEO:
resetVideo(); resetVideo();
break; break;
case ControlMessage.TYPE_REQUEST_IDR:
requestIdr();
break;
default: default:
// do nothing // do nothing
} }
@ -728,4 +737,11 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
surfaceCapture.requestInvalidate(); surfaceCapture.requestInvalidate();
} }
} }
public void requestIdr() {
if (surfaceEncoder != null) {
Ln.i("Requesting IDR");
surfaceEncoder.requestIdr();
}
}
} }

View file

@ -20,6 +20,7 @@ import android.os.Build;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
import android.view.Surface; import android.view.Surface;
import android.os.Bundle;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -52,6 +53,8 @@ public class SurfaceEncoder implements AsyncProcessor {
private final CaptureReset reset = new CaptureReset(); private final CaptureReset reset = new CaptureReset();
private final AtomicBoolean idrRequested = new AtomicBoolean();
public SurfaceEncoder(SurfaceCapture capture, Streamer streamer, Options options) { public SurfaceEncoder(SurfaceCapture capture, Streamer streamer, Options options) {
this.capture = capture; this.capture = capture;
this.streamer = streamer; this.streamer = streamer;
@ -199,6 +202,13 @@ public class SurfaceEncoder implements AsyncProcessor {
boolean eos; boolean eos;
do { do {
if (idrRequested.get()) {
Bundle bundle = new Bundle();
bundle.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
codec.setParameters(bundle);
idrRequested.set(false);
}
int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1); int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);
try { try {
eos = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0; eos = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
@ -323,4 +333,8 @@ public class SurfaceEncoder implements AsyncProcessor {
thread.join(); thread.join();
} }
} }
public void requestIdr() {
idrRequested.set(true);
}
} }