Complete WebRTC implementation in pure PHP for real-time communication without external servers
No external dependencies or servers required
PHP WebRTC is a full implementation of the WebRTC protocol in pure PHP, designed to enable real-time communication (RTC) for PHP applications without relying on external servers.
Audio/video transport via RTP/SRTP with full codec support
SCTP DataChannels for real-time data exchange
STUN/TURN support for NAT traversal and connectivity
ICE and DTLS handling with encryption support
To use PHP WebRTC, you must have the following installed:
Create a file named install-deps.sh
:
#!/bin/bash
set -e
echo "π§ Updating system and installing base dependencies..."
apt-get update && apt-get install -y \
apt-transport-https \
ca-certificates \
gnupg \
autoconf \
automake \
build-essential \
cmake \
git \
libtool \
pkg-config \
yasm \
nasm \
wget \
unzip \
curl \
libzip-dev \
libgmp-dev \
libssl-dev \
libsrtp2-dev \
libx264-dev \
libffi-dev \
libprotobuf-dev \
php-gmp \
php-zip \
php-ffi \
php-cli \
php-mbstring \
php-curl \
php-xml \
php-bcmath \
php-sockets \
php-pcntl \
python3 \
&& rm -rf /var/lib/apt/lists/*
echo "β
Base system dependencies installed."
echo "π¦ Installing PHP Protobuf extension..."
pecl install protobuf
echo "extension=protobuf.so" > /etc/php/8.4/cli/conf.d/30-protobuf.ini
echo "β
Protobuf extension installed and enabled."
echo "βοΈ Enabling PHP FFI..."
echo "ffi.enable=true" > /etc/php/8.4/cli/conf.d/30-ffi.ini
echo "β
FFI enabled."
echo "π¬ Building and installing FFmpeg 7.1.1..."
cd /tmp
wget https://ffmpeg.org/releases/ffmpeg-7.1.1.tar.bz2
tar xjf ffmpeg-7.1.1.tar.bz2
cd ffmpeg-7.1.1
./configure --enable-shared --enable-gpl --enable-libx264 --enable-libopus --enable-libvpx
make -j$(nproc)
make install
ldconfig
cd /tmp && rm -rf ffmpeg-7.1.1*
echo "β
FFmpeg 7.1.1 installed."
echo "π§ Building and installing libopus 1.4..."
cd /tmp
wget https://github.com/xiph/opus/releases/download/v1.4/opus-1.4.tar.gz
tar xzvf opus-1.4.tar.gz
cd opus-1.4
./configure --prefix=/usr/local --enable-shared
make -j$(nproc)
make install
ldconfig
cd /tmp && rm -rf opus-1.4*
echo "β
libopus 1.4 installed."
echo "π₯ Building and installing libvpx 1.15.0..."
cd /tmp
wget https://github.com/webmproject/libvpx/archive/refs/tags/v1.15.0.tar.gz
tar xzvf v1.15.0.tar.gz
cd libvpx-1.15.0
./configure --prefix=/usr/local --enable-shared --disable-examples
make -j$(nproc)
make install
ldconfig
cd /tmp && rm -rf libvpx-1.15.0*
echo "β
libvpx 1.15.0 installed."
echo "π All dependencies installed successfully!"
Give the script execution permission and run it:
chmod +x install-deps.sh
./install-deps.sh
Install PHP WebRTC via Composer:
composer require quasarstream/webrtc
Alternatively, add the dependency to your composer.json
file:
{
"require": {
"quasarstream/webrtc": "^1.0"
}
}
The RTCConfiguration class provides options to configure your RTCPeerConnection. This includes STUN/TURN servers and optional TLS certificate and private key paths for secure DTLS connections.
use Webrtc\Webrtc\RTCConfiguration;
use Webrtc\ICE\RTCIceServer;
$stunServer = new RTCIceServer();
$stunServer->setUrls(['stun:stun.l.google.com:19302']);
$turnServer = new RTCIceServer();
$turnServer->setUrls(['turn:turn.example.com']);
$turnServer->setUsername('user');
$turnServer->setCredential('pass');
$turnServer->setCredentialType('password');
$config = new RTCConfiguration(
iceServers: [$stunServer, $turnServer],
certificatePath: '/etc/ssl/certs/rtc-cert.pem',
privateKeyPath: '/etc/ssl/private/rtc-key.pem'
);
// You can also construct a default configuration (uses Google's public STUN):
$configuration = new RTCConfiguration();
$pc = new RTCPeerConnection($configuration);
// Or don't pass any variable
$pc = new RTCPeerConnection();
There is also an option to pass an associative array to config:
use Webrtc\Webrtc\RTCConfiguration;
$pc = new RTCPeerConnection([
'iceServers' => [
[
'urls' => ['stun:stun.l.google.com:19302']
],
[
'urls' => ['turn:turn.example.com'],
'username' => 'user',
'credential' => 'pass',
'credentialType' => 'password'
]
],
'certificatePath' => '/etc/ssl/certs/rtc-cert.pem',
'privateKeyPath' => '/etc/ssl/private/rtc-key.pem'
]);
The RTCPeerConnection class in PHP WebRTC is at the heart of peer-to-peer communication. It manages:
This creates a new WebRTC peer connection instance. You can optionally pass a RTCConfiguration object here if you want to customize ICE servers or DTLS certificates.
$pc = new RTCPeerConnection();
$pc->on("datachannel", function (RTCDataChannel $channel) {
$channel->on("message", function (string $message) use ($channel) {
$channel->send($message); // Echo the message
});
});
datachannel
event is fired. Inside this event, you can listen
for message
events and react. You can use DataChannels to
send JSON, binary data, or even implement custom signaling protocols.
$pc->on("track", function (MediaStreamTrack $track) use ($pc) {
$pc->addTrack($track);
});
When a remote peer adds a media track (audio/video), the track
event fires. You can optionally choose to call $pc->addTrack($track)
to register it on your end, or attach
it to a MediaStream.
The following code snippet demonstrates how to handle an incoming SDP offer in a WebRTC session using PHP. It follows the offer/answer exchange model defined by the WebRTC standard and mirrors the behavior seen in JavaScript-based implementations.
$pc->setRemoteDescription($request->getOffer())
->then(fn() => $pc->createAnswer())
->then(fn(RTCSessionDescription $description) => $pc->setLocalDescription($description))
->then(fn() => $request->sendOffer($pc->getLocalDescription()))
->catch(fn(Throwable $e) => $request->respondError($e->getMessage(), 400));
This performs the classic WebRTC "answer" process:
datachannel
$request is an abstraction in your app that receives/sends WebRTC offers/answers via HTTP/WebSocket.
You can observe the internal state transitions of a WebRTC RTCPeerConnection instance using event listeners. These events help track the lifecycle and connectivity status of the connection, which is essential for debugging and diagnostics.
// state changes trigger
$pc->on("connectionstatechange", function () use ($pc) {
echo sprintf("connectionstatechange: %s\n", $pc->getConnectionState()->name);
});
$pc->on("iceconnectionstatechange", function () use ($pc) {
echo sprintf("iceconnectionstatechange: %s\n", $pc->getIceConnectionState()->name);
});
$pc->on("icegatheringstatechange", function () use ($pc) {
echo sprintf("icegatheringstatechange: %s\n", $pc->getIceGatheringState()->name);
});
$pc->on("signalingstatechange", function () use ($pc) {
echo sprintf("signalingstatechange: %s\n", $pc->getSignalingState()->name);
});
This code sets up event listeners on a WebRTC RTCPeerConnection object ($pc
) to log changes in its internal states. When state
transitions occur in the connection (connectionstatechange
),
ICE connection (iceconnectionstatechange
), ICE gathering
(icegatheringstatechange
), or signaling (signalingstatechange
), corresponding messages are printed to
the console. This is useful for debugging or observing connection lifecycle events in real
time during a WebRTC session.
WebRTC provides detailed statistics through the getStats()
methodβsimilar to the JavaScript WebRTC API βallowing developers to inspect media streams,
transports, and encoding performance.
//Stats
use React\EventLoop\Loop;
$loop = Loop::get();
$lastBytesSent = 0;
$lastBytesReceived = 0;
$loop->addPeriodicTimer(1, function () use ($pc, &$lastBytesSent, &$lastBytesReceived) {
$stats = $pc->getStats();
$bytesSent = 0;
$bytesReceived = 0;
foreach ($stats->getStats() as $stat) {
// if ($stat instanceof RTCOutboundRTPStreamStats) {
// $bytesSent += $stat->bytesSent;
// }
//
// if ($stat instanceof RTCInboundRTPStreamStats) {
// $bytesReceived += $stat->packetsReceived;
// }
if ($stat instanceof RTCTransportStats) {
$bytesSent += $stat->bytesSent;
$bytesReceived += $stat->bytesReceived;
}
}
echo sprintf(
"\rUpload Bandwidth: %d Kbps | Download Bandwidth: %d Kbps",
intval(($bytesSent - $lastBytesSent) / 1024),
intval(($bytesReceived - $lastBytesReceived) / 1024)
);
$lastBytesSent = $bytesSent;
$lastBytesReceived = $bytesReceived;
});
RTCTransportStats
. You can also
use RTCOutboundRTPStreamStats
and RTCInboundRTPStreamStats
for calculating bandwidth. You can
extend this to collect round-trip time, jitter, frame rates, codec info, and packet loss by
inspecting other stat types like RTCRemoteInboundRTPStreamStats
and RTCRemoteOutboundRTPStreamStats
.
If you're initiating the connection, you can also create a DataChannel before the offer:
$channel = $pc->createDataChannel("chat");
$channel->on("open", function () use ($channel) {
$channel->send("Hello!");
});
$channel->on("message", function (string $msg) {
echo "Received: $msg\n";
});
The following examples show how to use PHP WebRTC as the applicant side in a real WebRTC session using RTCPeerConnection.
Main Page
Echo Example
We are not stopping here. We're actively continuing development on the PHP WebRTC packages.
Right now, we're working privately on a Selective Forwarding Unit (SFU) implementation and a Laravel package that bundles everything together, including this WebRTC package. Once that's ready, our goal is to build a minimal video conferencing web app using Laravel.
Also We are actively improving PHP WebRTC, and exciting updates are on the way in the coming months! These will include:
If you're interested in building real-time communication tools in PHP, like a video conferencing app, you are more than welcome to join the project. Fork the repos, contribute to them, and help grow this community.
If you'd like to become a contributor or teammate, we'd love to hear from you. This is an open source, non-profit project, so we're mainly looking for passionate developers.
You should have at least one project that demonstrates your knowledge and ability in PHP, Laravel, and WebRTC(only one of them is also enough). If that sounds like you, this could be a great place to get involved.
Feel free to reach out by this form with your GitHub username (for example: github.com/your-username) and we'll get back to you soon.
We've been working on webrtc repository and many other PHP WebRTC-related ones (such as ICE, RTP, RTCP, and more than 22 others) for a long time privately(in our git server) before making them open source(We released our packages only after they have been fully tested and thoroughly debugged). Originally, there was a long commit history that reflected all our work.
However, we decided to remove that history in the initial public commit to protect our privacy. The original commits included details like our working hours based on commit times and counts, as well as our personal email addresses, which we did not feel comfortable sharing publicly.
Removing the history helps us keep that information private and stay a bit safer from potential security risks.
If you find this project useful, please support us:
Together, we can make PHP a first-class citizen in real-time communication.