From c464464005dec292902fadb77b0bdea1166bcd85 Mon Sep 17 00:00:00 2001 From: Michael Scherle Date: Fri, 23 May 2025 15:44:56 +0200 Subject: [PATCH] HACK: add 444 support and other hacks. --- server/display-channel.cpp | 9 +++++ server/gstreamer-encoder.c | 78 +++++++++++++++++++++++++++++++------- server/reds.cpp | 18 ++++++++- 3 files changed, 90 insertions(+), 15 deletions(-) diff --git a/server/display-channel.cpp b/server/display-channel.cpp index 53a4724d..c2a864c0 100644 --- a/server/display-channel.cpp +++ b/server/display-channel.cpp @@ -2230,6 +2230,15 @@ DisplayChannel::DisplayChannel(RedsState *reds, set_cap(SPICE_DISPLAY_CAP_PREF_VIDEO_CODEC_TYPE); set_cap(SPICE_DISPLAY_CAP_STREAM_REPORT); + //TODO actualy check + set_cap(SPICE_DISPLAY_CAP_CODEC_H264); + set_cap(SPICE_DISPLAY_CAP_CODEC_VP9); + set_cap(SPICE_DISPLAY_CAP_CODEC_H265); + set_cap(SPICE_DISPLAY_CAP_CODEC_AV1); + set_cap(SPICE_DISPLAY_CAP_CODEC_VP9_444); + set_cap(SPICE_DISPLAY_CAP_CODEC_H265_444); + // set_cap(SPICE_DISPLAY_CAP_CODEC_VP9_U); + reds_register_channel(reds, this); } diff --git a/server/gstreamer-encoder.c b/server/gstreamer-encoder.c index 7619e55e..8fcfc34b 100644 --- a/server/gstreamer-encoder.c +++ b/server/gstreamer-encoder.c @@ -919,10 +919,17 @@ static const gchar* get_gst_codec_name(const SpiceGstEncoder *encoder) return "avenc_mjpeg"; case SPICE_VIDEO_CODEC_TYPE_VP8: return "vp8enc"; + case SPICE_VIDEO_CODEC_TYPE_H264_444: case SPICE_VIDEO_CODEC_TYPE_H264: return "x264enc"; + case SPICE_VIDEO_CODEC_TYPE_VP9_444: case SPICE_VIDEO_CODEC_TYPE_VP9: return "vp9enc"; + case SPICE_VIDEO_CODEC_TYPE_H265_444: + case SPICE_VIDEO_CODEC_TYPE_H265: + return "x265enc"; + case SPICE_VIDEO_CODEC_TYPE_AV1: + return "av1enc"; default: /* gstreamer_encoder_new() should have rejected this codec type */ spice_warning("unsupported codec type %d", encoder->base.codec_type); @@ -937,6 +944,16 @@ static const char video_codecs[][8] = { { "h264" }, { "vp9" }, { "h265" }, + { "av1" }, + { "h264" }, + { "vp9" }, + { "h265" }, + { "av1" }, + { "h264"}, + { "vp9"}, + { "h265"}, + { "av1"}, + }; static bool gst_features_lookup(const gchar *feature_name) @@ -990,7 +1007,7 @@ static gchar *find_best_hw_plugin(const gchar *codec_name) } g_free(feature_name); if(i == 1 && codec_name){ - feature_name = g_strconcat(plugins[i], "lpenc", NULL); + feature_name = g_strconcat(plugins[i], codec_name, "lpenc", NULL); if (gst_features_lookup(feature_name)) { return feature_name; } @@ -1022,7 +1039,7 @@ static gchar *get_hw_gstenc_opts(const gchar *encoder, const gchar *codec_name) } } else { if (strcmp(codec_name, "vp9") == 0) { - gstenc_opts = g_strdup("min-qp=15 max-qp=35 rate-control=16 ref-frames=0 target-usage=7"); + gstenc_opts = g_strdup("min-qp=15 max-qp=35 rate-control=16 target-usage=7 gf-group-size=1"); } else { gstenc_opts = g_strdup("rate-control=16 b-frames=0 target-usage=7 min-qp=15 max-qp=35"); } @@ -1030,10 +1047,12 @@ static gchar *get_hw_gstenc_opts(const gchar *encoder, const gchar *codec_name) return gstenc_opts; } -static void try_intel_hw_plugins(const gchar *codec_name, gchar **converter, +static void try_intel_hw_plugins(SpiceGstEncoder* spice_encoder, gchar **converter, gchar **gstenc_name, gchar **gstenc_opts) { gchar *encoder, *vpp; + SpiceVideoCodecType codec_type = spice_encoder->base.codec_type; + const char *codec_name = video_codecs[codec_type]; if (strcmp(codec_name, "vp8") == 0) { return; @@ -1055,11 +1074,20 @@ static void try_intel_hw_plugins(const gchar *codec_name, gchar **converter, *gstenc_name = encoder; *gstenc_opts = get_hw_gstenc_opts(encoder, codec_name); - if (g_str_has_prefix(vpp, "vaapi")) { - *converter = g_strconcat(vpp, " ! video/x-raw(memory:VASurface),format=NV12", NULL); - } else { - *converter = g_strconcat(vpp, " ! video/x-raw(memory:VAMemory),format=NV12", NULL); + char format[5] = "NV12"; + if( codec_type == SPICE_VIDEO_CODEC_TYPE_AV1_444 || + codec_type == SPICE_VIDEO_CODEC_TYPE_H264_444 || + codec_type == SPICE_VIDEO_CODEC_TYPE_H265_444 || + codec_type == SPICE_VIDEO_CODEC_TYPE_VP9_444 ) { + g_strlcpy(format,"VUYA",5); } + + char memory[11] = "VASurface"; + if(!g_str_has_prefix(vpp, "vaapi")){ + g_strlcpy(memory,"VAMemory",11); + } + *converter = g_strdup_printf(" %s ! capsfilter name=vpp caps=\"video/x-raw(memory:%s),format=%s\"", + vpp, memory,format); g_free(vpp); } @@ -1113,6 +1141,7 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder) case SPICE_VIDEO_CODEC_TYPE_MJPEG: gstenc_opts = g_strdup(""); break; + case SPICE_VIDEO_CODEC_TYPE_VP9_444: case SPICE_VIDEO_CODEC_TYPE_VP9: case SPICE_VIDEO_CODEC_TYPE_VP8: { /* See http://www.webmproject.org/docs/encoder-parameters/ @@ -1134,6 +1163,7 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder) gstenc_opts = g_strdup_printf("end-usage=cbr min-quantizer=10 error-resilient=default lag-in-frames=0 deadline=1 cpu-used=4"); break; } + case SPICE_VIDEO_CODEC_TYPE_H264_444: case SPICE_VIDEO_CODEC_TYPE_H264: /* - Set tune and sliced-threads to ensure a zero-frame latency * - qp-min ensures the bitrate does not get needlessly high. @@ -1146,6 +1176,23 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder) */ gstenc_opts = g_strdup("byte-stream=true aud=true qp-min=15 qp-max=35 tune=4 sliced-threads=true speed-preset=ultrafast intra-refresh=true"); break; + case SPICE_VIDEO_CODEC_TYPE_H265_444: + case SPICE_VIDEO_CODEC_TYPE_H265: + /* - Set tune and sliced-threads to ensure a zero-frame latency + * - qp-min ensures the bitrate does not get needlessly high. + * - qp-max ensures the compression does not go so high that the videoclear + * is unrecognizable. When that threshold is reached it is better to + * drop frames to lower the bit rate further. + * - Set speed-preset to get realtime speed. + * - Set intra-refresh to get more uniform compressed frame sizes, + * thus helping with streaming. + */ + gstenc_opts = g_strdup("qp=15 tune=4 speed-preset=ultrafast "); + break; + case SPICE_VIDEO_CODEC_TYPE_AV1_444: + case SPICE_VIDEO_CODEC_TYPE_AV1: + gstenc_opts = g_strdup(""); + break; default: /* gstreamer_encoder_new() should have rejected this codec type */ spice_warning("unsupported codec type %d", encoder->base.codec_type); @@ -1154,10 +1201,9 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder) return FALSE; } - const char *codec_name = video_codecs[encoder->base.codec_type]; GpuVendor vendor = spice_udev_detect_gpu(INTEL_VENDOR_ID); if (vendor == VENDOR_GPU_DETECTED) { - try_intel_hw_plugins(codec_name, &converter, &gstenc_name, + try_intel_hw_plugins(encoder, &converter, &gstenc_name, &gstenc_opts); } @@ -1673,9 +1719,9 @@ spice_gst_encoder_configure_pipeline(SpiceGstEncoder *encoder, return VIDEO_ENCODER_FRAME_UNSUPPORTED; } - if (width != encoder->width || height != encoder->height || + if ((width != encoder->width || height != encoder->height || encoder->spice_format != spice_format || - encoder->drm_format != drm_format) { + encoder->drm_format != drm_format) && (width > 0 && height > 0)) { spice_debug("video format change: width %d -> %d, height %d -> %d," "spice format %d -> %d, drm format %u -> %u", encoder->width, width, encoder->height, height, @@ -1716,7 +1762,7 @@ spice_gst_encoder_configure_pipeline(SpiceGstEncoder *encoder, if (handle_server_drops(encoder, frame_mm_time) || frame_mm_time < encoder->next_frame_mm_time) { /* Drop the frame to limit the outgoing bit rate. */ - return VIDEO_ENCODER_FRAME_DROP; + //return VIDEO_ENCODER_FRAME_DROP; } if (!configure_pipeline(encoder)) { @@ -2023,7 +2069,13 @@ VideoEncoder *gstreamer_encoder_new(SpiceVideoCodecType codec_type, spice_return_val_if_fail(codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG || codec_type == SPICE_VIDEO_CODEC_TYPE_VP8 || codec_type == SPICE_VIDEO_CODEC_TYPE_VP9 || - codec_type == SPICE_VIDEO_CODEC_TYPE_H264, NULL); + codec_type == SPICE_VIDEO_CODEC_TYPE_H264|| + codec_type == SPICE_VIDEO_CODEC_TYPE_H264 || + codec_type == SPICE_VIDEO_CODEC_TYPE_H265 || + codec_type == SPICE_VIDEO_CODEC_TYPE_H264_444 || + codec_type == SPICE_VIDEO_CODEC_TYPE_H265_444 || + codec_type == SPICE_VIDEO_CODEC_TYPE_VP9_444 || + codec_type == SPICE_VIDEO_CODEC_TYPE_AV1, NULL); GError *err = NULL; if (!gst_init_check(NULL, NULL, &err)) { diff --git a/server/reds.cpp b/server/reds.cpp index b0f1be6a..2cdf9250 100644 --- a/server/reds.cpp +++ b/server/reds.cpp @@ -3461,11 +3461,11 @@ err: static const char default_renderer[] = "sw"; #if defined(HAVE_GSTREAMER_1_0) -#define GSTREAMER_CODECS "gstreamer:mjpeg;gstreamer:h264;gstreamer:vp8;gstreamer:vp9;" +#define GSTREAMER_CODECS "gstreamer:h264;gstreamer:h265;gstreamer:vp8;gstreamer:vp9;gstreamer:av1;gstreamer:h265_444;gstreamer:vp9_444;gstreamer:mjpeg;" #else #define GSTREAMER_CODECS "" #endif -static const char default_video_codecs[] = "spice:mjpeg;" GSTREAMER_CODECS; +static const char default_video_codecs[] = GSTREAMER_CODECS "spice:mjpeg;"; /* new interface */ SPICE_GNUC_VISIBLE SpiceServer *spice_server_new(void) @@ -3583,6 +3583,13 @@ static const EnumNames video_codec_names[] = { {SPICE_VIDEO_CODEC_TYPE_VP8, "vp8"}, {SPICE_VIDEO_CODEC_TYPE_H264, "h264"}, {SPICE_VIDEO_CODEC_TYPE_VP9, "vp9"}, + {SPICE_VIDEO_CODEC_TYPE_H265, "h265"}, + {SPICE_VIDEO_CODEC_TYPE_AV1, "av1"}, + {SPICE_VIDEO_CODEC_TYPE_H264_444, "h264_444"}, + {SPICE_VIDEO_CODEC_TYPE_VP9_444, "vp9_444"}, + {SPICE_VIDEO_CODEC_TYPE_H265_444, "h265_444"}, + {SPICE_VIDEO_CODEC_TYPE_AV1_444, "av1_444"}, + {0, nullptr}, }; @@ -3591,6 +3598,13 @@ static const int video_codec_caps[] = { SPICE_DISPLAY_CAP_CODEC_VP8, SPICE_DISPLAY_CAP_CODEC_H264, SPICE_DISPLAY_CAP_CODEC_VP9, + SPICE_DISPLAY_CAP_CODEC_H265, + SPICE_DISPLAY_CAP_CODEC_AV1, + SPICE_DISPLAY_CAP_CODEC_H264_444, + SPICE_DISPLAY_CAP_CODEC_VP9_444, + SPICE_DISPLAY_CAP_CODEC_H265_444, + SPICE_DISPLAY_CAP_CODEC_AV1_444, + }; char *reds_get_video_codec_fullname(RedVideoCodec *codec)