major remote desktop upgrades + do opus. Hopefully all the tests pass 😭
This commit is contained in:
@@ -503,6 +503,7 @@ func startDesktopAudioSession(ctx context.Context, env *runtime.Env, sessionID s
|
||||
stopDesktopAudioSession()
|
||||
|
||||
vCtx, cancel := context.WithCancel(ctx)
|
||||
legacyConverter := &desktopAudioLegacyConverter{}
|
||||
session, err := audio.StartCaptureOnlySession(vCtx, source, func(chunk []byte) {
|
||||
if len(chunk) == 0 {
|
||||
return
|
||||
@@ -513,10 +514,14 @@ func startDesktopAudioSession(ctx context.Context, env *runtime.Env, sessionID s
|
||||
samples := pcm16BytesToInt16(chunk)
|
||||
_ = webrtcpub.WriteAudio(webrtcpub.KindAudio, samples)
|
||||
}
|
||||
legacyPCM := legacyConverter.Convert(chunk)
|
||||
if len(legacyPCM) == 0 {
|
||||
return
|
||||
}
|
||||
msg := map[string]interface{}{
|
||||
"type": "desktop_audio_uplink",
|
||||
"sessionId": sessionID,
|
||||
"data": chunk,
|
||||
"data": legacyPCM,
|
||||
}
|
||||
_ = wire.WriteMsg(vCtx, env.Conn, msg)
|
||||
})
|
||||
@@ -904,6 +909,24 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
log.Printf("desktop: set target fps=%d", fps)
|
||||
sendCommandResultSafe(env, cmdID, true, "")
|
||||
return nil
|
||||
case "desktop_set_bitrate":
|
||||
payload, _ := envelope["payload"].(map[string]interface{})
|
||||
bitrateMbps := 0
|
||||
if payload != nil {
|
||||
if v, ok := payloadInt(payload, "bitrateMbps"); ok {
|
||||
bitrateMbps = v
|
||||
}
|
||||
}
|
||||
if bitrateMbps < 0 {
|
||||
bitrateMbps = 0
|
||||
}
|
||||
if bitrateMbps > 50 {
|
||||
bitrateMbps = 50
|
||||
}
|
||||
bps := capture.SetH264TargetBitrate(bitrateMbps * 1_000_000)
|
||||
log.Printf("desktop: set target bitrate=%d Mbps (0=auto)", bps/1_000_000)
|
||||
sendCommandResultSafe(env, cmdID, true, "")
|
||||
return nil
|
||||
case "clipboard_sync_start":
|
||||
payload, _ := envelope["payload"].(map[string]interface{})
|
||||
source := "rd"
|
||||
|
||||
@@ -23,6 +23,43 @@ func pcm16BytesToInt16(chunk []byte) []int16 {
|
||||
return out
|
||||
}
|
||||
|
||||
// WebRTC desktop audio is captured at 48 kHz stereo. Keep the established raw
|
||||
// WebSocket fallback wire format at 16 kHz mono by averaging each stereo frame
|
||||
// and decimating groups of three frames. pending preserves sample groups when
|
||||
// an audio callback ends between frames or decimation windows.
|
||||
type desktopAudioLegacyConverter struct {
|
||||
pending []byte
|
||||
}
|
||||
|
||||
func (c *desktopAudioLegacyConverter) Convert(chunk []byte) []byte {
|
||||
if len(chunk) == 0 {
|
||||
return nil
|
||||
}
|
||||
data := make([]byte, 0, len(c.pending)+len(chunk))
|
||||
data = append(data, c.pending...)
|
||||
data = append(data, chunk...)
|
||||
const windowBytes = 3 * 2 * 2 // three stereo PCM16 frames
|
||||
windows := len(data) / windowBytes
|
||||
if windows == 0 {
|
||||
c.pending = append(c.pending[:0], data...)
|
||||
return nil
|
||||
}
|
||||
out := make([]byte, 0, windows*2)
|
||||
for window := 0; window < windows; window++ {
|
||||
var sum int64
|
||||
for frame := 0; frame < 3; frame++ {
|
||||
offset := window*windowBytes + frame*4
|
||||
left := int32(int16(binary.LittleEndian.Uint16(data[offset:])))
|
||||
right := int32(int16(binary.LittleEndian.Uint16(data[offset+2:])))
|
||||
sum += int64(left+right) / 2
|
||||
}
|
||||
sample := int16(sum / 3)
|
||||
out = binary.LittleEndian.AppendUint16(out, uint16(sample))
|
||||
}
|
||||
c.pending = append(c.pending[:0], data[windows*windowBytes:]...)
|
||||
return out
|
||||
}
|
||||
|
||||
func kindFromPayload(payload map[string]interface{}) webrtcpub.Kind {
|
||||
switch s, _ := payload["kind"].(string); s {
|
||||
case "backstage":
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDesktopAudioLegacyPCMDownmixesAndDecimates(t *testing.T) {
|
||||
// Three 48 kHz stereo frames become one 16 kHz mono sample. Stereo frame
|
||||
// means are 2000, 4000, and 6000; their anti-aliasing mean is 4000.
|
||||
input := make([]byte, 0, 12)
|
||||
for _, pair := range [][2]int16{{1000, 3000}, {3000, 5000}, {5000, 7000}} {
|
||||
input = binary.LittleEndian.AppendUint16(input, uint16(pair[0]))
|
||||
input = binary.LittleEndian.AppendUint16(input, uint16(pair[1]))
|
||||
}
|
||||
converter := &desktopAudioLegacyConverter{}
|
||||
if out := converter.Convert(input[:5]); len(out) != 0 {
|
||||
t.Fatalf("fragment produced %d bytes before a complete window", len(out))
|
||||
}
|
||||
out := converter.Convert(input[5:])
|
||||
if len(out) != 2 {
|
||||
t.Fatalf("legacy PCM length = %d, want 2", len(out))
|
||||
}
|
||||
if got := int16(binary.LittleEndian.Uint16(out)); got != 4000 {
|
||||
t.Fatalf("legacy PCM sample = %d, want 4000", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user