Documentation
¶
Overview ¶
Package gotgcall is a pure-Go library for streaming audio and video into Telegram group calls. The public API mirrors ntgcalls method names so bot code translates one-to-one.
The library is blob-only: signaling JSON is exchanged through your own MTProto client (typically gogram). Two calls are required:
params, _ := client.CreateCall(chatID)
resp, _ := tg.PhoneJoinGroupCall(... Params: &DataJson{Data: params})
client.Connect(chatID, resp.Updates[...].Call.Params.Data)
client.SetStreamSources(chatID, gotgcall.FromFile("song.mp3", gotgcall.EncodeOptions{}))
See README.md for the full pattern.
Index ¶
- Constants
- Variables
- type CallInfo
- type Client
- func (c *Client) AudioSSRC(chatID int64) (uint32, error)
- func (c *Client) Calls() map[int64]CallInfo
- func (c *Client) Close() error
- func (c *Client) Connect(chatID int64, telegramParams string) error
- func (c *Client) CreateCall(chatID int64) (string, error)
- func (c *Client) GetState(chatID int64) (MediaState, error)
- func (c *Client) Mute(chatID int64) (bool, error)
- func (c *Client) OnConnectionChange(fn func(chatID int64, info NetworkInfo))
- func (c *Client) OnMediaStateChange(fn func(chatID int64, state MediaState))
- func (c *Client) OnStreamEnd(fn func(chatID int64, t StreamType, d Device, err error))
- func (c *Client) Pause(chatID int64) (bool, error)
- func (c *Client) Resume(chatID int64) (bool, error)
- func (c *Client) SetStreamSources(chatID int64, src Source) error
- func (c *Client) StartRTMP(chatID int64, rtmpURL string) error
- func (c *Client) Stop(chatID int64) error
- func (c *Client) Time(chatID int64) (uint64, error)
- func (c *Client) Unmute(chatID int64) (bool, error)
- type ConnState
- type Device
- type EncodeOptions
- type ICEServer
- type MediaState
- type NetworkInfo
- type NetworkType
- type Option
- func WithConnectTimeout(d time.Duration) Option
- func WithDTLSCertPool(n int) Option
- func WithDebugLogs() Option
- func WithDispatchBuffer(n int) Option
- func WithFFmpegPath(p string) Option
- func WithFFmpegStderrLog() Option
- func WithICECandidateLogs() Option
- func WithICEMaxBindingRequests(n uint16) Option
- func WithICEPreConnectDelay(d time.Duration) Option
- func WithICEServers(servers []ICEServer) Option
- func WithICETimeouts(disconnect, failed, keepalive time.Duration) Option
- func WithLogger(l *slog.Logger) Option
- func WithNetworkTypes(types ...NetworkType) Option
- func WithPionTraceLogs() Option
- func WithSharedUDPMux() Option
- func WithVerboseConnectionLogs() Option
- type SeekableSource
- type Source
- type StreamType
- type Track
Constants ¶
const ( NetworkTypeUDP4 = wrtc.NetworkTypeUDP4 NetworkTypeUDP6 = wrtc.NetworkTypeUDP6 NetworkTypeTCP4 = wrtc.NetworkTypeTCP4 NetworkTypeTCP6 = wrtc.NetworkTypeTCP6 )
const ( TrackAudio = media.TrackAudio TrackVideo = media.TrackVideo Audio = models.Audio Video = models.Video Microphone = models.Microphone Camera = models.Camera Connecting = models.Connecting Connected = models.Connected Disconnected = models.Disconnected Failed = models.Failed Closed = models.Closed )
Variables ¶
var ( FromFile = media.FromFile FromURL = media.FromURL FromShell = media.FromShell FromShells = media.FromShells )
var ( ErrConnectionExists = models.ErrConnectionExists ErrConnectionNotFound = models.ErrConnectionNotFound ErrConnectionFailed = models.ErrConnectionFailed ErrInvalidParams = models.ErrInvalidParams ErrFFmpegSpawn = models.ErrFFmpegSpawn ErrFFmpegCrashed = models.ErrFFmpegCrashed ErrFile = models.ErrFile ErrClosed = models.ErrClosed ErrInternal = models.ErrInternal ErrNotConnected = models.ErrNotConnected ErrWrongMode = models.ErrWrongMode )
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client multiplexes many concurrent group calls behind a single process-wide handle. Safe for concurrent use.
func New ¶
New constructs a Client with the given options. Fails fast if the ffmpeg binary isn't on PATH (or wherever WithFFmpegPath points) so callers see the error at startup rather than on first stream.
func (*Client) AudioSSRC ¶
AudioSSRC returns the audio SSRC of a WebRTC call. Pass as Source to phone.LeaveGroupCall. Returns ErrWrongMode for RTMP calls.
func (*Client) Connect ¶
Connect finishes the WebRTC handshake using Telegram's response JSON. On error the call is auto-reaped so the caller can immediately retry CreateCall without coordinating a separate Stop.
func (*Client) CreateCall ¶
CreateCall starts a new WebRTC group-call instance for chatID and returns the JSON params the caller must pass to phone.JoinGroupCall.
Concurrent CreateCall / StartRTMP calls for the same chat are serialized; the first one wins, others get ErrConnectionExists without allocating a pion PeerConnection.
func (*Client) GetState ¶
func (c *Client) GetState(chatID int64) (MediaState, error)
GetState returns the current media-state (mute/pause flags).
func (*Client) OnConnectionChange ¶
func (c *Client) OnConnectionChange(fn func(chatID int64, info NetworkInfo))
OnConnectionChange registers a callback for ICE/DTLS state transitions.
func (*Client) OnMediaStateChange ¶ added in v0.6.5
func (c *Client) OnMediaStateChange(fn func(chatID int64, state MediaState))
OnMediaStateChange registers a callback fired whenever the call's outgoing media state (muted / paused / video-stopped) transitions.
Use this to keep the Telegram-side participant flags (settable via phone.editGroupCallParticipant in your MTProto layer) in sync with the library-side streamer state. Most importantly: when /play (audio-only) is followed by /vplay (video) on the SAME call, this fires with state.VideoStopped=false, signaling that you should flip the participant's video_stopped flag MTProto-side. Without that signal, Telegram's SFU may drop the late video even though our RTP is correct.
Mirror of ntgcalls' onUpgrade(MediaState) pattern. Fires on the dispatcher goroutine, safe to re-enter the Client API from within.
func (*Client) OnStreamEnd ¶
func (c *Client) OnStreamEnd(fn func(chatID int64, t StreamType, d Device, err error))
OnStreamEnd registers a callback fired when a track ends (EOF, crash, stop). Called on the dispatcher goroutine so it is safe to re-enter the Client API from within.
func (*Client) SetStreamSources ¶
SetStreamSources installs or replaces the streaming source for chatID. Encode options (FPS, tracks, bitrates) ride along with the Source — set them on the constructor (FromFile/FromURL).
On error the call is auto-reaped (closed and removed from the per-client registry) so the caller can immediately retry CreateCall without first invoking Stop. This covers the failure modes the user would otherwise need to clean up by hand: ICE/DTLS gate timeout, "connection closed during setup", and ffmpeg / source-open errors.
func (*Client) StartRTMP ¶
StartRTMP creates an RTMP-push call for chatID. The caller obtains rtmpURL via phone.GetGroupCallStreamRtmpUrl gogram-side. Serialised with CreateCall via the same per-chat creation mutex.
func (*Client) Stop ¶
Stop tears down the call and clears the per-chat call entry. The per-chat create-mutex is intentionally kept (see reap) so a concurrent CreateCall parked on mu.Lock() doesn't end up racing a later one on a fresh mutex. The mutex memory is negligible (sizeof sync.Mutex per chatID ever used).
type EncodeOptions ¶
type EncodeOptions = media.EncodeOptions
type ICEServer ¶ added in v0.6.0
ICEServer is re-exported so callers can configure STUN/TURN without importing pion directly.
type MediaState ¶
type MediaState = models.MediaState
type NetworkInfo ¶
type NetworkInfo = models.NetworkInfo
type NetworkType ¶ added in v0.6.0
type NetworkType = wrtc.NetworkType
NetworkType is re-exported for WithNetworkTypes.
type Option ¶
type Option func(*config)
func WithConnectTimeout ¶ added in v0.6.17
WithConnectTimeout overrides how long SetSource/Resume wait for the WebRTC connection to reach Connected before giving up. Default 10s — matches ntgcalls' own internal connection timeout. With pion running as ICE-CONTROLLED (since v0.6.26) and Telegram's edges responding within 50-300ms in healthy networks, 10s is generous. Set higher on unstable networks where ICE re-pairing on cross-DC moves takes longer.
func WithDTLSCertPool ¶
WithDTLSCertPool sets the size of the pre-generated DTLS certificate pool. Larger pools absorb bigger call-creation bursts without keygen latency. 0 disables pre-generation.
func WithDebugLogs ¶ added in v0.6.0
func WithDebugLogs() Option
WithDebugLogs is a convenience that installs a Debug-level text handler writing to os.Stderr. Equivalent to:
WithLogger(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug})))
Use this when reporting bugs — debug-level output covers ICE/DTLS state, ffmpeg exit codes, streamer pacing, and pion-internal events bridged through the new pion→slog adapter.
func WithDispatchBuffer ¶
WithDispatchBuffer sizes the event dispatcher's channel. Default 256.
func WithFFmpegPath ¶
WithFFmpegPath overrides the ffmpeg binary path (default "ffmpeg").
func WithFFmpegStderrLog ¶ added in v0.6.0
func WithFFmpegStderrLog() Option
WithFFmpegStderrLog tees ffmpeg's stderr output to the library logger at Debug level while the process is running. Without this, ffmpeg stderr is only surfaced in the final error message (last 512 bytes) when the subprocess crashes — useful for crash diagnosis but useless for "ffmpeg is running but I see no audio" symptoms. Enable for verbose diagnosis.
func WithICECandidateLogs ¶ added in v0.6.3
func WithICECandidateLogs() Option
WithICECandidateLogs logs every locally-gathered ICE candidate (host / srflx / relay, address, port, foundation) at Debug level via the PeerConnection's OnICECandidate hook. Pairs well with WithPionTraceLogs for "why is ICE failing" diagnosis: this option shows what we offered, pion-trace shows which pairs were tried, and the remote answer's candidate list (parsed in jsonparams) shows what Telegram returned.
func WithICEMaxBindingRequests ¶ added in v0.6.22
WithICEMaxBindingRequests overrides pion's per-pair STUN binding retry budget. Library default is 150 (≈30 s of retries at the 200 ms check interval), matching the standard connect gate so a slow Telegram SFU registration still recovers within the gate window. Pion's own default is 7, which lets each pair die in ~1.4 s if early STUN bindings get error responses (common when the SFU hasn't yet bound our ICE credentials post-JoinGroupCall) — pion then idle-ticks on a dead checklist until the connect gate times out.
Use this when you want a tighter or looser per-pair budget independent of the connect gate. 0 keeps the library default.
func WithICEPreConnectDelay ¶ added in v0.6.22
WithICEPreConnectDelay sleeps inside PeerConnection.Connect, after parsing Telegram's remote params but before pion's SetRemoteDescription kicks off ICE. Gives Telegram's SFU a head-start to register our credentials so the first STUN binding succeeds instead of being rejected with an error response (which would waste retries from the ICEMaxBindingRequests budget).
Library default is 250 ms (imperceptible to users; covers typical SFU registration windows around 150-200 ms post-JoinGroupCall). Pass any negative duration to disable the delay entirely. Pair with WithICEMaxBindingRequests for defense in depth — the delay reduces wasted STUN traffic in the SFU registration window; the higher binding budget recovers when the delay alone wasn't long enough.
func WithICEServers ¶ added in v0.6.0
WithICEServers overrides the ICE server list (default: none, host candidates only — matching ntgcalls). Pass STUN/TURN entries for bots behind symmetric NAT or restrictive firewalls.
gotgcall.WithICEServers([]gotgcall.ICEServer{
{URLs: []string{"turn:turn.example.com:3478"},
Username: "u", Credential: "p"},
})
func WithICETimeouts ¶ added in v0.6.0
WithICETimeouts overrides pion's ICE timing. Pass 0 for any value to keep the library default (60s disconnect grace / 120s failed / 2s keepalive). Telegram's edge wobble on rejoin often takes 60-90s to settle, hence the generous defaults. Use longer values on unstable networks where brief connectivity drops shouldn't kill the call; pass shorter values for ultra-responsive UIs that need faster fail-detection.
func WithLogger ¶
WithLogger sets a structured logger for internal events.
func WithNetworkTypes ¶ added in v0.6.0
func WithNetworkTypes(types ...NetworkType) Option
WithNetworkTypes overrides the ICE candidate network-type whitelist. Default is UDP4+UDP6 (matching ntgcalls' PORTALLOCATOR_ENABLE_IPV6). Telegram's SFU accepts IPv6 candidates and dual-stack hosts get more candidate pairs. Add TCP for restrictive environments where UDP is blocked.
gotgcall.WithNetworkTypes(
gotgcall.NetworkTypeUDP4,
gotgcall.NetworkTypeUDP6,
gotgcall.NetworkTypeTCP4,
)
func WithPionTraceLogs ¶ added in v0.6.3
func WithPionTraceLogs() Option
WithPionTraceLogs remaps pion's Trace-level output (per-ICE-check, per- candidate-pair, per-binding-request) to slog.LevelDebug instead of the default sub-debug level. Use this when ICE is stuck in "Checking" and you need to see exactly which candidate pairs are being tried, which fail, and which (if any) get a response from the remote.
gotgcall.New(gotgcall.WithDebugLogs(), gotgcall.WithPionTraceLogs())
Volume warning: ICE Trace at scale is several hundred lines per call. Use for diagnosis, not steady-state production.
func WithSharedUDPMux ¶
func WithSharedUDPMux() Option
WithSharedUDPMux makes all calls share one UDP socket for ICE traffic. Useful for high-concurrency setups (100+ simultaneous calls).
func WithVerboseConnectionLogs ¶ added in v0.6.25
func WithVerboseConnectionLogs() Option
WithVerboseConnectionLogs is a one-flag bundle for diagnosing "ICE/DTLS did not reach Connected within Ns" failures. It enables:
- Debug-level slog handler to stderr (WithDebugLogs)
- Per-candidate gather logging (WithICECandidateLogs)
- Pion's per-binding-request / per-pair-check trace at Debug (WithPionTraceLogs)
Use when reporting a stuck-in-Connecting bug — the resulting log contains every signal the library can surface about the ICE state machine. Library still also emits an Info-level checking-phase snapshot every ~5 seconds without this flag, so most issues can be triaged from a non-Debug run; flip this when those Info lines aren't enough.
type SeekableSource ¶
type SeekableSource = media.SeekableSource
type StreamType ¶
type StreamType = models.StreamType
Directories
¶
| Path | Synopsis |
|---|---|
|
Package instances holds the per-chat call state.
|
Package instances holds the per-chat call state. |
|
jsonparams
Package jsonparams encodes and decodes the SDP-like JSON envelope that Telegram's group-call signaling uses in place of standard SDP O/A.
|
Package jsonparams encodes and decodes the SDP-like JSON envelope that Telegram's group-call signaling uses in place of standard SDP O/A. |