Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Shaka Laravel Package

aminyazdanpanah/php-shaka

PHP library that wraps Shaka Packager for DASH and HLS packaging and encryption, with Common Encryption support for Widevine, PlayReady, and raw key DRM. Includes live streaming and ad insertion examples.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install the PHP wrapper and the actual binary. The Composer package is only a command builder; nothing works until the Shaka Packager executable exists on the machine.

composer require aminyazdanpanah/php-shaka

Then get Shaka Packager via Docker, a prebuilt release, or a source build, rename the artifact to packager (packager.exe on Windows), and either put it on the system PATH or remember its absolute path. In a Laravel app, put that decision in config rather than code:

// config/shaka.php
return [
    'binaries' => env('SHAKA_PACKAGER_BINARY', '/usr/local/bin/packager'),
    'timeout'  => (int) env('SHAKA_TIMEOUT', 3600),
];
// AppServiceProvider::register()
$this->app->singleton(\Shaka\Shaka::class, fn () => \Shaka\Shaka::initialize([
    'packager.binaries' => config('shaka.binaries'),
    'timeout'           => config('shaka.timeout'),
]));

2. Where to look first. Read the examples/ directory before the source — the README explicitly recommends it, and the class API is self-documenting but spread across Stream, DASHStream, HLSStream, and the option callback objects. Then skim the upstream Shaka Packager documentation, because every method here maps 1:1 onto a CLI flag and the upstream docs are where the real semantics live.

3. First use case: inspect, then transmux, then package. The safest entry point is mediaFileAnalysis(), which dumps stream info without writing anything — perfect for validating that the binary is reachable and that your input file is what you think it is:

$stream = \Shaka\Options\Streams\Stream::input(storage_path('app/input/master.mp4'));

$export = $shaka->streams($stream)->mediaFileAnalysis()->export(); // StreamCollection

Once that returns content, do a plain transmux to prove your paths and permissions, and only then add DASH/HLS or DRM:

$video = Stream::input($input)->streamSelector('video')->output("{$dir}/video.mp4");
$audio = Stream::input($input)->streamSelector('audio')->output("{$dir}/audio.mp4");

$shaka->streams($video, $audio)->mediaPackaging()->export();

Implementation Patterns

Build streams as value objects, not inline chains. Streams are the unit of reuse. A factory that maps your domain (a Media model, an upload) to one or more Stream instances keeps controllers thin and makes multi-bitrate output trivial:

class StreamFactory
{
    public function forSource(string $path, string $outDir): array
    {
        return collect(config('transcodes.renditions'))->map(function ($r) use ($path, $outDir) {
            return Stream::input($path)
                ->streamSelector($r['type'])
                ->output("{$outDir}/{$r['name']}.mp4")
                ->bandwidth($r['bandwidth'])
                ->language($r['lang'] ?? null);
        })->all();
    }
}

Always package inside a queued job. Packaging is a long, blocking, CPU-bound child process. In Laravel, that belongs in a job with an explicit $timeout larger than the packager timeout, $tries = 1 (retrying a half-written segment tree is worse than failing), and a failed() hook that cleans the working directory.

class PackageVideo implements ShouldQueue
{
    public $timeout = 3700;
    public $tries   = 1;

    public function handle(\Shaka\Shaka $shaka, StreamFactory $factory): void
    {
        $dir = storage_path("app/packaging/{$this->media->id}");
        File::ensureDirectoryExists($dir);

        try {
            $shaka->streams(...$factory->forSource($this->media->source_path, $dir))
                  ->mediaPackaging()
                  ->DASH("{$dir}/manifest.mpd")
                  ->HLS("{$dir}/master.m3u8")
                  ->export();

            $this->publishToDisk($dir); // push to S3 / CDN origin
        } finally {
            File::deleteDirectory($dir);
        }
    }
}

Use the options callback for anything beyond the defaults. DASH, HLS and DRM each take a closure receiving an options object. The closure must return that object — this is the single most common integration bug:

$shaka->streams(...$streams)
    ->mediaPackaging()
    ->DASH('manifest.mpd', function ($options) {
        return $options->generateStaticLiveMpd()
                       ->minBufferTime(2)
                       ->suggestedPresentationDelay(30)
                       ->baseUrls('https://cdn.example.com/vod/');
    })
    ->HLS('master.m3u8', fn ($options) => $options->hlsMasterPlaylistName('master.m3u8'))
    ->export();

Keep DRM keys in config/services, never in the repository. Widevine, PlayReady and raw-key modes all accept key material. Load them from the environment or a secrets manager, pass them into the job payload, and log nothing that includes the key string:

->Widevine([
    'key_server_url' => config('drm.widevine.url'),
    'content_id'     => $media->id,
    'protection_scheme' => 'cbcs',
])

For offline/licensed flows where you hold the keys yourself, prefer RawKey with keys sourced from your KMS in the job, so rotation only touches secrets.

Compose a pipeline: analysis → transcode → package → publish. mediaFileAnalysis() is useful as a pre-flight gate in the same job — verify the input actually contains the tracks your rendition config assumes before spending minutes packaging. It also gives you a cheap way to fail fast with a meaningful exception instead of a cryptic packager exit code.

Separate local write path from remote storage. The packager writes to the local filesystem only. Standard pattern: write to storage/app/packaging/{uuid}, then Storage::disk('s3')->putFileAs(...) per manifest and segment, or ship the whole directory via a multipart upload. Never point the packager at a stream wrapper — it is an external process and knows nothing about s3://.

Use stream selectors deliberately for text and multi-language. DASH roles come from DASHStream::input(...)->DASHRoles('main'), and defaultLanguage()/defaultTextLanguage() on the DASH options control which track the player picks. If you have multiple audio tracks, each needs its own Stream with language() set — otherwise players will default arbitrarily.

Gotchas and Tips

  • "Packager not found" is a PATH problem, not a PHP problem. When a queue worker runs under a different user or systemd unit than your shell, packager on your interactive PATH may be invisible. Always pass packager.binaries explicitly in production. Verify with which packager as the worker user.
  • PHP 7.2+ constraint is stale. The README targets 7.2; the package has a small maintainer surface and low adoption (91 stars, 0 dependents). Test against your actual PHP 8.x on a branch before adopting, and pin the version in composer.json.
  • The timeout is your responsibility. timeout defaults can silently truncate long live or 4K jobs. Set both the Shaka config timeout and the queue worker's $timeout, with the worker's larger so you get a real exception rather than a killed process.
  • Callback options are easy to no-op. Returning $options is mandatory; function ($options) { $options->baseUrls(...); } silently discards the change. Same for stream chains: $stream->output(...) mutates and returns $this, so don't assume the original variable is untouched elsewhere.
  • Option naming is inconsistent with the CLI. You'll see paired inverses like generateSidxInMediaSegments() / nogenerateSidxInMediaSegments(), and oddly-cased methods like Mp4IncludePsshInStream(). When something doesn't behave, don't guess: reproduce the equivalent packager command line by hand and confirm the flag exists in your installed binary's version. Flag sets drift between packager releases.
  • Segment template consistency across streams. If you use segmentTemplate(), every stream in the packaging call must agree — mixed templated/non-templated streams produce broken manifests. Related: $Time$ in a template requires generateSidxInMediaSegments() per spec, and disables the approximate-timeline optimizations.
  • Encryption changes the manifest, not just the segments. With Mp4IncludePsshInStream() on by default, PSSH boxes land in the stream; if your player reports "no license request," check the mpd's ContentProtection elements and whether you also needed protection_scheme. Getting cbcs vs cenc wrong is a classic Apple-vs-everyone-else failure.
  • Clean up aggressively. Failed runs leave partial init.mp4/segment trees that will be re-uploaded on retry. Delete the working directory in a finally block and in failed().
  • Escape paths. Absolute paths with spaces or non-ASCII filenames break shell-based wrappers. Keep packaging directories to a controlled, ASCII, UUID-named path, and validate the original filename before it reaches the stream.
  • Debugging via exit codes. The wrapper surfaces the child process result and the StreamCollection export; capture and log the raw output verbatim before parsing it. Most "the library is broken" reports are packager saying Cannot open file or Unsupported codec, not PHP.
  • Ad insertion and live are the fragile edges. Live packaging runs indefinitely and never returns a tidy export — treat it as a supervised daemon, not a queued job. Test ad-insertion (SCTE-35 / --ad_cues) against a real player early; it's the feature least likely to survive a packager upgrade.
  • Extension point if you need one: fork the command builder. Since the package is a thin flag-builder, adding a new packager flag is usually a small method on the relevant options class. If upstream lags, subclass/patch the options object rather than shelling out manually — you keep the type-safe stream API.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin