Install
StreamKit SDK ships as a single UMD bundle plus a stylesheet. Drop both into your page — no build step, no package manager required.
<link rel="stylesheet" href="dist/skplayer.min.css"> <script src="dist/skplayer.min.js"></script>
hls.js and dash.js are not bundled — they're fetched from a CDN on demand, only when the source you give StreamKit SDK actually needs that engine (.m3u8 → hls.js, .mpd → dash.js). Native MP4/native HLS (Safari) needs neither.
SKPlayer.preload("hls") (or "dash") — or pass preload: "hls" in config — to start fetching the engine script before playback is requested, shaving the head-start off first play.Quick Start
Video
<div id="player"></div> <script> new SKPlayer({ containerId: "player", mediaType: "video", src: "https://example.com/stream.m3u8", backupsrc: "https://backup-cdn.example.com/stream.m3u8", autoplay: 1, muted: 1, poster: "https://example.com/poster.jpg", }); </script>
Audio
new SKPlayer({ containerId: "player", mediaType: "audio", src: "https://example.com/stream.mp3", });
containerId must point to an element already in the DOM — the constructor throws if it isn't found. See Audio Mode for what's different when mediaType: "audio".
Config Options
Everything below is a top-level key on the object passed to new SKPlayer({...}). Defaults shown are what applies when the key is omitted entirely.
| Key | Type | Default | Description |
|---|---|---|---|
containerId | string | required | id of an existing DOM element the player mounts into |
mediaType | "video" | "audio" | — | anything other than exactly "video" creates an <audio> element — see Audio Mode |
src | string | — | media URL. Engine is auto-selected by extension (.m3u8→HLS, .mpd→DASH, else native) unless streamType is set |
backupsrc | string | — | fallback URL swapped in automatically on a fatal stream error. One retry only |
streamType | "hls" | "dash" | — | forces engine selection, bypassing URL-extension sniffing |
hlsUrl / dashUrl | string | jsDelivr CDN URL | override the CDN URL StreamKit SDK fetches hls.js / dash.js from |
autoplay | 0/1/bool | 0 | attempts autoplay with sound first, falls back to muted-autoplay automatically if the browser blocks it |
muted | 0/1/bool | 0 | starts the player muted |
poster | string | — | poster image, video mode only |
controls | bool | object | true | false = headless mode (no built-in UI at all). Object form: { ui, tapToUnmute, contextMenu, fullscreen, volume }, each independently toggleable |
preload | "hls" | "dash" | — | starts fetching that engine's script immediately, ahead of playback |
drm | object | — | see Multi-DRM |
ads | object | — | see IMA Ads |
lband | object | — | see L-Band. Presence of the key at all (even {}) turns it on |
cmcd | bool | object | — | see CMCD |
analytics | object | — | see GA4 Analytics |
licenseKey | string | — | see License Protection |
playlist | array | — | see Playlist |
autoplayNext | bool | true when a playlist is set | auto-advance to the next track on ended |
loop | bool | false | playlist wraps to the first track after the last (or last track on prevTrack() from the first) |
Query-String Overrides
Useful for demos and support links — appending these to the page's own URL overrides matching config keys at runtime, taking priority over whatever the embedding code passed in.
| Param | Overrides |
|---|---|
?autoplay=1 | autoplay |
?muted=1 | muted |
?controls=0 | controls |
?ui=, ?tapToUnmute=, ?contextMenu=, ?fullscreen=, ?volume= | the matching controls.* sub-flag |
?lband=1 | turns L-Band on |
?lband_tag=, ?lband_thickness=, ?lband_refresh=, ?lband_duration=, ?lband_pause= | the matching lband.* field |
?ga4= | analytics.ga4MeasurementId |
?ga4debug=1 | analytics.debug |
?preload=hls|dash | same as the preload config key, evaluated at script-parse time |
Instance Methods
Called on the object returned by new SKPlayer(...).
| Method | Description |
|---|---|
play() | starts/resumes playback |
pause() | pauses playback |
seek(seconds) | seeks to an absolute time |
isLive() | returns true if the current stream has no fixed duration |
setSource(config) | swaps to a new source at runtime — pauses, tears down the old stream engine, and reloads with the given config (same shape as the constructor's) |
destroy() | full teardown — stream/ads/L-Band/listeners, empties the container |
on(event, callback) | subscribes to internal lifecycle events: "ready", "play", "pause", "buffering", "ended", "error", "fatal", "sourcechange" |
goToTrack(index) | playlist only — jumps to a track by index and plays it |
nextTrack() / prevTrack() | playlist only — advance/go back one track (wraps if loop: true) |
getPlaylistIndex() | playlist only — current 0-based index |
getPlaylist() | playlist only — the playlist array, or [] |
setVolume(), mute(), or toggleFullscreen() on the instance today — those are only reachable through the built-in control-bar UI, or directly via player.core.media.el.volume / .muted and player.core.container.requestFullscreen().Quality control (via player.core.stream)
Not exposed on the top-level instance — reach through player.core.stream:
getAvailableQualities() | list of available bitrate/quality levels |
setQuality(value) | manually select a quality level |
setAutoQuality() | return to ABR (automatic) quality selection |
getCurrentQuality() / getCurrentQualityLabel() / getCurrentQualityBadge() | current selection, for building custom quality UI |
L-Band (via player.core.lband)
show() / hide() | manually toggle the squeeze-back ad layout |
refresh() | force-refresh both GPT ad slots |
isVisible() | current visibility state |
cycleRemaining() | returns { displayDuration, pauseDuration } for the current auto-show/hide cycle |
Static Methods
SKPlayer.version | current SDK version string |
SKPlayer.preload(type) | preloads "hls" or "dash" engine script ahead of any instance being created |
SKPlayer.pauseAll() | pauses every SKPlayer instance currently on the page |
SKPlayer.destroyAll() | destroys every SKPlayer instance currently on the page |
Events
Custom DOM events, dispatched on the media element unless noted otherwise. Listen with document.getElementById("player").querySelector("video").addEventListener(...), or use the container-level ones directly on your containerId element.
| Event | Target | Fires when |
|---|---|---|
avplayer-fatal-error | media element | hls.js/dash.js retries exhausted, or a DRM/FairPlay setup failure — triggers automatic backupsrc failover if configured |
avplayer-retry-requested | media element | user clicks "Retry" after a CDN script load failure with no backupsrc set |
avplayer-license-invalid | media element | the license server explicitly returns "not licensed" (never on a network error — that fails open) |
avplayer-error | bubbles from the player container | any handled error — network/media/stream/config/fatal/license — carrying the full error object |
avplayer-live | media element | the stream is detected as live (no fixed duration) |
avplayer-levels-ready | media element | quality/bitrate levels have been parsed and are available |
avplayer-quality-changed | media element | a manual or automatic ABR quality switch completed |
avplayer-playlist-changed | container | { index, item, total } — fired by goToTrack() / nextTrack() / prevTrack() / auto-advance |
Multi-DRM
One drm config block covers Widevine, PlayReady, FairPlay, and ClearKey.
| Key | Fields |
|---|---|
widevine | licenseUrl, headers, serverCertificateUrl |
playready | licenseUrl, headers |
fairplay | licenseUrl, certificateUrl, headers — native EME path, Safari/iOS |
clearkey | keys: { "<kidHex>": "<keyHex>" } — DASH only, no license server needed |
Widevine · DASH
drm: {
widevine: { licenseUrl: "https://your-license-server.com/widevine" }
}
Multi-DRM (Widevine + PlayReady) · DASH
drm: {
widevine: { licenseUrl: "https://.../AcquireLicense", headers: { "X-AxDRM-Message": token } },
playready: { licenseUrl: "https://.../AcquireLicense", headers: { "X-AxDRM-Message": token } },
}
ClearKey · DASH
drm: {
clearkey: { keys: { "4060a865887842679cbf91ae5bae1e72": "fc35340837310cc0fb53de97e22a69e0" } }
}
Widevine · HLS (via hls.js EME)
drm: {
widevine: { licenseUrl: "https://your-license-server.com/widevine" }
}
Try any of these live on the Live Demo's DRM tab.
IMA Ads — Pre/Mid/Post-roll
Google IMA SDK integration. An empty ads: {} does nothing — at least one of preroll/midroll/postroll must be set.
// Preroll only ads: { preroll: "https://pubads.g.doubleclick.net/gampad/ads?..." } // Postroll only ads: { postroll: "https://pubads.g.doubleclick.net/gampad/ads?..." } // Midroll — multiple cue points, one shared tag ads: { midroll: { times: [30, 90, 180], tag: "..." } } // Midroll — per-cue tags ads: { midrolls: [ { time: 30, tag: "..." }, { time: 90, tag: "..." } ] } // All three together ads: { preroll: "...", midroll: { times: [30, 90, 180], tag: "..." }, postroll: "...", }
Midrolls are VOD-only — they're automatically cancelled if the stream turns out to be live. A 8-second internal timeout guards every ad request; if the ad manager never responds, content plays through uninterrupted rather than hanging.
L-Band Display Ads
A squeeze-back display-ad format (Google Ad Manager/GPT) — the video shrinks into a corner and opens two ad slots, left vertical + bottom horizontal.
| Key | Default | Description |
|---|---|---|
adUnit | "/6355419/Travel" | GPT ad unit path used for both slots unless overridden per-side |
leftAdUnit / bottomAdUnit | falls back to adUnit | per-slot ad unit override |
thickness | 100 | bar thickness in px (width of the left bar, height of the bottom bar) |
refresh | 60 | seconds between automatic ad refreshes, once visible |
autoShow | true | show automatically once both slots render |
displayDuration | 0 | seconds visible before auto-collapsing (0 = stays forever) |
pauseDuration | 0 | seconds hidden before re-showing with fresh ads (0 = no cycle) |
lband: {
adUnit: "/1234567/YourNetwork/Video",
thickness: 100,
refresh: 60,
}
GPT requests each slot at its computed exact size plus common IAB fallback sizes (300×250, 728×90, 160×600, etc.), since real ad inventory is bought at standard sizes. See it live on the Live Demo's L-Band tab.
CMCD
Common Media Client Data (CTA-5004) — attaches session/QoS data to every manifest and segment request so your CDN can make smarter delivery decisions.
// auto-generated session ID cmcd: true // or customize fields cmcd: { sessionId: "my-session-id", contentId: "my-content-id" }
Delivered as hls.js's native cmcd option for HLS, dash.js's streaming.cmcd for DASH, and appended as a CMCD= query parameter for native/Safari HLS playback where no per-request hook exists.
GA4 Analytics
Push standard playback events straight into your own GA4 property. Zero overhead when not configured — no measurement ID, no tracking calls at all.
analytics: {
ga4MeasurementId: "G-XXXXXXXXXX",
debug: false // true = tag every hit for GA4 DebugView
}
Reuses the host page's existing gtag if present, otherwise lazy-loads one. Events tracked automatically: init, play, video_start, resume, pause, seek, buffering_start/buffering_end, video_complete, error, fullscreen_enter/fullscreen_exit, volume_change, mute/unmute, plus ad-lifecycle events (ad_request, ad_loaded, ad_start, ad_complete, ad_skip, ad_failed). Each hit carries an anonymous per-device ID (localStorage-persisted), a per-viewing session ID, domain, browser/OS, and screen size — no PII.
License Protection
Domain-locked license keys, checked server-side against the embedding domain.
licenseKey: "avp_your_key_here"
licenseKey today has no effect on playback — this will change with no action needed on your part once enforcement goes live, and it always fails open (a network hiccup never blocks playback) even once it does.When active: fails open on any network error/timeout, always bypasses localhost, and only stops playback on an explicit "not licensed" response from the server — shown as a clear, non-recoverable message rather than a generic error.
Playlist
An array of per-track configs — each item takes the same keys as a single-source player config.
player = new SKPlayer({ containerId: "player", mediaType: "video", autoplay: 1, muted: 1, playlist: [ { title: "Track 1", src: "https://.../a.m3u8", mediaType: "video" }, { title: "Track 2", src: "https://.../b.mpd", mediaType: "video" }, ], autoplayNext: true, loop: false, }); player.nextTrack(); player.goToTrack(1);
Extra fields like title pass through harmlessly — StreamKit SDK only reads src/mediaType/autoplay/muted/etc. from each item; anything else is yours to use for your own UI (track titles, thumbnails).
Audio Mode
mediaType: "audio" reuses the entire control bar, seek bar, volume, and settings UI — same engine, same reliability, just no video canvas.
- • The control bar never auto-hides (video mode hides it after 3s of inactivity)
- • No touch-to-reveal-controls mobile behavior — the bar is always visible
- • No right-click context menu (video-only feature)
- • All streaming engines, DRM, ads, CMCD, and analytics work identically to video mode