Skip to content
FiveM Phone
All articles

Deep dive · 8 min read

Video Calls in FiveM: How Realtime Phone Calls Actually Work

Voice channels, pma-voice, SaltyChat, TURN servers and WebRTC explained for FiveM server owners who want face-to-face video calls on the phone.

· FiveM Phone team

Video calls are the FiveM phone feature server owners ask about most, and the one that is most often misunderstood. A FiveM video call is really two systems working side by side: your voice resource carries the audio, and a WebRTC connection inside the phone UI carries the picture. This deep dive explains how both parts work, why you need a TURN server, and exactly what to configure to get face-to-face calls running on your server.

Everything below describes how our phone implements it, with the real config keys. The concepts apply to any FiveM phone with video.

Two layers: voice audio and WebRTC video

When two players are on a call, audio never goes through the phone UI. It stays in your voice system, exactly like a normal phone call. The video layer is added on top only when a player switches the call to video.

  • Audio: the server puts both callers into a call channel of your voice resource. Players hear each other through the same voice pipeline as proximity chat, with the same quality and the same plugin settings.
  • Video: each player's phone UI renders the in-game camera view, front or rear, onto a canvas and streams it to the other player through a WebRTC peer connection.

This split matters. It means video calls cannot break your voice setup, and if video fails, the audio call keeps going.

Voice channels: pma-voice, Enhanced Voice and SaltyChat

The call audio depends on the voice provider in your build, set in the configurator and stored as Config.Integrations.voice.

pma-voice

With pma-voice (6.6.2 or newer), the server places both callers into a pma-voice call channel through its server export setPlayerCall, and removes them by setting the channel back to 0. The server creates the channel, so a client cannot join someone else's call by guessing a number. pma-voice must be started before the phone.

FiveM Enhanced Voice

With FiveM Enhanced Voice, the phone creates its own server-side voice channel for every call (CreateVoiceChannel) and adds both players to it. Because the phone owns the channel, it can also mute players and relay speakerphone audio to players standing nearby. If the server runs out of voice channels, the call fails with a clear error instead of silently connecting no one.

SaltyChat

A SaltyChat adapter is included in the resource. It uses SaltyChat's documented EstablishCall and EndCall exports and is used by the Radio app. It is statically verified only and not yet tested on a live SaltyChat server, and the configurator currently offers pma-voice, FiveM Enhanced Voice or none for phone calls. If you run SaltyChat, talk to support before buying a build that depends on calls.

No voice

You can set voice to none. Calls are then disabled, and so is video, because video is an extension of a call.

How the video connection is set up

WebRTC needs three things: a way for both sides to exchange connection offers (signaling), a list of servers that help them find a route to each other (ICE servers), and a media stream.

Signaling through your FiveM server

The phone does not need a separate signaling server. Offers, answers and ICE candidates travel as normal FiveM events through your server, and the server checks every message before forwarding it:

  • The sender must be a participant in an active call, looked up in the database.
  • The device must support the camera capability (more on device shells below).
  • Messages are size-validated and rate-limited per call.
  • An answer is only accepted from the other participant, never from the player who sent the offer.

The server then forwards the message only to the other caller.

Why you need a TURN server

Most players sit behind home routers, mobile hotspots or carrier-grade NAT. Two players often cannot open a direct connection to each other. A TURN server solves that by relaying the video when a direct path is not possible. Without TURN, video calls will work for some pairs of players and fail for others, which is worse for support than not offering video at all.

That is why our configurator refuses to enable video without an RTC provider.

Configuring TURN: Cloudflare or your own

There are two RTC providers, picked in the configurator and stored as Config.Integrations.rtc. Credentials go into server ConVars, never into config.lua or the UI.

Cloudflare

Create a TURN key in Cloudflare's Realtime product, then add the key ID and API token to server.cfg:

set PHONE_TURN_KEY_ID "your-turn-key-id"
set PHONE_TURN_API_TOKEN "your-turn-api-token"

For every call, the server requests short-lived ICE credentials from Cloudflare (one-hour TTL, tagged with the call ID), hands them only to the two callers, and revokes them when the session ends.

Custom endpoint

If you run your own TURN server, such as coturn, point the phone at an HTTPS endpoint that issues credentials:

set PHONE_TURN_ENDPOINT "https://turn.example.com/credentials"
set PHONE_TURN_ENDPOINT_TOKEN "shared-secret"
set PHONE_TURN_REVOKE_ENDPOINT "https://turn.example.com/revoke"

The server sends a POST with a JSON body containing sessionId, callId and ttl, using the token as a Bearer header. Your endpoint answers like this:

{
  "iceServers": [
    { "urls": "turn:turn.example.com:3478", "username": "1730000000:call-42", "credential": "..." }
  ],
  "expiresAt": 1730003600,
  "revokeToken": "optional-token"
}

expiresAt is a Unix timestamp in seconds and must be in the future; up to eight ICE server entries are accepted. If you return a revokeToken, the phone posts it to the revoke endpoint when the call ends.

Always use set, not setr, for these values. setr replicates ConVars to every client.

Tuning video quality

The configurator exposes the video stream settings, shipped in Config.Features.realtime:

  • width and height: from 640 by 360 up to 1920 by 1080.
  • fps: 12 to 30.
  • bitrate: 250 kbit/s to 4 Mbit/s.

Higher values look better but cost every player bandwidth and, if your TURN provider bills for traffic, cost you money. Start in the middle and only raise the values if players ask. During a video call the phone switches to landscape, and players can flip between front and rear camera.

Call recording and nearby audio

Pro and Ultimate builds can also allow call recording (recording in the same block). Two settings keep it bounded: maxDurationSeconds (5 to 60 seconds) and maxBytes (up to 25 MB).

Recording is visible by design. When a player starts recording, both callers get a recording indicator. If you enable nearbyAudio, players standing within nearbyRadius (1 to 15 meters, default 7.5) of either caller and in the same routing bucket are notified too, because their voices may end up on the recording. For a roleplay server this is the fair default: nobody is recorded without a visible cue.

Device shells and capabilities

Ultimate builds can map phone items to standard, fold or retro shells, each with its own capability list. The server enforces it: if a device does not have the camera capability, every video and recording callback is refused with camera_not_supported. A retro flip phone can make normal calls but not video calls, which is a nice roleplay detail rather than a bug.

Requirements checklist

  1. Edition: Pro or Ultimate. Core builds cannot enable realtime features.
  2. Realtime enabled with video turned on in the configurator.
  3. Voice provider running: pma-voice or FiveM Enhanced Voice. Video is only offered when the voice provider is available.
  4. RTC provider set to Cloudflare or custom, with ConVars filled in.
  5. Media provider configured if you want recordings and AirShare to be saved.

Troubleshooting video calls

  • No video button. Check the edition, that video is enabled, and that the voice resource is started. Video capability is reported as off when voice is unavailable.
  • rtc_disabled or custom_turn_not_configured. The RTC provider is not set, or the ConVars are empty or misspelled.
  • turn_credentials_failed. The provider rejected the request: wrong key ID, expired token, or your endpoint returned a non-2xx status.
  • Video works for some players only. You are probably missing a TURN relay URL in your ICE servers, so only players with open NAT can connect.
  • turn_credentials_invalid. Your endpoint answered, but the JSON is missing iceServers, has more than eight entries, or expiresAt is not in the future.

More install context is in how to install a FiveM phone.

Next steps

You can place a demo call in the browser right now: the configurator runs the real phone UI. Video calls are included from Pro; compare editions on the pricing page or see everything on the features page.

Try the phone, not just the article.

Try the real phone in your browser, configure it in two minutes and install it tonight.