diff --git a/Dockerfile.example b/Dockerfile.example index 11f01f60..8bed503e 100644 --- a/Dockerfile.example +++ b/Dockerfile.example @@ -2,7 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. -# Supported base images: ubuntu:20.04, ubuntu:18.04 +# Supported base images: ubuntu:22.04, ubuntu:20.04, ubuntu:18.04 ARG UBUNTU_RELEASE=20.04 ARG GSTREAMER_BASE_IMAGE=ghcr.io/selkies-project/selkies-gstreamer/gstreamer ARG GSTREAMER_BASE_IMAGE_RELEASE=master @@ -16,7 +16,7 @@ ARG UBUNTU_RELEASE LABEL maintainer "https://github.com/danisla" -# Install Selkies GStreamer system dependencies +# Install Selkies-GStreamer system dependencies RUN \ apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ build-essential \ @@ -106,7 +106,7 @@ ARG PACKAGE_VERSION=0.0.0.dev0 COPY --from=selkies-build /opt/pypi/dist/${PYPI_PACKAGE}-${PACKAGE_VERSION}-py3-none-any.whl . RUN pip3 install /opt/${PYPI_PACKAGE}-${PACKAGE_VERSION}-py3-none-any.whl -# Setup global bashrc to configure gstreamer environment +# Setup global bashrc to configure GStreamer environment RUN echo 'export DISPLAY=:0' \ >> /etc/bash.bashrc && \ echo 'export GST_DEBUG=*:2' \ @@ -120,13 +120,13 @@ RUN echo 'export DISPLAY=:0' \ RUN echo "#!/bin/bash\n\ export DISPLAY=:0\n\ export GST_DEBUG=*:2\n\ -export PULSE_SERVER=127.0.0.1:4713\n\ export GSTREAMER_PATH=/opt/gstreamer\n\ source /opt/gstreamer/gst-env\n\ Xvfb -screen :0 8192x4096x24 +extension RANDR +extension GLX +extension MIT-SHM -nolisten tcp -noreset -shmem 2>&1 >/tmp/Xvfb.log &\n\ until [[ -S /tmp/.X11-unix/X0 ]]; do sleep 1; done && echo 'X Server is ready'\n\ +export PULSE_SERVER=tcp:127.0.0.1:4713\n\ sudo /usr/bin/pulseaudio -k >/dev/null 2>&1\n\ -sudo /usr/bin/pulseaudio --daemonize --system --verbose --log-target=file:/tmp/pulseaudio.log --realtime=true --disallow-exit -L 'module-native-protocol-tcp auth-ip-acl=127.0.0.0/8 port=4713 auth-anonymous=1'\n\ +/usr/bin/pulseaudio --daemonize --verbose --log-target=file:/tmp/pulseaudio.log --disallow-exit -L 'module-native-protocol-tcp auth-ip-acl=127.0.0.0/8 port=4713 auth-anonymous=1'\n\ [[ \${START_XFCE4:-true} == true ]] && rm -rf ~/.config/xfce4 && xfce4-session &\n\ export WEBRTC_ENCODER=\${WEBRTC_ENCODER:-x264enc}\n\ export WEBRTC_ENABLE_RESIZE=\${WEBRTC_ENABLE_RESIZE:-true}\n\ diff --git a/README.md b/README.md index 7100e021..bec21fbf 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ docker run --name selkies -it --rm -p 8080:8080 ghcr.io/selkies-project/selkies- Repositories [`selkies-vdi`](https://github.com/selkies-project/selkies-vdi) or [`selkies-examples`](https://github.com/selkies-project/selkies-examples) from the [Selkies Project](https://github.com/selkies-project) provide containerized virtual desktop infrastructure (VDI) templates. -[`docker-nvidia-glx-desktop`](https://github.com/ehfd/docker-nvidia-glx-desktop) and [`docker-nvidia-egl-desktop`](https://github.com/ehfd/docker-nvidia-egl-desktop) are expandable ready-to-go zero-configuration batteries-included containerized remote desktop implementations of `selkies-gstreamer` supporting hardware acceleration on NVIDIA and other GPUs. +[`docker-nvidia-glx-desktop`](https://github.com/selkies-project/docker-nvidia-glx-desktop) and [`docker-nvidia-egl-desktop`](https://github.com/selkies-project/docker-nvidia-egl-desktop) are expandable ready-to-go zero-configuration batteries-included containerized remote desktop implementations of `selkies-gstreamer` supporting hardware acceleration on NVIDIA and other GPUs. ### Install the packaged version on a standalone machine or cloud instance @@ -102,30 +102,30 @@ export GST_DEBUG=*:2 # Initialize the GStreamer environment after setting GSTREAMER_PATH to the path of your GStreamer directory export GSTREAMER_PATH=/opt/gstreamer source /opt/gstreamer/gst-env -# Start a virtual X server, skip this line if an X server already exists or you are already using a display +# Start a virtual X11 server, skip this line if an X server already exists or you are already using a display Xvfb -screen :0 8192x4096x24 +extension RANDR +extension GLX +extension MIT-SHM -nolisten tcp -noreset -shmem 2>&1 >/tmp/Xvfb.log & # Ensure the X server is ready until [[ -S /tmp/.X11-unix/X0 ]]; do sleep 1; done && echo 'X Server is ready' -# Initialize PulseAudio, omit the below lines if PulseAudio server is already running -export PULSE_SERVER=127.0.0.1:4713 +# Initialize PulseAudio, omit the below lines if a PulseAudio server is already running +export PULSE_SERVER=tcp:127.0.0.1:4713 sudo /usr/bin/pulseaudio -k >/dev/null 2>&1 -sudo /usr/bin/pulseaudio --daemonize --system --verbose --log-target=file:/tmp/pulseaudio.log --realtime=true --disallow-exit -L 'module-native-protocol-tcp auth-ip-acl=127.0.0.0/8 port=4713 auth-anonymous=1' +/usr/bin/pulseaudio --daemonize --verbose --log-target=file:/tmp/pulseaudio.log --disallow-exit -L 'module-native-protocol-tcp auth-ip-acl=127.0.0.0/8 port=4713 auth-anonymous=1' # Replace this line with your desktop environment session or skip this line if already running, use VirtualGL `vglrun` here if needed [[ "${START_XFCE4:-true}" == "true" ]] && rm -rf ~/.config/xfce4 && xfce4-session & # Write Progressive Web App (PWA) config. export PWA_APP_NAME="Selkies WebRTC" export PWA_APP_SHORT_NAME="selkies" export PWA_START_URL="/index.html" -sed -i \ +sudo sed -i \ -e "s|PWA_APP_NAME|${PWA_APP_NAME}|g" \ -e "s|PWA_APP_SHORT_NAME|${PWA_APP_SHORT_NAME}|g" \ -e "s|PWA_START_URL|${PWA_START_URL}|g" \ /opt/gst-web/manifest.json -sed -i \ +sudo sed -i \ -e "s|PWA_CACHE|${PWA_APP_SHORT_NAME}-webrtc-pwa|g" \ /opt/gst-web/sw.js # Choose your video encoder -export WEBRTC_ENCODER=x264enc +export WEBRTC_ENCODER=${WEBRTC_ENCODER:-x264enc} # Do not enable resize if there is a physical display export WEBRTC_ENABLE_RESIZE=${WEBRTC_ENABLE_RESIZE:-false} # Replace to your resolution if using without resize, skip if there is a physical display @@ -201,30 +201,30 @@ export GST_DEBUG=*:2 # Initialize the GStreamer environment after setting GSTREAMER_PATH to the path of your GStreamer directory export GSTREAMER_PATH=/opt/gstreamer source /opt/gstreamer/gst-env -# Start a virtual X server, skip this line if an X server already exists or you are already using a display +# Start a virtual X11 server, skip this line if an X server already exists or you are already using a display Xvfb -screen :0 8192x4096x24 +extension RANDR +extension GLX +extension MIT-SHM -nolisten tcp -noreset -shmem 2>&1 >/tmp/Xvfb.log & # Ensure the X server is ready until [[ -S /tmp/.X11-unix/X0 ]]; do sleep 1; done && echo 'X Server is ready' -# Initialize PulseAudio, omit the below lines if PulseAudio server is already running -export PULSE_SERVER=127.0.0.1:4713 +# Initialize PulseAudio, omit the below lines if a PulseAudio server is already running +export PULSE_SERVER=tcp:127.0.0.1:4713 sudo /usr/bin/pulseaudio -k >/dev/null 2>&1 -sudo /usr/bin/pulseaudio --daemonize --system --verbose --log-target=file:/tmp/pulseaudio.log --realtime=true --disallow-exit -L 'module-native-protocol-tcp auth-ip-acl=127.0.0.0/8 port=4713 auth-anonymous=1' +/usr/bin/pulseaudio --daemonize --verbose --log-target=file:/tmp/pulseaudio.log --disallow-exit -L 'module-native-protocol-tcp auth-ip-acl=127.0.0.0/8 port=4713 auth-anonymous=1' # Replace this line with your desktop environment session or skip this line if already running, use VirtualGL `vglrun` here if needed [[ "${START_XFCE4:-true}" == "true" ]] && rm -rf ~/.config/xfce4 && xfce4-session & # Write Progressive Web App (PWA) config. export PWA_APP_NAME="Selkies WebRTC" export PWA_APP_SHORT_NAME="selkies" export PWA_START_URL="/index.html" -sed -i \ +sudo sed -i \ -e "s|PWA_APP_NAME|${PWA_APP_NAME}|g" \ -e "s|PWA_APP_SHORT_NAME|${PWA_APP_SHORT_NAME}|g" \ -e "s|PWA_START_URL|${PWA_START_URL}|g" \ /opt/gst-web/manifest.json -sed -i \ +sudo sed -i \ -e "s|PWA_CACHE|${PWA_APP_SHORT_NAME}-webrtc-pwa|g" \ /opt/gst-web/sw.js # Choose your video encoder -export WEBRTC_ENCODER=x264enc +export WEBRTC_ENCODER=${WEBRTC_ENCODER:-x264enc} # Do not enable resize if there is a physical display export WEBRTC_ENABLE_RESIZE=${WEBRTC_ENABLE_RESIZE:-false} # Replace to your resolution if using without resize, skip if there is a physical display @@ -287,7 +287,7 @@ This table specifies the currently supported transport protocol components. |---|---|---|---|---|---| | [`webrtcbin`](https://gstreamer.freedesktop.org/documentation/webrtc/index.html) | [WebRTC](https://webrtc.org) | All | All Major | Various | N/A | -## Using a TURN server +## (IMPORTANT) Using a TURN server **You are at the right place if the HTML5 web interface loads and the signalling connection works, but the WebRTC connection fails and therefore the remote desktop does not start.** diff --git a/src/selkies_gstreamer/gstwebrtc_app.py b/src/selkies_gstreamer/gstwebrtc_app.py index e814beec..4079b533 100644 --- a/src/selkies_gstreamer/gstwebrtc_app.py +++ b/src/selkies_gstreamer/gstwebrtc_app.py @@ -755,8 +755,16 @@ def set_pointer_visible(self, visible): "pipeline", {"status": "Set pointer visibility to: %d" % visible}) def send_clipboard_data(self, data): - self.__send_data_channel_message( - "clipboard", {"content": base64.b64encode(data.encode()).decode("utf-8")}) + # TODO: WebRTC DataChannel accepts a maximum length of 65489 (= 65535 - 46 for '{"type": "clipboard", "data": {"content": ""}}'), remove this restriction after implementing DataChannel chunking + CLIPBOARD_RESTRICTION = 65488 + clipboard_message = base64.b64encode(data.encode()).decode("utf-8") + clipboard_length = len(clipboard_message) + logger.debug("clipboard base64 encoded message length: %d" % clipboard_length) + if clipboard_length <= CLIPBOARD_RESTRICTION: + self.__send_data_channel_message( + "clipboard", {"content": clipboard_message}) + else: + logger.warning("clipboard may not be sent to the client because the base64 message length {} is {} above the maximum length of {}".format(clipboard_length, clipboard_length - CLIPBOARD_RESTRICTION, CLIPBOARD_RESTRICTION)) def send_cursor_data(self, data): self.last_cursor_sent = data @@ -883,7 +891,7 @@ def __send_data_channel_message(self, msg_type, data): msg = { "type": msg_type, - "data": data, + "data": data } self.data_channel.emit("send-string", json.dumps(msg)) @@ -916,7 +924,7 @@ def __on_offer_created(self, promise, _, __): logger.warning("injecting modified rtx-time to SDP") sdp_text = re.sub(r'rtx-time=\d+', r'rtx-time=125', sdp_text) # Firefox needs profile-level-id=42e01f in the offer, but webrtcbin does not add this. - # Remove when fixed in webrtcbin. + # TODO: Remove when fixed in webrtcbin. # https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1106 if '264' in self.encoder: if 'profile-level-id' not in sdp_text: