Skip to content

Add device orientation information to header information #5918

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
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
Binary file added .DS_Store
Binary file not shown.
30 changes: 22 additions & 8 deletions app/src/demuxer.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,26 @@ sc_demuxer_recv_codec_id(struct sc_demuxer *demuxer, uint32_t *codec_id) {
return true;
}

static void
handle_video_session_packet(uint8_t *data){
int width = sc_read32be(data + 1);
int height = sc_read32be(data + 5);
bool isFlip = data[9] == 1;
int direction = (data[10] == (uint8_t)1 ? 2 : 0) + (data[11] == (uint8_t)1 ? 1 : 0);
LOGI("Width=%d, Height=%d, Flip=%s, Direction=%d", width, height, isFlip ? "True" : "False", direction);
}

static bool
sc_demuxer_recv_video_size(struct sc_demuxer *demuxer, uint32_t *width,
uint32_t *height) {
uint8_t data[8];
ssize_t r = net_recv_all(demuxer->socket, data, 8);
if (r < 8) {
uint8_t data[12];
ssize_t r = net_recv_all(demuxer->socket, data, 12);
if (r < 12) {
return false;
}

*width = sc_read32be(data);
*height = sc_read32be(data + 4);
*width = sc_read32be(data + 1);
*height = sc_read32be(data + 5);
handle_video_session_packet(data);
return true;
}

Expand Down Expand Up @@ -104,6 +113,11 @@ sc_demuxer_recv_packet(struct sc_demuxer *demuxer, AVPacket *packet) {
if (r < SC_PACKET_HEADER_SIZE) {
return false;
}
if(header[0] == 0xff){
handle_video_session_packet(header);
return true;
}


uint64_t pts_flags = sc_read64be(header);
uint32_t len = sc_read32be(&header[8]);
Expand Down Expand Up @@ -218,11 +232,11 @@ run_demuxer(void *data) {
LOGE("Demuxer '%s': could not open codec", demuxer->name);
goto finally_free_context;
}

if (!sc_packet_source_sinks_open(&demuxer->packet_source, codec_ctx)) {
goto finally_free_context;
}

// Config packets must be merged with the next non-config packet only for
// H.26x
bool must_merge_config_packet = raw_codec_id == SC_CODEC_ID_H264
Expand Down
3 changes: 1 addition & 2 deletions app/src/screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,7 @@ sc_screen_frame_sink_open(struct sc_frame_sink *sink,

struct sc_screen *screen = DOWNCAST(sink);

if (ctx->width <= 0 || ctx->width > 0xFFFF
|| ctx->height <= 0 || ctx->height > 0xFFFF) {
if (ctx->width <= 0 || ctx->width > 0xFFFF || ctx->height <= 0 || ctx->height > 0xFFFF) {
LOGE("Invalid video size: %dx%d", ctx->width, ctx->height);
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion server/src/main/java/com/genymobile/scrcpy/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc
surfaceCapture = new NewDisplayCapture(controller, options);
} else {
assert options.getDisplayId() != Device.DISPLAY_ID_NONE;
surfaceCapture = new ScreenCapture(controller, options);
surfaceCapture = new ScreenCapture(controller, videoStreamer, options);
}
} else {
surfaceCapture = new CameraCapture(options);
Expand Down
19 changes: 17 additions & 2 deletions server/src/main/java/com/genymobile/scrcpy/device/Streamer.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
Expand Down Expand Up @@ -44,12 +45,26 @@ public void writeAudioHeader() throws IOException {
}
}

public void writeVideoHeader(Size videoSize) throws IOException {
public void writeVideoHeader() throws IOException {
if (sendCodecMeta) {
ByteBuffer buffer = ByteBuffer.allocate(12);
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.putInt(codec.getId());
buffer.flip();
IO.writeFully(fd, buffer);
}
}

public void writeVideoSession(Size videoSize, boolean isFlip, int rotation) throws IOException{
if(sendCodecMeta){
ByteBuffer buffer = ByteBuffer.allocate(12);
buffer.put((byte) 0xff);
buffer.putInt(videoSize.getWidth());
buffer.putInt(videoSize.getHeight());
buffer.put((byte) (isFlip ? 1 : 0));
int[] vertical = {0, 0, 1, 1};
int[] horizontal = {0, 1, 0, 1};
buffer.put((byte) vertical[rotation]);
buffer.put((byte) horizontal[rotation]);
buffer.flip();
IO.writeFully(fd, buffer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,35 @@
import android.os.HandlerThread;
import android.view.IDisplayWindowListener;

import java.util.Objects;

public class DisplaySizeMonitor {

private static class SessionInfo {
private Size size;
private int rotation;

public SessionInfo(Size size, int rotation) {
this.size = size;
this.rotation = rotation;
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof SessionInfo))
return false;
SessionInfo that = (SessionInfo) o;
return rotation == that.rotation && Objects.equals(size, that.size);
}

@Override
public String toString() {
return "{" + "size=" + size + ", rotation=" + rotation + '}';
}
}

public interface Listener {
void onDisplaySizeChanged();
}
Expand All @@ -34,7 +61,7 @@ public interface Listener {

private int displayId = Device.DISPLAY_ID_NONE;

private Size sessionDisplaySize;
private SessionInfo sessionInfo;

private Listener listener;

Expand Down Expand Up @@ -98,12 +125,16 @@ public void stopAndRelease() {
}
}

private synchronized Size getSessionDisplaySize() {
return sessionDisplaySize;
private synchronized SessionInfo getSessionInfo() {
return sessionInfo;
}

private synchronized void setSessionInfo(SessionInfo sessionInfo) {
this.sessionInfo = sessionInfo;
}

public synchronized void setSessionDisplaySize(Size sessionDisplaySize) {
this.sessionDisplaySize = sessionDisplaySize;
public void setSessionInfo(Size size, int rotation) {
setSessionInfo(new SessionInfo(size, rotation));
}

private void checkDisplaySizeChanged() {
Expand All @@ -112,29 +143,29 @@ private void checkDisplaySizeChanged() {
Ln.w("DisplayInfo for " + displayId + " cannot be retrieved");
// We can't compare with the current size, so reset unconditionally
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
Ln.v("DisplaySizeMonitor: requestReset(): " + getSessionDisplaySize() + " -> (unknown)");
Ln.v("DisplaySizeMonitor: requestReset(): " + getSessionInfo() + " -> (unknown)");
}
setSessionDisplaySize(null);
setSessionInfo(null);
listener.onDisplaySizeChanged();
} else {
Size size = di.getSize();
SessionInfo si = new SessionInfo(di.getSize(), di.getRotation());

// The field is hidden on purpose, to read it with synchronization
@SuppressWarnings("checkstyle:HiddenField")
Size sessionDisplaySize = getSessionDisplaySize(); // synchronized
SessionInfo sessionInfo = getSessionInfo(); // synchronized

// .equals() also works if sessionDisplaySize == null
if (!size.equals(sessionDisplaySize)) {
if (!si.equals(sessionInfo)) {
// Reset only if the size is different
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
Ln.v("DisplaySizeMonitor: requestReset(): " + sessionDisplaySize + " -> " + size);
Ln.v("DisplaySizeMonitor: requestReset(): " + sessionInfo + " -> " + si);
}
// Set the new size immediately, so that a future onDisplayChanged() event called before the asynchronous prepare()
// considers that the current size is the requested size (to avoid a duplicate requestReset())
setSessionDisplaySize(size);
setSessionInfo(si);
listener.onDisplaySizeChanged();
} else if (Ln.isEnabled(Ln.Level.VERBOSE)) {
Ln.v("DisplaySizeMonitor: Size not changed (" + size + "): do not requestReset()");
Ln.v("DisplaySizeMonitor: Size and rotation not changed (" + sessionInfo + "): do not requestReset()");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public void prepare() {
videoSize = displaySize;
displayRotation = 0;
// Set the current display size to avoid an unnecessary call to invalidate()
displaySizeMonitor.setSessionDisplaySize(displaySize);
displaySizeMonitor.setSessionInfo(displaySize, displayRotation);
} else {
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(virtualDisplay.getDisplay().getDisplayId());
displaySize = displayInfo.getSize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.genymobile.scrcpy.device.Device;
import com.genymobile.scrcpy.device.DisplayInfo;
import com.genymobile.scrcpy.device.Orientation;
import com.genymobile.scrcpy.device.Streamer;
import com.genymobile.scrcpy.device.Size;
import com.genymobile.scrcpy.opengl.AffineOpenGLFilter;
import com.genymobile.scrcpy.opengl.OpenGLFilter;
Expand Down Expand Up @@ -34,6 +35,7 @@ public class ScreenCapture extends SurfaceCapture {
private Orientation.Lock captureOrientationLock;
private Orientation captureOrientation;
private final float angle;
private final Streamer streamer;

private DisplayInfo displayInfo;
private Size videoSize;
Expand All @@ -46,7 +48,7 @@ public class ScreenCapture extends SurfaceCapture {
private AffineMatrix transform;
private OpenGLRunner glRunner;

public ScreenCapture(VirtualDisplayListener vdListener, Options options) {
public ScreenCapture(VirtualDisplayListener vdListener, Streamer streamer, Options options) {
this.vdListener = vdListener;
this.displayId = options.getDisplayId();
assert displayId != Device.DISPLAY_ID_NONE;
Expand All @@ -57,6 +59,7 @@ public ScreenCapture(VirtualDisplayListener vdListener, Options options) {
assert captureOrientationLock != null;
assert captureOrientation != null;
this.angle = options.getAngle();
this.streamer = streamer;
}

@Override
Expand All @@ -77,27 +80,35 @@ public void prepare() throws ConfigurationException {
}

Size displaySize = displayInfo.getSize();
displaySizeMonitor.setSessionDisplaySize(displaySize);
int displayRotation = displayInfo.getRotation();
displaySizeMonitor.setSessionInfo(displaySize, displayRotation);

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());
captureOrientation = Orientation.fromRotation(displayRotation);
}

VideoFilter filter = new VideoFilter(displaySize);

if (crop != null) {
boolean transposed = (displayInfo.getRotation() % 2) != 0;
boolean transposed = (displayRotation % 2) != 0;
filter.addCrop(crop, transposed);
}

boolean locked = captureOrientationLock != Orientation.Lock.Unlocked;
filter.addOrientation(displayInfo.getRotation(), locked, captureOrientation);
filter.addOrientation(displayRotation, locked, captureOrientation);
filter.addAngle(angle);

transform = filter.getInverseTransform();
videoSize = filter.getOutputSize().limit(maxSize).round8();

try {
boolean isFlipped = captureOrientation.isFlipped();
streamer.writeVideoSession(videoSize, isFlipped, displayRotation);
} catch (Exception e) {
Ln.e("Video Session failed to send", e);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ private void streamCapture() throws IOException, ConfigurationException {
boolean headerWritten = false;

do {
reset.consumeReset(); // If a capture reset was requested, it is implicitly fulfilled
capture.prepare();
Size size = capture.getSize();
if (!headerWritten) {
streamer.writeVideoHeader(size);
streamer.writeVideoHeader();
headerWritten = true;
}
reset.consumeReset(); // If a capture reset was requested, it is implicitly fulfilled
capture.prepare();
Size size = capture.getSize();

format.setInteger(MediaFormat.KEY_WIDTH, size.getWidth());
format.setInteger(MediaFormat.KEY_HEIGHT, size.getHeight());
Expand Down