Laravel Streamer
Queue Integration
Queues
Encoding takes a long time, often longer than the video itself. Always run it in a queued job.
A streaming job
namespace App\Jobs;
use App\Models\Video;
use Foxws\Streamer\Facades\Streamer;
use Foxws\Streamer\Support\VideoResolution;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Throwable;
class StreamVideo implements ShouldQueue
{
use Queueable;
public $timeout = 14400;
public $tries = 1;
public function __construct(public Video $video) {}
public function handle(): void
{
$streamer = Streamer::fromDisk('media')->open($this->video->path);
try {
$streamer
->addVideoStream($this->video->path, 'video.mp4')
->addAudioStream($this->video->path, 'audio.mp4')
->withResolutions(VideoResolution::make($this->video->height)->toArray())
->withMpdOutput('index.mpd')
->withHlsMasterPlaylist('master.m3u8')
->export()
->toDisk('s3')
->toPath("streams/{$this->video->id}/")
->afterSaving(fn () => $this->video->markAsReady())
->save();
} finally {
$streamer->cleanupTemporaryFiles();
}
}
public function failed(Throwable $exception): void
{
$this->video->markAsFailed();
}
}
StreamVideo::dispatch($video)->onQueue('encoding');
A retry starts the encode again from the beginning, so keep $tries low.
Timeouts
Three timeouts need to line up:
STREAMER_TIMEOUTstops the Shaka Streamer process. The default is 14400 seconds (4 hours).- The job's
$timeoutstops the worker. Keep it at or aboveSTREAMER_TIMEOUT, or the worker is killed while Shaka Streamer still runs. - The queue connection's
retry_aftermust be longer than the job's$timeout. Otherwise another worker picks up the same job while the first one is still encoding.
// config/queue.php
'encoding' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'encoding',
'retry_after' => 14460,
],
How many at once
FFmpeg already uses every CPU core for one encode. Running several jobs at once rarely finishes the queue faster, and it needs room in temporary_files_root for every job. With hardware encoding, the GPU limits how many encodes run well at once. Start with one worker:
// config/horizon.php
'supervisor-encoding' => [
'connection' => 'encoding',
'queue' => ['encoding'],
'maxProcesses' => 1,
'timeout' => 14400,
'tries' => 1,
],
If temporary_files_root is a size-limited mount, set a storage floor. A job then fails right away instead of halfway through.
Long-running workers
Queue workers live for many jobs, so:
- Always call
cleanupTemporaryFiles()infinally. A failed job otherwise leaves its files behind. - Call
useSystemBinaries()on every job that needs it. It only applies to that job. - Don't change the shared driver with
app(ShakaStreamer::class)->setTimeout()inside a job. The driver is a singleton, so the change sticks for every later job in that worker. - Use
WithoutOverlappingorShouldBeUniqueif the same video can be queued twice.
