Usage
shaka() returns laravel-media's packaging builder with the Shaka Packager driver. Everything in laravel-media's packaging docs works the same way; this page covers what Shaka Packager adds.
The basic flow
Open the input, add streams, choose the manifests, then save to a directory on the target disk:
use Foxws\Media\Facades\Media;
$result = Media::fromDisk('media')
->open('videos/clip.mp4')
->shaka()
->addVideoStream(output: 'video.mp4')
->addAudioStream(output: 'audio.mp4', language: 'en')
->withHlsPlaylist('master.m3u8')
->withDashManifest('manifest.mpd')
->forVod()
->toDisk('s3')
->save('streams/clip');
$result->paths(); // ['streams/clip/master.m3u8', 'streams/clip/manifest.mpd', ...]
- The output defaults to the disk the media was opened from.
withVisibility('private')sets the visibility of the uploaded files. addStreamsFrom()probes every opened file and adds its video and audio streams.- Streams are written as fragmented MP4 (CMAF), so one set of segments works for both DASH and HLS. Use a
.tsoutput for MPEG-TS HLS, or.webmfor WebM DASH. - There's nothing to clean up: laravel-media deletes temporary files after every queue job and request.
Several qualities
Shaka Packager doesn't encode, so each quality has to exist as its own file. Encode them with laravel-media's ladder first, then open them all and add each one:
use Foxws\Media\Encoding\Ladder;
Media::fromDisk('media')->open('videos/clip.mp4')->ladder(Ladder::standard(), 'renditions/{height}p.mp4')->save();
Media::fromDisk('media')
->open(['renditions/1080p.mp4', 'renditions/720p.mp4', 'renditions/480p.mp4'])
->shaka()
->addVideoStream('renditions/1080p.mp4', '1080p.mp4')
->addVideoStream('renditions/720p.mp4', '720p.mp4')
->addVideoStream('renditions/480p.mp4', '480p.mp4')
->addAudioStream('renditions/1080p.mp4', 'audio.mp4')
->withHlsPlaylist()
->withDashManifest()
->save('streams/clip');
Stream options
The options argument sets Shaka Packager's stream descriptor fields:
->addAudioStream(output: 'audio-nl.mp4', language: 'nl', options: ['hls_name' => 'Nederlands'])
Commas in values are replaced, and field names are checked, so a value can't add other fields or options.
Subtitles
Add WebVTT files from the same disk as text streams. Use an .mp4 output: with a plain .vtt output, the DASH manifest has no segment index, and Shaka Player skips the track.
->addTextStream('captions/clip.en.vtt', 'subtitles-en.mp4', language: 'en', options: ['dash_roles' => 'subtitle'])
Encryption
laravel-media's withEncryption() encrypts with a raw AES key (Common Encryption). Shaka Packager supports every part of it:
use Foxws\Media\Encryption\ProtectionScheme;
$result = Media::fromDisk('media')->open('videos/clip.mp4')
->shaka()
->addStreamsFrom()
->withHlsPlaylist()
->withDashManifest()
->withEncryption(scheme: ProtectionScheme::Cbcs)
->withKeyRotation(300)
->withClearLead(2)
->save('streams/clip');
$result->encryptionKey(); // the key, to store for your key or license route
cbcs gives one set of segments that plays with both HLS and DASH, including Safari. With key rotation, Shaka Packager derives later keys from the first one.
Shaka Packager options
ShakaOptions sets Shaka Packager's other options with typed and validated methods. Pass it to withOptions():
use Foxws\Shaka\ProtectionSystem;
use Foxws\Shaka\ShakaOptions;
->withOptions(
ShakaOptions::make()
->baseUrls('https://cdn.example.com/streams/clip/')
->protectionSystems(ProtectionSystem::Widevine, ProtectionSystem::PlayReady)
->createSessionKeys()
)
| Group | Methods |
|---|---|
| URLs and numbering | baseUrls(), hlsBaseUrl(), hlsMediaSequenceNumber(), hlsStartTimeOffset(), startSegmentNumber(), transportStreamTimestampOffset() |
| Live DASH | minBufferTime(), minimumUpdatePeriod(), suggestedPresentationDelay(), timeShiftBufferDepth(), preservedSegmentsOutsideLiveWindow(), utcTimings(), lowLatencyDashMode(), generateStaticLiveMpd(), forceClIndex() |
| Encryption | protectionSystems(), createSessionKeys(), cryptByteBlock(), skipByteBlock(), vp9SubsampleEncryption(), playreadyExtraHeaderData(), iv(), pssh() |
| Key servers | widevine(), enableEntitlementLicense(), maxPixels(), groupId(), aesSigning(), rsaSigning(), playready(), keyServerTls() |
| Encrypted input | decrypt() |
Any other flag can be passed with withOption('name', $value): true passes a flag, and null or false leaves it out.
Serving the streams
Serve private streams with laravel-media's DynamicHLSPlaylist and DynamicDASHManifest, which sign every URL in the playlist when it's requested:
return Media::fromDisk('s3')->open('streams/clip/master.m3u8')
->hlsPlaylist()
->resolveMediaUrlsUsing(fn (string $path) => Storage::disk('s3')->temporaryUrl($path, now()->addHour()))
->resolveKeyUrlsUsing(fn (string $key) => route('videos.key', $video))
->toResponse($request);
Queues and errors
- Package in a queued job. Give long runs
->timeout($seconds)below the job's$timeout(default:shaka.timeout). - A failed run throws laravel-media's
ProcessFailedException, with Shaka Packager's error output; useisRetryable()to choose betweenrelease()andfail(). A missing binary throwsExecutableNotFoundException. save()dispatches laravel-media'sExportCompletedandExportFailed, with the data fromwithContext().beforeSaving()andafterSaving()run around it.command()returns the command line without running it, with keys redacted.
