Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
5f2cf12acf | |||
2a3c6b64dd |
@ -11,7 +11,6 @@ src = [
|
||||
'src/event_converter.c',
|
||||
'src/file_handler.c',
|
||||
'src/fps_counter.c',
|
||||
'src/frame_texture.c',
|
||||
'src/input_manager.c',
|
||||
'src/opengl.c',
|
||||
'src/receiver.c',
|
||||
@ -47,7 +46,6 @@ if not get_option('crossbuild_windows')
|
||||
dependency('libavformat'),
|
||||
dependency('libavcodec'),
|
||||
dependency('libavutil'),
|
||||
dependency('libswscale'),
|
||||
dependency('sdl2'),
|
||||
]
|
||||
|
||||
|
@ -167,13 +167,6 @@ Set the initial display rotation. Possibles values are 0, 1, 2 and 3. Each incre
|
||||
.BI "\-s, \-\-serial " number
|
||||
The device serial number. Mandatory only if several devices are connected to adb.
|
||||
|
||||
.TP
|
||||
.BI "\-\-scale\-filter filter
|
||||
Supported filters are "none" and "trilinear".
|
||||
|
||||
Trilinear filtering is only available if the renderer is OpenGL 3.0+ or OpenGL
|
||||
ES 2.0+.
|
||||
|
||||
.TP
|
||||
.BI "\-\-shortcut\-mod " key[+...]][,...]
|
||||
Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper".
|
||||
|
@ -103,6 +103,11 @@ scrcpy_print_usage(const char *arg0) {
|
||||
" --no-key-repeat\n"
|
||||
" Do not forward repeated key events when a key is held down.\n"
|
||||
"\n"
|
||||
" --no-mipmaps\n"
|
||||
" If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then\n"
|
||||
" mipmaps are automatically generated to improve downscaling\n"
|
||||
" quality. This option disables the generation of mipmaps.\n"
|
||||
"\n"
|
||||
" -p, --port port[:port]\n"
|
||||
" Set the TCP port (range) used by the client to listen.\n"
|
||||
" Default is %d:%d.\n"
|
||||
@ -149,11 +154,6 @@ scrcpy_print_usage(const char *arg0) {
|
||||
" The device serial number. Mandatory only if several devices\n"
|
||||
" are connected to adb.\n"
|
||||
"\n"
|
||||
" --scale-filter filter\n"
|
||||
" Supported filters are \"none\" and \"trilinear\".\n"
|
||||
" Trilinear filtering is only available if the renderer is\n"
|
||||
" OpenGL 3.0+ or OpenGL ES 2.0+.\n"
|
||||
"\n"
|
||||
" --shortcut-mod key[+...]][,...]\n"
|
||||
" Specify the modifiers to use for scrcpy shortcuts.\n"
|
||||
" Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\",\n"
|
||||
@ -641,62 +641,6 @@ guess_record_format(const char *filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_scale_filter(const char *optarg, enum sc_scale_filter *filter) {
|
||||
// TODO store in a map and loop over the entries instead
|
||||
if (!strcmp(optarg, "none")) {
|
||||
*filter = SC_SCALE_FILTER_NONE;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "trilinear")) {
|
||||
*filter = SC_SCALE_FILTER_TRILINEAR;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "bilinear")) {
|
||||
*filter = SC_SCALE_FILTER_BILINEAR;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "bicubic")) {
|
||||
*filter = SC_SCALE_FILTER_BICUBIC;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "x")) {
|
||||
*filter = SC_SCALE_FILTER_X;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "point")) {
|
||||
*filter = SC_SCALE_FILTER_POINT;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "area")) {
|
||||
*filter = SC_SCALE_FILTER_AREA;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "bicublin")) {
|
||||
*filter = SC_SCALE_FILTER_BICUBLIN;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "gauss")) {
|
||||
*filter = SC_SCALE_FILTER_GAUSS;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "sinc")) {
|
||||
*filter = SC_SCALE_FILTER_SINC;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "lanczos")) {
|
||||
*filter = SC_SCALE_FILTER_LANCZOS;
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(optarg, "spline")) {
|
||||
*filter = SC_SCALE_FILTER_SPLINE;
|
||||
return true;
|
||||
}
|
||||
LOGE("Unsupported scale filter: %s "
|
||||
"(expected one of [TODO])", optarg);
|
||||
return false;
|
||||
}
|
||||
|
||||
#define OPT_RENDER_EXPIRED_FRAMES 1000
|
||||
#define OPT_WINDOW_TITLE 1001
|
||||
#define OPT_PUSH_TARGET 1002
|
||||
@ -723,7 +667,6 @@ parse_scale_filter(const char *optarg, enum sc_scale_filter *filter) {
|
||||
#define OPT_FORWARD_ALL_CLICKS 1023
|
||||
#define OPT_LEGACY_PASTE 1024
|
||||
#define OPT_ENCODER_NAME 1025
|
||||
#define OPT_SCALE_FILTER 1026
|
||||
|
||||
bool
|
||||
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
@ -760,7 +703,6 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
{"render-expired-frames", no_argument, NULL,
|
||||
OPT_RENDER_EXPIRED_FRAMES},
|
||||
{"rotation", required_argument, NULL, OPT_ROTATION},
|
||||
{"scale-filter", required_argument, NULL, OPT_SCALE_FILTER},
|
||||
{"serial", required_argument, NULL, 's'},
|
||||
{"shortcut-mod", required_argument, NULL, OPT_SHORTCUT_MOD},
|
||||
{"show-touches", no_argument, NULL, 't'},
|
||||
@ -915,9 +857,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
opts->render_driver = optarg;
|
||||
break;
|
||||
case OPT_NO_MIPMAPS:
|
||||
LOGW("Deprecated option --no-mipmaps. "
|
||||
"Use --scale-filter=none instead.");
|
||||
opts->scale_filter = SC_SCALE_FILTER_NONE;
|
||||
opts->mipmaps = false;
|
||||
break;
|
||||
case OPT_NO_KEY_REPEAT:
|
||||
opts->forward_key_repeat = false;
|
||||
@ -945,11 +885,6 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
case OPT_LEGACY_PASTE:
|
||||
opts->legacy_paste = true;
|
||||
break;
|
||||
case OPT_SCALE_FILTER:
|
||||
if (!parse_scale_filter(optarg, &opts->scale_filter)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// getopt prints the error message on stderr
|
||||
return false;
|
||||
|
@ -1,247 +0,0 @@
|
||||
#include "frame_texture.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswscale/swscale.h>
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static inline bool
|
||||
is_swscale_enabled(enum sc_scale_filter scale_filter) {
|
||||
return scale_filter != SC_SCALE_FILTER_NONE
|
||||
&& scale_filter != SC_SCALE_FILTER_TRILINEAR;
|
||||
}
|
||||
|
||||
static SDL_Texture *
|
||||
create_texture(struct sc_frame_texture *ftex, struct size size) {
|
||||
SDL_Renderer *renderer = ftex->renderer;
|
||||
SDL_PixelFormatEnum fmt = is_swscale_enabled(ftex->scale_filter)
|
||||
? SDL_PIXELFORMAT_RGB24
|
||||
: SDL_PIXELFORMAT_YV12;
|
||||
SDL_Texture *texture = SDL_CreateTexture(renderer, fmt,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
size.width, size.height);
|
||||
if (!texture) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ftex->scale_filter == SC_SCALE_FILTER_TRILINEAR) {
|
||||
struct sc_opengl *gl = &ftex->gl;
|
||||
|
||||
SDL_GL_BindTexture(texture, NULL, NULL);
|
||||
|
||||
// Enable trilinear filtering for downscaling
|
||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
GL_LINEAR_MIPMAP_LINEAR);
|
||||
gl->TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -1.f);
|
||||
|
||||
SDL_GL_UnbindTexture(texture);
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_frame_texture_init(struct sc_frame_texture *ftex, SDL_Renderer *renderer,
|
||||
enum sc_scale_filter scale_filter,
|
||||
struct size initial_size) {
|
||||
SDL_RendererInfo renderer_info;
|
||||
int r = SDL_GetRendererInfo(renderer, &renderer_info);
|
||||
const char *renderer_name = r ? NULL : renderer_info.name;
|
||||
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)");
|
||||
|
||||
ftex->scale_filter = scale_filter;
|
||||
|
||||
// starts with "opengl"
|
||||
bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
|
||||
if (use_opengl) {
|
||||
struct sc_opengl *gl = &ftex->gl;
|
||||
sc_opengl_init(gl);
|
||||
|
||||
LOGI("OpenGL version: %s", gl->version);
|
||||
|
||||
if (scale_filter == SC_SCALE_FILTER_TRILINEAR) {
|
||||
bool supports_mipmaps =
|
||||
sc_opengl_version_at_least(gl, 3, 0, /* OpenGL 3.0+ */
|
||||
2, 0 /* OpenGL ES 2.0+ */);
|
||||
if (!supports_mipmaps) {
|
||||
LOGW("Trilinear filtering disabled "
|
||||
"(OpenGL 3.0+ or ES 2.0+ required)");
|
||||
ftex->scale_filter = SC_SCALE_FILTER_NONE;
|
||||
}
|
||||
}
|
||||
} else if (scale_filter == SC_SCALE_FILTER_TRILINEAR) {
|
||||
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
|
||||
}
|
||||
|
||||
LOGD("Scale filter: %s\n", sc_scale_filter_name(ftex->scale_filter));
|
||||
|
||||
LOGI("Initial texture: %" PRIu16 "x%" PRIu16, initial_size.width,
|
||||
initial_size.height);
|
||||
ftex->renderer = renderer;
|
||||
ftex->texture = create_texture(ftex, initial_size);
|
||||
if (!ftex->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
SDL_DestroyRenderer(ftex->renderer);
|
||||
return false;
|
||||
}
|
||||
|
||||
ftex->texture_size = initial_size;
|
||||
ftex->decoded_frame = NULL;
|
||||
memset(ftex->data, 0, sizeof(ftex->data));
|
||||
memset(ftex->linesize, 0, sizeof(ftex->linesize));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sc_frame_texture_destroy(struct sc_frame_texture *ftex) {
|
||||
if (ftex->texture) {
|
||||
SDL_DestroyTexture(ftex->texture);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
to_sws_filter(enum sc_scale_filter scale_filter) {
|
||||
switch (scale_filter) {
|
||||
case SC_SCALE_FILTER_BILINEAR: return SWS_BILINEAR;
|
||||
case SC_SCALE_FILTER_BICUBIC: return SWS_BICUBIC;
|
||||
case SC_SCALE_FILTER_X: return SWS_X;
|
||||
case SC_SCALE_FILTER_POINT: return SWS_POINT;
|
||||
case SC_SCALE_FILTER_AREA: return SWS_AREA;
|
||||
case SC_SCALE_FILTER_BICUBLIN: return SWS_BICUBLIN;
|
||||
case SC_SCALE_FILTER_GAUSS: return SWS_GAUSS;
|
||||
case SC_SCALE_FILTER_SINC: return SWS_SINC;
|
||||
case SC_SCALE_FILTER_LANCZOS: return SWS_LANCZOS;
|
||||
case SC_SCALE_FILTER_SPLINE: return SWS_SPLINE;
|
||||
default: assert(!"unsupported filter");
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
screen_generate_resized_frame(struct sc_frame_texture *ftex,
|
||||
struct size target_size) {
|
||||
assert(is_swscale_enabled(ftex->scale_filter));
|
||||
// TODO
|
||||
|
||||
if (ftex->data[0]) {
|
||||
av_freep(&ftex->data[0]);
|
||||
}
|
||||
|
||||
int ret = av_image_alloc(ftex->data, ftex->linesize, target_size.width,
|
||||
target_size.height, AV_PIX_FMT_RGB24, 16);
|
||||
if (ret < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const AVFrame *input = ftex->decoded_frame;
|
||||
assert(input);
|
||||
|
||||
int flags = to_sws_filter(ftex->scale_filter);
|
||||
struct SwsContext *ctx =
|
||||
sws_getContext(input->width, input->height, AV_PIX_FMT_YUV420P,
|
||||
target_size.width, target_size.height, AV_PIX_FMT_RGB24,
|
||||
flags, NULL, NULL, NULL);
|
||||
if (!ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sws_scale(ctx, (const uint8_t *const *) input->data, input->linesize, 0,
|
||||
input->height, ftex->data, ftex->linesize);
|
||||
sws_freeContext(ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
sc_frame_texture_update_direct(struct sc_frame_texture *ftex,
|
||||
const AVFrame *frame) {
|
||||
struct size frame_size = {frame->width, frame->height};
|
||||
if (ftex->texture_size.width != frame_size.width
|
||||
|| ftex->texture_size.height != frame_size.height) {
|
||||
// Frame dimensions changed, destroy texture
|
||||
SDL_DestroyTexture(ftex->texture);
|
||||
|
||||
LOGI("New texture: %" PRIu16 "x%" PRIu16, frame_size.width,
|
||||
frame_size.height);
|
||||
ftex->texture = create_texture(ftex, frame_size);
|
||||
if (!ftex->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
ftex->texture_size = frame_size;
|
||||
}
|
||||
|
||||
SDL_UpdateYUVTexture(ftex->texture, NULL,
|
||||
frame->data[0], frame->linesize[0],
|
||||
frame->data[1], frame->linesize[1],
|
||||
frame->data[2], frame->linesize[2]);
|
||||
|
||||
if (ftex->scale_filter == SC_SCALE_FILTER_TRILINEAR) {
|
||||
SDL_GL_BindTexture(ftex->texture, NULL, NULL);
|
||||
ftex->gl.GenerateMipmap(GL_TEXTURE_2D);
|
||||
SDL_GL_UnbindTexture(ftex->texture);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
sc_frame_texture_update_swscale(struct sc_frame_texture *ftex,
|
||||
const AVFrame *frame, struct size target_size) {
|
||||
if (ftex->texture_size.width != target_size.width
|
||||
|| ftex->texture_size.height != target_size.height) {
|
||||
// Frame dimensions changed, destroy texture
|
||||
SDL_DestroyTexture(ftex->texture);
|
||||
|
||||
ftex->texture = create_texture(ftex, target_size);
|
||||
if (!ftex->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
ftex->texture_size = target_size;
|
||||
}
|
||||
|
||||
// The frame is valid until the next call to
|
||||
// video_buffer_take_rendering_frame()
|
||||
ftex->decoded_frame = frame;
|
||||
|
||||
bool ok = screen_generate_resized_frame(ftex, target_size);
|
||||
if (!ok) {
|
||||
LOGE("Failed to resize frame\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_UpdateTexture(ftex->texture, NULL, ftex->data[0], ftex->linesize[0]);
|
||||
|
||||
if (ftex->scale_filter == SC_SCALE_FILTER_TRILINEAR) {
|
||||
SDL_GL_BindTexture(ftex->texture, NULL, NULL);
|
||||
ftex->gl.GenerateMipmap(GL_TEXTURE_2D);
|
||||
SDL_GL_UnbindTexture(ftex->texture);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_frame_texture_update(struct sc_frame_texture *ftex, const AVFrame *frame,
|
||||
struct size target_size) {
|
||||
if (is_swscale_enabled(ftex->scale_filter)) {
|
||||
return sc_frame_texture_update_swscale(ftex, frame, target_size);
|
||||
}
|
||||
return sc_frame_texture_update_direct(ftex, frame);
|
||||
}
|
||||
|
||||
bool
|
||||
sc_frame_texture_resize(struct sc_frame_texture *ftex,
|
||||
struct size target_size) {
|
||||
if (is_swscale_enabled(ftex->scale_filter)) {
|
||||
return sc_frame_texture_update_swscale(ftex, ftex->decoded_frame,
|
||||
target_size);
|
||||
}
|
||||
|
||||
// Nothing to do
|
||||
return true;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#ifndef SC_FRAME_TEXTURE_H
|
||||
#define SC_FRAME_TEXTURE_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "coords.h"
|
||||
#include "opengl.h"
|
||||
#include "scrcpy.h"
|
||||
|
||||
struct sc_frame_texture {
|
||||
SDL_Renderer *renderer; // owned by struct screen
|
||||
|
||||
enum sc_scale_filter scale_filter;
|
||||
struct sc_opengl gl;
|
||||
|
||||
SDL_Texture *texture;
|
||||
struct size texture_size;
|
||||
|
||||
// For swscaling
|
||||
const AVFrame *decoded_frame; // owned by the video_buffer
|
||||
uint8_t *data[4];
|
||||
int linesize[4];
|
||||
};
|
||||
|
||||
bool
|
||||
sc_frame_texture_init(struct sc_frame_texture *ftex, SDL_Renderer *renderer,
|
||||
enum sc_scale_filter scale_filter,
|
||||
struct size initial_size);
|
||||
|
||||
void
|
||||
sc_frame_texture_destroy(struct sc_frame_texture *ftex);
|
||||
|
||||
bool
|
||||
sc_frame_texture_update(struct sc_frame_texture *ftex, const AVFrame *frame,
|
||||
struct size target_size);
|
||||
|
||||
bool
|
||||
sc_frame_texture_resize(struct sc_frame_texture *ftex, struct size target_size);
|
||||
|
||||
#endif
|
@ -29,26 +29,6 @@
|
||||
#include "util/log.h"
|
||||
#include "util/net.h"
|
||||
|
||||
static const char *sc_scale_filter_names[SC_SCALE_FILTER__COUNT] = {
|
||||
[SC_SCALE_FILTER_NONE] = "none",
|
||||
[SC_SCALE_FILTER_TRILINEAR] = "trilinear",
|
||||
[SC_SCALE_FILTER_BILINEAR] = "bilinear",
|
||||
[SC_SCALE_FILTER_BICUBIC] = "bicubic",
|
||||
[SC_SCALE_FILTER_X] = "x",
|
||||
[SC_SCALE_FILTER_POINT] = "point",
|
||||
[SC_SCALE_FILTER_AREA] = "area",
|
||||
[SC_SCALE_FILTER_BICUBLIN] = "bicublin",
|
||||
[SC_SCALE_FILTER_GAUSS] = "gauss",
|
||||
[SC_SCALE_FILTER_SINC] = "sinc",
|
||||
[SC_SCALE_FILTER_LANCZOS] = "lanczos",
|
||||
[SC_SCALE_FILTER_SPLINE] = "spline",
|
||||
};
|
||||
|
||||
const char *
|
||||
sc_scale_filter_name(enum sc_scale_filter scale_filter) {
|
||||
return sc_scale_filter_names[scale_filter];
|
||||
}
|
||||
|
||||
static struct server server;
|
||||
static struct screen screen = SCREEN_INITIALIZER;
|
||||
static struct fps_counter fps_counter;
|
||||
@ -204,9 +184,7 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) {
|
||||
}
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
if (screen.has_frame) {
|
||||
screen_handle_window_event(&screen, &event->window);
|
||||
}
|
||||
screen_handle_window_event(&screen, &event->window);
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
if (!options->control) {
|
||||
@ -454,7 +432,7 @@ scrcpy(const struct scrcpy_options *options) {
|
||||
options->window_y, options->window_width,
|
||||
options->window_height,
|
||||
options->window_borderless,
|
||||
options->rotation, options->scale_filter)) {
|
||||
options->rotation, options->mipmaps)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -41,25 +41,6 @@ struct sc_port_range {
|
||||
uint16_t last;
|
||||
};
|
||||
|
||||
enum sc_scale_filter {
|
||||
SC_SCALE_FILTER_NONE,
|
||||
SC_SCALE_FILTER_TRILINEAR, // mipmaps
|
||||
SC_SCALE_FILTER_BILINEAR,
|
||||
SC_SCALE_FILTER_BICUBIC,
|
||||
SC_SCALE_FILTER_X,
|
||||
SC_SCALE_FILTER_POINT,
|
||||
SC_SCALE_FILTER_AREA,
|
||||
SC_SCALE_FILTER_BICUBLIN,
|
||||
SC_SCALE_FILTER_GAUSS,
|
||||
SC_SCALE_FILTER_SINC,
|
||||
SC_SCALE_FILTER_LANCZOS,
|
||||
SC_SCALE_FILTER_SPLINE,
|
||||
SC_SCALE_FILTER__COUNT,
|
||||
};
|
||||
|
||||
const char *
|
||||
sc_scale_filter_name(enum sc_scale_filter scale_filter);
|
||||
|
||||
#define SC_WINDOW_POSITION_UNDEFINED (-0x8000)
|
||||
|
||||
struct scrcpy_options {
|
||||
@ -75,7 +56,6 @@ struct scrcpy_options {
|
||||
enum sc_record_format record_format;
|
||||
struct sc_port_range port_range;
|
||||
struct sc_shortcut_mods shortcut_mods;
|
||||
enum sc_scale_filter scale_filter;
|
||||
uint16_t max_size;
|
||||
uint32_t bit_rate;
|
||||
uint16_t max_fps;
|
||||
@ -95,6 +75,7 @@ struct scrcpy_options {
|
||||
bool render_expired_frames;
|
||||
bool prefer_text;
|
||||
bool window_borderless;
|
||||
bool mipmaps;
|
||||
bool stay_awake;
|
||||
bool force_adb_forward;
|
||||
bool disable_screensaver;
|
||||
@ -122,7 +103,6 @@ struct scrcpy_options {
|
||||
.data = {SC_MOD_LALT, SC_MOD_LSUPER}, \
|
||||
.count = 2, \
|
||||
}, \
|
||||
.scale_filter = SC_SCALE_FILTER_TRILINEAR, \
|
||||
.max_size = DEFAULT_MAX_SIZE, \
|
||||
.bit_rate = DEFAULT_BIT_RATE, \
|
||||
.max_fps = 0, \
|
||||
@ -142,6 +122,7 @@ struct scrcpy_options {
|
||||
.render_expired_frames = false, \
|
||||
.prefer_text = false, \
|
||||
.window_borderless = false, \
|
||||
.mipmaps = true, \
|
||||
.stay_awake = false, \
|
||||
.force_adb_forward = false, \
|
||||
.disable_screensaver = false, \
|
||||
|
143
app/src/screen.c
143
app/src/screen.c
@ -5,6 +5,7 @@
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "icon.xpm"
|
||||
#include "scrcpy.h"
|
||||
#include "tiny_xpm.h"
|
||||
#include "video_buffer.h"
|
||||
#include "util/log.h"
|
||||
@ -194,12 +195,39 @@ screen_init(struct screen *screen) {
|
||||
*screen = (struct screen) SCREEN_INITIALIZER;
|
||||
}
|
||||
|
||||
static inline SDL_Texture *
|
||||
create_texture(struct screen *screen) {
|
||||
SDL_Renderer *renderer = screen->renderer;
|
||||
struct size size = screen->frame_size;
|
||||
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
size.width, size.height);
|
||||
if (!texture) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (screen->mipmaps) {
|
||||
struct sc_opengl *gl = &screen->gl;
|
||||
|
||||
SDL_GL_BindTexture(texture, NULL, NULL);
|
||||
|
||||
// Enable trilinear filtering for downscaling
|
||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
GL_LINEAR_MIPMAP_LINEAR);
|
||||
gl->TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -1.f);
|
||||
|
||||
SDL_GL_UnbindTexture(texture);
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
bool
|
||||
screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
struct size frame_size, bool always_on_top,
|
||||
int16_t window_x, int16_t window_y, uint16_t window_width,
|
||||
uint16_t window_height, bool window_borderless,
|
||||
uint8_t rotation, enum sc_scale_filter scale_filter) {
|
||||
uint8_t rotation, bool mipmaps) {
|
||||
screen->frame_size = frame_size;
|
||||
screen->rotation = rotation;
|
||||
if (rotation) {
|
||||
@ -242,17 +270,39 @@ 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;
|
||||
}
|
||||
|
||||
bool ok = sc_frame_texture_init(&screen->ftex, screen->renderer,
|
||||
scale_filter, frame_size);
|
||||
if (!ok) {
|
||||
LOGC("Could not init frame texture");
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
SDL_DestroyWindow(screen->window);
|
||||
return false;
|
||||
SDL_RendererInfo renderer_info;
|
||||
int r = SDL_GetRendererInfo(screen->renderer, &renderer_info);
|
||||
const char *renderer_name = r ? NULL : renderer_info.name;
|
||||
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)");
|
||||
|
||||
// starts with "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);
|
||||
|
||||
LOGI("OpenGL version: %s", gl->version);
|
||||
|
||||
if (mipmaps) {
|
||||
bool supports_mipmaps =
|
||||
sc_opengl_version_at_least(gl, 3, 0, /* OpenGL 3.0+ */
|
||||
2, 0 /* OpenGL ES 2.0+ */);
|
||||
if (supports_mipmaps) {
|
||||
LOGI("Trilinear filtering enabled");
|
||||
screen->mipmaps = true;
|
||||
} else {
|
||||
LOGW("Trilinear filtering disabled "
|
||||
"(OpenGL 3.0+ or ES 2.0+ required)");
|
||||
}
|
||||
} else {
|
||||
LOGI("Trilinear filtering disabled");
|
||||
}
|
||||
} else {
|
||||
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
|
||||
}
|
||||
|
||||
SDL_Surface *icon = read_xpm(icon_xpm);
|
||||
@ -263,6 +313,15 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
LOGW("Could not load icon");
|
||||
}
|
||||
|
||||
LOGI("Initial texture: %" PRIu16 "x%" PRIu16, frame_size.width,
|
||||
frame_size.height);
|
||||
screen->texture = create_texture(screen);
|
||||
if (!screen->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
screen_destroy(screen);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset the window size to trigger a SIZE_CHANGED event, to workaround
|
||||
// HiDPI issues with some SDL renderers when several displays having
|
||||
// different HiDPI scaling are connected
|
||||
@ -280,9 +339,15 @@ screen_show_window(struct screen *screen) {
|
||||
|
||||
void
|
||||
screen_destroy(struct screen *screen) {
|
||||
sc_frame_texture_destroy(&screen->ftex);
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
SDL_DestroyWindow(screen->window);
|
||||
if (screen->texture) {
|
||||
SDL_DestroyTexture(screen->texture);
|
||||
}
|
||||
if (screen->renderer) {
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
}
|
||||
if (screen->window) {
|
||||
SDL_DestroyWindow(screen->window);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -343,10 +408,13 @@ screen_set_rotation(struct screen *screen, unsigned rotation) {
|
||||
}
|
||||
|
||||
// recreate the texture and resize the window if the frame size has changed
|
||||
static void
|
||||
static bool
|
||||
prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
||||
if (screen->frame_size.width != new_frame_size.width
|
||||
|| screen->frame_size.height != new_frame_size.height) {
|
||||
// frame dimension changed, destroy texture
|
||||
SDL_DestroyTexture(screen->texture);
|
||||
|
||||
screen->frame_size = new_frame_size;
|
||||
|
||||
struct size new_content_size =
|
||||
@ -354,6 +422,32 @@ prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
||||
set_content_size(screen, new_content_size);
|
||||
|
||||
screen_update_content_rect(screen);
|
||||
|
||||
LOGI("New texture: %" PRIu16 "x%" PRIu16,
|
||||
screen->frame_size.width, screen->frame_size.height);
|
||||
screen->texture = create_texture(screen);
|
||||
if (!screen->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// write the frame into the texture
|
||||
static void
|
||||
update_texture(struct screen *screen, const AVFrame *frame) {
|
||||
SDL_UpdateYUVTexture(screen->texture, NULL,
|
||||
frame->data[0], frame->linesize[0],
|
||||
frame->data[1], frame->linesize[1],
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,13 +455,11 @@ 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};
|
||||
prepare_for_frame(screen, new_frame_size);
|
||||
|
||||
struct size rect_size = {screen->rect.w, screen->rect.h};
|
||||
bool ok = sc_frame_texture_update(&screen->ftex, frame, rect_size);
|
||||
if (!ok) {
|
||||
if (!prepare_for_frame(screen, new_frame_size)) {
|
||||
sc_mutex_unlock(&vb->mutex);
|
||||
return false;
|
||||
}
|
||||
update_texture(screen, frame);
|
||||
|
||||
screen_render(screen, false);
|
||||
return true;
|
||||
@ -377,18 +469,11 @@ void
|
||||
screen_render(struct screen *screen, bool update_content_rect) {
|
||||
if (update_content_rect) {
|
||||
screen_update_content_rect(screen);
|
||||
struct size rect_size = {screen->rect.w, screen->rect.h};
|
||||
if (!sc_frame_texture_resize(&screen->ftex, rect_size)) {
|
||||
// FIXME return error
|
||||
LOGC("oops");
|
||||
}
|
||||
}
|
||||
|
||||
struct sc_frame_texture *ftex = &screen->ftex;
|
||||
SDL_RenderClear(ftex->renderer);
|
||||
SDL_RenderClear(screen->renderer);
|
||||
if (screen->rotation == 0) {
|
||||
SDL_RenderCopy(screen->renderer, ftex->texture, NULL,
|
||||
&screen->rect);
|
||||
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &screen->rect);
|
||||
} else {
|
||||
// rotation in RenderCopyEx() is clockwise, while screen->rotation is
|
||||
// counterclockwise (to be consistent with --lock-video-orientation)
|
||||
@ -408,10 +493,10 @@ screen_render(struct screen *screen, bool update_content_rect) {
|
||||
dstrect = &screen->rect;
|
||||
}
|
||||
|
||||
SDL_RenderCopyEx(screen->renderer, ftex->texture, NULL, dstrect,
|
||||
SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect,
|
||||
angle, NULL, 0);
|
||||
}
|
||||
SDL_RenderPresent(ftex->renderer);
|
||||
SDL_RenderPresent(screen->renderer);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -8,17 +8,16 @@
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "coords.h"
|
||||
#include "frame_texture.h"
|
||||
#include "opengl.h"
|
||||
#include "scrcpy.h"
|
||||
|
||||
struct video_buffer;
|
||||
|
||||
struct screen {
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
struct sc_frame_texture ftex;
|
||||
|
||||
SDL_Texture *texture;
|
||||
bool use_opengl;
|
||||
struct sc_opengl gl;
|
||||
struct size frame_size;
|
||||
struct size content_size; // rotated frame_size
|
||||
|
||||
@ -35,12 +34,15 @@ struct screen {
|
||||
bool fullscreen;
|
||||
bool maximized;
|
||||
bool no_window;
|
||||
bool mipmaps;
|
||||
};
|
||||
|
||||
#define SCREEN_INITIALIZER { \
|
||||
.window = NULL, \
|
||||
.renderer = NULL, \
|
||||
.ftex = {0}, \
|
||||
.texture = NULL, \
|
||||
.use_opengl = false, \
|
||||
.gl = {0}, \
|
||||
.frame_size = { \
|
||||
.width = 0, \
|
||||
.height = 0, \
|
||||
@ -65,6 +67,7 @@ struct screen {
|
||||
.fullscreen = false, \
|
||||
.maximized = false, \
|
||||
.no_window = false, \
|
||||
.mipmaps = false, \
|
||||
}
|
||||
|
||||
// initialize default values
|
||||
@ -78,7 +81,7 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
struct size frame_size, bool always_on_top,
|
||||
int16_t window_x, int16_t window_y, uint16_t window_width,
|
||||
uint16_t window_height, bool window_borderless,
|
||||
uint8_t rotation, enum sc_scale_filter scale_filter);
|
||||
uint8_t rotation, bool mipmaps);
|
||||
|
||||
// show the window
|
||||
void
|
||||
|
@ -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);
|
||||
|
Reference in New Issue
Block a user