From: Sergey Matveev Date: Sun, 28 Apr 2024 13:51:07 +0000 (+0300) Subject: Audio frame counter X-Git-Tag: v3.0.0~5 X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=7afcba47af72ecc00e63a520b5af24336a19a226f91dc8f7d607c5fb43a600bc;p=vors.git Audio frame counter --- diff --git a/cmd/client/main.go b/cmd/client/main.go index 81db880..902492b 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -44,6 +44,8 @@ import ( type Stream struct { name string ctr uint32 + actr uint32 + muted bool in chan []byte stats *Stats } @@ -441,7 +443,9 @@ func main() { for buf := range stream.in { copy(nonce[len(nonce)-4:], buf) mac.Reset() - if _, err = mac.Write(buf[:len(buf)-siphash.Size]); err != nil { + if _, err = mac.Write( + buf[:len(buf)-siphash.Size], + ); err != nil { log.Fatal(err) } mac.Sum(tag[:0]) @@ -452,11 +456,13 @@ func main() { stream.stats.bads++ continue } - ciph, err = chacha20.NewUnauthenticatedCipher(keyCiph, nonce) + ciph, err = chacha20.NewUnauthenticatedCipher( + keyCiph, nonce, + ) if err != nil { log.Fatal(err) } - pkt = buf[4 : len(buf)-siphash.Size] + pkt = buf[4+3 : len(buf)-siphash.Size] ciph.XORKeyStream(pkt, pkt) ctr = binary.BigEndian.Uint32(nonce[len(nonce)-4:]) @@ -467,6 +473,8 @@ func main() { lost = int(ctr - (stream.ctr + 1)) } stream.ctr = ctr + stream.actr = uint32(buf[4+0])<<16 | + uint32(buf[4+1])<<8 | uint32(buf[4+2]) stream.stats.lost += int64(lost) if lost > vors.MaxLost { lost = 0 @@ -508,7 +516,7 @@ func main() { close(playerTx) } }() - go statsDrawer(stream.stats, stream.name) + go statsDrawer(stream) Streams[sid] = stream case vors.CmdDel: sid := args[1][0] @@ -528,7 +536,7 @@ func main() { log.Println("unknown sid:", sid) continue } - s.stats.muted = true + s.muted = true case vors.CmdUnmuted: sid := args[1][0] s := Streams[sid] @@ -536,7 +544,7 @@ func main() { log.Println("unknown sid:", sid) continue } - s.stats.muted = false + s.muted = false default: log.Fatal("unexpected cmd:", cmd) } @@ -578,7 +586,7 @@ func main() { } stream = Streams[buf[0]] if stream == nil { - // log.Println("unknown stream:", buf[0]) + log.Println("unknown stream:", buf[0]) continue } stream.stats.pkts++ @@ -592,7 +600,7 @@ func main() { } }() - go statsDrawer(OurStats, *Name) + go statsDrawer(&Stream{name: *Name, stats: OurStats}) go func() { <-LoggerReady for now := range time.NewTicker(time.Second).C { @@ -616,6 +624,7 @@ func main() { tag := make([]byte, siphash.Size) buf := make([]byte, 2*vors.FrameLen) pcm := make([]int16, vors.FrameLen) + actr := make([]byte, 3) nonce := make([]byte, 12) nonce[len(nonce)-4] = sid var pkt []byte @@ -626,6 +635,7 @@ func main() { log.Println("mic:", err) break } + incr(actr[:]) if Muted { continue } @@ -635,7 +645,7 @@ func main() { if vad != 0 && vors.RMS(pcm) < vad { continue } - n, err = opusEnc.Encode(pcm, buf[4:]) + n, err = opusEnc.Encode(pcm, buf[4+len(actr):]) if err != nil { log.Fatal(err) } @@ -646,18 +656,22 @@ func main() { incr(nonce[len(nonce)-3:]) copy(buf, nonce[len(nonce)-4:]) + copy(buf[4:], actr) ciph, err = chacha20.NewUnauthenticatedCipher(keyCiphOur, nonce) if err != nil { log.Fatal(err) } - ciph.XORKeyStream(buf[4:4+n], buf[4:4+n]) + ciph.XORKeyStream( + buf[4+len(actr):4+len(actr)+n], + buf[4+len(actr):4+len(actr)+n], + ) mac.Reset() - if _, err = mac.Write(buf[:4+n]); err != nil { + if _, err = mac.Write(buf[:4+len(actr)+n]); err != nil { log.Fatal(err) } mac.Sum(tag[:0]) - copy(buf[4+n:], tag) - pkt = buf[:4+n+siphash.Size] + copy(buf[4+len(actr)+n:], tag) + pkt = buf[:4+len(actr)+n+siphash.Size] OurStats.pkts++ OurStats.bytes += vors.IPHdrLen(srvAddrUDP.IP) + 8 + uint64(len(pkt)) diff --git a/cmd/client/stats.go b/cmd/client/stats.go index 30a490a..d157f44 100644 --- a/cmd/client/stats.go +++ b/cmd/client/stats.go @@ -36,7 +36,6 @@ type Stats struct { last time.Time vol uint64 volN uint64 - muted bool dead chan struct{} } @@ -49,7 +48,7 @@ func (stats *Stats) AddRMS(pcm []int16) { atomic.AddUint64(&stats.volN, uint64(len(pcm))) } -func statsDrawer(stats *Stats, name string) { +func statsDrawer(s *Stream) { var err error tick := time.Tick(vors.ScreenRefresh) var now time.Time @@ -59,37 +58,38 @@ func statsDrawer(stats *Stats, name string) { var rep int for { select { - case <-stats.dead: - GUI.DeleteView(name) - GUI.DeleteView(name + "-vol") + case <-s.stats.dead: + GUI.DeleteView(s.name) + GUI.DeleteView(s.name + "-vol") return case now = <-tick: - s := fmt.Sprintf( - "%s | %s | B/L/R: %s/%s/%s", - humanize.Comma(stats.pkts), - humanize.IBytes(stats.bytes), - humanize.Comma(stats.bads), - humanize.Comma(stats.lost), - humanize.Comma(stats.reorder), + l := fmt.Sprintf( + "%s | %s | S/B/L/R: %s/%s/%s/%s", + humanize.Comma(s.stats.pkts), + humanize.IBytes(s.stats.bytes), + humanize.Comma(int64(s.actr-(s.ctr&0x00FFFF))/50), + humanize.Comma(s.stats.bads), + humanize.Comma(s.stats.lost), + humanize.Comma(s.stats.reorder), ) - if name == *Name && Muted { - s += " | " + vors.CRed + "MUTE" + vors.CReset + if s.name == *Name && Muted { + l += " | " + vors.CRed + "MUTE" + vors.CReset } else { - if stats.muted { - s += " | " + vors.CRed + "MUTE" + vors.CReset + if s.muted { + l += " | " + vors.CRed + "MUTE" + vors.CReset } - if stats.last.Add(vors.ScreenRefresh).After(now) { - s += " | " + vors.CGreen + "TALK" + vors.CReset + if s.stats.last.Add(vors.ScreenRefresh).After(now) { + l += " | " + vors.CGreen + "TALK" + vors.CReset } } - v, err = GUI.View(name) + v, err = GUI.View(s.name) if err == nil { v.Clear() - v.Write([]byte(s)) + v.Write([]byte(l)) } - vol = float64(atomic.SwapUint64(&stats.vol, 0)) - volN = float64(atomic.SwapUint64(&stats.volN, 0)) - v, err = GUI.View(name + "-vol") + vol = float64(atomic.SwapUint64(&s.stats.vol, 0)) + volN = float64(atomic.SwapUint64(&s.stats.volN, 0)) + v, err = GUI.View(s.name + "-vol") if err == nil { v.Clear() if volN == 0 { diff --git a/doc/proto.texi b/doc/proto.texi index 0bb1404..1e2d754 100644 --- a/doc/proto.texi +++ b/doc/proto.texi @@ -7,8 +7,13 @@ feature when the number of lost frame does not exceed 32 count. DTX (discontinuous transmission) is also on. Each frame has a single byte stream identifier (unique identifier of the -participant) and 24-bit big-endian packet counter. Reordered packets are -dropped. 24-bit counter is long enough for very long talk sessions. +participant), 24-bit big-endian packet counter and 24-bit big-endian +audio frame counter. Reordered packets are dropped. 24-bit counter is +long enough for very long talk sessions. Audio frame counter is +increased every 20ms data from microphone is read. When peer is muted, +then no packets are sent, but audio frames are still counted. That gives +ability to distinguish jitters and delays from lack of audio +transmission. Each packet is encrypted with ChaCha20 and authenticated with SipHash24. Their keys are generated from BLAKE2s-XOF, which is fed with completed @@ -17,7 +22,7 @@ participants. The stream identifier together with the packet counter is used as a nonce. It is tuned for 24Kbps bandwidth. But remember that it has additional 8B -of MAC tag, 4B VoRS, 8B UDP and 40B IPv6 headers. +of MAC tag, 7B VoRS, 8B UDP and 40B IPv6 headers. Each client handshakes with the server over TCP connection using the @url{http://noiseprotocol.org/, Noise}-NK protocol pattern with