Introduction

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. It includes support for:

  • Media (audio/video) transport via RTP/SRTP
  • SCTP DataChannels
  • STUN/TURN for NAT traversal
  • SDP negotiation
  • ICE and DTLS handling
  • Statistics and congestion control

This library is ideal for PHP developers looking to integrate peer-to-peer communication into web apps, signaling servers, or backend services using native PHP.

GitHub: https://github.com/php-webrtc

Requirements & Installation

Required Dependencies

To use PHP WebRTC, you must have the following installed:

  • Linux (Ubuntu/Debian preferred)
    • Windows users: Use WSL or Docker Desktop.
    • macOS users: an emulator like UTM or simply run the project using Docker.
      Note: Native Windows and macOS support is planned for an upcoming releases (in a few months.).
  • PHP 8.4+ with FFI enabled
  • PHP extensions: gmp, protobuf(for RTCSignaling)
  • FFmpeg shared libraries (libav*)
  • Opus v1.4
  • libvpx v1.15.0

Bash Installation Script

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-dev \
    php-pear \
    php-gmp \
    php-zip \
    php-ffi \
    php-cli \
    php-mbstring \
    php-curl \
    php-xml \
    php-bcmath \
    php-tokenizer \
    php-dom \
    php-pcov \
    php-sqlite3 \
    php-pdo \
    php-json \
    php-opcache \
    php-readline \
    php-soap \
    php-intl \
    php-exif \
    php-fileinfo \
    php-phar \
    php-fpm \
    php-common \
    php-iconv \
    php-posix \
    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.3.1*
echo "โœ… libopus 1.3.1 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!"

Running the Script

Give the script execution permission and run it:

                                            chmod +x install-deps.sh
./install-deps.sh"

Composer Installation

Install PHP WebRTC via Composer:

composer require quasarstream/webrtc

Alternatively, add the dependency to your composer.json file:

"require": {
    "quasarstream/webrtc": "^1.0"
}

RTCPeerConnection Configuration

RTCConfiguration Overview

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 an option to pass an associative array to config as blow

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'
]);

RTCPeerConnection

The RTCPeerConnection class in PHP WebRTC is at the heart of peer-to-peer communication. It manages:

  • ICE negotiation
  • DTLS encryption
  • RTP media transmission
  • SCTP DataChannel communication
Initialization

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(See above example).

$pc = new RTCPeerConnection();
Handling Incoming DataChannels
$pc->on("datachannel", function (RTCDataChannel $channel) {
    $channel->on("message", function (string $message) use ($channel) {
        $channel->send($message); // Echo the message
    });
});
  • When the remote peer creates a DataChannel, the datachannel event is fired.
  • Inside this event, you can listen for message events and react.
  • In this example, we echo the incoming message back to the sender.

๐Ÿ” You can use DataChannels to send JSON, binary data, or even implement custom signaling protocols.

Handling Media Tracks
$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.
Negotiating Session Descriptions

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:

  • 1. Set Remote Offer โ€“ from the other peer
  • 2. Create Answer โ€“ your SDP response
  • 3. Create Answer โ€“ your generated SDP answer
  • 4. Send Local SDP back โ€“ e.g., via your signaling server

๐Ÿ” This assumes that $request is an abstraction in your app that receives/sends WebRTC offers/answers via HTTP/WebSocket.

State Changes

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.

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.

// 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);
});
Stats

WebRTC provides detailed statistics through the getStats() methodโ€”similar to the JavaScript WebRTC API โ€”allowing developers to inspect media streams, transports, and encoding performance.

In this example, we use a periodic timer to gather key statistics from a RTCPeerConnection. Specifically, we calculate the current upload and download bandwidth by summing byte counts from RTCTransportStats. You can also use RTCOutboundRTPStreamStats and RTCInboundRTPStreamStats for calculating bandwidth

This gives you real-time visibility into media transmission performance, helping you monitor network conditions, track degradation, or trigger adaptive behaviors (e.g. lowering bitrate on poor connections).

//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;
});

Tip: You can also extend this to collect round-trip time, jitter, frame rates, codec info, and packet loss by inspecting other stat types like RTCRemoteInboundRTPStreamStats and RTCRemoteOutboundRTPStreamStats

DataChannel

If you're initiating the connection, you can also create a DataChannel before the offer:

Creating Datachannel:
$channel = $pc->createDataChannel("chat");

$channel->on("open", function () use ($channel) {
    $channel->send("Hello!");
});

$channel->on("message", function (string $msg) {
    echo "Received: $msg\n";
});

Examples

The following example shows how to use PHP WebRTC as the applicant side in a real WebRTC session using RTCPeerConnection.

See: https://github.com/PHP-WebRTC/examples

Screenshots

Main page:

Main Page

Echo Example:

Please note: Additional examples and updates will be added in the coming weeks.

Upcoming and Our Plans for the Future

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:

  • Expanded documentation and usage guides
  • More signaling examples (WebSocket, HTTP, UNIX sockets)
  • Media processing tools and integrations
  • Performance tuning and benchmarks
  • Advanced topics like simulcast, bundling, and congestion control

Contribution and Collaboration

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.

Please note: contributions should primarily focus on fixing bugs. If you'd like to add a new feature or function, it must match the original WebRTC API behavior. For example, if you want to add a method to the RTCPeerConnection class, it should exist in the original JavaScript WebRTC API and follow the same naming and purpose.

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.

About Git History

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.

Support Us

If you find this project useful, please support us:

  • โญ Star the repository on GitHub
  • ๐Ÿ”— Fork the repository on GitHub and fix bugs
  • ๐Ÿ” Share it with your developer communities
  • ๐Ÿงฉ Contribute by opening issues or pull requests
  • ๐Ÿ’ฌ Give feedback or feature requests via GitHub Discussions or Issues
  • โค๏ธ Spread the word and help bring full WebRTC to the PHP ecosystem!

Together, we can make PHP a first-class citizen in real-time communication.

๐Ÿ‘‰ https://github.com/PHP-WebRTC