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.
Architecture fit. This is not a Laravel package in any structural sense — there is no service provider, facade, config publish, or exception hierarchy tuned for Laravel; it is a framework-agnostic PHP 7.2+ fluent builder that composes a Shaka Packager command line and shells out to a packager binary via a process executor. Architecturally it is a DSL over CLI flags: Stream/DASHStream/HLSStream objects mirror packager stream descriptors almost 1:1, and mediaPackaging()->DASH('out.mpd', fn($o) => ...)->export() maps to an argv array plus Symfony\Process. That is a good fit for a Laravel job class (stateless, container-resolvable, no framework coupling), but a poor fit for anything expecting a managed media abstraction. Critically, this library does not transcode or build an ABR ladder — inputs must already be mezzanine/rendition encodes; the package only repackages, segments, and encrypts. Any estimate that assumes "upload video, get DASH/HLS" is wrong by roughly one ffmpeg stage.
Integration feasibility. Composer install is trivial (aminyazdanpanah/php-shaka, MIT). The integration cost lives entirely in the external dependency: a working Shaka Packager binary on PATH or an explicit packager.binaries path, with a configurable timeout (README example: 3600s). That means Dockerfile/CI work, platform-specific binaries (linux/mac/win), and glibc-vs-musl concerns if the app image is Alpine. The wrapper performs no capability negotiation with the binary — there is no version handshake, so a binary upgrade can change flag semantics and fail at runtime, not at install time. The declared PHP floor (7.2) and Travis/AppVeyor badges suggest the compatibility matrix has not been re-validated for PHP 8.2/8.3 + Laravel 10–12; that must be proven with a spike, not assumed.
Technical risk. Ranked:
Process invocation) to rule out command injection; also verify orphan-process cleanup when timeout fires.export() model — confirm before committing to live.BaseURL, playback in dash.js/hls.js/Shaka Player).Key questions. (1) Who owns binary provisioning, pinning, and upgrade cadence — us or the vendor? (2) Are content keys permitted in application env/DB, or must they stay in a KMS with the packager calling out? (3) Is packaging synchronous (no) or a queued job with a defined SLA per asset? (4) VOD-only, or is live on the near-term roadmap? (5) What is the exit cost — can we swap to direct shaka-packager invocation, Bento4, ffmpeg, or a managed service (MediaConvert, Bitmovin, Mux) behind an internal interface? (6) Have we confirmed PHP 8.x + our Laravel version in a container with the real binary?
Stack fit. Treat the package as an adapter, not a foundation. Define an internal Packager contract with a ShakaPackagerAdapter implementation, bind it as a singleton in a small service provider that reads config/media.php (packager.binaries, timeout, key-server config, temp/working disk, output disk). Invoke it only from queue jobs (PackageAssetJob), never from a request cycle. Because packager writes to the local filesystem, plan an explicit staging directory plus an upload-to-S3 (or chosen disk) step; do not assume Storage::disk('s3') paths work as packager arguments.
Migration path. Incremental and reversible: run alongside the existing pipeline rather than replacing it. Phase the work so the first shippable increment is VOD transmux/DASH packaging for a low-traffic content class, with the old path still serving. Keep the binary invocation and any DRM key handling behind the internal contract so a later swap to direct CLI use, Bento4, or a managed service is a one-class change. If DRM is required early, gate it behind a feature flag per asset type.
Compatibility. Pin the Composer version exactly (avoid ^1.0 drift), pin the Shaka Packager binary version in the image, and record the pairing in the Dockerfile. Verify: PHP 8.x runtime with the wrapper's declared 7.2 floor, symfony/process availability and version alignment, proc_open/exec not disabled by host hardening, filesystem permissions for the queue user, and queue.retry_after > worker timeout > packager timeout so long assets fail cleanly instead of being double-executed. Confirm base image libc compatibility for prebuilt binaries, or build from source.
Sequencing. (1) Spike: binary in a container, mediaFileAnalysis() smoke test — one day, answers feasibility and PHP 8 compatibility. (2) Container/CI provisioning and version pinning. (3) Basic transmux on a fixture, assert exit code and output files. (4) DASH then HLS packaging with manifest validation and real playback in a reference player. (5) DRM with a test key server, validated by playback of encrypted content and a licence acquisition trace. (6) Live only if the roadmap requires it, and only after confirming the process model. Do not sequence DRM concurrently with steps 1–4; it multiplies variables.
Maintenance. Low code churn, non-trivial ownership. Budget an internal owner for the binary (build image, patch cadence, security updates, platform matrix) and a pinned-version upgrade ritual that includes a regression fixture pack. Given the single-maintainer profile and zero dependents, assume no upstream responsiveness: keep a fork/vendored copy path in reserve, and do not build features that depend on upstream changes landing. Shaka Packager's own release cadence is the real upgrade driver, and its flag surface is broad enough that upgrades are never no-ops.
Support. Community-only — GitHub issues, no SLA, no vendor escalation. Support load will concentrate in two places: playback failures reported by clients (which surface days after packaging, with no server-side signal) and DRM configuration errors. Instrument accordingly: persist the exact argv, exit code, stderr, and packager binary version on every job; retain manifests for a period; add a synthetic playback check per packaged asset class in staging.
Scaling. Packaging is CPU- and IO-bound in child processes, which is convenient — PHP memory is not the constraint, worker concurrency is. Scale horizontally on queue depth with a per-host concurrency cap (a small multiple of vCPU), and monitor disk headroom and inode usage in the staging volume, since segment writes are bursty and numerous. Keep staging on fast local disk, not network storage. For live, plan dedicated hosts with a persistent process per stream — it does not fit the elastic job-worker model and should not share a cluster with VOD packaging.
Failure modes. Expect: binary missing/not executable/wrong architecture (fails at first job, loud); packager timeout exceeded with a lingering child process (silent resource leak); disk full mid-package (partial output that may look valid); malformed or unsupported input codec (non-zero exit, stderr only); DRM key/KeyID mismatch producing a well-formed manifest that no player can decrypt; BaseURL or segment-template mismatch producing 404s at the CDN rather than at packaging time; missing initSegment/segmentTemplate consistency across streams producing manifests that fail in some players only. Design jobs to be idempotent, write to a temp prefix, and atomically promote on success — never leave partial manifests addressable by the CDN.
Ramp-up. A Laravel developer is productive on the fluent API within hours; the README plus examples/ is genuinely sufficient for transmux and DASH/HLS packaging. The real ramp is domain: Shaka Packager flag semantics, ABR ladder design, CMAF/segment alignment, and DRM (PSSH, KeyID, licence acquisition, device testing). Budget 2–4 weeks for one engineer to reach independent, production-quality output on VOD without DRM; add a meaningful multiple for DRM and again for live. Include an external DRM/playback reviewer — this is not a skill the team should improvise.
How can I help you explore Laravel packages today?