diff --git a/app/meson.build b/app/meson.build index 18838f8b..81d92cbf 100644 --- a/app/meson.build +++ b/app/meson.build @@ -47,6 +47,7 @@ if not get_option('crossbuild_windows') dependency('libavformat'), dependency('libavcodec'), dependency('libavutil'), + dependency('libswscale'), dependency('sdl2'), ] diff --git a/app/src/cli.c b/app/src/cli.c index 4543835e..db51d856 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -642,6 +642,7 @@ guess_record_format(const char *filename) { 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; @@ -650,8 +651,49 @@ parse_scale_filter(const char *optarg, enum sc_scale_filter *filter) { *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 \"none\" or \"trilinear\")", optarg); + "(expected one of [TODO])", optarg); return false; } diff --git a/app/src/resizer.c b/app/src/resizer.c index 4bb6ab85..25198658 100644 --- a/app/src/resizer.c +++ b/app/src/resizer.c @@ -1,12 +1,14 @@ #include "resizer.h" #include +#include #include "util/log.h" bool sc_resizer_init(struct sc_resizer *resizer, struct video_buffer *vb_in, - struct video_buffer *vb_out, struct size size) { + struct video_buffer *vb_out, enum sc_scale_filter scale_filter, + struct size size) { bool ok = sc_mutex_init(&resizer->mutex); if (!ok) { return false; @@ -27,6 +29,7 @@ sc_resizer_init(struct sc_resizer *resizer, struct video_buffer *vb_in, resizer->vb_in = vb_in; resizer->vb_out = vb_out; + resizer->scale_filter = scale_filter; resizer->size = size; resizer->input_frame = NULL; @@ -44,11 +47,57 @@ sc_resizer_destroy(struct sc_resizer *resizer) { sc_mutex_destroy(&resizer->mutex); } +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 sc_resizer_swscale(struct sc_resizer *resizer) { assert(!resizer->resized_frame->buf[0]); // The frame must be "empty" + assert(resizer->size.width); + assert(resizer->size.height); - return false; + const AVFrame *in = resizer->input_frame; + struct size size = resizer->size; + + AVFrame *out = resizer->resized_frame; + out->format = AV_PIX_FMT_RGB24; + out->width = size.width; + out->height = size.height; + + int ret = av_frame_get_buffer(out, 32); + if (ret < 0) { + return false; + } + + int flags = to_sws_filter(resizer->scale_filter); + struct SwsContext *ctx = + sws_getContext(in->width, in->height, AV_PIX_FMT_YUV420P, + size.width, size.height, AV_PIX_FMT_RGB24, flags, NULL, + NULL, NULL); + if (!ctx) { + av_frame_unref(out); + return false; + } + + sws_scale(ctx, (const uint8_t *const *) in->data, in->linesize, 0, + in->height, out->data, out->linesize); + sws_freeContext(ctx); + + return true; } static int diff --git a/app/src/resizer.h b/app/src/resizer.h index eb1d929c..5bd53de7 100644 --- a/app/src/resizer.h +++ b/app/src/resizer.h @@ -7,12 +7,14 @@ #include #include "coords.h" +#include "scrcpy.h" #include "util/thread.h" #include "video_buffer.h" struct sc_resizer { struct video_buffer *vb_in; struct video_buffer *vb_out; + enum sc_scale_filter scale_filter; struct size size; // valid until the next call to video_buffer_consumer_take_frame(vb_in) @@ -30,7 +32,8 @@ struct sc_resizer { bool sc_resizer_init(struct sc_resizer *resizer, struct video_buffer *vb_in, - struct video_buffer *vb_out, struct size initial_size); + struct video_buffer *vb_out, enum sc_scale_filter scale_filter, + struct size size); void sc_resizer_destroy(struct sc_resizer *resizer); diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 62fe1fce..d9e18c5b 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -44,6 +44,17 @@ struct sc_port_range { 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, }; #define SC_WINDOW_POSITION_UNDEFINED (-0x8000) diff --git a/app/src/screen.c b/app/src/screen.c index 63554b9e..8b035654 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -241,7 +241,7 @@ create_texture(struct screen *screen) { return NULL; } - if (screen->mipmaps) { + if (screen->scale_filter == SC_SCALE_FILTER_TRILINEAR) { struct sc_opengl *gl = &screen->gl; SDL_GL_BindTexture(texture, NULL, NULL); @@ -257,6 +257,12 @@ create_texture(struct screen *screen) { return texture; } +static inline bool +is_swscale(enum sc_scale_filter scale_filter) { + return scale_filter != SC_SCALE_FILTER_NONE + && scale_filter != SC_SCALE_FILTER_TRILINEAR; +} + bool screen_init_rendering(struct screen *screen, const char *window_title, struct size frame_size, bool always_on_top, @@ -329,10 +335,10 @@ screen_init_rendering(struct screen *screen, const char *window_title, 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)"); + scale_filter = SC_SCALE_FILTER_NONE; } } else { LOGI("Trilinear filtering disabled"); @@ -341,6 +347,29 @@ screen_init_rendering(struct screen *screen, const char *window_title, LOGD("Trilinear filtering disabled (not an OpenGL renderer)"); } + screen->use_swscale = is_swscale(scale_filter); + if (screen->use_swscale) { + bool ok = sc_resizer_init(&screen->resizer, screen->vb, + &screen->resizer_vb, scale_filter, + window_size); + if (!ok) { + LOGE("Could not create resizer"); + SDL_DestroyRenderer(screen->renderer); + SDL_DestroyWindow(screen->window); + return false; + } + + ok = sc_resizer_start(&screen->resizer); + if (!ok) { + LOGE("Could not start resizer"); + sc_resizer_destroy(&screen->resizer); + SDL_DestroyRenderer(screen->renderer); + SDL_DestroyWindow(screen->window); + } + } + + screen->scale_filter = scale_filter; + SDL_Surface *icon = read_xpm(icon_xpm); if (icon) { SDL_SetWindowIcon(screen->window, icon); @@ -376,6 +405,11 @@ screen_show_window(struct screen *screen) { void screen_destroy(struct screen *screen) { + if (screen->use_swscale) { + sc_resizer_stop(&screen->resizer); + sc_resizer_join(&screen->resizer); + sc_resizer_destroy(&screen->resizer); + } if (screen->texture) { SDL_DestroyTexture(screen->texture); } @@ -476,7 +510,7 @@ update_texture(struct screen *screen, const AVFrame *frame) { frame->data[1], frame->linesize[1], frame->data[2], frame->linesize[2]); - if (screen->mipmaps) { + if (screen->scale_filter == SC_SCALE_FILTER_TRILINEAR) { SDL_GL_BindTexture(screen->texture, NULL, NULL); screen->gl.GenerateMipmap(GL_TEXTURE_2D); SDL_GL_UnbindTexture(screen->texture); diff --git a/app/src/screen.h b/app/src/screen.h index acd574fe..10c0f2d8 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -9,7 +9,9 @@ #include "coords.h" #include "opengl.h" +#include "resizer.h" #include "scrcpy.h" +#include "video_buffer.h" struct video_buffer; @@ -37,6 +39,13 @@ struct screen { bool fullscreen; bool maximized; bool mipmaps; + + enum sc_scale_filter scale_filter; + bool use_swscale; + + // For swscale resizing + struct video_buffer resizer_vb; + struct sc_resizer resizer; }; // initialize default values