PHP code example of maatrics / livekit-server-sdk-php

1. Go to this page and download the library: Download maatrics/livekit-server-sdk-php library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

maatrics / livekit-server-sdk-php example snippets


use LiveKit\LiveKitAPI;
use LiveKit\Options\CreateRoomOptions;

$livekit = new LiveKitAPI('https://my-project.livekit.cloud', 'API_KEY', 'API_SECRET');

$room = $livekit->room->createRoom(new CreateRoomOptions(name: 'my-room', emptyTimeout: 300));

foreach ($livekit->room->listRooms() as $existing) {
    echo $existing->getName(), PHP_EOL;
}

// One request, and the response the server built. The same call the Go, Python
// and Ruby SDKs give you, cursor and all.
$page   = $livekit->egress->listEgress();
$items  = $page->getItems();
$cursor = $page->getNextPageToken()?->getToken();

// One page at a time. The next is fetched only when you reach for it, so
// leaving the loop early leaves the remaining requests unmade.
foreach ($livekit->egress->iterateEgress() as $egress) {
    break;
}

// Every page, collected into one array.
$all = $livekit->egress->listAllEgress();

use LiveKit\LiveKitAPI;
use LiveKit\Options\ClientOptions;

// Key and secret — the usual choice for a backend. The host needs its scheme:
// http(s), or ws(s), which is rewritten since the HTTP API shares the origin.
new LiveKitAPI('https://my-project.livekit.cloud', 'API_KEY', 'API_SECRET');

// A pre-signed token, for somewhere the API secret must not go. Its grants have
// to cover the calls you make with it.
new LiveKitAPI(
    host: 'https://my-project.livekit.cloud',
    options: new ClientOptions(token: $token),
);

// Nothing passed: read from LIVEKIT_URL plus either LIVEKIT_TOKEN, or
// LIVEKIT_API_KEY and LIVEKIT_API_SECRET.
new LiveKitAPI();

use LiveKit\AccessToken;
use LiveKit\Options\AccessTokenOptions;
use LiveKit\Grants\VideoGrant;

$token = new AccessToken('API_KEY', 'API_SECRET', new AccessTokenOptions(
    identity: 'alice',
    name: 'Alice',
    ttl: '6h',
));

$token->addGrant(new VideoGrant(roomJoin: true, room: 'my-room'));

echo $token->toJwt();

// Plain PHP
$receiver = new LiveKit\WebhookReceiver('API_KEY', 'API_SECRET');
$event = $receiver->receive(
    file_get_contents('php://input'),
    $_SERVER['HTTP_AUTHORIZATION'] ?? null,
);

// Laravel
$event = $receiver->receive($request->getContent(), $request->header('Authorization'));

// Symfony
$event = $receiver->receive($request->getContent(), $request->headers->get('Authorization'));

use GuzzleHttp\Client;

$livekit = new LiveKit\LiveKitAPI(
    host: 'https://my-project.livekit.cloud',
    apiKey: 'API_KEY',
    apiSecret: 'API_SECRET',
    httpClient: new Client(['timeout' => 40]),
);

use LiveKit\Options\UpdateParticipantOptions;

foreach ($livekit->room->listParticipants('my-room') as $participant) {
    echo $participant->getIdentity(), PHP_EOL;
}

$alice = $livekit->room->getParticipant('my-room', 'alice');

// Partial: only the fields you pass are changed. Everything omitted is left as
// it is rather than cleared, so you do not have to read-modify-write.
$livekit->room->updateParticipant('my-room', 'alice', new UpdateParticipantOptions(
    name: 'Alice (host)',
    metadata: '{"role":"host"}',
));

$livekit->room->mutePublishedTrack('my-room', 'alice', 'TR_abc123', true);

$livekit->room->removeParticipant('my-room', 'alice');

$livekit->room->updateSubscriptions('my-room', 'alice', ['TR_abc123'], false);

$livekit->room->updateRoomMetadata('my-room', '{"stage":"q-and-a"}');

use LiveKit\Options\SendDataOptions;
use LiveKit\Proto\DataPacket\Kind;

$livekit->room->sendData(
    'my-room',
    json_encode(['type' => 'announcement', 'body' => 'starting now'], JSON_THROW_ON_ERROR),
    Kind::RELIABLE,
    new SendDataOptions(destinationIdentities: ['alice'], topic: 'chat'),
);

use LiveKit\Options\SendDataOptions;
use LiveKit\Proto\DataPacket\Kind;

$payload = 'starting now';
$options = new SendDataOptions(topic: 'chat', nonce: random_bytes(16));

// If this times out, retry it with the same $options rather than new ones.
$livekit->room->sendData('my-room', $payload, Kind::RELIABLE, $options);

use LiveKit\Options\EncodedOutputs;
use LiveKit\Options\RoomCompositeOptions;
use LiveKit\Proto\EncodedFileOutput;
use LiveKit\Proto\S3Upload;

$egress = $livekit->egress->startRoomCompositeEgress(
    'my-room',
    new EncodedOutputs(file: new EncodedFileOutput()
        ->setFilepath('my-room-{time}.mp4')
        ->setS3(new S3Upload()->setBucket('recordings')->setRegion('eu-central-1'))),
    new RoomCompositeOptions(layout: 'speaker'),
);

foreach ($livekit->egress->listEgress() as $running) {
    echo $running->getEgressId(), ' ', $running->getStatus(), PHP_EOL;
}

$livekit->egress->stopEgress($egress->getEgressId());

use LiveKit\Options\CreateIngressOptions;
use LiveKit\Options\ListIngressOptions;
use LiveKit\Options\UpdateIngressOptions;
use LiveKit\Proto\IngressInput;

$ingress = $livekit->ingress->createIngress(new CreateIngressOptions(
    inputType: IngressInput::RTMP_INPUT,
    name: 'studio feed',
    roomName: 'my-room',
    participantIdentity: 'rtmp-source',
));

echo $ingress->getUrl(), ' ', $ingress->getStreamKey(), PHP_EOL;

// Only the fields you pass are changed. Everything omitted keeps its current
// value rather than being cleared, which is why the options are all nullable.
$livekit->ingress->updateIngress($ingress->getIngressId(), new UpdateIngressOptions(
    participantName: 'Studio',
));

$livekit->ingress->listIngress(new ListIngressOptions(roomName: 'my-room'));
$livekit->ingress->deleteIngress($ingress->getIngressId());

use LiveKit\Options\CreateDispatchOptions;

$dispatch = $livekit->agentDispatch->createDispatch(
    'my-room',
    'my-agent',
    // Reaches the agent as job metadata: which customer, which language, which prompt.
    new CreateDispatchOptions(metadata: '{"locale":"tr"}'),
);

$livekit->agentDispatch->listDispatch('my-room');

$one = $livekit->agentDispatch->getDispatch(dispatchId: $dispatch->getId(), room: 'my-room');

$livekit->agentDispatch->deleteDispatch(dispatchId: $dispatch->getId(), room: 'my-room');

use LiveKit\Options\CreateSipInboundTrunkOptions;
use LiveKit\Options\CreateSipOutboundTrunkOptions;

$inbound = $livekit->sip->createSipInboundTrunk(
    'support line',
    ['+15551234567'],
    new CreateSipInboundTrunkOptions(allowedAddresses: ['203.0.113.0/24']),
);

$outbound = $livekit->sip->createSipOutboundTrunk(
    'provider',
    'sip.provider.example',
    ['+15551234567'],
    new CreateSipOutboundTrunkOptions(authUsername: 'user', authPassword: 'secret'),
);

use LiveKit\Options\CreateSipDispatchRuleOptions;
use LiveKit\Proto\SIPDispatchRule;
use LiveKit\Proto\SIPDispatchRuleIndividual;

// Every caller gets their own room, named from the prefix. SIPDispatchRuleDirect
// sends all callers to one named room instead; SIPDispatchRuleCallee keys the
// room on the number that was dialled.
$rule = new SIPDispatchRule()->setDispatchRuleIndividual(
    new SIPDispatchRuleIndividual()->setRoomPrefix('call-')
);

$livekit->sip->createSipDispatchRule($rule, new CreateSipDispatchRuleOptions(
    name: 'support',
    trunkIds: [$inbound->getSipTrunkId()],
));

use LiveKit\Options\CreateSipParticipantOptions;

$participant = $livekit->sip->createSipParticipant(
    $outbound->getSipTrunkId(),
    '+15559876543',
    'support-call',
    new CreateSipParticipantOptions(participantIdentity: 'caller', waitUntilAnswered: true),
);

$livekit->sip->transferSipParticipant('support-call', 'caller', 'tel:+15551112222');

use LiveKit\Options\DialWhatsAppCallOptions;

$call = $livekit->connector->dialWhatsAppCall(
    whatsappPhoneNumberId: 'PHONE_NUMBER_ID',
    whatsappToPhoneNumber: '+15551234567',
    whatsappApiKey: 'META_API_KEY',
    whatsappCloudApiVersion: '23.0',
    options: new DialWhatsAppCallOptions(roomName: 'support-call', ringingTimeout: 45),
);

echo $call->getWhatsappCallId(), ' in ', $call->getRoomName(), PHP_EOL;

use LiveKit\Options\AcceptWhatsAppCallOptions;

$accepted = $livekit->connector->acceptWhatsAppCall(
    whatsappPhoneNumberId: 'PHONE_NUMBER_ID',
    whatsappApiKey: 'META_API_KEY',
    whatsappCloudApiVersion: '23.0',
    whatsappCallId: $event['call_id'],
    sdp: $sdpFromWebhook,
    options: new AcceptWhatsAppCallOptions(roomName: 'support-call', waitUntilAnswered: true),
);

use LiveKit\Proto\ConnectTwilioCallRequest\TwilioCallDirection;

$twilio = $livekit->connector->connectTwilioCall(
    TwilioCallDirection::TWILIO_CALL_DIRECTION_INBOUND,
    'support-call',
);

echo $twilio->getConnectUrl(), PHP_EOL; // wss://...

use LiveKit\Options\ConnectWhatsAppCallOptions;
use LiveKit\Proto\SessionDescription;

$livekit->connector->connectWhatsAppCall(
    $callIdFromTheDial,
    new SessionDescription()->setType('answer')->setSdp($sdpFromTheWebhook),
    new ConnectWhatsAppCallOptions(waitUntilAnswered: true, timeout: 45),
);

$livekit->connector->disconnectWhatsAppCall($callIdFromTheDial, 'META_API_KEY');

$livekit = new LiveKit\LiveKitAPI(
    host: 'https://my-project.livekit.cloud',
    apiKey: 'API_KEY',
    apiSecret: 'API_SECRET',
    options: new LiveKit\Options\ClientOptions(failover: false), // opt out
);

use LiveKit\Exceptions\SipCallError;
use LiveKit\Exceptions\TwirpErrorCode;
use LiveKit\Exceptions\TwirpException;

try {
    $livekit->room->deleteRoom('my-room');
} catch (TwirpException $e) {
    if ($e->getTwirpCode() === TwirpErrorCode::NOT_FOUND) {
        // ...
    }

    echo $e->getHttpStatus();  // e.g. 404
    print_r($e->getMeta());
}

try {
    $livekit->room->deleteRoom('my-room');
} catch (TwirpException $e) {
    if (($e->getMeta()[TwirpErrorCode::META_FROM_INTERMEDIARY] ?? null) === 'true') {
        // Something between you and LiveKit answered: the original status is in
        // meta['status_code'], and the body it sent in meta['body'].
    }
}

try {
    $livekit->sip->createSipParticipant(/* ... */);
} catch (SipCallError $e) {
    echo $e->getSipStatusCode(); // e.g. 486
    echo $e->getSipStatus();     // e.g. "Busy Here"
} catch (TwirpException $e) {
    // any other Twirp failure
}

use LiveKit\Exceptions\TokenVerificationException;

try {
    $claims = new LiveKit\TokenVerifier()->verify($jwt);
} catch (TokenVerificationException $e) {
    if ($e->getPrevious() instanceof Firebase\JWT\ExpiredException) {
        // ask for a fresh token
    }
}

use LiveKit\AccessToken;
use LiveKit\Options\AccessTokenOptions;
use LiveKit\Proto\RoomConfiguration;

$token = new AccessToken('API_KEY', 'API_SECRET', new AccessTokenOptions(
    identity: 'alice',
    roomConfig: new RoomConfiguration()->setEmptyTimeout(300),
));

foreach ($room->getEnabledCodecs() as $codec) {
    echo $codec->getMime(), PHP_EOL; // $codec is a Codec, not mixed
}

$room->setName(123);        // Parameter #1 $var of method Room::setName() expects string
$room->setEmptyTimeout('x'); // ...expects int

use LiveKit\Contracts\RoomServiceClientInterface;

final readonly class RoomProvisioner
{
    public function __construct(private RoomServiceClientInterface $rooms)
    {
    }

    public function provision(string $name): string
    {
        return $this->rooms->createRoom(new CreateRoomOptions(name: $name))->getSid();
    }
}

// Container wiring: one LiveKitAPI, its clients bound to their interfaces.
$livekit = new LiveKit\LiveKitAPI();

$container->set(RoomServiceClientInterface::class, $livekit->room);
$container->set(SipClientInterface::class, $livekit->sip);

$rooms = $this->createStub(RoomServiceClientInterface::class);
$rooms->method('createRoom')->willReturn(new Room()->setSid('RM_test'));

self::assertSame('RM_test', new RoomProvisioner($rooms)->provision('my-room'));