From beda1b45d6dd0c09f620caa330c2f894d7b070c5 Mon Sep 17 00:00:00 2001 From: tgt Date: Mon, 20 Jan 2025 01:40:54 -0500 Subject: [PATCH] chore: make progress on audio support --- apps/web/src/app/view/[slug]/page.tsx | 6 +- workspaces/chromium/Dockerfile | 10 +- workspaces/debian/Dockerfile | 10 +- workspaces/scripts/prepare.sh | 1 + workspaces/scripts/start.sh | 10 +- workspaces/scripts/tcpulse.c | 221 ++++++++++++++++++++++++++ workspaces/template/Dockerfile | 10 +- 7 files changed, 250 insertions(+), 18 deletions(-) create mode 100644 workspaces/scripts/tcpulse.c diff --git a/apps/web/src/app/view/[slug]/page.tsx b/apps/web/src/app/view/[slug]/page.tsx index d675ac5..8e505dd 100644 --- a/apps/web/src/app/view/[slug]/page.tsx +++ b/apps/web/src/app/view/[slug]/page.tsx @@ -146,7 +146,11 @@ export default function View(props: { params: Promise<{ slug: string }> }) { }; }, [session, params.slug]); useEffect(() => { - if (connected && document.hasFocus()) audioRef.current?.start(); + const listener = () => audioRef.current?.start(); + if (connected && !audioRef.current?.connected) { + document.querySelector("canvas")?.addEventListener("keydown", listener); + } + return () => document.querySelector("canvas")?.removeEventListener("keydown", listener); }, [connected]); useEffect(() => { if (connected && vncRef.current?.rfb) { diff --git a/workspaces/chromium/Dockerfile b/workspaces/chromium/Dockerfile index a6ab45e..9df0314 100644 --- a/workspaces/chromium/Dockerfile +++ b/workspaces/chromium/Dockerfile @@ -8,10 +8,10 @@ ENV DEBIAN_FRONTEND=noninteractive COPY ./scripts /opt/stardust/scripts RUN apt-get update && apt-get install --no-install-recommends -y \ - xfonts-75dpi xvfb passwd sudo dbus dbus-x11 libxrandr2 libxext-dev libxrender-dev libxtst-dev imagemagick x11-apps \ - python3 python3-pip xterm git procps python3-numpy xfwm4 xfce4-terminal xfce4-session xfconf xfce4-notifyd \ - wget curl inetutils-ping vim tigervnc-tools tigervnc-standalone-server tigervnc-common wmctrl \ - chromium + xfonts-75dpi xvfb passwd sudo dbus dbus-x11 libxrandr2 libxext-dev libxrender-dev libxtst-dev imagemagick x11-apps build-essential pulseaudio gstreamer1.0* \ + python3 python3-pip xterm git procps python3-numpy xfwm4 xfce4-terminal xfce4-session xfconf xfce4-notifyd \ + wget curl inetutils-ping vim tigervnc-tools tigervnc-standalone-server tigervnc-common wmctrl \ + chromium RUN bash /opt/stardust/scripts/prepare.sh USER stardust @@ -20,4 +20,4 @@ RUN bash /opt/stardust/scripts/vnc-setup.sh WORKDIR /home/stardust CMD ["bash", "/opt/stardust/scripts/start.sh"] -EXPOSE 5901 +EXPOSE 5901 4713 diff --git a/workspaces/debian/Dockerfile b/workspaces/debian/Dockerfile index 55c7887..d3c0769 100644 --- a/workspaces/debian/Dockerfile +++ b/workspaces/debian/Dockerfile @@ -8,10 +8,10 @@ ENV DEBIAN_FRONTEND=noninteractive COPY ./scripts /opt/stardust/scripts RUN apt-get update && apt-get install --no-install-recommends -y \ - xfonts-75dpi xvfb passwd sudo dbus dbus-x11 libxrandr2 libxext-dev libxrender-dev libxtst-dev imagemagick x11-apps \ - python3 python3-pip xterm git procps python3-numpy neofetch \ - xfce4 wget curl xfce4-goodies inetutils-ping firefox-esr chromium gimp remmina remmina-plugin-vnc remmina-plugin-rdp flatpak vim \ - tigervnc-tools tigervnc-standalone-server tigervnc-common + xfonts-75dpi xvfb passwd sudo dbus dbus-x11 libxrandr2 libxext-dev libxrender-dev libxtst-dev imagemagick x11-apps build-essential pulseaudio gstreamer1.0* \ + python3 python3-pip xterm git procps python3-numpy neofetch \ + xfce4 wget curl xfce4-goodies inetutils-ping firefox-esr chromium gimp remmina remmina-plugin-vnc remmina-plugin-rdp flatpak vim \ + tigervnc-tools tigervnc-standalone-server tigervnc-common RUN bash /opt/stardust/scripts/prepare.sh USER stardust @@ -20,4 +20,4 @@ RUN bash /opt/stardust/scripts/vnc-setup.sh WORKDIR /home/stardust CMD ["bash", "/opt/stardust/scripts/start.sh"] -EXPOSE 5901 +EXPOSE 5901 4713 diff --git a/workspaces/scripts/prepare.sh b/workspaces/scripts/prepare.sh index 00bfa01..7b071a7 100644 --- a/workspaces/scripts/prepare.sh +++ b/workspaces/scripts/prepare.sh @@ -7,3 +7,4 @@ chmod 777 /home/stardust chmod 777 /opt/stardust echo "stardust ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers usermod -aG sudo stardust +gcc -o /opt/stardust/tcpulse /opt/stardust/scripts/tcpulse.c diff --git a/workspaces/scripts/start.sh b/workspaces/scripts/start.sh index 8eed0f1..11576ce 100644 --- a/workspaces/scripts/start.sh +++ b/workspaces/scripts/start.sh @@ -2,8 +2,8 @@ sudo chmod +x /home/stardust/.vnc/xstartup echo $VNCPASSWORD | vncpasswd -f > /home/stardust/.vnc/passwd if [[ "$WIPEVNCENV" == "true" ]]; then - unset VNCPASSWORD - unset WIPEVNCENV + unset VNCPASSWORD + unset WIPEVNCENV fi vncserver -kill :1 sudo rm -rf /run/dbus @@ -11,6 +11,12 @@ sudo mkdir -p /run/dbus sleep 1 echo "while : do +/opt/stardust/tcpulse 0.0.0.0 4713 +sleep 5 +done +" | bash & +echo "while : +do vncserver :1 -passwd /home/stardust/.vnc/passwd -fg -localhost no sleep 5 done diff --git a/workspaces/scripts/tcpulse.c b/workspaces/scripts/tcpulse.c new file mode 100644 index 0000000..5a0e866 --- /dev/null +++ b/workspaces/scripts/tcpulse.c @@ -0,0 +1,221 @@ +// https://coredump.ws/index.php?dir=code&post=NoVNC_with_audio +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// amount of milliseconds to wait in between send/recv attempts +#define GRACE_TIME 20 +// send/recv buffer size +#define BUF_SIZE 4096 +// the command we need to pipe to our socket +#define COMMAND \ + "gst-launch-1.0 -q -v alsasrc ! audio/x-raw, channels=2, rate=24000 ! " \ + "cutter ! voaacenc ! mp4mux streamable=true fragment_duration=10 ! fdsink " \ + "fd=1" + +// little helper macro +#define max(a, b) (a > b ? a : b) + +// sets the non-blocking flag for an IO descriptor +void set_nonblock(int fd) { + // retrieve all the flags for this file descriptor + int fl = fcntl(fd, F_GETFL, 0); + + if (fl < 0) { + fprintf(stderr, "Failed to get flags for file descriptor %d: %s\n", fd, + strerror(errno)); + exit(1); + } + + // add the non-blocking flag to this file descriptor's flags + if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) < 0) { + fprintf(stderr, "Failed to set flags for file descriptor %d: %s\n", fd, + strerror(errno)); + exit(1); + } +} + +int create_server_sock(char *addr, int port) { + int on = 1; + static struct sockaddr_in client_addr; + + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + fprintf(stderr, "Failed to create server socket.\n"); + return -1; + } + + memset(&client_addr, '\0', sizeof(client_addr)); + + client_addr.sin_family = AF_INET; + client_addr.sin_addr.s_addr = inet_addr(addr); + client_addr.sin_port = htons(port); + + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, 4) < 0) { + fprintf(stderr, "Failed to set socket address %s:%d\n", addr, port); + return -1; + } + + if (bind(sock, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0) { + fprintf(stderr, "Failed to bind server socket on %s:%d\n", addr, port); + return -1; + } + + if (listen(sock, 5) < 0) { + fprintf(stderr, "Failed to listen on %s:%d\n", addr, port); + return -1; + } + + fprintf(stdout, "Listening on %s port %d\n", addr, port); + + return sock; +} + +int wait_for_client(int s) { + struct sockaddr_in peer; + socklen_t len = sizeof(struct sockaddr); + fprintf(stdout, "Accepting connections with file descriptor: %d\n", s); + + int newsock = accept(s, (struct sockaddr *)&peer, &len); + + if (newsock < 0 && errno != EINTR) { + fprintf(stdout, "Failed to accept connection with file descriptor %d: %s\n", + s, strerror(errno)); + return -1; + } + + struct hostent *hostinfo = + gethostbyaddr((char *)&peer.sin_addr.s_addr, len, AF_INET); + fprintf(stdout, "Incoming connection accepted from %s[%s]\n", + !hostinfo ? "" : hostinfo->h_name, inet_ntoa(peer.sin_addr)); + + set_nonblock(newsock); + return (newsock); +} + +void pipe_to_socket(int outputSocket, int sourceSocket) { + char sbuf[BUF_SIZE]; + char dummy[BUF_SIZE]; + int sendCount = 0; + struct timeval to = {0, 100}; + + for (;;) { + // if we've read data from our cmmand we need to send it + if (sendCount) { + int sent = write(outputSocket, sbuf, sendCount); + + // if an error happens during sending - the client probably gave up + if (sent < 0 && errno != EWOULDBLOCK) { + fprintf(stdout, "client disconnected: %s\n", strerror(errno)); + exit(1); + } + + // reduce our buffer by the amount of data sent + if (sent != sendCount) + memmove(sbuf, sbuf + sent, sendCount - sent); + sendCount -= sent; + } + + // use select to wait until anything happens to our source file. + fd_set R; + FD_ZERO(&R); + FD_SET(sourceSocket, &R); + int selectValue = + select(max(outputSocket, sourceSocket) + 1, &R, 0, 0, &to); + + if (selectValue > 0) { + read(outputSocket, dummy, + BUF_SIZE); // read potential input from the client - and discard it. + + // read our input stream into the buffer that's to be transferred to the + // client + int readCount = + read(sourceSocket, sbuf + sendCount, BUF_SIZE - sendCount); + + // if our program closed - end the session + if (readCount < 0 && errno != EWOULDBLOCK) { + fprintf(stdout, "command stopped : %s\n", strerror(errno)); + exit(1); + } + + // add the amount of data to the buffer that we have to send + if (readCount > 0) + sendCount += readCount; + } + + // if our client disconnected for any reason + if (selectValue < 0 && errno != EINTR) { + fprintf(stdout, "client disconnected: %s\n", strerror(errno)); + close(sourceSocket); + close(outputSocket); + _exit(0); + } + + usleep(GRACE_TIME); + } +} + +int main(int argc, char *argv[]) { + char *localaddr = NULL; + int localport = -1; + int client = -1; + int server = -1; + int server_socket = -1; + + if (3 != argc) { + fprintf(stderr, "usage: %s laddr lport\n", argv[0]); + exit(1); + } + + server_socket = create_server_sock(argv[1], atoi(argv[2])); + + for (;;) { + if ((client = wait_for_client(server_socket)) < 0) + continue; + + FILE *pipeFile = popen(COMMAND, "r"); + + if (pipeFile <= 0) { + fprintf(stderr, "Failed to open pipe for command\n"); + close(client); + client = -1; + continue; + } + + if ((server = fileno(pipeFile)) < 0) { + close(client); + client = -1; + continue; + } + + set_nonblock(server); + + if (0 == fork()) { + close(server_socket); + pipe_to_socket(client, server); + abort(); + } + + close(client); + client = -1; + + close(server); + server = -1; + } +} diff --git a/workspaces/template/Dockerfile b/workspaces/template/Dockerfile index 6dccf07..d02f614 100644 --- a/workspaces/template/Dockerfile +++ b/workspaces/template/Dockerfile @@ -8,10 +8,10 @@ ENV DEBIAN_FRONTEND=noninteractive COPY ./scripts /opt/stardust/scripts # idk if all of these are needed someone fix this - @incognitotgt RUN apt-get update && apt-get install --no-install-recommends -y \ - xfonts-75dpi xvfb passwd sudo dbus dbus-x11 libxrandr2 libxext-dev libxrender-dev libxtst-dev imagemagick x11-apps \ - python3 python3-pip xterm git procps python3-numpy xfwm4 xfce4-terminal xfce4-session xfconf xfce4-notifyd \ - wget curl inetutils-ping vim tigervnc-tools tigervnc-standalone-server tigervnc-common \ - {{APPLICATION_PACKAGE}} + xfonts-75dpi xvfb passwd sudo dbus dbus-x11 libxrandr2 libxext-dev libxrender-dev libxtst-dev imagemagick x11-apps build-essential pulseaudio gstreamer1.0* \ + python3 python3-pip xterm git procps python3-numpy xfwm4 xfce4-terminal xfce4-session xfconf xfce4-notifyd \ + wget curl inetutils-ping vim tigervnc-tools tigervnc-standalone-server tigervnc-common \ + {{APPLICATION_PACKAGE}} RUN bash /opt/stardust/scripts/prepare.sh USER stardust @@ -20,4 +20,4 @@ RUN bash /opt/stardust/scripts/vnc-setup.sh WORKDIR /home/stardust CMD ["bash", "/opt/stardust/scripts/start.sh"] -EXPOSE 5901 +EXPOSE 5901 4713