foxws / laravel-streamer
A Laravel package to interact with Shaka Streamer for media packaging.
Fund package maintenance!
Foxws
Installs: 277
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 1
pkg:composer/foxws/laravel-streamer
Requires
- php: ^8.3
- illuminate/contracts: ^11.0 || ^12.0
- illuminate/filesystem: ^11.0 || ^12.0
- illuminate/process: ^11.0 || ^12.0
- illuminate/support: ^11.0 || ^12.0
- psr/log: ^3.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^9.0 || ^10.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- spatie/laravel-ray: ^1.35
This package is auto-updated.
Last update: 2026-02-12 08:14:35 UTC
README
A Laravel integration for Google's Shaka Streamer, enabling you to package adaptive streaming content (HLS, DASH) with a fluent, Laravel-style API.
use Foxws\Streamer\Facades\Streamer; Streamer::fromDisk('s3') ->open('videos/input.mp4') ->addVideoStream('videos/input.mp4', 'video.mp4') ->addAudioStream('videos/input.mp4', 'audio.mp4') ->withHlsMasterPlaylist('master.m3u8') ->withSegmentDuration(6) ->export() ->toDisk('export') ->save();
Features
- 🎬 Fluent API — Laravel-style chainable methods for packaging media
- 📁 Filesystem Integration — Read from and write to any Laravel disk (local, S3, etc.)
- 🎯 Adaptive Bitrate — Create multi-quality HLS & DASH streams
- 🔒 AES Encryption — Built-in content protection with optional key rotation
- 📺 Dynamic Manifests — Rewrite HLS playlists and DASH MPDs with signed URLs at serve-time
- 📡 Events — Hooks for
StreamingStarted,StreamingCompleted, andStreamingFailed - 📝 PHP 8.3+ — Strict types, readonly properties, and modern PHP throughout
Documentation
- Quick Reference — Complete API at a glance
- Configuration — Environment variables and config options
- AES Encryption — Encryption with key rotation
- URL Resolvers — Signed URLs for HLS & DASH
- Troubleshooting — Common issues and solutions
Requirements
- PHP 8.3+
- Laravel 11 or 12
- Shaka Streamer binary (
pip install shaka-streamer)
Installation
composer require foxws/laravel-streamer
Publish the configuration file:
php artisan vendor:publish --tag="streamer-config"
Verify the binary is accessible:
php artisan streamer:info
Quick Start
Basic Packaging
use Foxws\Streamer\Facades\Streamer; Streamer::open('input.mp4') ->addVideoStream('input.mp4', 'video.mp4') ->addAudioStream('input.mp4', 'audio.mp4') ->withHlsMasterPlaylist('master.m3u8') ->export() ->save();
Cross-Disk Workflows
Read from one disk, write to another:
Streamer::fromDisk('s3') ->open('videos/input.mp4') ->addVideoStream('videos/input.mp4', 'video.mp4') ->addAudioStream('videos/input.mp4', 'audio.mp4') ->withHlsMasterPlaylist('master.m3u8') ->export() ->toDisk('export') ->toPath('streams/') ->withVisibility('public') ->save();
Encryption
// AES-128 encryption with auto-generated key Streamer::open('input.mp4') ->addVideoStream('input.mp4', 'video.mp4') ->addAudioStream('input.mp4', 'audio.mp4') ->withHlsMasterPlaylist('master.m3u8') ->withAESEncryption() ->export() ->save(); // With key rotation (rotates every 60 seconds) Streamer::open('input.mp4') ->addVideoStream('input.mp4', 'video.mp4') ->addAudioStream('input.mp4', 'audio.mp4') ->withHlsMasterPlaylist('master.m3u8') ->withAESEncryption('key', 'cenc') ->withKeyRotationDuration(60) ->export() ->toDisk('s3') ->save();
See the AES Encryption Guide for protection schemes, codec-specific examples, and key management.
Dynamic URL Resolvers
Serve HLS and DASH content with signed URLs — useful for S3, CDNs, or multi-tenant apps.
HLS:
use Foxws\Streamer\Http\DynamicHLSPlaylist; use Illuminate\Support\Facades\Storage; return (new DynamicHLSPlaylist('s3')) ->open("videos/{$video->id}/master.m3u8") ->setKeyUrlResolver(fn (string $key) => Storage::disk('s3')->temporaryUrl( "videos/{$video->id}/{$key}", now()->addHour(), )) ->setMediaUrlResolver(fn (string $file) => Storage::disk('s3')->temporaryUrl( "videos/{$video->id}/{$file}", now()->addHours(2), )) ->setPlaylistUrlResolver(fn (string $playlist) => route('video.playlist', [ 'video' => $video, 'playlist' => $playlist, ])) ->toResponse(request());
DASH:
use Foxws\Streamer\Http\DynamicDASHManifest; use Illuminate\Support\Facades\Storage; return (new DynamicDASHManifest('s3')) ->open("videos/{$video->id}/manifest.mpd") ->setMediaUrlResolver(fn (string $file) => Storage::disk('s3')->temporaryUrl( "videos/{$video->id}/{$file}", now()->addHours(2), )) ->setInitUrlResolver(fn (string $file) => Storage::disk('s3')->temporaryUrl( "videos/{$video->id}/{$file}", now()->addHours(2), )) ->toResponse(request());
See URL Resolvers for more details.
Events
Listen to streaming lifecycle events:
| Event | Payload |
|---|---|
StreamingStarted |
MediaCollection $mediaCollection, array $options |
StreamingCompleted |
StreamerResult $result, float $executionTime |
StreamingFailed |
Exception $exception, float $executionTime |
Post-Export Inspection
After saving, you can inspect the result:
$exporter = Streamer::open('input.mp4') ->addVideoStream('input.mp4', 'video.mp4') ->addAudioStream('input.mp4', 'audio.mp4') ->withHlsMasterPlaylist('master.m3u8') ->export(); $exporter->afterSaving(function ($exporter, $result) { $summary = $exporter->getCopySummary(); // ['total' => 12, 'copied' => 12, 'failed' => 0, 'totalSize' => 8421376] $keys = $result->getUploadedEncryptionKeys(); }); $exporter->toDisk('s3')->save();
API Reference
Streamer Facade → MediaOpener
| Method | Description |
|---|---|
fromDisk($disk) |
Set the source filesystem disk |
open($paths) |
Open one or more media files |
openFromDisk($disk, $paths) |
Set disk and open files in one call |
get() |
Get the MediaCollection |
export() |
Start the export chain (returns MediaExporter) |
dynamicHLSPlaylist(?string $disk) |
Create a DynamicHLSPlaylist instance |
dynamicDASHManifest(?string $disk) |
Create a DynamicDASHManifest instance |
cleanupTemporaryFiles() |
Delete all temporary directories |
Stream Configuration (via MediaOpener → Streamer)
| Method | Description |
|---|---|
addVideoStream($input, $output, $options) |
Add a video stream |
addAudioStream($input, $output, $options) |
Add an audio stream |
addTextStream($input, $output, $options) |
Add a text/subtitle stream |
addStream(array $stream) |
Add a raw Shaka stream descriptor (in, stream, output, …) |
withHlsMasterPlaylist($path) |
Set HLS output |
withMpdOutput($path) |
Set DASH/MPD output |
withSegmentDuration(int $seconds) |
Set segment duration |
withManifestFormat(array $formats) |
Set manifest formats (e.g. ['dash', 'hls']) |
withResolutions(array $resolutions) |
Set encoding resolutions |
withVideoCodecs(array $codecs) |
Set video codecs (e.g. ['h264', 'hw:vp9']) |
withAudioCodecs(array $codecs) |
Set audio codecs (e.g. ['aac', 'opus']) |
withSegmentPerFile(bool $enabled) |
Enable segment-per-file output |
withLowLatencyDashMode(bool $enabled) |
Enable low-latency DASH |
withStreamingMode(string $mode) |
Set mode ('vod' or 'live') |
withEncryption(array $config) |
Set raw encryption config |
withAESEncryption($keyFilename, $scheme, $label) |
Auto-generate AES encryption key |
withKeyRotationDuration(int $seconds) |
Enable key rotation (requires 'cenc' or 'cbcs') |
withOption($key, $value) |
Set a custom pipeline option |
withOptions(array $options) |
Set multiple custom pipeline options |
getCommand() |
Get the built config array (for debugging) |
MediaExporter (returned by export())
| Method | Description |
|---|---|
toDisk($disk) |
Set target disk for output |
toPath(string $path) |
Set target subdirectory |
withVisibility(string $visibility) |
Set file visibility ('public', 'private') |
afterSaving(callable $callback) |
Register post-save callback |
save(?string $path) |
Execute packaging and copy files to disk |
getCommand() |
Get the built config array |
dd() |
Dump config and die |
getCopySummary() |
Get {total, copied, failed, totalSize} |
getCopiedFiles() |
Get array of successfully copied files |
getFailedFiles() |
Get array of failed file copies |
hasCopyFailures() |
Check if any files failed to copy |
DynamicHLSPlaylist
| Method | Description |
|---|---|
open(string $path) |
Open a playlist file |
setKeyUrlResolver(callable) |
Resolve encryption key URLs |
setMediaUrlResolver(callable) |
Resolve media segment URLs |
setPlaylistUrlResolver(callable) |
Resolve sub-playlist URLs |
get() |
Get processed playlist content |
all() |
Get all processed playlists (master + variants) |
toResponse($request) |
Return as application/vnd.apple.mpegurl response |
DynamicDASHManifest
| Method | Description |
|---|---|
open(string $path) |
Open a manifest file |
setMediaUrlResolver(callable) |
Resolve media segment URLs |
setInitUrlResolver(callable) |
Resolve initialization segment URLs |
get() |
Get processed manifest content |
toResponse($request) |
Return as application/dash+xml response |
Configuration
Key options in config/streamer.php:
| Option | Default | Description |
|---|---|---|
streamer.streamer_binary |
'shaka-streamer' |
Path to the Shaka Streamer binary |
timeout |
14400 (4h) |
Process timeout in seconds |
temporary_files_root |
storage_path('app/streamer/temp') |
Directory for temporary files |
cache_files_root |
'/dev/shm' |
Fast storage for small files (keys, manifests) |
log_channel |
null |
Log channel for streamer output |
See Configuration for all options and environment variables.
Testing
composer test
Contributing
Please see CONTRIBUTING for details.
Security
If you discover a security vulnerability, please report it via a private channel (e.g. email or GitHub Security Advisories) rather than publicly disclosing it.
Acknowledgments
This package was inspired by and learned from:
- Laravel FFmpeg — Architecture patterns and Laravel integration approach
- quasarstream/shaka-php — Shaka Packager wrapper and command building patterns
License
The MIT License (MIT). Please see License File for more information.