Laravel Media
Queues
Queues, progress and errors
Encoding takes long, so run it in queued jobs. This page covers what those jobs need.
Progress
onProgress() receives a Progress about twice a second while ffmpeg runs:
use Foxws\Media\Process\Progress;
$media->ffmpeg()
->inFormat(Format::h264())
->onProgress(function (Progress $progress) use ($video) {
$video->update(['progress' => $progress->percentage()]); // 0-100, null when the duration is unknown
$progress->remaining(); // estimated seconds left
$progress->speed; // e.g. 2.5 times real time
})
->save('encoded.mp4');
- Two-pass encodes report one percentage over both passes.
- Return
falsefrom the callback to cancel. ffmpeg is stopped, nothing is saved, and aProcessFailedExceptionwith the reasonCancelledis thrown.
Events
| Event | When |
|---|---|
Foxws\Media\Events\ProgressReported |
every progress update |
Foxws\Media\Events\ExportCompleted |
after the files are on the target disk |
Foxws\Media\Events\ExportFailed |
when an export throws |
Foxws\Media\Process\Events\ProcessStarted, ProcessCompleted, ProcessFailed |
every ffmpeg and ffprobe run, with the command redacted |
withContext() tells listeners what an export is about:
$media->ffmpeg()->withContext(['video_id' => $video->id])->inFormat(Format::h264())->save('encoded.mp4');
Event::listen(function (ProgressReported $event) {
broadcast(new VideoEncoding($event->context['video_id'], $event->progress->percentage()));
});
Failures and retries
Failed runs throw ProcessFailedException. Its reason is recognised from ffmpeg's error output, and isRetryable() tells whether trying again can help:
use Foxws\Media\Exceptions\ProcessFailedException;
public function handle(): void
{
try {
Media::fromDisk('s3')->open($this->path)->ffmpeg()
->timeout(1800)
->inFormat(Format::h264())
->save($this->output);
} catch (ProcessFailedException $exception) {
$exception->isRetryable() ? $this->release(60) : $this->fail($exception);
}
}
- Network errors, timeouts and full disks are retryable. Broken files, missing codecs and invalid options fail the same way every time.
- Reported exceptions include the exit code, the redacted command and the last lines of ffmpeg's output, so they're readable in Sentry, Flare or Nightwatch.
- Give long encodes a
timeout()below the job's$timeout, so ffmpeg is stopped and the job fails with a retryable timeout instead of the worker being killed. - Set
MEDIA_FFMPEG_LOG_LEVEL=warningto log the warnings of runs that succeeded, for example to see why output from a damaged file looks wrong.
Temporary files and crashes
- Temporary files are deleted after every queue job and request. Call
$media->cleanupTemporaryFiles()to free them earlier. - When a job times out, the running ffmpeg is stopped before the worker exits, so it doesn't keep running on its own.
- Schedule
media:cleanfor what crashes and OOM kills leave behind, see Installation. - Set
MEDIA_TEMPORARY_FILES_MIN_FREEto make jobs fail early withInsufficientStorageExceptionwhen the temporary disk is nearly full. - To keep two jobs from processing the same video at once, use Laravel's
WithoutOverlappingjob middleware.
