Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Janusclient Bundle Laravel Package

ice/janusclient-bundle

Symfony bundle that integrates a Janus WebRTC Gateway client, adding configuration and services for connecting to Janus, managing sessions and plugins, and building real-time audio/video and data-channel features in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and inspect before you wire anything up

composer require ice/janusclient-bundle

Before writing a line of application code, open the package in vendor/ice/janusclient-bundle and read three things in this order:

  • composer.json — confirm the PHP constraint, the HTTP client it pulls in (Guzzle vs. Symfony HttpClient), and whether it declares a Symfony bundle class. The -bundle suffix and the cambridgeuniversity origin strongly suggest a Symfony bundle, not a Laravel package. That single fact shapes everything below.
  • src/ — find the client class and the plugin/handle abstractions. You want to know the constructor signature (base URL, API secret, HTTP client, logger) because you will be constructing it yourself.
  • DependencyInjection/Configuration.php (Symfony bundles almost always have one) — this is the canonical list of config keys. Mirror those keys into a Laravel config file rather than guessing at env var names.

2. Register it the Laravel way

There is no laravel/framework service provider auto-discovery here, and no Facade. Add a small binding in AppServiceProvider (or a dedicated JanusServiceProvider):

// config/janus.php
return [
    'base_url' => env('JANUS_BASE_URL', 'https://janus.example.edu'),
    'api_secret' => env('JANUS_API_SECRET'),
    'timeout' => env('JANUS_TIMEOUT', 30),
];

// AppServiceProvider::register()
$this->app->singleton(JanusClient::class, function ($app) {
    return new JanusClient(
        $app['config']->get('janus.base_url'),
        $app['config']->get('janus.api_secret'),
        $app['config']->get('janus.timeout'),
    );
});

Keep it a singleton — the session/handle state is meaningful, and you don't want a fresh TCP/TLS handshake per resolution.

3. First use case: prove connectivity with the echo test plugin

Do not start with videoroom. Start with the smallest possible round trip:

  1. Create a Janus session (create).
  2. Attach the janus.plugin.echo plugin to a handle.
  3. Send one message and read back the response.
  4. Destroy the handle and the session.

If that returns cleanly you have proven: base URL, API secret, TLS, JSON encoding, and the client's response parsing. Everything after that is plugin-specific business logic. Wrap this in a throwaway php artisan tinker call or a one-off artisan command — not a route, because you'll be debugging blocking calls.

Implementation Patterns

Wrap the client, don't scatter it. Create App\Services\Janus\JanusService that takes the client via constructor injection and exposes domain methods (createRoom(), listParticipants(), publishStream()). The package API is low-level and message-oriented; without a wrapper you will have raw Janus JSON payloads leaking into controllers within a week.

Session lifecycle is explicit and manual. The pattern is always: create session → attach handle → exchange messages → detach handle → destroy session. Model it as a try/finally around a session scope:

$session = $janus->createSession();
try {
    $handle = $session->attach('janus.plugin.videoroom');
    try {
        $handle->send(['janus' => 'message', 'body' => [...]]);
        $response = $handle->receive(); // or poll
    } finally {
        $handle->detach();
    }
} finally {
    $session->destroy();
}

Long-polling belongs in queues, not requests. Janus's HTTP transport blocks waiting for events. If the bundle exposes a long-poll receive, a web request will hang past your FPM request_terminate_timeout. Push the polling loop into a queued job or a dedicated artisan janus:listen command supervised by systemd/Horizon. For request/response interactions (create room, mute a participant) use short-timeout calls with no polling.

Dispatch Laravel events from Janus responses. Janus returns typed events (participants, joined, left, event payloads). Normalize them in your wrapper and event(new JanusParticipantJoined(...)) so the rest of the app reacts via listeners rather than parsing Janus envelopes in ten places.

Multi-tenant / multi-room isolation. If you run several rooms or tenants on one Janus instance, keep a janus_rooms table holding the Janus room ID and map your application IDs to it. Never hardcode room IDs; Janus assigns numeric IDs on creation.

Configuration via env with a sane default host. Put JANUS_BASE_URL, JANUS_API_SECRET in .env, add both to config/janus.php, and add a janus block to phpunit.xml with a fake URL so tests never hit a real gateway.

Test with an HTTP fake. Because the client ultimately does HTTP, you can either Http::fake() (if it's built on Laravel's client — unlikely) or, more realistically, inject a Guzzle MockHandler / Symfony MockHttpClient through the constructor. This is the single biggest argument for not using the singleton blindly in tests: bind a mock client in the test container.

Integration tip: put the client behind an interface. JanusClientInterface with one concrete implementation lets you swap in a recording stub for feature tests and keeps the Symfony-typed classes from becoming load-bearing across your codebase.

Gotchas and Tips

This is a Symfony bundle, not a Laravel package. The biggest trap. Consequences:

  • The documented config is YAML (config/packages/ice_janus.yaml); you must transcribe it to config/janus.php.
  • The bundle's extension class expects Symfony's container, parameter bag, and possibly framework.http_client. Don't try to register the bundle; instantiate the underlying client class directly.
  • Any Symfony service/event-dispatcher wiring in the package will not run. If the client requires a PSR-18/PSR-3 dependency, satisfy it with Laravel's bindings (Psr\Log\LoggerInterface is already bound by Laravel).
  • Service names, tagged services, and autoconfiguration are all inert — budget an hour for this rather than assuming composer require is the whole install.

Session and handle timeouts are real. Janus destroys idle sessions server-side (default ~60s). If you cache a session in the singleton and reuse it across requests, it will be dead by the next one. Either create-per-operation or implement a keepalive and handle "session not found" by transparently re-creating. Do not assume the singleton survives a queue worker's lifetime.

Correlate by transaction ID. Janus responses are asynchronous and out of order; the only safe way to match a request to a response is the transaction string you generate. Wrap this in the client wrapper so callers never see it. Randomized string transaction IDs, checked on every response.

Long-poll + PHP-FPM = 504s. Related to the queue advice above: set an explicit short timeout for request/response calls and never call a blocking receive from a web request. Under Laravel Octane, a blocking long-poll also stalls the worker — another reason to isolate it.

Janus error codes are not HTTP errors. "janus": "error" comes back with HTTP 200 and a numeric error_code (e.g. 458 for "session not found"). The bundle may or may not throw on these — check the client's response handling explicitly and translate them into your own exception types. Silent null returns on failed plugin messages are a classic source of "it just doesn't work" debugging sessions.

TLS and internal hosts. Janus deployments are frequently behind self-signed certs or internal hostnames. You'll need to pass a Guzzle client configured with verify => false (only for local dev) or a custom CA bundle. Find where the package accepts an injected HTTP client — that's your extension point.

Debugging: log raw envelopes. Set a debug flag in config/janus.php that logs the full outbound and inbound JSON at debug level. Half of all integration bugs are field-name mismatches (audiocodec vs audio_codec, publishers vs publisher) and comparing raw payloads against the Janus plugin docs resolves them instantly.

Extension points to know about. The injected HTTP client (swap for mocks or for TLS config), the PSR-3 logger, and the base URL/config object. There's likely no plugin abstraction you can subclass to add a custom Janus plugin — if you need one, extend the handle wrapper in your own namespace rather than forking the vendor directory.

Vendor risk: this is a zero-star, zero-dependent package. Treat it as internal glue rather than a maintained dependency. Pin the exact version in composer.json, keep the client behind your own interface, and be prepared to replace it — the Janus HTTP/WebSocket API is small and a direct Guzzle implementation of the handful of endpoints you actually use is a realistic fallback if the package lags behind your PHP version or Symfony's HTTP client major versions.

Check PHP and dependency compatibility early. A Cambridge-published bundle of this vintage may target Symfony 4/5 components that conflict with a modern Laravel illuminate/* tree. Run composer why-not on the package before committing to it; resolving a version conflict after you've built twenty call sites is far more expensive.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json