Compare commits

..

2 Commits

Author SHA1 Message Date
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
2a3c6b64dd Assert non-recursive usage of mutexes 2021-02-04 08:12:22 +01:00
10 changed files with 133 additions and 452 deletions

View File

@ -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'),
]

View File

@ -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".

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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, \

View File

@ -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

View File

@ -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

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);