Compare commits

..

8 Commits

Author SHA1 Message Date
Romain Vimont
5f2cf12acf Add intermediate frame in video buffer
There were only two frames simultaneously:
 - one used by the decoder;
 - one used by the renderer.

When the decoder finished decoding a frame, it swapped it with the
rendering frame.

Adding a third frame provides several benefits:
 - the decoder do not have to wait for the renderer to release the
   mutex;
 - it simplifies the video_buffer API;
 - it makes the rendering frame valid until the next call to
   video_buffer_take_rendering_frame(), which will be useful for
   swscaling on window resize.
2021-02-04 08:12:22 +01:00
Romain Vimont
2a3c6b64dd Assert non-recursive usage of mutexes 2021-02-04 08:12:22 +01:00
Romain Vimont
a9582b1d43 Add mutex assertions 2021-02-04 08:12:22 +01:00
Romain Vimont
fa3e84b700 Expose mutex assertions
Add a function to assert that the mutex is held (or not).
2021-02-04 08:12:22 +01:00
Romain Vimont
1521de9051 Expose thread id 2021-02-04 08:12:22 +01:00
Romain Vimont
eabaabdb78 Wrap SDL thread functions into scrcpy-specific API
The goal is to expose a consistent API for system tools, and paves the
way to make the "core" independant of SDL in the future.
2021-02-04 08:12:22 +01:00
Romain Vimont
8b48003074 Replace SDL_strdup() by strdup()
The functions SDL_malloc(), SDL_free() and SDL_strdup() were used only
because strdup() was not available everywhere.

Now that it is available, use the native version of these functions.
2021-02-04 08:12:22 +01:00
Romain Vimont
74bd25a0ed Provide strdup() compat
Make strdup() available on all platforms.
2021-02-04 08:12:22 +01:00
13 changed files with 140 additions and 137 deletions

View File

@@ -99,6 +99,9 @@ foreach f : check_functions
endif
endforeach
# expose the build type
conf.set('NDEBUG', get_option('buildtype') != 'debug')
# the version, updated on release
conf.set_quoted('SCRCPY_VERSION', meson.project_version())

View File

@@ -56,7 +56,7 @@
# define SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
#endif
#ifndef HAVE_STRDUP
#ifdef HAVE_STRDUP
char *strdup(const char *s);
#endif

View File

@@ -1,2 +1,3 @@
#define EVENT_NEW_FRAME SDL_USEREVENT
#define EVENT_STREAM_STOPPED (SDL_USEREVENT + 1)
#define EVENT_NEW_SESSION SDL_USEREVENT
#define EVENT_NEW_FRAME (SDL_USEREVENT + 1)
#define EVENT_STREAM_STOPPED (SDL_USEREVENT + 2)

View File

@@ -286,7 +286,7 @@ rotate_client_right(struct screen *screen) {
screen_set_rotation(screen, new_rotation);
}
static void
void
input_manager_process_text_input(struct input_manager *im,
const SDL_TextInputEvent *event) {
if (is_shortcut_mod(im, SDL_GetModState())) {
@@ -366,7 +366,7 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
return true;
}
static void
void
input_manager_process_key(struct input_manager *im,
const SDL_KeyboardEvent *event) {
// control: indicates the state of the command-line option --no-control
@@ -551,7 +551,7 @@ convert_mouse_motion(const SDL_MouseMotionEvent *from, struct screen *screen,
return true;
}
static void
void
input_manager_process_mouse_motion(struct input_manager *im,
const SDL_MouseMotionEvent *event) {
if (!event->state) {
@@ -605,7 +605,7 @@ convert_touch(const SDL_TouchFingerEvent *from, struct screen *screen,
return true;
}
static void
void
input_manager_process_touch(struct input_manager *im,
const SDL_TouchFingerEvent *event) {
struct control_msg msg;
@@ -637,7 +637,7 @@ convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
return true;
}
static void
void
input_manager_process_mouse_button(struct input_manager *im,
const SDL_MouseButtonEvent *event) {
bool control = im->control;
@@ -736,7 +736,7 @@ convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
return true;
}
static void
void
input_manager_process_mouse_wheel(struct input_manager *im,
const SDL_MouseWheelEvent *event) {
struct control_msg msg;
@@ -746,46 +746,3 @@ input_manager_process_mouse_wheel(struct input_manager *im,
}
}
}
bool
input_manager_handle_event(struct input_manager *im, SDL_Event *event) {
switch (event->type) {
case SDL_TEXTINPUT:
if (!im->control) {
return true;
}
input_manager_process_text_input(im, &event->text);
return true;
case SDL_KEYDOWN:
case SDL_KEYUP:
// some key events do not interact with the device, so process the
// event even if control is disabled
input_manager_process_key(im, &event->key);
return true;
case SDL_MOUSEMOTION:
if (!im->control) {
break;
}
input_manager_process_mouse_motion(im, &event->motion);
return true;
case SDL_MOUSEWHEEL:
if (!im->control) {
break;
}
input_manager_process_mouse_wheel(im, &event->wheel);
return true;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
// some mouse events do not interact with the device, so process
// the event even if control is disabled
input_manager_process_mouse_button(im, &event->button);
return true;
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP:
input_manager_process_touch(im, &event->tfinger);
return true;
}
return false;
}

View File

@@ -40,7 +40,28 @@ void
input_manager_init(struct input_manager *im,
const struct scrcpy_options *options);
bool
input_manager_handle_event(struct input_manager *im, SDL_Event *event);
void
input_manager_process_text_input(struct input_manager *im,
const SDL_TextInputEvent *event);
void
input_manager_process_key(struct input_manager *im,
const SDL_KeyboardEvent *event);
void
input_manager_process_mouse_motion(struct input_manager *im,
const SDL_MouseMotionEvent *event);
void
input_manager_process_touch(struct input_manager *im,
const SDL_TouchFingerEvent *event);
void
input_manager_process_mouse_button(struct input_manager *im,
const SDL_MouseButtonEvent *event);
void
input_manager_process_mouse_wheel(struct input_manager *im,
const SDL_MouseWheelEvent *event);
#endif

View File

@@ -30,7 +30,7 @@
#include "util/net.h"
static struct server server;
static struct screen screen;
static struct screen screen = SCREEN_INITIALIZER;
static struct fps_counter fps_counter;
static struct video_buffer video_buffer;
static struct stream stream;
@@ -173,6 +173,54 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) {
case SDL_QUIT:
LOGD("User requested to quit");
return EVENT_RESULT_STOPPED_BY_USER;
case EVENT_NEW_FRAME:
if (!screen.has_frame) {
screen.has_frame = true;
// this is the very first frame, show the window
screen_show_window(&screen);
}
if (!screen_update_frame(&screen, &video_buffer)) {
return EVENT_RESULT_CONTINUE;
}
break;
case SDL_WINDOWEVENT:
screen_handle_window_event(&screen, &event->window);
break;
case SDL_TEXTINPUT:
if (!options->control) {
break;
}
input_manager_process_text_input(&input_manager, &event->text);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
// some key events do not interact with the device, so process the
// event even if control is disabled
input_manager_process_key(&input_manager, &event->key);
break;
case SDL_MOUSEMOTION:
if (!options->control) {
break;
}
input_manager_process_mouse_motion(&input_manager, &event->motion);
break;
case SDL_MOUSEWHEEL:
if (!options->control) {
break;
}
input_manager_process_mouse_wheel(&input_manager, &event->wheel);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
// some mouse events do not interact with the device, so process
// the event even if control is disabled
input_manager_process_mouse_button(&input_manager, &event->button);
break;
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP:
input_manager_process_touch(&input_manager, &event->tfinger);
break;
case SDL_DROPFILE: {
if (!options->control) {
break;
@@ -194,16 +242,6 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) {
break;
}
}
bool consumed = screen_handle_event(&screen, event);
if (consumed) {
goto end;
}
consumed = input_manager_handle_event(&input_manager, event);
(void) consumed;
end:
return EVENT_RESULT_CONTINUE;
}
@@ -389,8 +427,6 @@ scrcpy(const struct scrcpy_options *options) {
const char *window_title =
options->window_title ? options->window_title : device_name;
screen_init(&screen, &video_buffer);
if (!screen_init_rendering(&screen, window_title, frame_size,
options->always_on_top, options->window_x,
options->window_y, options->window_width,

View File

@@ -4,7 +4,6 @@
#include <string.h>
#include <SDL2/SDL.h>
#include "events.h"
#include "icon.xpm"
#include "scrcpy.h"
#include "tiny_xpm.h"
@@ -192,9 +191,8 @@ screen_update_content_rect(struct screen *screen) {
}
void
screen_init(struct screen *screen, struct video_buffer *vb) {
screen_init(struct screen *screen) {
*screen = (struct screen) SCREEN_INITIALIZER;
screen->vb = vb;
}
static inline SDL_Texture *
@@ -272,7 +270,7 @@ screen_init_rendering(struct screen *screen, const char *window_title,
SDL_RENDERER_ACCELERATED);
if (!screen->renderer) {
LOGC("Could not create renderer: %s", SDL_GetError());
SDL_DestroyWindow(screen->window);
screen_destroy(screen);
return false;
}
@@ -282,8 +280,8 @@ screen_init_rendering(struct screen *screen, const char *window_title,
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)");
// starts with "opengl"
bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
if (use_opengl) {
screen->use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
if (screen->use_opengl) {
struct sc_opengl *gl = &screen->gl;
sc_opengl_init(gl);
@@ -303,7 +301,7 @@ screen_init_rendering(struct screen *screen, const char *window_title,
} else {
LOGI("Trilinear filtering disabled");
}
} else if (mipmaps) {
} else {
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
}
@@ -320,8 +318,6 @@ screen_init_rendering(struct screen *screen, const char *window_title,
screen->texture = create_texture(screen);
if (!screen->texture) {
LOGC("Could not create texture: %s", SDL_GetError());
SDL_DestroyRenderer(screen->renderer);
SDL_DestroyWindow(screen->window);
screen_destroy(screen);
return false;
}
@@ -346,8 +342,12 @@ screen_destroy(struct screen *screen) {
if (screen->texture) {
SDL_DestroyTexture(screen->texture);
}
SDL_DestroyRenderer(screen->renderer);
SDL_DestroyWindow(screen->window);
if (screen->renderer) {
SDL_DestroyRenderer(screen->renderer);
}
if (screen->window) {
SDL_DestroyWindow(screen->window);
}
}
static void
@@ -444,17 +444,19 @@ update_texture(struct screen *screen, const AVFrame *frame) {
frame->data[2], frame->linesize[2]);
if (screen->mipmaps) {
assert(screen->use_opengl);
SDL_GL_BindTexture(screen->texture, NULL, NULL);
screen->gl.GenerateMipmap(GL_TEXTURE_2D);
SDL_GL_UnbindTexture(screen->texture);
}
}
static bool
screen_update_frame(struct screen *screen) {
const AVFrame *frame = video_buffer_take_rendering_frame(screen->vb);
bool
screen_update_frame(struct screen *screen, struct video_buffer *vb) {
const AVFrame *frame = video_buffer_take_rendering_frame(vb);
struct size new_frame_size = {frame->width, frame->height};
if (!prepare_for_frame(screen, new_frame_size)) {
sc_mutex_unlock(&vb->mutex);
return false;
}
update_texture(screen, frame);
@@ -544,52 +546,31 @@ screen_resize_to_pixel_perfect(struct screen *screen) {
content_size.height);
}
bool
screen_handle_event(struct screen *screen, SDL_Event *event) {
switch (event->type) {
case EVENT_NEW_FRAME:
if (!screen->has_frame) {
screen->has_frame = true;
// this is the very first frame, show the window
screen_show_window(screen);
void
screen_handle_window_event(struct screen *screen,
const SDL_WindowEvent *event) {
switch (event->event) {
case SDL_WINDOWEVENT_EXPOSED:
screen_render(screen, true);
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
screen_render(screen, true);
break;
case SDL_WINDOWEVENT_MAXIMIZED:
screen->maximized = true;
break;
case SDL_WINDOWEVENT_RESTORED:
if (screen->fullscreen) {
// On Windows, in maximized+fullscreen, disabling fullscreen
// mode unexpectedly triggers the "restored" then "maximized"
// events, leaving the window in a weird state (maximized
// according to the events, but not maximized visually).
break;
}
bool ok = screen_update_frame(screen);
if (!ok) {
LOGW("Frame update failed\n");
}
return true;
case SDL_WINDOWEVENT:
if (!screen->has_frame) {
// Do nothing
return true;
}
switch (event->window.event) {
case SDL_WINDOWEVENT_EXPOSED:
screen_render(screen, true);
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
screen_render(screen, true);
break;
case SDL_WINDOWEVENT_MAXIMIZED:
screen->maximized = true;
break;
case SDL_WINDOWEVENT_RESTORED:
if (screen->fullscreen) {
// On Windows, in maximized+fullscreen, disabling
// fullscreen mode unexpectedly triggers the "restored"
// then "maximized" events, leaving the window in a
// weird state (maximized according to the events, but
// not maximized visually).
break;
}
screen->maximized = false;
apply_pending_resize(screen);
break;
}
return true;
screen->maximized = false;
apply_pending_resize(screen);
break;
}
return false;
}
struct point

View File

@@ -13,10 +13,10 @@
struct video_buffer;
struct screen {
struct video_buffer *vb;
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *texture;
bool use_opengl;
struct sc_opengl gl;
struct size frame_size;
struct size content_size; // rotated frame_size
@@ -38,10 +38,10 @@ struct screen {
};
#define SCREEN_INITIALIZER { \
.vb = NULL, \
.window = NULL, \
.renderer = NULL, \
.texture = NULL, \
.use_opengl = false, \
.gl = {0}, \
.frame_size = { \
.width = 0, \
@@ -72,7 +72,7 @@ struct screen {
// initialize default values
void
screen_init(struct screen *screen, struct video_buffer *vb);
screen_init(struct screen *screen);
// initialize screen, create window, renderer and texture (window is hidden)
// window_x and window_y accept SC_WINDOW_POSITION_UNDEFINED
@@ -91,6 +91,10 @@ screen_show_window(struct screen *screen);
void
screen_destroy(struct screen *screen);
// resize if necessary and write the rendered frame into the texture
bool
screen_update_frame(struct screen *screen, struct video_buffer *vb);
// render the texture to the renderer
//
// Set the update_content_rect flag if the window or content size may have
@@ -114,9 +118,9 @@ screen_resize_to_pixel_perfect(struct screen *screen);
void
screen_set_rotation(struct screen *screen, unsigned rotation);
// react to SDL events
bool
screen_handle_event(struct screen *screen, SDL_Event *event);
// react to window events
void
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
// convert point from window coordinates to frame coordinates
// x and y are expressed in pixels

View File

@@ -375,6 +375,8 @@ server_init(struct server *server) {
server->video_socket = INVALID_SOCKET;
server->control_socket = INVALID_SOCKET;
server->port_range.first = 0;
server->port_range.last = 0;
server->local_port = 0;
server->tunnel_enabled = false;
@@ -408,6 +410,8 @@ run_wait_server(void *data) {
bool
server_start(struct server *server, const char *serial,
const struct server_params *params) {
server->port_range = params->port_range;
if (serial) {
server->serial = strdup(serial);
if (!server->serial) {

View File

@@ -26,6 +26,7 @@ struct server {
socket_t server_socket; // only used if !tunnel_forward
socket_t video_socket;
socket_t control_socket;
struct sc_port_range port_range;
uint16_t local_port; // selected from port_range
bool tunnel_enabled;
bool tunnel_forward; // use "adb forward" instead of "adb reverse"

View File

@@ -61,7 +61,6 @@ sc_mutex_lock(sc_mutex *mutex) {
void
sc_mutex_unlock(sc_mutex *mutex) {
#ifndef NDEBUG
assert(sc_mutex_held(mutex));
mutex->locker = 0;
#endif
int r = SDL_UnlockMutex(mutex->mutex);

View File

@@ -4,7 +4,6 @@ project('scrcpy', 'c',
default_options: [
'c_std=c11',
'warning_level=2',
'b_ndebug=if-release',
])
if get_option('compile_app')

View File

@@ -7,7 +7,6 @@ import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.Build;
import android.os.IBinder;
import android.view.Surface;
@@ -226,9 +225,7 @@ public class ScreenEncoder implements Device.RotationListener {
}
private static IBinder createDisplay() {
// Since Android 12, secure displays could not be created with shell permissions anymore
boolean secure = Build.VERSION.SDK_INT <= Build.VERSION_CODES.R;
return SurfaceControl.createDisplay("scrcpy", secure);
return SurfaceControl.createDisplay("scrcpy", true);
}
private static void configure(MediaCodec codec, MediaFormat format) {