Usage
Encode a file on a disk
Open a video on any Laravel disk with laravel-media, choose a quality target, and save the result to any disk:
use Foxws\AbAv1\AbAv1Builder;
use Foxws\AbAv1\AbAv1Result;
use Foxws\Media\Facades\Media;
$result = Media::fromDisk('media')
->open('videos/clip.mp4')
->abAv1()
->withPreset(6)
->withMinVMAF(95)
->toDisk('s3')
->withVisibility('private')
->afterSaving(fn (AbAv1Builder $builder, AbAv1Result $result) => logger("Encoded at CRF {$result->crf}"))
->save('encoded/clip.mp4');
$result->crf; // e.g. 30.25
$result->vmaf; // e.g. 95.1
$result->path(); // "encoded/clip.mp4"
save() runs ab-av1's auto-encode: it test-encodes samples to find the CRF that reaches your VMAF target, encodes the whole video with it, and moves or uploads the result. Without toDisk(), it's saved to the disk the video was opened from.
withPreset() and withMinVMAF() are optional when you set defaults in the config file.
Files on remote disks like S3 are downloaded to laravel-media's temporary directory first. laravel-media deletes its temporary files after every queue job and at the end of each request, so there's nothing to clean up yourself.
Encode at a fixed CRF
With withCRF(), save() runs a plain encode at that CRF instead of searching:
Media::fromDisk('media')->open('videos/clip.mp4')->abAv1()->withCRF(30)->save('encoded/clip.mp4');
CRF values can have quarter steps with svt-av1, for example withCRF(30.25). Lower means better quality and larger files.
Choose the quality
| Method | What it does |
|---|---|
withMinVMAF(95) |
The VMAF score to reach, from 0 to 100. Around 95 is very hard to tell apart from the original. |
withMinXPSNR(40) |
Use XPSNR as the quality measure instead of VMAF. |
withMaxEncodedPercent(80) |
Fail when the result would be larger than this percentage of the original. |
withPreset(6) |
Encoder speed. For svt-av1, from 0 (slowest, smallest files) to 13 (fastest). |
withSamples(8) |
How many samples to test. More is more accurate, but slower. |
Other commands
| Method | ab-av1 command | Use it to |
|---|---|---|
save($path) |
auto-encode, or encode after withCRF() |
Encode the whole video and save it. |
crfSearch() |
crf-search |
Only find the right CRF, without encoding. |
sampleEncode() |
sample-encode |
Test one CRF on a few samples, to preview quality and size. Needs withCRF(). |
vmaf() |
vmaf |
Get the VMAF score of the second opened file, compared to the first. |
xpsnr() |
xpsnr |
Get the XPSNR score of the second opened file, compared to the first. |
Each returns an AbAv1Result. To compare two files, open the original first:
$score = Media::fromDisk('media')
->open('videos/clip.mp4', 'encoded/clip.mp4')
->abAv1()
->vmaf()
->vmaf; // e.g. 95.4
Every command only gets the options it accepts, so config defaults like the preset never break vmaf().
Use another encoder
ab-av1 uses svt-av1 (libsvtav1) by default. To use a hardware encoder, like VAAPI on Intel and AMD graphics, pass FFmpeg's encoder name and its input options:
Media::fromDisk('media')
->open('videos/clip.mp4')
->abAv1()
->withEncoder('av1_vaapi')
->withFFmpegOptions(['hwaccel' => 'vaapi', 'hwaccel_output_format' => 'vaapi'])
->withMinVMAF(95)
->save('encoded/clip.mp4');
withEncoderArgs('svtav1-params=tune=0') passes encoder arguments (--enc), and withPixelFormat(PixelFormat::Yuv420p10le) sets the pixel format. Any other ab-av1 option goes through withOption('keyint', '10s'); true passes a flag, and null removes an option.
Filters
Video filters from laravel-media are applied before encoding and measuring, passed as --vfilter:
use Foxws\Media\Filters\Custom;
use Foxws\Media\Filters\Scale;
$builder->addFilter(Scale::to(height: 720), Custom::video('fps=30'));
withVideoFilter('scale=1280:-2') takes a raw filter chain. ab-av1 only filters video, so audio filters throw an InvalidArgumentException.
Check the result
Encodes can fail without FFmpeg noticing, for example on a damaged input. With ab-av1 v0.11.7 or later, you can catch that:
$builder->withVerify()->withFailFast();
withVerify()decodes the finished encode, and fails on decode errors or when its length doesn't match the input.withFailFast()stops the encode at the first error FFmpeg reports.
Results
An AbAv1Result has:
| Property or method | Holds |
|---|---|
crf |
The CRF that was chosen or used, e.g. 30.25. |
vmaf / xpsnr |
The quality score. |
predictedSize |
The predicted video stream size in bytes. |
predictedPercent |
The predicted size as a percentage of the input. |
predictedTime |
The predicted encoding time in seconds. |
export / path() |
For save(): laravel-media's ExportResult, and the saved path. |
output |
ab-av1's full output, for debugging. |
Queues
Encodes are slow, often longer than the video itself without hardware encoding, so run them in a queued job. timeout() overrides the ab-av1.timeout config for one encode; keep it below the job's $timeout:
use Foxws\Media\Exceptions\ProcessFailedException;
public function handle(): void
{
try {
Media::fromDisk('media')
->open($this->video->path)
->abAv1()
->timeout(7000)
->withContext(['video_id' => $this->video->id])
->save("encoded/{$this->video->id}.mp4");
} catch (ProcessFailedException $exception) {
$exception->isRetryable() ? $this->release(60) : $this->fail($exception);
}
}
withContext() is passed to laravel-media's ExportCompleted and ExportFailed events, which save() dispatches. The runner also dispatches ProcessStarted, ProcessCompleted and ProcessFailed for every command. What ab-av1 reports on its error output, such as each tried CRF, isn't logged as a warning.
Progress
onProgress() receives a Foxws\Media\Process\Progress while save() encodes the whole file, and laravel-media's ProgressReported event is dispatched with the context. Return false from the callback to cancel: ab-av1 is stopped and a ProcessFailedException with reason Cancelled is thrown.
->abAv1()
->withContext(['video_id' => $video->id])
->onProgress(fn (Progress $progress) => $video->update(['progress' => $progress->percentage()]))
->save("encoded/{$video->id}.mp4");
ab-av1 logs its progress after 16, 32, 64 seconds and so on, doubling each time, so expect few updates, then a finished one at 100%. The CRF search before an auto-encode reports none. remaining() follows ab-av1's estimate.
Errors
Foxws\Media\Exceptions\ProcessFailedException: ab-av1 failed or timed out.context()has the command, exit code and error output for error trackers, andisRetryable()tells whether a retry may help.Foxws\Media\Exceptions\ExecutableNotFoundException: ab-av1, FFmpeg or ffprobe can't be found.Foxws\Media\Exceptions\MediaNotFoundException: nothing was opened, or the input can't be read.Foxws\AbAv1\Exceptions\AbAv1Exception:sampleEncode()without a CRF, a comparison with one file, or an encode that wrote no file.
Invalid values, like a preset of 20, throw an InvalidArgumentException straight away.
