// optional offline-replay dump of the two media streams; gated by // CALLS_CAPTURE_DIR (no-op when unset). big-endian throughout, ts is ms // since writer construction. // header: [magic 8B][ver u8=1][reserved 7B] // PCM rec (tg->xmpp): [ts_ms i64][ssrc u32][len u32][pcm...] // RTP rec (xmpp->tg): [ts_ms i64][ssrc u32][seq u16][rtp_ts u32][pt u8][len u32][opus...] package audio import ( "encoding/binary" "errors" "fmt" "io" "os" "path/filepath" "sync" "time" ) const ( captureEnvVar = "CALLS_CAPTURE_DIR" captureVer = byte(1) ) var ( magicPCM = [8]byte{'T', 'G', 'C', 'A', 'P', 'P', 'C', 'M'} magicRTP = [8]byte{'T', 'G', 'C', 'A', 'P', 'R', 'T', 'P'} ) // per-call file sink; nil-safe so call sites can stay branchless type captureWriter struct { mu sync.Mutex f *os.File start time.Time closed bool } // returns (nil, nil) when CALLS_CAPTURE_DIR is unset func newCaptureWriter(name string, magic [8]byte) (*captureWriter, error) { dir := os.Getenv(captureEnvVar) if dir == "" { return nil, nil } if err := os.MkdirAll(dir, 0o755); err != nil { return nil, fmt.Errorf("capture: mkdir %s: %w", dir, err) } path := filepath.Join(dir, name) f, err := os.Create(path) if err != nil { return nil, fmt.Errorf("capture: create %s: %w", path, err) } var hdr [16]byte copy(hdr[0:8], magic[:]) hdr[8] = captureVer if _, err := f.Write(hdr[:]); err != nil { _ = f.Close() return nil, fmt.Errorf("capture: write header: %w", err) } return &captureWriter{f: f, start: time.Now()}, nil } func (w *captureWriter) writePCM(ssrc uint32, data []byte) { if w == nil { return } w.mu.Lock() defer w.mu.Unlock() if w.closed { return } var hdr [16]byte binary.BigEndian.PutUint64(hdr[0:8], uint64(time.Since(w.start).Milliseconds())) binary.BigEndian.PutUint32(hdr[8:12], ssrc) binary.BigEndian.PutUint32(hdr[12:16], uint32(len(data))) _, _ = w.f.Write(hdr[:]) _, _ = w.f.Write(data) } func (w *captureWriter) writeRTP(ssrc uint32, seq uint16, rtpTS uint32, pt uint8, data []byte) { if w == nil { return } w.mu.Lock() defer w.mu.Unlock() if w.closed { return } var hdr [23]byte binary.BigEndian.PutUint64(hdr[0:8], uint64(time.Since(w.start).Milliseconds())) binary.BigEndian.PutUint32(hdr[8:12], ssrc) binary.BigEndian.PutUint16(hdr[12:14], seq) binary.BigEndian.PutUint32(hdr[14:18], rtpTS) hdr[18] = pt binary.BigEndian.PutUint32(hdr[19:23], uint32(len(data))) _, _ = w.f.Write(hdr[:]) _, _ = w.f.Write(data) } func (w *captureWriter) close() { if w == nil { return } w.mu.Lock() defer w.mu.Unlock() if w.closed { return } w.closed = true _ = w.f.Close() } // one tg->xmpp record from a PCM capture file type CapturedPCM struct { TimestampMs int64 SSRC uint32 Data []byte } func ReadPCMCapture(r io.Reader) ([]CapturedPCM, error) { if err := verifyMagic(r, magicPCM); err != nil { return nil, err } var out []CapturedPCM for { var hdr [16]byte _, err := io.ReadFull(r, hdr[:]) if errors.Is(err, io.EOF) { return out, nil } if err != nil { return nil, err } rec := CapturedPCM{ TimestampMs: int64(binary.BigEndian.Uint64(hdr[0:8])), SSRC: binary.BigEndian.Uint32(hdr[8:12]), } length := binary.BigEndian.Uint32(hdr[12:16]) rec.Data = make([]byte, length) if _, err := io.ReadFull(r, rec.Data); err != nil { return nil, err } out = append(out, rec) } } func verifyMagic(r io.Reader, want [8]byte) error { var hdr [16]byte if _, err := io.ReadFull(r, hdr[:]); err != nil { return fmt.Errorf("capture: read header: %w", err) } var got [8]byte copy(got[:], hdr[0:8]) if got != want { return fmt.Errorf("capture: bad magic %q, want %q", string(got[:]), string(want[:])) } if hdr[8] != captureVer { return fmt.Errorf("capture: unsupported version %d", hdr[8]) } return nil }