From 28a8fa05ccb63f3ab5529d8405c1876d08c7adec Mon Sep 17 00:00:00 2001 From: Qishuai Liu Date: Thu, 26 Jun 2025 00:30:00 +0900 Subject: [PATCH] feat: use native jetkvm-audio --- audio.go | 77 ++++--------------------------------------------------- main.go | 6 ++++- native.go | 23 ++++------------- webrtc.go | 4 +-- 4 files changed, 17 insertions(+), 93 deletions(-) diff --git a/audio.go b/audio.go index cea1c86..7e0f7c9 100644 --- a/audio.go +++ b/audio.go @@ -1,81 +1,14 @@ package kvm import ( - "fmt" - "net" "os/exec" - "sync" - "syscall" - "time" ) -func startFFmpeg() (cmd *exec.Cmd, err error) { - binaryPath := "/userdata/jetkvm/bin/ffmpeg" - // Run the binary in the background - cmd = exec.Command(binaryPath, - "-f", "alsa", - "-channels", "2", - "-sample_rate", "48000", - "-i", "hw:1,0", - "-c:a", "libopus", - "-b:a", "64k", // ought to be enough for anybody - "-vbr", "off", - "-frame_duration", "20", - "-compression_level", "2", - "-f", "rtp", - "rtp://127.0.0.1:3333") - - nativeOutputLock := sync.Mutex{} - nativeStdout := &nativeOutput{ - mu: &nativeOutputLock, - logger: nativeLogger.Info().Str("pipe", "stdout"), - } - nativeStderr := &nativeOutput{ - mu: &nativeOutputLock, - logger: nativeLogger.Info().Str("pipe", "stderr"), - } - - // Redirect stdout and stderr to the current process - cmd.Stdout = nativeStdout - cmd.Stderr = nativeStderr - - // Set the process group ID so we can kill the process and its children when this process exits - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setpgid: true, - Pdeathsig: syscall.SIGKILL, - } - - // Start the command - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("failed to start binary: %w", err) - } - - return +func runAudioClient() (cmd *exec.Cmd, err error) { + return startNativeBinary("/userdata/jetkvm/bin/jetkvm_audio") } -func StartRtpAudioServer(handleClient func(net.Conn)) { - scopedLogger := nativeLogger.With(). - Logger() - - listener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 3333}) - if err != nil { - scopedLogger.Warn().Err(err).Msg("failed to start server") - return - } - - scopedLogger.Info().Msg("server listening") - - go func() { - for { - cmd, err := startFFmpeg() - if err != nil { - scopedLogger.Error().Err(err).Msg("failed to start ffmpeg") - } - err = cmd.Wait() - scopedLogger.Error().Err(err).Msg("ffmpeg exited, restarting") - time.Sleep(2 * time.Second) - } - }() - - go handleClient(listener) +func StartAudioServer() { + nativeAudioSocketListener = StartNativeSocketServer("/var/run/jetkvm_audio.sock", handleAudioClient, false) + nativeLogger.Debug().Msg("native app audio sock started") } diff --git a/main.go b/main.go index 54c2904..4d3c3fc 100644 --- a/main.go +++ b/main.go @@ -77,7 +77,11 @@ func Main() { // initialize usb gadget initUsbGadget() - StartRtpAudioServer(handleAudioClient) + + StartAudioServer() + if _, err := runAudioClient(); err != nil { + logger.Warn().Err(err).Msg("failed to run audio client") + } if err := setInitialVirtualMediaState(); err != nil { logger.Warn().Err(err).Msg("failed to set initial virtual media state") diff --git a/native.go b/native.go index b3996e4..2776798 100644 --- a/native.go +++ b/native.go @@ -13,8 +13,6 @@ import ( "time" "github.com/jetkvm/kvm/resource" - "github.com/pion/rtp" - "github.com/pion/webrtc/v4/pkg/media" ) @@ -107,6 +105,7 @@ func WriteCtrlMessage(message []byte) error { var nativeCtrlSocketListener net.Listener //nolint:unused var nativeVideoSocketListener net.Listener //nolint:unused +var nativeAudioSocketListener net.Listener //nolint:unused var ctrlClientConnected = make(chan struct{}) @@ -260,8 +259,6 @@ func handleAudioClient(conn net.Conn) { scopedLogger.Info().Msg("native audio socket client connected") inboundPacket := make([]byte, maxAudioFrameSize) - var timestamp uint32 - var packet rtp.Packet for { n, err := conn.Read(inboundPacket) if err != nil { @@ -270,20 +267,10 @@ func handleAudioClient(conn net.Conn) { } if currentSession != nil { - if err := packet.Unmarshal(inboundPacket[:n]); err != nil { - scopedLogger.Warn().Err(err).Msg("error unmarshalling audio socket packet") - continue - } - - timestamp += 960 - packet.Header.Timestamp = timestamp - buf, err := packet.Marshal() - if err != nil { - scopedLogger.Warn().Err(err).Msg("error marshalling packet") - continue - } - - if _, err := currentSession.AudioTrack.Write(buf); err != nil { + if err := currentSession.AudioTrack.WriteSample(media.Sample{ + Data: inboundPacket[:n], + Duration: 20 * time.Millisecond, + }); err != nil { scopedLogger.Warn().Err(err).Msg("error writing sample") } } diff --git a/webrtc.go b/webrtc.go index a5c358c..f14b72a 100644 --- a/webrtc.go +++ b/webrtc.go @@ -18,7 +18,7 @@ import ( type Session struct { peerConnection *webrtc.PeerConnection VideoTrack *webrtc.TrackLocalStaticSample - AudioTrack *webrtc.TrackLocalStaticRTP + AudioTrack *webrtc.TrackLocalStaticSample ControlChannel *webrtc.DataChannel RPCChannel *webrtc.DataChannel HidChannel *webrtc.DataChannel @@ -137,7 +137,7 @@ func newSession(config SessionConfig) (*Session, error) { return nil, err } - session.AudioTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus}, "audio", "kvm") + session.AudioTrack, err = webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus}, "audio", "kvm") if err != nil { return nil, err }