How to Control PTZ Cameras in OBS via WebSockets from Laravel?

Updated: Feb 24, 2025

How to Control PTZ Cameras in OBS via WebSockets from Laravel?

To control PTZ cameras in OBS (Open Broadcasting Software) via WebSockets from Laravel, you'll need to set up a few things:

  1. Install and configure OBS with PTZ hardware and WebSockets.
  2. Set up a Laravel project and install necessary dependencies.
  3. Create a WebSocket server in Laravel to handle OBS camera control requests.
  4. Create a route and controller to handle incoming camera control requests.
  5. Send camera control commands from Laravel to OBS using WebSockets.

Here's a step-by-step guide to help you achieve this:

Step 1: Install and configure OBS with PTZ hardware and WebSockets

First, you need to have a PTZ camera connected to your OBS system. Ensure that your camera is compatible with OBS and supports WebSockets for PTZ control.

To enable WebSockets in OBS, follow these steps:

  1. Open OBS and go to Settings > Hotkeys.
  2. Click on Add to create a new hotkey.
  3. Name the hotkey, e.g., "OBS WebSocket".
  4. Set the command to obs_frontend --websocket.
  5. Save the settings.

Now, you need to install the obs-websocket package to enable WebSockets in OBS. You can install it using the following command:

sudo apt-get install obs-websocket

Step 2: Set up a Laravel project and install necessary dependencies

Create a new Laravel project using Composer:

composer create --prefer-dist laravel/laravel obs-camera-control

Install the laravel-websockets package to handle WebSocket connections:

composer require yajra/laravel-websockets

Step 3: Create a WebSocket server in Laravel to handle OBS camera control requests

Create a new file app/ObsWebSocket.php to handle the WebSocket connection:

<?php

namespace App\ObsWebSocket;

use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\WebSocket\WsServer;
use Ratchet\WebSocket\WsServerInterface;
use Ratchet\WebSocket\WsMessageComponentInterface;
use Ratchet\WebSocket\WsMessageComponent;
use Ratchet\WebSocket\WsServer as RatchetWsServer;
use Ratchet\MessageComponentInterface as RatchetMessageComponentInterface;
use Ratchet\ConnectionInterface as RatchetConnectionInterface;

class ObsWebSocketServiceProvider extends ServiceProvider implements WsMessageComponentInterface
{
    protected $name = 'obs';

    public function register()
    {
        $this->app->singleton('obs', function () {
            return new ObsWebSocket($this->app['events']);
        });

        Broadcast::routes(['middleware' => ['auth:api']]);

        $this->app->make(WsServer::class)->listen(
            $this->app['config']['app.key'] . ':6001',
            $this
        );
    }

    public function boot()
    {
        $this->registerRoutes();
    }

    public function handleConnection(ConnectionInterface $conn, $server)
    {
        $server->onOpen($conn->onOpen->bindTo($this));
        $server->onMessage($conn->onMessage->bindTo($this));
        $server->onClose($conn->onClose->bindTo($this));
        $server->onError($conn->onError->bindTo($this));
    }

    public function onOpen(ConnectionInterface $conn)
    {
        Log::info('New connection: ' . $conn->resourceId);
    }

    public function onMessage(ConnectionInterface $from, $msg)
    {
        Log::info('Message: ' . $msg);

        // Handle OBS camera control commands here
    }

    public function onClose(ConnectionInterface $conn)
    {
        Log::info('Connection (' . $conn->resourceId . ') has disconnected');
    }

    public function onError(ConnectionInterface $conn, \Exception $e)
    {
        Log::error('Error: ' . $e->getMessage());
        $conn->close();
    }
}

Create a new file app/ObsWebSocket.php to handle the OBS camera control logic:

<?php

namespace App\ObsWebSocket;

use Illuminate\Support\Facades\Log;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use OBS\OBS;

class ObsWebSocket implements MessageComponentInterface
{
    protected $events;

    public function __construct($events)
    {
        $this->events = $events;
    }

    public function onOpen(ConnectionInterface $conn)
    {
        // Initialize OBS instance
        $this->obs = new OBS();

        // Subscribe to OBS events
        $this->events->listen('obs.scene.transitioned', [$this, 'onSceneTransitioned']);
    }

    public function onMessage(ConnectionInterface $from, $msg)
    {
        // Handle incoming camera control commands
        switch ($msg['command']) {
            case 'panLeft':
                $this->obs->PTZControl('panleft');
                break;
            case 'panRight':
                $this->obs->PTZControl('panright');
                break;
            case 'tiltUp':
                $this->obs->PTZControl('tiltup');
                break;
            case 'tiltDown':
                $this->obs->PTZControl('tiltdown');
                break;
            case 'zoomIn':
                $this->obs->PTZControl('zoom', 1);
                break;
            case 'zoomOut':
                $this->obs->PTZControl('zoom', -1);
                break;
            default:
                Log::warning('Invalid command: ' . $msg['command']);
        }
    }

    public function onClose(ConnectionInterface $conn)
    {
        // Clean up OBS instance when connection is closed
        unset($this->obs);
    }

    public function onSceneTransitioned($scene)
    {
        // Broadcast scene transition event to all connected clients
        $this->events->dispatch('obs.scene.transitioned', [$scene]);
    }
}

Update the app/Providers/AppServiceProvider.php file to register the ObsWebSocketServiceProvider:

<?php

namespace App\Providers;

use App\ObsWebSocket\ObsWebSocketServiceProvider;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->register(ObsWebSocketServiceProvider::class);
    }
}

Step 4: Create a route and controller to handle incoming camera control requests

Create a new file routes/web.php to define the route for handling camera control requests:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ObsController;

Route::get('/obs/{command}', [ObsController::class, 'control']);

Create a new file app/Http/Controllers/ObsController.php to handle the camera control logic:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\ObsWebSocket\ObsWebSocket;

class ObsController extends Controller
{
    protected $obsWebSocket;

    public function __construct(ObsWebSocket $obsWebSocket)
    {
        $this->obsWebSocket = $obsWebSocket;
    }

    public function control(Request $request, $command)
    {
        // Send camera control command to OBS via WebSocket
        $this->obsWebSocket->sendCommand($command);

        return response()->json(['status' => 'success]);
    }
}

Step 5: Send camera control commands from Laravel to OBS using WebSockets

Update the app/ObsWebSocket.php file to