diff --git a/app/src/demuxer.c b/app/src/demuxer.c index 634b010f..f0709424 100644 --- a/app/src/demuxer.c +++ b/app/src/demuxer.c @@ -76,10 +76,10 @@ sc_demuxer_recv_header(struct sc_demuxer *demuxer, // which only contains a 12-byte header: // // byte 0 byte 1 byte 2 byte 3 - // 10000000 00000000 00000000 00000000 - // ^<--------------------------------> - // | padding - // `- session packet flag + // 10000000 00000000 00000000 0000000. + // ^<------------------------------->^ + // | padding | + // `- session packet flag `- client resized flag // // byte 4 byte 5 byte 6 byte 7 byte 8 byte 9 byte 10 byte 11 // ........ ........ ........ ........ ........ ........ ........ ........ @@ -126,6 +126,7 @@ sc_demuxer_parse_session(const uint8_t *header, assert(sc_demuxer_is_session(header)); session->video.width = sc_read32be(&header[4]); session->video.height = sc_read32be(&header[8]); + session->video.client_resized = header[3] & 1; } static bool diff --git a/app/src/screen.c b/app/src/screen.c index 628ef628..7959b843 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -329,6 +329,8 @@ sc_screen_frame_sink_open(struct sc_frame_sink *sink, screen->content_size = get_oriented_size(screen->frame_size, screen->orientation); + screen->current_session = *session; + bool ok = sc_push_event(SC_EVENT_OPEN_WINDOW); if (!ok) { return false; @@ -361,6 +363,7 @@ sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) { sc_mutex_lock(&screen->mutex); bool previous_skipped = sc_frame_buffer_has_frame(&screen->fb); bool ok = sc_frame_buffer_push(&screen->fb, frame); + screen->prevent_auto_resize = screen->current_session.video.client_resized; sc_mutex_unlock(&screen->mutex); if (!ok) { return false; @@ -381,6 +384,14 @@ sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) { return true; } +static bool +sc_screen_frame_sink_push_session(struct sc_frame_sink *sink, + const struct sc_stream_session *session) { + struct sc_screen *screen = DOWNCAST(sink); + screen->current_session = *session; + return true; +} + bool sc_screen_init(struct sc_screen *screen, const struct sc_screen_params *params) { @@ -403,6 +414,8 @@ sc_screen_init(struct sc_screen *screen, screen->req.fullscreen = params->fullscreen; screen->req.start_fps_counter = params->start_fps_counter; + screen->prevent_auto_resize = false; + bool ok = sc_mutex_init(&screen->mutex); if (!ok) { return false; @@ -570,10 +583,13 @@ sc_screen_init(struct sc_screen *screen, } #endif + memset(&screen->current_session, 0, sizeof(screen->current_session)); + static const struct sc_frame_sink_ops ops = { .open = sc_screen_frame_sink_open, .close = sc_screen_frame_sink_close, .push = sc_screen_frame_sink_push, + .push_session = sc_screen_frame_sink_push_session, }; screen->frame_sink.ops = &ops; @@ -725,16 +741,19 @@ resize_for_content(struct sc_screen *screen, struct sc_size old_content_size, } static void -set_content_size(struct sc_screen *screen, struct sc_size new_content_size) { +set_content_size(struct sc_screen *screen, struct sc_size new_content_size, + bool resize) { assert(screen->video); - if (is_windowed(screen)) { - resize_for_content(screen, screen->content_size, new_content_size); - } else if (!screen->resize_pending) { - // Store the windowed size to be able to compute the optimal size once - // fullscreen/maximized/minimized are disabled - screen->windowed_content_size = screen->content_size; - screen->resize_pending = true; + if (resize) { + if (is_windowed(screen)) { + resize_for_content(screen, screen->content_size, new_content_size); + } else if (!screen->resize_pending) { + // Store the windowed size to be able to compute the optimal size + // once fullscreen/maximized/minimized are disabled + screen->windowed_content_size = screen->content_size; + screen->resize_pending = true; + } } screen->content_size = new_content_size; @@ -764,7 +783,7 @@ sc_screen_set_orientation(struct sc_screen *screen, struct sc_size new_content_size = get_oriented_size(screen->frame_size, orientation); - set_content_size(screen, new_content_size); + set_content_size(screen, new_content_size, true); screen->orientation = orientation; LOGI("Display orientation set to %s", sc_orientation_get_name(orientation)); @@ -773,7 +792,7 @@ sc_screen_set_orientation(struct sc_screen *screen, } static bool -sc_screen_apply_frame(struct sc_screen *screen) { +sc_screen_apply_frame(struct sc_screen *screen, bool can_resize) { assert(screen->video); assert(screen->window_shown); @@ -790,7 +809,7 @@ sc_screen_apply_frame(struct sc_screen *screen) { struct sc_size new_content_size = get_oriented_size(new_frame_size, screen->orientation); - set_content_size(screen, new_content_size); + set_content_size(screen, new_content_size, can_resize); sc_screen_update_content_rect(screen); } @@ -826,8 +845,10 @@ sc_screen_update_frame(struct sc_screen *screen) { av_frame_unref(screen->frame); sc_mutex_lock(&screen->mutex); sc_frame_buffer_consume(&screen->fb, screen->frame); + // read with lock held + bool can_resize = !screen->prevent_auto_resize; sc_mutex_unlock(&screen->mutex); - return sc_screen_apply_frame(screen); + return sc_screen_apply_frame(screen, can_resize); } void @@ -845,7 +866,7 @@ sc_screen_set_paused(struct sc_screen *screen, bool paused) { av_frame_free(&screen->frame); screen->frame = screen->resume_frame; screen->resume_frame = NULL; - bool ok = sc_screen_apply_frame(screen); + bool ok = sc_screen_apply_frame(screen, true); if (!ok) { LOGE("Resume frame update failed"); } diff --git a/app/src/screen.h b/app/src/screen.h index 384402d7..d363c682 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -46,6 +46,8 @@ struct sc_screen { struct sc_mutex mutex; struct sc_frame_buffer fb; // protected by mutex + // When true, a frame size change must not cause the window to be resized + bool prevent_auto_resize; // protected by mutex // The initial requested window properties struct { @@ -77,6 +79,9 @@ struct sc_screen { struct SDL_FRect rect; bool window_shown; + // only accessed from the thread calling sc_frame_sink_ops functions + struct sc_stream_session current_session; + AVFrame *frame; bool paused; diff --git a/app/src/trait/packet_sink.h b/app/src/trait/packet_sink.h index db742b2f..6312d529 100644 --- a/app/src/trait/packet_sink.h +++ b/app/src/trait/packet_sink.h @@ -18,6 +18,7 @@ struct sc_packet_sink { struct sc_stream_session_video { uint32_t width; uint32_t height; + bool client_resized; }; struct sc_stream_session { diff --git a/doc/develop.md b/doc/develop.md index 3bf69caa..62982900 100644 --- a/doc/develop.md +++ b/doc/develop.md @@ -370,10 +370,10 @@ session (a session changes when the device rotates): ``` byte 0 byte 1 byte 2 byte 3 - 10000000 00000000 00000000 00000000 - ^<--------------------------------> - | padding - `- session packet flag + 10000000 00000000 00000000 0000000. + ^<------------------------------->^ + | padding | + `- session packet flag `- client resized flag byte 4 byte 5 byte 6 byte 7 byte 8 byte 9 byte 10 byte 11 ........ ........ ........ ........ ........ ........ ........ ........ @@ -381,6 +381,11 @@ session (a session changes when the device rotates): video width video height ``` +The "client resized" flag is used for _flex displays_ to indicate that the frame +size changed due to a client resize request (see [#6772]). + +[#6772]: https://github.com/Genymobile/scrcpy/pull/6772 + For the _audio_ stream, there are no _session packets_. Then _media packets_ are sent, each containing the payload produced by diff --git a/server/src/main/java/com/genymobile/scrcpy/device/Streamer.java b/server/src/main/java/com/genymobile/scrcpy/device/Streamer.java index a00b0280..d6d654ff 100644 --- a/server/src/main/java/com/genymobile/scrcpy/device/Streamer.java +++ b/server/src/main/java/com/genymobile/scrcpy/device/Streamer.java @@ -88,11 +88,15 @@ public final class Streamer { writePacket(codecBuffer, pts, config, keyFrame); } - public void writeSessionMeta(int width, int height) throws IOException { + public void writeSessionMeta(int width, int height, boolean clientResize) throws IOException { if (sendStreamMeta) { headerBuffer.clear(); - headerBuffer.putInt((int) (PACKET_FLAG_SESSION >> 32)); // Set the first bit to 1 + int flags = (int) (PACKET_FLAG_SESSION >> 32); // set the first bit to 1 + if (clientResize) { + flags |= 1; + } + headerBuffer.putInt(flags); headerBuffer.putInt(width); headerBuffer.putInt(height); headerBuffer.flip(); diff --git a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java index 8a0c9b38..41811e82 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java @@ -128,7 +128,7 @@ public class SurfaceEncoder implements AsyncProcessor { } else { if (!captureControl.isResetRequested()) { // If a reset is requested during encode(), it will interrupt the encoding by an EOS - streamer.writeSessionMeta(size.getWidth(), size.getHeight()); + streamer.writeSessionMeta(size.getWidth(), size.getHeight(), false); encode(mediaCodec, streamer); }