package station import ( "encoding/binary" "fmt" "io" ) const ( tcpHeartbeat = 1 tcpProtoVer = 5 tcpAuthChallenge = 10 tcpAuthResponse = 11 tcpAuthOK = 12 tcpError = 13 tcpServerInfo = 100 tcpTalkerStart = 104 tcpTalkerStop = 105 tcpSelectTG = 106 tcpNodeInfo = 111 udpHeartbeat = 1 udpAudio = 101 udpFlush = 102 maxFrame = 32768 ) type frame struct { typ uint16 body []byte } func writeFrame(w io.Writer, typ uint16, body []byte) error { payload := make([]byte, 2+len(body)) binary.BigEndian.PutUint16(payload[:2], typ) copy(payload[2:], body) var hdr [4]byte binary.BigEndian.PutUint32(hdr[:], uint32(len(payload))) if _, err := w.Write(hdr[:]); err != nil { return err } _, err := w.Write(payload) return err } func readFrame(r io.Reader) (frame, error) { var hdr [4]byte if _, err := io.ReadFull(r, hdr[:]); err != nil { return frame{}, err } n := binary.BigEndian.Uint32(hdr[:]) if n < 2 || n > maxFrame { return frame{}, fmt.Errorf("frame length %d", n) } buf := make([]byte, n) if _, err := io.ReadFull(r, buf); err != nil { return frame{}, err } return frame{typ: binary.BigEndian.Uint16(buf[:2]), body: buf[2:]}, nil } func appendU16(b []byte, v uint16) []byte { var buf [2]byte binary.BigEndian.PutUint16(buf[:], v) return append(b, buf[:]...) } func appendString(b []byte, s string) []byte { b = appendU16(b, uint16(len(s))) return append(b, s...) } func appendBlob(b []byte, p []byte) []byte { b = appendU16(b, uint16(len(p))) return append(b, p...) } func parseU16(b []byte) (uint16, []byte, error) { if len(b) < 2 { return 0, nil, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(b[:2]), b[2:], nil } func parseU32(b []byte) (uint32, []byte, error) { if len(b) < 4 { return 0, nil, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(b[:4]), b[4:], nil } func parseString(b []byte) (string, []byte, error) { n, rest, err := parseU16(b) if err != nil { return "", nil, err } if len(rest) < int(n) { return "", nil, io.ErrUnexpectedEOF } return string(rest[:n]), rest[n:], nil } func parseBlob(b []byte) ([]byte, []byte, error) { n, rest, err := parseU16(b) if err != nil { return nil, nil, err } if len(rest) < int(n) { return nil, nil, io.ErrUnexpectedEOF } return rest[:n], rest[n:], nil } func parseStringList(b []byte) ([]string, []byte, error) { n, rest, err := parseU16(b) if err != nil { return nil, nil, err } out := make([]string, 0, n) for range int(n) { s, next, err := parseString(rest) if err != nil { return nil, nil, err } out = append(out, s) rest = next } return out, rest, nil } func parseServerInfo(b []byte) (uint16, []string, error) { _, rest, err := parseU16(b) if err != nil { return 0, nil, err } id, rest, err := parseU16(rest) if err != nil { return 0, nil, err } _, rest, err = parseStringList(rest) if err != nil { return 0, nil, err } codecs, _, err := parseStringList(rest) if err != nil { return 0, nil, err } return id, codecs, nil } func hasOPUS(codecs []string) bool { for _, c := range codecs { if c == "OPUS" { return true } } return false }