]> Sergey Matveev's repositories - vors.git/commitdiff
Audio frame counter
authorSergey Matveev <stargrave@stargrave.org>
Sun, 28 Apr 2024 13:51:07 +0000 (16:51 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Mon, 29 Apr 2024 11:28:12 +0000 (14:28 +0300)
cmd/client/main.go
cmd/client/stats.go
doc/proto.texi

index 81db880b3b2842e51d74fe60344f32898d6f73b4000eece8188f3e127a8ad566..902492b8a990f14522361ba9d91b1dd335260054f3e1aaeded824b659796540b 100644 (file)
@@ -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))
index 30a490a74c405902ef4bb144f578c20d27c9daa2253ebb8c33c5d0afd1021a78..d157f44d265e4ad444a56bcaf491f95038661eec6e71dc65405ee186fbc574a5 100644 (file)
@@ -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 {
index 0bb140474af3cc9371e9576f3ad03dcbe9f22253744eb7ac8d8b6919e539042f..1e2d75422ea51248b4426f8abf053b37acda599a3362b27d4d78d13270d17554 100644 (file)
@@ -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