Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 38 additions & 19 deletions server/src/main/java/com/genymobile/scrcpy/opengl/OpenGLRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -226,33 +226,52 @@ private void render(Size outputSize) {
public void stopAndRelease() {
final Semaphore sem = new Semaphore(0);

handler.post(() -> {
stopped = true;
surfaceTexture.setOnFrameAvailableListener(null, handler);

filter.release();
boolean posted = false;
if (handlerThread != null
&& handlerThread.isAlive()
&& handlerThread.getLooper() != null
&& handlerThread.getLooper().getThread().isAlive()) {

posted = handler.post(() -> {
try {
performRelease();
} finally {
sem.release();
}
});
}

int[] textures = {textureId};
GLES20.glDeleteTextures(1, textures, 0);
GLUtils.checkGlError();

EGL14.eglDestroySurface(eglDisplay, eglSurface);
EGL14.eglDestroyContext(eglDisplay, eglContext);
EGL14.eglTerminate(eglDisplay);
eglDisplay = EGL14.EGL_NO_DISPLAY;
eglContext = EGL14.EGL_NO_CONTEXT;
eglSurface = EGL14.EGL_NO_SURFACE;
surfaceTexture.release();
inputSurface.release();
if (!posted) {

performRelease();
sem.release();
});
}

try {
sem.acquire();
} catch (InterruptedException e) {
// Behave as if this method call was synchronous
Thread.currentThread().interrupt();
}
}

private void performRelease() {
stopped = true;
surfaceTexture.setOnFrameAvailableListener(null, handler);

filter.release();

int[] textures = {textureId};
GLES20.glDeleteTextures(1, textures, 0);
GLUtils.checkGlError();

EGL14.eglDestroySurface(eglDisplay, eglSurface);
EGL14.eglDestroyContext(eglDisplay, eglContext);
EGL14.eglTerminate(eglDisplay);
eglDisplay = EGL14.EGL_NO_DISPLAY;
eglContext = EGL14.EGL_NO_CONTEXT;
eglSurface = EGL14.EGL_NO_SURFACE;

surfaceTexture.release();
inputSurface.release();
}
}
124 changes: 114 additions & 10 deletions server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
import com.genymobile.scrcpy.wrappers.ServiceManager;
import com.genymobile.scrcpy.wrappers.SurfaceControl;

import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.display.VirtualDisplay;
import android.os.Build;
import android.os.IBinder;
import android.view.Surface;
import android.hardware.display.DisplayManager;

import java.io.IOException;

Expand Down Expand Up @@ -79,11 +81,7 @@ public void prepare() throws ConfigurationException {
Size displaySize = displayInfo.getSize();
displaySizeMonitor.setSessionDisplaySize(displaySize);

if (captureOrientationLock == Orientation.Lock.LockedInitial) {
// The user requested to lock the video orientation to the current orientation
captureOrientationLock = Orientation.Lock.LockedValue;
captureOrientation = Orientation.fromRotation(displayInfo.getRotation());
}


VideoFilter filter = new VideoFilter(displaySize);

Expand All @@ -92,6 +90,12 @@ public void prepare() throws ConfigurationException {
filter.addCrop(crop, transposed);
}

if (captureOrientationLock == Orientation.Lock.LockedInitial) {
// The user requested to lock the video orientation to the current orientation
captureOrientationLock = Orientation.Lock.LockedValue;
captureOrientation = Orientation.fromRotation(displayInfo.getRotation());
}

boolean locked = captureOrientationLock != Orientation.Lock.Unlocked;
filter.addOrientation(displayInfo.getRotation(), locked, captureOrientation);
filter.addAngle(angle);
Expand All @@ -104,17 +108,19 @@ public void prepare() throws ConfigurationException {
public void start(Surface surface) throws IOException {
if (display != null) {
SurfaceControl.destroyDisplay(display);
Ln.i("DESTROY_DISPLAY: " +display);
display = null;
}
if (virtualDisplay != null) {
virtualDisplay.release();
Ln.i("DESTROY_VIRTUAL_DISPLAY: " +virtualDisplay);
virtualDisplay = null;
}

Size inputSize;
if (transform != null) {
// If there is a filter, it must receive the full display content
inputSize = displayInfo.getSize();
inputSize = videoSize;
assert glRunner == null;
OpenGLFilter glFilter = new AffineOpenGLFilter(transform);
glRunner = new OpenGLRunner(glFilter);
Expand All @@ -125,6 +131,8 @@ public void start(Surface surface) throws IOException {
}

try {
int densityDpi = displayInfo.getDpi();
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
virtualDisplay = ServiceManager.getDisplayManager()
.createVirtualDisplay("scrcpy", inputSize.getWidth(), inputSize.getHeight(), displayId, surface);
Ln.d("Display: using DisplayManager API");
Expand All @@ -134,7 +142,13 @@ public void start(Surface surface) throws IOException {

Size deviceSize = displayInfo.getSize();
int layerStack = displayInfo.getLayerStack();


waitForDisplayReady(deviceSize, 500);
waitForDisplayStableResolution("scrcpy", 500, 5);

setDisplaySurface(display, surface, deviceSize.toRect(), inputSize.toRect(), layerStack);

Ln.d("Display: using SurfaceControl API");
} catch (Exception surfaceControlException) {
Ln.e("Could not create display using DisplayManager", displayManagerException);
Expand Down Expand Up @@ -196,22 +210,112 @@ public boolean setMaxSize(int newMaxSize) {
private static IBinder createDisplay() throws Exception {
// Since Android 12 (preview), secure displays could not be created with shell permissions anymore.
// On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S".
boolean secure = Build.VERSION.SDK_INT < AndroidVersions.API_30_ANDROID_11 || (Build.VERSION.SDK_INT == AndroidVersions.API_30_ANDROID_11
&& !"S".equals(Build.VERSION.CODENAME));
boolean secure = false;
return SurfaceControl.createDisplay("scrcpy", secure);
}

private static void setDisplaySurface(IBinder display, Surface surface, Rect deviceRect, Rect displayRect, int layerStack) {
public void DestroyDisplay(){
if (display != null) {
SurfaceControl.destroyDisplay(display);
display = null;
}
if (virtualDisplay != null) {
virtualDisplay.release();
virtualDisplay = null;
}
}


public void setupDisplay(Surface surface){
if (display != null) {
SurfaceControl.destroyDisplay(display);
display = null;
}

try{
display = createDisplay();
} catch (Exception e) {
throw new RuntimeException(e);
}


Rect deviceRect = displayInfo.getSize().toRect();
Rect cropRect = (crop != null) ? crop : deviceRect;
int layerStack = displayInfo.getLayerStack();

SurfaceControl.openTransaction();
try {
SurfaceControl.setDisplaySurface(display, surface);
SurfaceControl.setDisplayProjection(display, 0, deviceRect, cropRect);
SurfaceControl.setDisplayLayerStack(display, layerStack);
} finally {
SurfaceControl.closeTransaction();
}
}

private void waitForDisplayStableResolution(String displayName, long delayMs, int maxTries) {
int lastWidth = -1;
int lastHeight = -1;

for (int i = 0; i < maxTries; i++) {
DisplayInfo info = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
if (info != null) {
int w = info.getSize().getWidth();
int h = info.getSize().getWidth();
if (w == lastWidth && h == lastHeight && w > 1 && h > 1) {
Ln.d("Display stabilized: " + w + "x" + h);
return;
}
lastWidth = w;
lastHeight = h;
}

try {
Thread.sleep(delayMs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}

Ln.w("Display resolution did not stabilize within expected time");
}


private void setDisplaySurface(IBinder display, Surface surface, Rect deviceRect, Rect displayRect, int layerStack) {
SurfaceControl.openTransaction();
try {
SurfaceControl.setDisplaySurface(display, surface);
SurfaceControl.setDisplayProjection(display, 0, deviceRect, displayRect);
// SurfaceControl.setDisplayProjection(display, 0, deviceRect, displayRect);
Rect cropRect = this.crop; // options.getCrop()
SurfaceControl.setDisplayProjection(display,0,deviceRect,cropRect);

SurfaceControl.setDisplayLayerStack(display, layerStack);
} finally {
SurfaceControl.closeTransaction();
}
}

private void waitForDisplayReady(Size expectedSize, int timeoutMs) {
final int interval = 50;
int waited = 0;
while (waited < timeoutMs) {
DisplayInfo info = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
if (info != null && info.getSize().equals(expectedSize)) {
Ln.i("Display is now ready: " + info.getSize());
return;
}
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
waited += interval;
}
Ln.w("Display did not reach expected size in time: " + expectedSize);
}

@Override
public void requestInvalidate() {
invalidate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
*/
public abstract class SurfaceCapture {

public void setupDisplay(Surface surface) {
}

public interface CaptureListener {
void onInvalidated();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.genymobile.scrcpy.util.IO;
import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.LogUtils;
import com.genymobile.scrcpy.wrappers.SurfaceControl;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
Expand Down Expand Up @@ -92,6 +93,8 @@ private void streamCapture() throws IOException, ConfigurationException {
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
surface = mediaCodec.createInputSurface();

capture.setupDisplay(surface);

capture.start(surface);
captureStarted = true;

Expand Down Expand Up @@ -143,6 +146,12 @@ private void streamCapture() throws IOException, ConfigurationException {
} finally {
mediaCodec.release();
capture.release();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

}
}

Expand Down Expand Up @@ -311,6 +320,9 @@ public void start(TerminationListener listener) {

@Override
public void stop() {

capture.release();

if (thread != null) {
stopped.set(true);
reset.reset();
Expand Down