From nobody Fri Dec 19 06:34:38 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744977674287946.7609195003047; Fri, 18 Apr 2025 05:01:14 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvG-0005KB-P5; Fri, 18 Apr 2025 07:30:18 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5jvC-0005Hk-UV for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:15 -0400 Received: from [94.136.29.99] (helo=zilli.proxmox.com) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jv3-0005aQ-TS for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:14 -0400 Received: by zilli.proxmox.com (Postfix, from userid 1000) id 309391C14F7; Fri, 18 Apr 2025 13:29:58 +0200 (CEST) From: Dietmar Maurer To: marcandre.lureau@redhat.com, qemu-devel@nongnu.org Cc: Dietmar Maurer Subject: [PATCH v3 1/9] new configure option to enable gstreamer Date: Fri, 18 Apr 2025 13:29:45 +0200 Message-Id: <20250418112953.1744442-2-dietmar@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418112953.1744442-1-dietmar@proxmox.com> References: <20250418112953.1744442-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 94.136.29.99 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=94.136.29.99; envelope-from=dietmar@zilli.proxmox.com; helo=zilli.proxmox.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, NO_DNS_FOR_FROM=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744977676719019000 Content-Type: text/plain; charset="utf-8" GStreamer is required to implement H264 encoding for VNC. Please note that QEMU already depends on this library when you enable Spice. Signed-off-by: Dietmar Maurer Reviewed-by: Marc-Andr=C3=A9 Lureau --- meson.build | 10 ++++++++++ meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 5 ++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 41f68d3806..28ca37855a 100644 --- a/meson.build +++ b/meson.build @@ -1348,6 +1348,14 @@ if not get_option('zstd').auto() or have_block required: get_option('zstd'), method: 'pkg-config') endif + +gstreamer =3D not_found +if not get_option('gstreamer').auto() or have_block + gstreamer =3D dependency('gstreamer-1.0 gstreamer-base-1.0', version: '>= =3D1.22.0', + required: get_option('gstreamer'), + method: 'pkg-config') +endif + qpl =3D not_found if not get_option('qpl').auto() or have_system qpl =3D dependency('qpl', version: '>=3D1.5.0', @@ -2563,6 +2571,7 @@ config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc= _trim) config_host_data.set('CONFIG_STATX', has_statx) config_host_data.set('CONFIG_STATX_MNT_ID', has_statx_mnt_id) config_host_data.set('CONFIG_ZSTD', zstd.found()) +config_host_data.set('CONFIG_GSTREAMER', gstreamer.found()) config_host_data.set('CONFIG_QPL', qpl.found()) config_host_data.set('CONFIG_UADK', uadk.found()) config_host_data.set('CONFIG_QATZIP', qatzip.found()) @@ -4836,6 +4845,7 @@ summary_info +=3D {'snappy support': snappy} summary_info +=3D {'bzip2 support': libbzip2} summary_info +=3D {'lzfse support': liblzfse} summary_info +=3D {'zstd support': zstd} +summary_info +=3D {'gstreamer support': gstreamer} summary_info +=3D {'Query Processing Library support': qpl} summary_info +=3D {'UADK Library support': uadk} summary_info +=3D {'qatzip support': qatzip} diff --git a/meson_options.txt b/meson_options.txt index 59d973bca0..11cd132be5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -254,6 +254,8 @@ option('vnc_sasl', type : 'feature', value : 'auto', description: 'SASL authentication for VNC server') option('vte', type : 'feature', value : 'auto', description: 'vte support for the gtk UI') +option('gstreamer', type : 'feature', value : 'auto', + description: 'for VNC H.264 encoding with gstreamer') =20 # GTK Clipboard implementation is disabled by default, since it may cause = hangs # of the guest VCPUs. See gitlab issue 1150: diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 3e8e00852b..b0c273d61e 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -229,6 +229,7 @@ meson_options_help() { printf "%s\n" ' Xen PCI passthrough support' printf "%s\n" ' xkbcommon xkbcommon support' printf "%s\n" ' zstd zstd compression support' + printf "%s\n" ' gstreamer gstreamer support (H264 for VNC)' } _meson_option_parse() { case $1 in @@ -581,6 +582,8 @@ _meson_option_parse() { --disable-xkbcommon) printf "%s" -Dxkbcommon=3Ddisabled ;; --enable-zstd) printf "%s" -Dzstd=3Denabled ;; --disable-zstd) printf "%s" -Dzstd=3Ddisabled ;; - *) return 1 ;; + --enable-gstreamer) printf "%s" -Dgstreamer=3Denabled ;; + --disable-gstreamer) printf "%s" -Dgstreamer=3Ddisabled ;; + *) return 1 ;; esac } --=20 2.39.5 From nobody Fri Dec 19 06:34:38 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744976246204247.29849871954218; Fri, 18 Apr 2025 04:37:26 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5k1X-0002vX-Ok; Fri, 18 Apr 2025 07:36:47 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5k1R-0002um-34 for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:36:41 -0400 Received: from [94.136.29.99] (helo=zilli.proxmox.com) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5k1J-0006N0-CA for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:36:40 -0400 Received: by zilli.proxmox.com (Postfix, from userid 1000) id 3225D1C1674; Fri, 18 Apr 2025 13:29:58 +0200 (CEST) From: Dietmar Maurer To: marcandre.lureau@redhat.com, qemu-devel@nongnu.org Cc: Dietmar Maurer Subject: [PATCH v3 2/9] add vnc h264 encoder Date: Fri, 18 Apr 2025 13:29:46 +0200 Message-Id: <20250418112953.1744442-3-dietmar@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418112953.1744442-1-dietmar@proxmox.com> References: <20250418112953.1744442-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 94.136.29.99 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=94.136.29.99; envelope-from=dietmar@zilli.proxmox.com; helo=zilli.proxmox.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, NO_DNS_FOR_FROM=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744976247956019100 Content-Type: text/plain; charset="utf-8" This patch implements H264 support for VNC. The RFB protocol extension is defined in: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#open-h-264-en= coding Currently the Gstreamer x264enc plugin (software encoder) is used to encode the video stream. The gstreamer pipe is: appsrc -> videoconvert -> x264enc -> appsink Note: videoconvert is required for RGBx to YUV420 conversion. The code still use the VNC server framebuffer change detection, and only encodes and sends video frames if there are changes. Signed-off-by: Dietmar Maurer --- ui/meson.build | 1 + ui/vnc-enc-h264.c | 282 ++++++++++++++++++++++++++++++++++++++++++++++ ui/vnc-jobs.c | 49 +++++--- ui/vnc.c | 21 ++++ ui/vnc.h | 21 ++++ 5 files changed, 359 insertions(+), 15 deletions(-) create mode 100644 ui/vnc-enc-h264.c diff --git a/ui/meson.build b/ui/meson.build index 35fb04cadf..34f1f33699 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -46,6 +46,7 @@ vnc_ss.add(files( )) vnc_ss.add(zlib, jpeg) vnc_ss.add(when: sasl, if_true: files('vnc-auth-sasl.c')) +vnc_ss.add(when: gstreamer, if_true: files('vnc-enc-h264.c')) system_ss.add_all(when: [vnc, pixman], if_true: vnc_ss) system_ss.add(when: vnc, if_false: files('vnc-stubs.c')) =20 diff --git a/ui/vnc-enc-h264.c b/ui/vnc-enc-h264.c new file mode 100644 index 0000000000..3abe6a1528 --- /dev/null +++ b/ui/vnc-enc-h264.c @@ -0,0 +1,282 @@ +/* + * QEMU VNC display driver: hextile encoding + * + * Copyright (C) 2025 Proxmox Server Solutions GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included= in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "vnc.h" + +#include + +static void destroy_encoder_context(VncState *vs) +{ + gst_clear_object(&vs->h264->source); + gst_clear_object(&vs->h264->convert); + gst_clear_object(&vs->h264->gst_encoder); + gst_clear_object(&vs->h264->sink); + gst_clear_object(&vs->h264->pipeline); +} + +static bool create_encoder_context(VncState *vs, int w, int h) +{ + g_autoptr(GstCaps) source_caps =3D NULL; + GstStateChangeReturn state_change_ret; + + g_assert(vs->h264 !=3D NULL); + + if (vs->h264->sink) { + if (w !=3D vs->h264->width || h !=3D vs->h264->height) { + destroy_encoder_context(vs); + } + } + + if (vs->h264->sink) { + return TRUE; + } + + vs->h264->width =3D w; + vs->h264->height =3D h; + + vs->h264->source =3D gst_element_factory_make("appsrc", "source"); + if (!vs->h264->source) { + VNC_DEBUG("Could not create gst source\n"); + goto error; + } + + vs->h264->convert =3D gst_element_factory_make("videoconvert", "conver= t"); + if (!vs->h264->convert) { + VNC_DEBUG("Could not create gst convert element\n"); + goto error; + } + + vs->h264->gst_encoder =3D gst_element_factory_make("x264enc", "gst-enc= oder"); + if (!vs->h264->gst_encoder) { + VNC_DEBUG("Could not create gst x264 encoder\n"); + goto error; + } + + g_object_set( + vs->h264->gst_encoder, + "tune", 4, /* zerolatency */ + /* + * fix for zerolatency with novnc (without, noVNC displays + * green stripes) + */ + "threads", 1, + "pass", 5, /* Constant Quality */ + "quantizer", 26, + /* avoid access unit delimiters (Nal Unit Type 9) - not required */ + "aud", false, + NULL); + + vs->h264->sink =3D gst_element_factory_make("appsink", "sink"); + if (!vs->h264->sink) { + VNC_DEBUG("Could not create gst sink\n"); + goto error; + } + + vs->h264->pipeline =3D gst_pipeline_new("vnc-h264-pipeline"); + if (!vs->h264->pipeline) { + VNC_DEBUG("Could not create gst pipeline\n"); + goto error; + } + + gst_object_ref(vs->h264->source); + if (!gst_bin_add(GST_BIN(vs->h264->pipeline), vs->h264->source)) { + gst_object_unref(vs->h264->source); + VNC_DEBUG("Could not add source to gst pipeline\n"); + goto error; + } + + gst_object_ref(vs->h264->convert); + if (!gst_bin_add(GST_BIN(vs->h264->pipeline), vs->h264->convert)) { + gst_object_unref(vs->h264->convert); + VNC_DEBUG("Could not add convert to gst pipeline\n"); + goto error; + } + + gst_object_ref(vs->h264->gst_encoder); + if (!gst_bin_add(GST_BIN(vs->h264->pipeline), vs->h264->gst_encoder)) { + gst_object_unref(vs->h264->gst_encoder); + VNC_DEBUG("Could not add encoder to gst pipeline\n"); + goto error; + } + + gst_object_ref(vs->h264->sink); + if (!gst_bin_add(GST_BIN(vs->h264->pipeline), vs->h264->sink)) { + gst_object_unref(vs->h264->sink); + VNC_DEBUG("Could not add sink to gst pipeline\n"); + goto error; + } + + source_caps =3D gst_caps_new_simple( + "video/x-raw", + "format", G_TYPE_STRING, "BGRx", + "framerate", GST_TYPE_FRACTION, 33, 1, + "width", G_TYPE_INT, w, + "height", G_TYPE_INT, h, + NULL); + + if (!source_caps) { + VNC_DEBUG("Could not create source caps filter\n"); + goto error; + } + + g_object_set(vs->h264->source, "caps", source_caps, NULL); + + if (gst_element_link_many( + vs->h264->source, + vs->h264->convert, + vs->h264->gst_encoder, + vs->h264->sink, + NULL + ) !=3D TRUE) { + VNC_DEBUG("Elements could not be linked.\n"); + goto error; + } + + /* Start playing */ + state_change_ret =3D gst_element_set_state( + vs->h264->pipeline, GST_STATE_PLAYING); + + if (state_change_ret =3D=3D GST_STATE_CHANGE_FAILURE) { + VNC_DEBUG("Unable to set the pipeline to the playing state.\n"); + goto error; + } + + return TRUE; + + error: + destroy_encoder_context(vs); + return FALSE; +} + +bool vnc_h264_encoder_init(VncState *vs) +{ + g_assert(vs->h264 =3D=3D NULL); + + vs->h264 =3D g_new0(VncH264, 1); + + return true; +} + +/* + * Returns the number of generated framebuffer updates, + * or -1 in case of errors + */ +int vnc_h264_send_framebuffer_update( + VncState *vs, + int _x, + int _y, + int _w, + int _h +) { + int n =3D 0; + int rdb_h264_flags =3D 0; + int width, height; + uint8_t *src_data_ptr =3D NULL; + size_t src_data_size; + GstFlowReturn flow_ret =3D GST_FLOW_ERROR; + GstBuffer *src_buffer =3D NULL; + + g_assert(vs->h264 !=3D NULL); + g_assert(vs->vd !=3D NULL); + g_assert(vs->vd->server !=3D NULL); + + width =3D pixman_image_get_width(vs->vd->server); + height =3D pixman_image_get_height(vs->vd->server); + + g_assert(width =3D=3D vs->client_width); + g_assert(height =3D=3D vs->client_height); + + if (vs->h264->sink) { + if (width !=3D vs->h264->width || height !=3D vs->h264->height) { + rdb_h264_flags =3D 2; + } + } else { + rdb_h264_flags =3D 2; + } + + if (!create_encoder_context(vs, width, height)) { + VNC_DEBUG("Create encoder context failed\n"); + return -1; + } + + g_assert(vs->h264->sink !=3D NULL); + + src_data_ptr =3D vnc_server_fb_ptr(vs->vd, 0, 0); + src_data_size =3D width * height * VNC_SERVER_FB_BYTES; + + src_buffer =3D gst_buffer_new_wrapped_full( + 0, src_data_ptr, src_data_size, 0, src_data_size, NULL, NULL); + + g_signal_emit_by_name( + vs->h264->source, "push-buffer", src_buffer, &flow_ret); + + if (flow_ret !=3D GST_FLOW_OK) { + VNC_DEBUG("gst appsrc push buffer failed\n"); + return -1; + } + + do { + GstSample *sample =3D NULL; + GstMapInfo map; + GstBuffer *out_buffer; + + /* Retrieve the buffer */ + g_signal_emit_by_name(vs->h264->sink, "try-pull-sample", 0, &sampl= e); + if (!sample) { + break; + } + out_buffer =3D gst_sample_get_buffer(sample); + if (gst_buffer_map(out_buffer, &map, 0)) { + vnc_framebuffer_update(vs, 0, 0, width, height, VNC_ENCODING_H= 264); + vnc_write_s32(vs, map.size); /* write data length */ + vnc_write_s32(vs, rdb_h264_flags); /* write flags */ + rdb_h264_flags =3D 0; + + VNC_DEBUG("GST vnc_h264_update send %ld\n", map.size); + + vnc_write(vs, map.data, map.size); + + gst_buffer_unmap(out_buffer, &map); + + n +=3D 1; + } else { + VNC_DEBUG("unable to map sample\n"); + } + gst_sample_unref(sample); + } while (true); + + return n; +} + +void vnc_h264_clear(VncState *vs) +{ + if (!vs->h264) { + return; + } + + destroy_encoder_context(vs); + + g_clear_pointer(&vs->h264, g_free); +} diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index fcca7ec632..853a547d9a 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -193,6 +193,7 @@ static void vnc_async_encoding_start(VncState *orig, Vn= cState *local) local->zlib =3D orig->zlib; local->hextile =3D orig->hextile; local->zrle =3D orig->zrle; + local->h264 =3D orig->h264; local->client_width =3D orig->client_width; local->client_height =3D orig->client_height; } @@ -204,6 +205,7 @@ static void vnc_async_encoding_end(VncState *orig, VncS= tate *local) orig->zlib =3D local->zlib; orig->hextile =3D local->hextile; orig->zrle =3D local->zrle; + orig->h264 =3D local->h264; orig->lossy_rect =3D local->lossy_rect; } =20 @@ -284,25 +286,42 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) vnc_write_u16(&vs, 0); =20 vnc_lock_display(job->vs->vd); - QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) { - int n; - - if (job->vs->ioc =3D=3D NULL) { - vnc_unlock_display(job->vs->vd); - /* Copy persistent encoding data */ - vnc_async_encoding_end(job->vs, &vs); - goto disconnected; - } =20 - if (vnc_worker_clamp_rect(&vs, job, &entry->rect)) { - n =3D vnc_send_framebuffer_update(&vs, entry->rect.x, entry->r= ect.y, - entry->rect.w, entry->rect.h); + if (vs.vnc_encoding =3D=3D VNC_ENCODING_H264) { + int width =3D pixman_image_get_width(vs.vd->server); + int height =3D pixman_image_get_height(vs.vd->server); + int n =3D vnc_send_framebuffer_update(&vs, 0, 0, width, height); + if (n >=3D 0) { + n_rectangles +=3D n; + } + QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) { + g_free(entry); + } + } else { + QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) { + int n; + + if (job->vs->ioc =3D=3D NULL) { + vnc_unlock_display(job->vs->vd); + /* Copy persistent encoding data */ + vnc_async_encoding_end(job->vs, &vs); + goto disconnected; + } =20 - if (n >=3D 0) { - n_rectangles +=3D n; + if (vnc_worker_clamp_rect(&vs, job, &entry->rect)) { + n =3D vnc_send_framebuffer_update( + &vs, + entry->rect.x, + entry->rect.y, + entry->rect.w, + entry->rect.h); + + if (n >=3D 0) { + n_rectangles +=3D n; + } } + g_free(entry); } - g_free(entry); } trace_vnc_job_nrects(&vs, job, n_rectangles); vnc_unlock_display(job->vs->vd); diff --git a/ui/vnc.c b/ui/vnc.c index 9241caaad9..aed25b0183 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -972,6 +972,9 @@ int vnc_send_framebuffer_update(VncState *vs, int x, in= t y, int w, int h) case VNC_ENCODING_ZYWRLE: n =3D vnc_zywrle_send_framebuffer_update(vs, x, y, w, h); break; + case VNC_ENCODING_H264: + n =3D vnc_h264_send_framebuffer_update(vs, x, y, w, h); + break; default: vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); n =3D vnc_raw_send_framebuffer_update(vs, x, y, w, h); @@ -1326,6 +1329,10 @@ void vnc_disconnect_finish(VncState *vs) vnc_tight_clear(vs); vnc_zrle_clear(vs); =20 +#ifdef CONFIG_GSTREAMER + vnc_h264_clear(vs); +#endif + #ifdef CONFIG_VNC_SASL vnc_sasl_client_cleanup(vs); #endif /* CONFIG_VNC_SASL */ @@ -2181,6 +2188,16 @@ static void set_encodings(VncState *vs, int32_t *enc= odings, size_t n_encodings) vnc_set_feature(vs, VNC_FEATURE_ZYWRLE); vs->vnc_encoding =3D enc; break; +#ifdef CONFIG_GSTREAMER + case VNC_ENCODING_H264: + if (vnc_h264_encoder_init(vs)) { + vnc_set_feature(vs, VNC_FEATURE_H264); + vs->vnc_encoding =3D enc; + } else { + VNC_DEBUG("vnc_h264_encoder_init failed\n"); + } + break; +#endif case VNC_ENCODING_DESKTOPRESIZE: vnc_set_feature(vs, VNC_FEATURE_RESIZE); break; @@ -4291,6 +4308,10 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Erro= r **errp) Error *local_err =3D NULL; char *id =3D (char *)qemu_opts_id(opts); =20 +#ifdef CONFIG_GSTREAMER + gst_init(NULL, NULL); +#endif + assert(id); vnc_display_init(id, &local_err); if (local_err) { diff --git a/ui/vnc.h b/ui/vnc.h index acc53a2cc1..a0d336738d 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -46,6 +46,10 @@ #include "vnc-enc-zrle.h" #include "ui/kbd-state.h" =20 +#ifdef CONFIG_GSTREAMER +#include +#endif + // #define _VNC_DEBUG 1 =20 #ifdef _VNC_DEBUG @@ -231,6 +235,14 @@ typedef struct VncZywrle { int buf[VNC_ZRLE_TILE_WIDTH * VNC_ZRLE_TILE_HEIGHT]; } VncZywrle; =20 +#ifdef CONFIG_GSTREAMER +typedef struct VncH264 { + GstElement *pipeline, *source, *gst_encoder, *sink, *convert; + size_t width; + size_t height; +} VncH264; +#endif + struct VncRect { int x; @@ -344,6 +356,9 @@ struct VncState VncHextile hextile; VncZrle *zrle; VncZywrle zywrle; +#ifdef CONFIG_GSTREAMER + VncH264 *h264; +#endif =20 Notifier mouse_mode_notifier; =20 @@ -404,6 +419,7 @@ enum { #define VNC_ENCODING_TRLE 0x0000000f #define VNC_ENCODING_ZRLE 0x00000010 #define VNC_ENCODING_ZYWRLE 0x00000011 +#define VNC_ENCODING_H264 0x00000032 /* 50 */ #define VNC_ENCODING_COMPRESSLEVEL0 0xFFFFFF00 /* -256 */ #define VNC_ENCODING_QUALITYLEVEL0 0xFFFFFFE0 /* -32 */ #define VNC_ENCODING_XCURSOR 0xFFFFFF10 /* -240 */ @@ -464,6 +480,7 @@ enum VncFeatures { VNC_FEATURE_XVP, VNC_FEATURE_CLIPBOARD_EXT, VNC_FEATURE_AUDIO, + VNC_FEATURE_H264, }; =20 =20 @@ -625,6 +642,10 @@ int vnc_zrle_send_framebuffer_update(VncState *vs, int= x, int y, int w, int h); int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, = int h); void vnc_zrle_clear(VncState *vs); =20 +bool vnc_h264_encoder_init(VncState *vs); +int vnc_h264_send_framebuffer_update(VncState *vs, int x, int y, int w, in= t h); +void vnc_h264_clear(VncState *vs); + /* vnc-clipboard.c */ void vnc_server_cut_text_caps(VncState *vs); void vnc_client_cut_text(VncState *vs, size_t len, uint8_t *text); --=20 2.39.5 From nobody Fri Dec 19 06:34:38 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744976204973244.38761274453748; Fri, 18 Apr 2025 04:36:44 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvK-0005Q9-U5; Fri, 18 Apr 2025 07:30:22 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5jvI-0005N1-O4 for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:20 -0400 Received: from [94.136.29.99] (helo=zilli.proxmox.com) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jv5-0005mh-Ri for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:20 -0400 Received: by zilli.proxmox.com (Postfix, from userid 1000) id 3368F1C167C; Fri, 18 Apr 2025 13:29:58 +0200 (CEST) From: Dietmar Maurer To: marcandre.lureau@redhat.com, qemu-devel@nongnu.org Cc: Dietmar Maurer Subject: [PATCH v3 3/9] vnc: h264: send additional frames after the display is clean Date: Fri, 18 Apr 2025 13:29:47 +0200 Message-Id: <20250418112953.1744442-4-dietmar@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418112953.1744442-1-dietmar@proxmox.com> References: <20250418112953.1744442-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 94.136.29.99 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=94.136.29.99; envelope-from=dietmar@zilli.proxmox.com; helo=zilli.proxmox.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, NO_DNS_FOR_FROM=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744976210701019100 Content-Type: text/plain; charset="utf-8" The H264 implementation only sends frames when it detects changes in the server's framebuffer. This leads to artifacts when there are no further changes, as the internal H264 encoder may still contain data. This patch modifies the code to send a few additional frames in such situations to flush the H264 encoder data. Signed-off-by: Dietmar Maurer Reviewed-by: Marc-Andr=C3=A9 Lureau --- ui/vnc.c | 25 ++++++++++++++++++++++++- ui/vnc.h | 3 +++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/ui/vnc.c b/ui/vnc.c index aed25b0183..badc7912c0 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3239,7 +3239,30 @@ static void vnc_refresh(DisplayChangeListener *dcl) vnc_unlock_display(vd); =20 QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) { - rects +=3D vnc_update_client(vs, has_dirty); + int client_dirty =3D has_dirty; + if (vs->h264) { + if (client_dirty) { + vs->h264->keep_dirty =3D VNC_H264_KEEP_DIRTY; + } else { + if (vs->h264->keep_dirty > 0) { + client_dirty =3D 1; + vs->h264->keep_dirty--; + } + } + } + + int count =3D vnc_update_client(vs, client_dirty); + rects +=3D count; + + if (vs->h264 && !count && vs->h264->keep_dirty) { + VncJob *job =3D vnc_job_new(vs); + int height =3D pixman_image_get_height(vd->server); + int width =3D pixman_image_get_width(vd->server); + vs->job_update =3D vs->update; + vs->update =3D VNC_STATE_UPDATE_NONE; + vnc_job_add_rect(job, 0, 0, width, height); + vnc_job_push(job); + } /* vs might be free()ed here */ } =20 diff --git a/ui/vnc.h b/ui/vnc.h index a0d336738d..a5ea134de8 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -236,10 +236,13 @@ typedef struct VncZywrle { } VncZywrle; =20 #ifdef CONFIG_GSTREAMER +/* Number of frames we send after the display is clean. */ +#define VNC_H264_KEEP_DIRTY 10 typedef struct VncH264 { GstElement *pipeline, *source, *gst_encoder, *sink, *convert; size_t width; size_t height; + guint keep_dirty; } VncH264; #endif =20 --=20 2.39.5 From nobody Fri Dec 19 06:34:38 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744978836688201.63265824038956; Fri, 18 Apr 2025 05:20:36 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvT-0005Vg-Iq; Fri, 18 Apr 2025 07:30:31 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5jvM-0005SB-12 for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:28 -0400 Received: from [94.136.29.99] (helo=zilli.proxmox.com) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jv5-0005cv-7C for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:23 -0400 Received: by zilli.proxmox.com (Postfix, from userid 1000) id 34DAF1C167F; Fri, 18 Apr 2025 13:29:58 +0200 (CEST) From: Dietmar Maurer To: marcandre.lureau@redhat.com, qemu-devel@nongnu.org Cc: Dietmar Maurer Subject: [PATCH v3 4/9] h264: search for available h264 encoder Date: Fri, 18 Apr 2025 13:29:48 +0200 Message-Id: <20250418112953.1744442-5-dietmar@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418112953.1744442-1-dietmar@proxmox.com> References: <20250418112953.1744442-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 94.136.29.99 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=94.136.29.99; envelope-from=dietmar@zilli.proxmox.com; helo=zilli.proxmox.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, NO_DNS_FOR_FROM=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744978839567019100 Content-Type: text/plain; charset="utf-8" The search list is currently hardcoded to: ["x264enc", "openh264enc"] x264enc: is probably the best available software encoder openh264enc: lower quality, but available on more systems. We restrict encoders to a known list because each encoder requires fine tuning to get reasonable/usable results. Signed-off-by: Dietmar Maurer --- ui/vnc-enc-h264.c | 89 +++++++++++++++++++++++++++++++++++++++-------- ui/vnc.h | 1 + 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/ui/vnc-enc-h264.c b/ui/vnc-enc-h264.c index 3abe6a1528..047f4a3128 100644 --- a/ui/vnc-enc-h264.c +++ b/ui/vnc-enc-h264.c @@ -27,6 +27,68 @@ =20 #include =20 +const char *encoder_list[] =3D { "x264enc", "openh264enc", NULL }; + +static const char *get_available_encoder(void) +{ + int i =3D 0; + do { + const char *encoder_name =3D encoder_list[i]; + if (encoder_name =3D=3D NULL) { + break; + } + GstElement *element =3D gst_element_factory_make( + encoder_name, "video-encoder"); + if (element !=3D NULL) { + gst_object_unref(element); + return encoder_name; + } + i =3D i + 1; + } while (true); + + return NULL; +} + +static GstElement *create_encoder(const char *encoder_name) +{ + GstElement *encoder =3D gst_element_factory_make( + encoder_name, "video-encoder"); + if (!encoder) { + VNC_DEBUG("Could not create gst '%s' video encoder\n", encoder_nam= e); + return NULL; + } + + if (!strcmp(encoder_name, "x264enc")) { + g_object_set( + encoder, + "tune", 4, /* zerolatency */ + /* + * fix for zerolatency with novnc (without, + * noVNC displays green stripes) + */ + "threads", 1, + "pass", 5, /* Constant Quality */ + "quantizer", 26, + /* avoid access unit delimiters (Nal Unit Type 9) - not requir= ed */ + "aud", false, + NULL); + } else if (!strcmp(encoder_name, "openh264enc")) { + g_object_set( + encoder, + "usage-type", 1, /* screen content */ + "complexity", 0, /* low, high speed */ + "rate-control", 0, /* quality mode */ + "qp-min", 20, + "qp-max", 27, + NULL); + } else { + VNC_DEBUG("Unknown H264 encoder name '%s' - not setting any proper= ties", + encoder_name); + } + + return encoder; +} + static void destroy_encoder_context(VncState *vs) { gst_clear_object(&vs->h264->source); @@ -68,26 +130,12 @@ static bool create_encoder_context(VncState *vs, int w= , int h) goto error; } =20 - vs->h264->gst_encoder =3D gst_element_factory_make("x264enc", "gst-enc= oder"); + vs->h264->gst_encoder =3D create_encoder(vs->h264->encoder_name); if (!vs->h264->gst_encoder) { VNC_DEBUG("Could not create gst x264 encoder\n"); goto error; } =20 - g_object_set( - vs->h264->gst_encoder, - "tune", 4, /* zerolatency */ - /* - * fix for zerolatency with novnc (without, noVNC displays - * green stripes) - */ - "threads", 1, - "pass", 5, /* Constant Quality */ - "quantizer", 26, - /* avoid access unit delimiters (Nal Unit Type 9) - not required */ - "aud", false, - NULL); - vs->h264->sink =3D gst_element_factory_make("appsink", "sink"); if (!vs->h264->sink) { VNC_DEBUG("Could not create gst sink\n"); @@ -172,9 +220,20 @@ static bool create_encoder_context(VncState *vs, int w= , int h) =20 bool vnc_h264_encoder_init(VncState *vs) { + const char *encoder_name; + g_assert(vs->h264 =3D=3D NULL); =20 + encoder_name =3D get_available_encoder(); + if (encoder_name =3D=3D NULL) { + VNC_DEBUG("No H264 encoder available.\n"); + return -1; + } + vs->h264 =3D g_new0(VncH264, 1); + vs->h264->encoder_name =3D encoder_name; + + VNC_DEBUG("Allow H264 using encoder '%s`\n", encoder_name); =20 return true; } diff --git a/ui/vnc.h b/ui/vnc.h index a5ea134de8..e97276349e 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -239,6 +239,7 @@ typedef struct VncZywrle { /* Number of frames we send after the display is clean. */ #define VNC_H264_KEEP_DIRTY 10 typedef struct VncH264 { + const char *encoder_name; GstElement *pipeline, *source, *gst_encoder, *sink, *convert; size_t width; size_t height; --=20 2.39.5 From nobody Fri Dec 19 06:34:38 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744977615096193.90673125972376; Fri, 18 Apr 2025 05:00:15 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvT-0005VV-1u; Fri, 18 Apr 2025 07:30:31 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5jvO-0005SS-Py for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:28 -0400 Received: from [94.136.29.99] (helo=zilli.proxmox.com) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvD-0005nT-Cz for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:26 -0400 Received: by zilli.proxmox.com (Postfix, from userid 1000) id 366791C1682; Fri, 18 Apr 2025 13:29:58 +0200 (CEST) From: Dietmar Maurer To: marcandre.lureau@redhat.com, qemu-devel@nongnu.org Cc: Dietmar Maurer Subject: [PATCH v3 5/9] h264: new vnc option to configure h264 at server side Date: Fri, 18 Apr 2025 13:29:49 +0200 Message-Id: <20250418112953.1744442-6-dietmar@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418112953.1744442-1-dietmar@proxmox.com> References: <20250418112953.1744442-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 94.136.29.99 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=94.136.29.99; envelope-from=dietmar@zilli.proxmox.com; helo=zilli.proxmox.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, NO_DNS_FOR_FROM=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744977617684019100 Content-Type: text/plain; charset="utf-8" Values can be 'on', 'off', or a space sparated list of allowed gstreamer encoders. - on: automatically select the encoder - off: disbale h264 - encoder-list: select first available encoder from that list. Signed-off-by: Dietmar Maurer --- ui/vnc-enc-h264.c | 30 ++++++++++++++++++++++-------- ui/vnc.c | 25 ++++++++++++++++++++----- ui/vnc.h | 6 +++++- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/ui/vnc-enc-h264.c b/ui/vnc-enc-h264.c index 047f4a3128..0f89cafbf6 100644 --- a/ui/vnc-enc-h264.c +++ b/ui/vnc-enc-h264.c @@ -27,13 +27,21 @@ =20 #include =20 -const char *encoder_list[] =3D { "x264enc", "openh264enc", NULL }; - -static const char *get_available_encoder(void) +static char *get_available_encoder(const char *encoder_list) { + g_assert(encoder_list !=3D NULL); + + if (!strcmp(encoder_list, "")) { + /* use default list */ + encoder_list =3D "x264enc openh264enc"; + } + + char *ret =3D NULL; + char **encoder_array =3D g_strsplit(encoder_list, " ", -1); + int i =3D 0; do { - const char *encoder_name =3D encoder_list[i]; + const char *encoder_name =3D encoder_array[i]; if (encoder_name =3D=3D NULL) { break; } @@ -41,12 +49,15 @@ static const char *get_available_encoder(void) encoder_name, "video-encoder"); if (element !=3D NULL) { gst_object_unref(element); - return encoder_name; + ret =3D strdup(encoder_name); + break; } i =3D i + 1; } while (true); =20 - return NULL; + g_strfreev(encoder_array); + + return ret; } =20 static GstElement *create_encoder(const char *encoder_name) @@ -220,11 +231,13 @@ static bool create_encoder_context(VncState *vs, int = w, int h) =20 bool vnc_h264_encoder_init(VncState *vs) { - const char *encoder_name; + char *encoder_name; =20 g_assert(vs->h264 =3D=3D NULL); + g_assert(vs->vd !=3D NULL); + g_assert(vs->vd->h264_encoder_list !=3D NULL); =20 - encoder_name =3D get_available_encoder(); + encoder_name =3D get_available_encoder(vs->vd->h264_encoder_list); if (encoder_name =3D=3D NULL) { VNC_DEBUG("No H264 encoder available.\n"); return -1; @@ -336,6 +349,7 @@ void vnc_h264_clear(VncState *vs) } =20 destroy_encoder_context(vs); + g_free(vs->h264->encoder_name); =20 g_clear_pointer(&vs->h264, g_free); } diff --git a/ui/vnc.c b/ui/vnc.c index badc7912c0..feab4c0043 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2190,11 +2190,11 @@ static void set_encodings(VncState *vs, int32_t *en= codings, size_t n_encodings) break; #ifdef CONFIG_GSTREAMER case VNC_ENCODING_H264: - if (vnc_h264_encoder_init(vs)) { - vnc_set_feature(vs, VNC_FEATURE_H264); - vs->vnc_encoding =3D enc; - } else { - VNC_DEBUG("vnc_h264_encoder_init failed\n"); + if (vs->vd->h264_encoder_list !=3D NULL) { /* if h264 is enabl= ed */ + if (vnc_h264_encoder_init(vs)) { + vnc_set_feature(vs, VNC_FEATURE_H264); + vs->vnc_encoding =3D enc; + } } break; #endif @@ -3634,6 +3634,9 @@ static QemuOptsList qemu_vnc_opts =3D { },{ .name =3D "power-control", .type =3D QEMU_OPT_BOOL, + },{ + .name =3D "h264", + .type =3D QEMU_OPT_STRING, }, { /* end of list */ } }, @@ -4196,6 +4199,18 @@ void vnc_display_open(const char *id, Error **errp) } #endif =20 +#ifdef CONFIG_GSTREAMER + const char *h264_opt =3D qemu_opt_get(opts, "h264"); + if (!strcmp(h264_opt, "off")) { + vd->h264_encoder_list =3D NULL; /* disable h264 */ + } else if (!strcmp(h264_opt, "on")) { + vd->h264_encoder_list =3D ""; /* use default encoder list */ + } else { + /* assume this is a list of endiers */ + vd->h264_encoder_list =3D h264_opt; + } +#endif + if (vnc_display_setup_auth(&vd->auth, &vd->subauth, vd->tlscreds, password, sasl, false, errp) < 0) { diff --git a/ui/vnc.h b/ui/vnc.h index e97276349e..789b18806b 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -188,6 +188,10 @@ struct VncDisplay VncDisplaySASL sasl; #endif =20 +#ifdef CONFIG_GSTREAMER + const char *h264_encoder_list; +#endif + AudioState *audio_state; }; =20 @@ -239,7 +243,7 @@ typedef struct VncZywrle { /* Number of frames we send after the display is clean. */ #define VNC_H264_KEEP_DIRTY 10 typedef struct VncH264 { - const char *encoder_name; + char *encoder_name; GstElement *pipeline, *source, *gst_encoder, *sink, *convert; size_t width; size_t height; --=20 2.39.5 From nobody Fri Dec 19 06:34:38 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744977882348153.3052750984184; Fri, 18 Apr 2025 05:04:42 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvb-0005Y9-Gg; Fri, 18 Apr 2025 07:30:39 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5jvT-0005Vq-FI for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:31 -0400 Received: from [94.136.29.99] (helo=zilli.proxmox.com) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvE-0005nc-Kb for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:29 -0400 Received: by zilli.proxmox.com (Postfix, from userid 1000) id 382811C1685; Fri, 18 Apr 2025 13:29:58 +0200 (CEST) From: Dietmar Maurer To: marcandre.lureau@redhat.com, qemu-devel@nongnu.org Cc: Dietmar Maurer Subject: [PATCH v3 6/9] h264: add hardware encoders Date: Fri, 18 Apr 2025 13:29:50 +0200 Message-Id: <20250418112953.1744442-7-dietmar@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418112953.1744442-1-dietmar@proxmox.com> References: <20250418112953.1744442-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 94.136.29.99 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=94.136.29.99; envelope-from=dietmar@zilli.proxmox.com; helo=zilli.proxmox.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, NO_DNS_FOR_FROM=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744977883559019100 Content-Type: text/plain; charset="utf-8" Signed-off-by: Dietmar Maurer --- ui/vnc-enc-h264.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/ui/vnc-enc-h264.c b/ui/vnc-enc-h264.c index 0f89cafbf6..840674dbdb 100644 --- a/ui/vnc-enc-h264.c +++ b/ui/vnc-enc-h264.c @@ -29,15 +29,17 @@ =20 static char *get_available_encoder(const char *encoder_list) { + char *ret =3D NULL; + char **encoder_array =3D NULL; + g_assert(encoder_list !=3D NULL); =20 if (!strcmp(encoder_list, "")) { /* use default list */ - encoder_list =3D "x264enc openh264enc"; + encoder_list =3D "nvh264enc vaapih264enc x264enc openh264enc"; } =20 - char *ret =3D NULL; - char **encoder_array =3D g_strsplit(encoder_list, " ", -1); + encoder_array =3D g_strsplit(encoder_list, " ", -1); =20 int i =3D 0; do { @@ -69,7 +71,19 @@ static GstElement *create_encoder(const char *encoder_na= me) return NULL; } =20 - if (!strcmp(encoder_name, "x264enc")) { + if (!strcmp(encoder_name, "nvh264enc")) { + g_object_set( + encoder, + "preset", 8, /* p1 - fastest */ + "multi-pass", 1, /* multipass disabled */ + "tune", 2, /* low latency */ + "zerolatency", true, /* low latency */ + /* avoid access unit delimiters (Nal Unit Type 9) - not requir= ed */ + "aud", false, + NULL); + } else if (!strcmp(encoder_name, "vaapih264enc")) { + g_object_set(encoder, "tune", 1, NULL); /* high compression */ + } else if (!strcmp(encoder_name, "x264enc")) { g_object_set( encoder, "tune", 4, /* zerolatency */ --=20 2.39.5 From nobody Fri Dec 19 06:34:38 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744976234359978.8891501789419; Fri, 18 Apr 2025 04:37:14 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvX-0005Wq-95; Fri, 18 Apr 2025 07:30:35 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5jvT-0005Vo-DO for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:31 -0400 Received: from [94.136.29.99] (helo=zilli.proxmox.com) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvI-0005nx-Dk for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:28 -0400 Received: by zilli.proxmox.com (Postfix, from userid 1000) id 396D01C1687; Fri, 18 Apr 2025 13:29:58 +0200 (CEST) From: Dietmar Maurer To: marcandre.lureau@redhat.com, qemu-devel@nongnu.org Cc: Dietmar Maurer Subject: [PATCH v3 7/9] h264: do not reduce vnc update speed while we have an active h264 stream Date: Fri, 18 Apr 2025 13:29:51 +0200 Message-Id: <20250418112953.1744442-8-dietmar@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418112953.1744442-1-dietmar@proxmox.com> References: <20250418112953.1744442-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 94.136.29.99 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=94.136.29.99; envelope-from=dietmar@zilli.proxmox.com; helo=zilli.proxmox.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, NO_DNS_FOR_FROM=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744976235780019100 Content-Type: text/plain; charset="utf-8" Signed-off-by: Dietmar Maurer --- ui/vnc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/vnc.c b/ui/vnc.c index feab4c0043..6db03a1550 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3222,6 +3222,7 @@ static void vnc_refresh(DisplayChangeListener *dcl) VncDisplay *vd =3D container_of(dcl, VncDisplay, dcl); VncState *vs, *vn; int has_dirty, rects =3D 0; + bool keep_dirty =3D false; =20 if (QTAILQ_EMPTY(&vd->clients)) { update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_MAX); @@ -3249,6 +3250,9 @@ static void vnc_refresh(DisplayChangeListener *dcl) vs->h264->keep_dirty--; } } + if (vs->h264->keep_dirty > 0) { + keep_dirty =3D true; + } } =20 int count =3D vnc_update_client(vs, client_dirty); @@ -3266,7 +3270,7 @@ static void vnc_refresh(DisplayChangeListener *dcl) /* vs might be free()ed here */ } =20 - if (has_dirty && rects) { + if ((has_dirty && rects) || keep_dirty) { vd->dcl.update_interval /=3D 2; if (vd->dcl.update_interval < VNC_REFRESH_INTERVAL_BASE) { vd->dcl.update_interval =3D VNC_REFRESH_INTERVAL_BASE; --=20 2.39.5 From nobody Fri Dec 19 06:34:38 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744977667924306.6428465756843; Fri, 18 Apr 2025 05:01:07 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvZ-0005Xa-2q; Fri, 18 Apr 2025 07:30:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5jvT-0005Vp-EK for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:31 -0400 Received: from [94.136.29.99] (helo=zilli.proxmox.com) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvI-0005nw-Fa for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:30 -0400 Received: by zilli.proxmox.com (Postfix, from userid 1000) id 3AAC31C1689; Fri, 18 Apr 2025 13:29:58 +0200 (CEST) From: Dietmar Maurer To: marcandre.lureau@redhat.com, qemu-devel@nongnu.org Cc: Dietmar Maurer Subject: [PATCH v3 8/9] vnc: initialize gst during argument processing Date: Fri, 18 Apr 2025 13:29:52 +0200 Message-Id: <20250418112953.1744442-9-dietmar@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418112953.1744442-1-dietmar@proxmox.com> References: <20250418112953.1744442-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 94.136.29.99 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=94.136.29.99; envelope-from=dietmar@zilli.proxmox.com; helo=zilli.proxmox.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, NO_DNS_FOR_FROM=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744977669032019100 Content-Type: text/plain; charset="utf-8" So that we can set --gst- options on the qemu command line. Signed-off-by: Dietmar Maurer --- system/vl.c | 8 ++++++++ ui/vnc.c | 4 ---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/system/vl.c b/system/vl.c index ec93988a03..c7fff02da2 100644 --- a/system/vl.c +++ b/system/vl.c @@ -140,6 +140,10 @@ #include "qemu/guest-random.h" #include "qemu/keyval.h" =20 +#ifdef CONFIG_GSTREAMER +#include +#endif + #define MAX_VIRTIO_CONSOLES 1 =20 typedef struct BlockdevOptionsQueueEntry { @@ -2848,6 +2852,10 @@ void qemu_init(int argc, char **argv) bool userconfig =3D true; FILE *vmstate_dump_file =3D NULL; =20 +#ifdef CONFIG_GSTREAMER + gst_init(&argc, &argv); +#endif + qemu_add_opts(&qemu_drive_opts); qemu_add_drive_opts(&qemu_legacy_drive_opts); qemu_add_drive_opts(&qemu_common_drive_opts); diff --git a/ui/vnc.c b/ui/vnc.c index 6db03a1550..8f6287e2e6 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -4350,10 +4350,6 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Erro= r **errp) Error *local_err =3D NULL; char *id =3D (char *)qemu_opts_id(opts); =20 -#ifdef CONFIG_GSTREAMER - gst_init(NULL, NULL); -#endif - assert(id); vnc_display_init(id, &local_err); if (local_err) { --=20 2.39.5 From nobody Fri Dec 19 06:34:38 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744981251281714.1892275567635; Fri, 18 Apr 2025 06:00:51 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvc-0005ZB-BS; Fri, 18 Apr 2025 07:30:40 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5jvT-0005WN-Sz for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:32 -0400 Received: from [94.136.29.99] (helo=zilli.proxmox.com) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5jvL-0005oZ-VP for qemu-devel@nongnu.org; Fri, 18 Apr 2025 07:30:31 -0400 Received: by zilli.proxmox.com (Postfix, from userid 1000) id 3BD761C168C; Fri, 18 Apr 2025 13:29:58 +0200 (CEST) From: Dietmar Maurer To: marcandre.lureau@redhat.com, qemu-devel@nongnu.org Cc: Dietmar Maurer Subject: [PATCH v3 9/9] h264: register shutdown notifiers, stop pipeline in destroy_encoder_context Date: Fri, 18 Apr 2025 13:29:53 +0200 Message-Id: <20250418112953.1744442-10-dietmar@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418112953.1744442-1-dietmar@proxmox.com> References: <20250418112953.1744442-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 94.136.29.99 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=94.136.29.99; envelope-from=dietmar@zilli.proxmox.com; helo=zilli.proxmox.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, NO_DNS_FOR_FROM=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744981254842019100 Content-Type: text/plain; charset="utf-8" Some encoders can hang indefinetly (i.e. nvh264enc) if the pipeline is not stopped before it is destroyed (Observed on Debian bookworm). Signed-off-by: Dietmar Maurer --- ui/vnc-enc-h264.c | 50 ++++++++++++++++++++++++++++++++++++++--------- ui/vnc.h | 1 + 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/ui/vnc-enc-h264.c b/ui/vnc-enc-h264.c index 840674dbdb..9dbfba3a16 100644 --- a/ui/vnc-enc-h264.c +++ b/ui/vnc-enc-h264.c @@ -23,6 +23,8 @@ */ =20 #include "qemu/osdep.h" +#include "system/runstate.h" + #include "vnc.h" =20 #include @@ -114,13 +116,33 @@ static GstElement *create_encoder(const char *encoder= _name) return encoder; } =20 -static void destroy_encoder_context(VncState *vs) +static void destroy_encoder_context(VncH264 *h264) { - gst_clear_object(&vs->h264->source); - gst_clear_object(&vs->h264->convert); - gst_clear_object(&vs->h264->gst_encoder); - gst_clear_object(&vs->h264->sink); - gst_clear_object(&vs->h264->pipeline); + GstStateChangeReturn state_change_ret; + + g_assert(h264 !=3D NULL); + + VNC_DEBUG("Destroy h264 context.\n"); + + /* + * Some encoders can hang indefinetly (i.e. nvh264enc) if + * the pipeline is not stopped before it is destroyed + * (Observed on Debian bookworm). + */ + if (h264->pipeline !=3D NULL) { + state_change_ret =3D gst_element_set_state( + h264->pipeline, GST_STATE_NULL); + + if (state_change_ret =3D=3D GST_STATE_CHANGE_FAILURE) { + VNC_DEBUG("Unable to stop the GST pipeline\n"); + } + } + + gst_clear_object(&h264->source); + gst_clear_object(&h264->convert); + gst_clear_object(&h264->gst_encoder); + gst_clear_object(&h264->sink); + gst_clear_object(&h264->pipeline); } =20 static bool create_encoder_context(VncState *vs, int w, int h) @@ -132,7 +154,7 @@ static bool create_encoder_context(VncState *vs, int w,= int h) =20 if (vs->h264->sink) { if (w !=3D vs->h264->width || h !=3D vs->h264->height) { - destroy_encoder_context(vs); + destroy_encoder_context(vs->h264); } } =20 @@ -239,10 +261,16 @@ static bool create_encoder_context(VncState *vs, int = w, int h) return TRUE; =20 error: - destroy_encoder_context(vs); + destroy_encoder_context(vs->h264); return FALSE; } =20 +static void shutdown_h264(Notifier *n, void *opaque) +{ + VncH264 *h264 =3D container_of(n, VncH264, shutdown_notifier); + destroy_encoder_context(h264); +} + bool vnc_h264_encoder_init(VncState *vs) { char *encoder_name; @@ -259,6 +287,8 @@ bool vnc_h264_encoder_init(VncState *vs) =20 vs->h264 =3D g_new0(VncH264, 1); vs->h264->encoder_name =3D encoder_name; + vs->h264->shutdown_notifier.notify =3D shutdown_h264; + qemu_register_shutdown_notifier(&vs->h264->shutdown_notifier); =20 VNC_DEBUG("Allow H264 using encoder '%s`\n", encoder_name); =20 @@ -362,7 +392,9 @@ void vnc_h264_clear(VncState *vs) return; } =20 - destroy_encoder_context(vs); + notifier_remove(&vs->h264->shutdown_notifier); + + destroy_encoder_context(vs->h264); g_free(vs->h264->encoder_name); =20 g_clear_pointer(&vs->h264, g_free); diff --git a/ui/vnc.h b/ui/vnc.h index 789b18806b..ea52085b19 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -248,6 +248,7 @@ typedef struct VncH264 { size_t width; size_t height; guint keep_dirty; + Notifier shutdown_notifier; } VncH264; #endif =20 --=20 2.39.5