[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6XCYXP57QRYXCW7QN1HXNVC":3,"skill-same:01M6XCYXP57QRYXCW7QN1HXNVC":69},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":48,"commitSha":49,"skillDirectory":50,"forksCount":14,"repositoryHtmlUrl":51,"repositoryHomepageUrl":6,"ownerHtmlUrl":52,"ownerBlogUrl":53,"ownerDescription":6,"updatedAt":54,"install":55,"resources":68},"01M6XCYXP57QRYXCW7QN1HXNVC","bitmovin-player-web",null,"Integrate the Bitmovin Web Player SDK into a web app. Use when the user asks to add video playback, embed a player, play HLS\u002FDASH\u002FMP4\u002FWHEP\u002FMOQ, set up DRM (Widevine\u002FPlayReady\u002FFairPlay), integrate ads, customize the Player UI, or work with the Bitmovin Player in any way. Covers both Player v8 (stable) and Player Web X \u002F PWX (next-gen).","bitmovin\u002Fskills","bitmovin","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F3273006?v=4","01M6XCX24ZDCBTPHWAAM18VJQY","Bitmovin","01M6XCX29H5E446MM0E5XAVQTC",1,"MIT","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-player-web\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[9,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"bitmovin player web","player","web","up","when","hls","dash","add","app","next","integrate","use","work","mp4","ads","Video","SET","ui","play","2026-11-10 10:12:30",false,"---\nname: bitmovin-player-web\ndescription: Integrate the Bitmovin Web Player SDK into a web app. Use when the user asks to add video playback, embed a player, play HLS\u002FDASH\u002FMP4\u002FWHEP\u002FMOQ, set up DRM (Widevine\u002FPlayReady\u002FFairPlay), integrate ads, customize the Player UI, or work with the Bitmovin Player in any way. Covers both Player v8 (stable) and Player Web X \u002F PWX (next-gen).\n---\n\n# Bitmovin Web Player Integration Skill\n\nYou are an expert at integrating the Bitmovin Web Player SDK. When the user asks you to add video playback, embed a player, integrate streaming, customize the Player UI, or work with the Bitmovin Player — use this skill.\n\n## When to activate\n\n- User asks to add video playback to a web app\n- User mentions Bitmovin Player, `bitmovin-player`, or streaming integration\n- User needs HLS, DASH, Smooth, WHEP, MOQ, or DRM playback in a browser\n- User wants to customize player UI, add ads, subtitles, analytics, or other player integrations\n\n## Choose the player version deliberately\n\n**Default to Player Web v8** unless the user explicitly asks for PWX, is evaluating PWX, or the architecture tradeoff matters.\n\nOnly ask which player they want when the answer changes the implementation, for example:\n- The user mentions PWX, Player Web X, bundles, or custom packages\n- The user wants to benchmark or compare v8 vs PWX\n- The user is migrating an existing PWX proof of concept\n\nRecommended options:\n\n1. **Player Web v8** (`bitmovin-player`) — the stable production default. Supports HLS, DASH, Smooth, WHEP, DRM (Widevine\u002FPlayReady\u002FFairPlay), ads (VAST\u002FVMAP\u002FIMA), analytics, subtitles, Chromecast, AirPlay, and is the primary choice for Web Player and Player UI integrations. It also supports a range of 3rd-party integrations such as Yospace SSAI and Conviva Analytics. Mature API, full documentation.\n\n2. **Player Web X \u002F PWX** (`@bitmovin\u002Fplayer-web-x`) — the next-generation modular player. Still evolving and feature-incomplete. Use when the user explicitly wants PWX, wants the package architecture, or is validating PWX-specific behavior. MOQ playback is currently in this version rather than v8.\n\n## Live docs MCP server\n\nFor anything this skill doesn't cover — obscure APIs, recent SDK releases, specific code samples — query the **Bitmovin Docs MCP** instead of guessing:\n\n```\nhttps:\u002F\u002Fagentic.bitmovin.com\u002Fdocumentation\u002Fmcp\n```\n\nIt indexes `developer.bitmovin.com` documentation and the official GitHub sample repositories (`bitmovin-player-web-samples`, `bitmovin-player-ios-samples`, `bitmovin-player-android-samples`, `bitmovin-player-roku-samples`, `bitmovin-api-sdk-examples`).\n\n**When to use it:**\n- You need a working code sample for a specific feature (DRM flavor, custom ad integration, subtitle styling, etc.)\n- The user asks about a feature not documented in this skill\n- You're unsure about a config field or API signature — verify against live docs before writing code\n- The user reports the SDK behavior differs from what this skill describes (SDK may have moved on)\n\nAdd it as an MCP connector in the chat client, or fetch URLs from `developer.bitmovin.com` directly. Prefer the MCP when it's available — it's faster than fetching individual doc pages.\n\n---\n\n# Player Web v8 (stable)\n\n## Installation\n\n```bash\nnpm install bitmovin-player\n```\n\nFor explicit UI customization or version pinning, also install the dedicated UI package:\n\n```bash\nnpm install bitmovin-player-ui\n```\n\nThe main package includes the player runtime and TypeScript types. The dedicated `bitmovin-player-ui` package ships the UI assets and type declarations for explicit UI work.\n\n### CDN alternative\n\n```html\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player@8\u002Fbitmovinplayer.js\">\u003C\u002Fscript>\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fjs\u002Fbitmovinplayer-ui.js\">\u003C\u002Fscript>\n\u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fcss\u002Fbitmovinplayer-ui.css\" \u002F>\n```\n\n**These are UMD bundles that attach to global namespaces — NOT ES modules.** Do not `import` from the CDN URL:\n- Player: `window.bitmovin.player.Player`\n- UI: `window.bitmovin.playerui.UIFactory`\n\nFor ES module `import` syntax, install via npm and use a bundler.\n\n### License key\n\nEvery Bitmovin Player instance needs a license key. `localhost` is auto-allowed; deployed domains must be added to the license allowlist.\n\nGet one of these ways:\n- **Bitmovin Dashboard**: https:\u002F\u002Fdashboard.bitmovin.com → Player → Licenses\n- **Bitmovin CLI** (if installed): `bitmovin player licenses list` — lists all licenses with their keys and allowed domains. `bitmovin player licenses get \u003Cid> --json` for details.\n\n## Basic integration\n\n```typescript\nimport { Player } from 'bitmovin-player';\n\nconst container = document.getElementById('player');\n\nconst player = new Player(container, {\n  key: 'YOUR_LICENSE_KEY',\n  playback: {\n    autoplay: false,\n    muted: false,\n  },\n});\n\n\u002F\u002F Load a source\nawait player.load({\n  hls: 'https:\u002F\u002Fexample.com\u002Fstream.m3u8',\n  title: 'My Video',\n  poster: 'https:\u002F\u002Fexample.com\u002Fposter.jpg',\n});\n```\n\n### UI guidance for current v8 releases\n\n**Last verified:** 2026-04-15 against Bitmovin Web release notes and UI v4 docs.\n\nFor new Web SDK integrations on current v8 releases, **do not assume you must wire `UIFactory` manually**. Since Web `8.245.0` (released 2026-02-09), Bitmovin's default Web UI integration migrated to UI v4 and standard setups can use the default UI without extra wiring.\n\nUse the explicit UI package only when you need one of these:\n- Pin a specific UI version\n- Customize the UI build process\n- Stay on an older player version where you still initialize the UI manually\n- Build a non-default UI variant intentionally\n\n### Explicit or custom UI integration\n\nFor explicit UI wiring, prefer the dedicated `bitmovin-player-ui` package because it ships type declarations.\n\n```typescript\nimport { Player } from 'bitmovin-player';\nimport { UIFactory } from 'bitmovin-player-ui';\nimport 'bitmovin-player-ui\u002Fdist\u002Fcss\u002Fbitmovinplayer-ui.css';\n\nconst player = new Player(document.getElementById('player'), {\n  key: 'YOUR_LICENSE_KEY',\n  ui: false,\n});\n\nUIFactory.buildUI(player);\n```\n\nWhen you want to pin the hosted UI assets explicitly, configure them through `PlayerConfig.location`:\n\n```typescript\nconst player = new Player(document.getElementById('player'), {\n  key: 'YOUR_LICENSE_KEY',\n  location: {\n    ui: 'https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fjs\u002Fbitmovinplayer-ui.js',\n    ui_css: 'https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fcss\u002Fbitmovinplayer-ui.css',\n  },\n});\n```\n\nIf you set `ui: false`, you are responsible for attaching a UI yourself.\n\n## Source configuration\n\nThe source object tells the player what to play. At least one of `hls`, `dash`, `smooth`, or `progressive` is required.\n\n**API reference:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-source-config.md\n\n```typescript\nawait player.load({\n  \u002F\u002F Stream URL — pick one:\n  hls: 'https:\u002F\u002Fcdn.example.com\u002Fstream.m3u8',       \u002F\u002F HLS\n  dash: 'https:\u002F\u002Fcdn.example.com\u002Fstream.mpd',        \u002F\u002F DASH\n  smooth: 'https:\u002F\u002Fcdn.example.com\u002Fstream.ism',      \u002F\u002F Smooth Streaming\n  progressive: 'https:\u002F\u002Fcdn.example.com\u002Fvideo.mp4',  \u002F\u002F Progressive MP4\u002FWebM\n\n  \u002F\u002F Metadata\n  title: 'Video Title',\n  description: 'Video description',\n  poster: 'https:\u002F\u002Fcdn.example.com\u002Fposter.jpg',\n\n  \u002F\u002F Subtitles\n  subtitleTracks: [\n    { url: 'https:\u002F\u002Fcdn.example.com\u002Fsubs_en.vtt', lang: 'en', label: 'English' },\n    { url: 'https:\u002F\u002Fcdn.example.com\u002Fsubs_de.vtt', lang: 'de', label: 'Deutsch' },\n  ],\n\n  \u002F\u002F Thumbnails (for seek preview)\n  thumbnailTrack: { url: 'https:\u002F\u002Fcdn.example.com\u002Fthumbs.vtt' },\n});\n```\n\n## Player configuration\n\nThe config object passed to `new Player(container, config)`.\n\n**API reference:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-player-config.md\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `key` | `string` | **Required.** License key from Bitmovin Dashboard. |\n| `playback.autoplay` | `boolean` | Start playback automatically. Default: `false`. |\n| `playback.muted` | `boolean` | Start muted (required for autoplay in most browsers). |\n| `ui` | `boolean` | Set to `false` when using UIFactory. |\n| `adaptation.startupBitrate` | `string` | Initial quality (e.g. `\"2000kbps\"`). |\n| `network.preprocessHttpRequest` | `function` | Modify requests before they're sent (add tokens, headers). |\n| `network.sendHttpRequest` | `function` | Completely replace the HTTP fetch mechanism. |\n| `buffer.video.forwardduration` | `number` | Forward buffer target in seconds. Default: 30. |\n| `style.width` | `string` | Player width. Default: `'100%'`. |\n| `style.aspectratio` | `string` | Aspect ratio. Default: `'16:9'`. |\n\n## Player API\n\n**API reference:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-player-api.md\n\n```typescript\n\u002F\u002F Playback control\nawait player.load(sourceConfig);\nplayer.play();\nplayer.pause();\nplayer.seek(120);         \u002F\u002F seek to 2:00\nplayer.setVolume(50);     \u002F\u002F 0-100\n\n\u002F\u002F State queries\nplayer.getCurrentTime();  \u002F\u002F seconds\nplayer.getDuration();     \u002F\u002F seconds (Infinity for live)\nplayer.isPlaying();\nplayer.isPaused();\nplayer.getVolume();\n\n\u002F\u002F Quality\nplayer.getAvailableVideoQualities();\nplayer.setVideoQuality('auto');\n\n\u002F\u002F Events\nplayer.on('play', () => { ... });\nplayer.on('pause', () => { ... });\nplayer.on('timechanged', () => { ... });\nplayer.on('error', (e) => { console.error(e.code, e.message); });\nplayer.on('ready', () => { ... });\nplayer.on('sourceloaded', () => { ... });\n\n\u002F\u002F Cleanup\nplayer.destroy();\n```\n\n## DRM (Digital Rights Management)\n\nAdd `drm` to the source config. The player handles license acquisition automatically.\n\n```typescript\nawait player.load({\n  dash: 'https:\u002F\u002Fcdn.example.com\u002Fencrypted.mpd',\n  drm: {\n    widevine: {\n      LA_URL: 'https:\u002F\u002Flicense.example.com\u002Fwidevine',\n      headers: { 'X-Auth': 'token123' },  \u002F\u002F optional\n    },\n    playready: {\n      LA_URL: 'https:\u002F\u002Flicense.example.com\u002Fplayready',\n    },\n    fairplay: {\n      LA_URL: 'https:\u002F\u002Flicense.example.com\u002Ffairplay',\n      certificateURL: 'https:\u002F\u002Flicense.example.com\u002Ffairplay\u002Fcert',\n    },\n  },\n});\n```\n\n**Key points:**\n- Widevine works on Chrome, Firefox, Edge, Android\n- PlayReady works on Edge (Windows), some Smart TVs\n- FairPlay works on Safari (macOS, iOS)\n- Provide multiple DRM configs for cross-browser support — the player auto-selects\n- HLS with FairPlay uses `hls` + `drm.fairplay`; DASH with Widevine uses `dash` + `drm.widevine`\n\n## Advertising\n\nSupports VAST, VMAP, and IMA SDK integration.\n\nAt minimum, configure `advertising: {}` if you want ad support enabled up front but plan to schedule ads dynamically later via `player.ads.schedule(...)`.\n\nBy default, the Google IMA SDK is used for ad playback. That is often the best choice when Google Ad Manager \u002F Google Ad Server is involved because it unlocks capabilities such as programmatic demand and ActiveView measurement. If you integrate with non-Google ad servers, consider switching to the Bitmovin Advertising Module (BAM) by importing `bitmovin-player\u002Fmodules\u002Fbitmovinplayer-advertising-bitmovin.js` and registering that module with the player. BAM can be preferable when you want Bitmovin's full ads UI and ads UI customization options.\n\n```typescript\nconst player = new Player(container, {\n  key: 'YOUR_KEY',\n  advertising: {\n    adBreaks: [\n      {\n        tag: { url: 'https:\u002F\u002Fexample.com\u002Fvast-preroll.xml', type: 'vast' },\n        position: 'pre',\n      },\n      {\n        tag: { url: 'https:\u002F\u002Fexample.com\u002Fvast-midroll.xml', type: 'vast' },\n        position: '50%',  \u002F\u002F midroll at 50%; other supported formats include time-based values like '90'\n      },\n      {\n        tag: { url: 'https:\u002F\u002Fexample.com\u002Fvast-postroll.xml', type: 'vast' },\n        position: 'post',\n      },\n    ],\n  },\n});\n```\n\nFor VMAP (server-defined ad schedule):\n```typescript\nadvertising: {\n  adBreaks: [\n    { tag: { url: 'https:\u002F\u002Fexample.com\u002Fvmap.xml', type: 'vmap' }, position: 'pre' },\n  ],\n}\n```\n\nFor mid-rolls, `position` supports multiple string formats, not just percentages. Common examples include percentage-based values such as `'50%'`, time-based values such as `'90'` for 1m30s, and other formats documented here: https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-advertising#position\n\n## Analytics\n\nBitmovin Analytics is bundled with the player. Enable by providing an analytics license key:\n\n```typescript\nconst player = new Player(container, {\n  key: 'YOUR_PLAYER_KEY',\n  analytics: {\n    key: 'YOUR_ANALYTICS_KEY',\n    title: 'My Video',\n    videoId: 'video-123',\n    customData1: 'category-sports',\n  },\n});\n```\n\nSet `analytics: false` to disable.\n\n## Network customization\n\n### Adding auth tokens to requests\n\n```typescript\nconst player = new Player(container, {\n  key: 'YOUR_KEY',\n  network: {\n    preprocessHttpRequest: (type, request) => {\n      if (type.startsWith('media\u002F') || type.startsWith('manifest\u002F')) {\n        request.headers['Authorization'] = 'Bearer ' + getToken();\n      }\n      return Promise.resolve(request);\n    },\n  },\n});\n```\n\n### Completely replacing the network layer\n\nUse `sendHttpRequest` to intercept ALL HTTP requests. Useful for proxying, offline playback, or running in restricted environments (like MCP App sandboxes).\n\n```typescript\nnetwork: {\n  sendHttpRequest: (type, request) => {\n    const isText = type.startsWith('manifest\u002F');\n    return {\n      getResponse: async () => {\n        const resp = await myCustomFetch(request.url);\n        return {\n          request,\n          url: request.url,\n          headers: {},\n          status: 200,\n          statusText: 'OK',\n          body: isText ? await resp.text() : await resp.arrayBuffer(),\n        };\n      },\n      setProgressListener: () => {},\n      cancel: () => {},\n    };\n  },\n}\n```\n\n## Framework integration patterns\n\n### React\n\n```tsx\nimport { useEffect, useRef } from 'react';\nimport { Player } from 'bitmovin-player';\n\nfunction BitmovinPlayer({ source, licenseKey }) {\n  const containerRef = useRef\u003CHTMLDivElement | null>(null);\n  const playerRef = useRef\u003CPlayer | null>(null);\n\n  useEffect(() => {\n    if (!containerRef.current) return;\n\n    const player = new Player(containerRef.current, {\n      key: licenseKey,\n    });\n    playerRef.current = player;\n\n    return () => {\n      playerRef.current?.destroy();\n      playerRef.current = null;\n    };\n  }, [licenseKey]);\n\n  useEffect(() => {\n    const player = playerRef.current;\n    if (!player) return;\n\n    let cancelled = false;\n\n    (async () => {\n      try {\n        await player.load(source);\n      } catch (error) {\n        if (!cancelled) {\n          console.error('Bitmovin load failed', error);\n        }\n      }\n    })();\n\n    return () => {\n      cancelled = true;\n    };\n  }, [source]);\n\n  return \u003Cdiv ref={containerRef} \u002F>;\n}\n```\n\n**Important:** Create the player once, then load new sources separately. Do not key the effect off `source.hls || source.dash || source.progressive` because that misses other source changes such as DRM, subtitles, poster, start offset, or analytics metadata.\n\n### Next.js\n\nThe player touches `window`\u002F`document` on import — it cannot SSR. Use dynamic import:\n\n```tsx\nimport dynamic from 'next\u002Fdynamic';\n\nconst BitmovinPlayer = dynamic(() => import('.\u002FBitmovinPlayer'), { ssr: false });\n```\n\n### Vue\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv ref=\"container\" \u002F>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\n  import { ref, onMounted, onUnmounted } from 'vue';\n  import { Player } from 'bitmovin-player';\n\n  const container = ref(null);\n  let player = null;\n\n  onMounted(async () => {\n    player = new Player(container.value, { key: 'YOUR_KEY' });\n    await player.load({ hls: 'https:\u002F\u002Fexample.com\u002Fstream.m3u8' });\n  });\n\n  onUnmounted(() => player?.destroy());\n\u003C\u002Fscript>\n```\n\n## Common mistakes\n\n1. **Assuming manual `UIFactory.buildUI()` is required on every v8 integration** — On current Web SDK releases, standard setups can use the default UI integration without manual wiring.\n\n2. **Using `UIFactory` or `UIManager` directly without disabling built-in UI handling** — If you wire the UI yourself, set `ui: false` in the player config so the player does not also try to manage the default UI.\n\n3. **Disabling the UI without attaching one** — If you set `ui: false`, the player will not create a default UI for you.\n\n4. **Not destroying on unmount** — The player creates DOM elements and event listeners. Always call `player.destroy()` when removing the player.\n\n5. **Autoplay without muted** — Browsers block unmuted autoplay. Set `playback: { autoplay: true, muted: true }` or handle the `play()` promise rejection.\n\n6. **SSR importing** — The player SDK accesses `window` and `document` at import time. Always use dynamic imports with `ssr: false` in Next.js\u002FNuxt.\n\n7. **Missing explicit UI assets when pinning or customizing the UI** — If you override `location.ui` \u002F `location.ui_css` or wire the UI package manually, load the matching JS and CSS assets together.\n\n8. **Loading multiple sources without awaiting** — `player.load()` returns a promise. Await it before calling `load()` again or querying state.\n\n9. **Hardcoding the license key** — Use environment variables. The key is exposed to the browser (client-side SDK) but shouldn't be in source control.\n\n## Modular builds\n\nReference: https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-modules.md\n\nFor smaller bundles, import only the modules you need:\n\n```typescript\nimport { Player } from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-core';\nimport EngineBitmovinModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-engine-bitmovin';\nimport MseRendererModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-mserenderer';\nimport HlsModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-hls';\nimport AbrModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-abr';\nimport ContainerTSModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-container-ts';\nimport ContainerMP4Module from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-container-mp4';\nimport PolyfillModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-polyfill';\n\n[EngineBitmovinModule, MseRendererModule, HlsModule, AbrModule,\n  ContainerTSModule, ContainerMP4Module, PolyfillModule]\n  .forEach(m => Player.addModule(m));\n```\n\nThis gives you HLS playback at ~1.2MB instead of 2.2MB. Add `DashModule`, `DrmModule`, etc. as needed.\n\nIf you use a modular build at all — meaning you import at least `Player` from `bitmovin-player\u002Fmodules\u002Fbitmovinplayer-core` — then do **not** import anything from `bitmovin-player` directly anywhere in that bundle. Keep all Bitmovin player imports on the `bitmovin-player\u002Fmodules\u002F*` path. Mixing modular imports with `bitmovin-player` can cause both the modular modules and the full player build to end up in the application bundle, defeating the bundle-size benefit.\n\n## TVs & gaming consoles\n\nWhen targeting TV or console platforms, check whether Bitmovin provides platform-specific modules and add them explicitly where needed. Some platform integrations are not part of the generic full player build and are expected to be included separately for the relevant target platform. Examples include Samsung Tizen TVs, LG webOS TVs, and PlayStation 5. If the user is building for one of these environments, verify the required platform module set before proposing the final integration.\n\n## Test streams\n\nUse these public streams for development and testing:\n\n| Stream | URL | Type |\n|--------|-----|------|\n| Sintel (HLS) | `https:\u002F\u002Fbitmovin-a.akamaihd.net\u002Fcontent\u002Fsintel\u002Fhls\u002Fplaylist.m3u8` | HLS |\n| Sintel (DASH) | `https:\u002F\u002Fbitmovin-a.akamaihd.net\u002Fcontent\u002Fsintel\u002Fsintel.mpd` | DASH |\n| Art of Motion (DASH) | `https:\u002F\u002Fbitmovin-a.akamaihd.net\u002Fcontent\u002FMI201109210084_1\u002Fmpds\u002Ff08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd` | DASH |\n| Big Buck Bunny (MP4) | `https:\u002F\u002Fcommondatastorage.googleapis.com\u002Fgtv-videos-bucket\u002Fsample\u002FBigBuckBunny.mp4` | Progressive |\n| Tears of Steel (HLS) | `https:\u002F\u002Fdemo.unified-streaming.com\u002Fk8s\u002Ffeatures\u002Fstable\u002Fvideo\u002Ftears-of-steel\u002Ftears-of-steel.ism\u002F.m3u8` | HLS |\n\n## Reference links (v8)\n\n- **Player Config:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-player-config.md\n- **Source Config:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-source-config.md\n- **Player API:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-player-api.md\n- **Getting Started Guide:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fgetting-started-web\n- **Web Release Notes:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Frelease-notes-web\n- **What's New in UI v4:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fwhats-new-in-ui-v4\n- **npm package:** https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fbitmovin-player\n- **UI package:** https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fbitmovin-player-ui\n- **GitHub samples:** https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fbitmovin-player-web-samples\n\n---\n\n# Player Web X \u002F PWX (next-generation)\n\nPlayer Web X is Bitmovin's next-generation web player built on the **Phoenix Framework** — a from-scratch architecture with structured concurrency, an effect system, and a package-first design. It is modular, extensible, and produces smaller bundles than v8.\n\n**Last verified:** 2026-07-02 against PWX 10.2.0 (getting started guide, v8 compatibility guide, support matrix, and package docs).\n\n**Status:** PWX is in active development and its capability surface is volatile. Use v8 for production by default unless the user explicitly needs PWX's package architecture or is validating PWX itself.\n\n## What works in PWX today\n\n- HLS playback\n- DASH playback (partially supported)\n- Progressive playback (played by the native `\u003Cvideo>` element, e.g. MP4)\n- Multi-technology sources: a single source can list several resources of different types (`hls`\u002F`dash`\u002F`progressive`) and PWX selects the one it can play in the current environment\n- Sources API \u002F multi-source workflows\n- Load control and view mode packages\n- WebVTT subtitles\n- Advertising: VAST ad scheduling (VMAP is experimental)\n- Optional analytics package support\n- v8 API compatibility layer (partial)\n\n## What does NOT work yet in PWX\n\n- DRM is still incomplete enough that you should verify the live docs before promising it\n- Stable VMAP advertising — VMAP handling is experimental; VAST is the stable path\n- Smooth Streaming — not supported\n- WebRTC — not supported\n- Network API support is currently marked unsupported in the official support matrix\n- Quality and playback APIs are still marked `Next` in the official support matrix\n- Some v8 compatibility APIs are NOPs\n\n## Installation\n\n```bash\nnpm install @bitmovin\u002Fplayer-web-x\n```\n\n### CDN bundles\n\n```html\n\u003C!-- HLS bundle (most common) -->\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@bitmovin\u002Fplayer-web-x@10\u002Fbundles\u002Fplayerx-hls.js\">\u003C\u002Fscript>\n\n\u003C!-- DASH bundle (preliminary) -->\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@bitmovin\u002Fplayer-web-x@10\u002Fbundles\u002Fplayerx-dash.js\">\u003C\u002Fscript>\n\n\u003C!-- v8 compatibility bundle (use v8 API with PWX engine) -->\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@bitmovin\u002Fplayer-web-x@10\u002Fbundles\u002Fplayerx-bitmovin-v8.js\">\u003C\u002Fscript>\n```\n\n**These are UMD bundles — NOT ES modules.** Load via `\u003Cscript src>`, not `import`. Global namespace attachments:\n\n| Bundle | Global |\n|--------|--------|\n| `playerx-hls.js`, `playerx-dash.js`, `playerx-core.js` | `window.bitmovin.playerx.Player` |\n| `playerx-bitmovin-v8.js`, `playerx-bitmovin-v8-core.js` | `window.bitmovin.player.Player` (⚠️ overwrites v8!) |\n\nFor ES module `import` syntax, install via npm (`@bitmovin\u002Fplayer-web-x`) and use a bundler.\n\n## Basic integration (native PWX API)\n\nThe PWX API is **fundamentally different from v8**. Do not mix them. Minimum working example:\n\n```typescript\nimport { Player } from '@bitmovin\u002Fplayer-web-x\u002Fbundles\u002Fplayerx-hls';\n\u002F\u002F CDN: const { Player } = window.bitmovin.playerx;\n\nconst player = Player({\n  key: 'YOUR_LICENSE_KEY',\n  defaultContainer: document.getElementById('player'),\n});\n\n\u002F\u002F sources.add() returns a source HANDLE synchronously — it is NOT a promise.\n\u002F\u002F Do not `await` it. The handle is where playback control lives.\n\u002F\u002F Every resource requires a `type` (as of PWX 10.2.0): 'hls' | 'dash' | 'progressive'.\nconst source = player.sources.add({\n  resources: [{ url: 'https:\u002F\u002Fexample.com\u002Fstream.m3u8', type: 'hls' }],\n});\n\n\u002F\u002F Trigger playback via the source, not the player. Autoplay config is unreliable;\n\u002F\u002F call source.play() explicitly (muted so browsers allow it).\nsource.play({ isMuted: true });\n\n\u002F\u002F Listen for first-frame \u002F state changes on the SOURCE, not the player.\nsource.events.on('playing', () => console.log('first frame rendered'));\n\n\u002F\u002F Teardown\nplayer.dispose();  \u002F\u002F NOT player.destroy() — that's a v8 name\n```\n\n### Critical differences from v8 — read this before coding\n\n| Concern | v8 | PWX (native) |\n|---------|----|-----|\n| Construct player | `new Player(container, config)` | `Player({ defaultContainer, ...config })` (no `new`) |\n| Load a stream | `player.load({ hls: url })` returns a Promise | `player.sources.add({ resources: [{ url, type }] })` returns a source handle synchronously |\n| Play \u002F pause | `player.play()`, `player.pause()` | `source.play({ isMuted: true })`, `source.pause()` — lives on the source handle, NOT the player |\n| Subscribe to events | `player.on('playing', fn)` | `source.events.on('playing', fn)` OR `player.events.on(...)` — the player instance has no `.on()` |\n| Dispatch commands | N\u002FA | `player.events.dispatch({ type: 'play', ... })` (event-driven architecture) |\n| Autoplay config | `playback: { autoplay: true, muted: true }` works | Config does not autoplay — must call `source.play()` |\n| Teardown | `player.destroy()` | `player.dispose()` |\n| Player instance surface | rich API: play, pause, seek, on, load, destroy, ... | minimal: `{ packages, events, sources, dispose }` |\n\n**DO NOT** call `play()` on the underlying `\u003Cvideo>` element to work around missing playback. That races the player's internal start flow and produces \"play() request was interrupted by a new load request.\" Always use `source.play()`.\n\n### PWX config\n\n- `key` — license key (same as v8)\n- `defaultContainer` — DOM element to mount into (v8 passes this as a constructor arg; PWX puts it in config)\n- `playback.muted`, `playback.autoplay` — present in config, but unreliable in current PWX. Prefer explicit `source.play({ isMuted: true })`.\n\n**Resources and `type` (PWX 10.2.0+):** a source's `resources` array replaces v8's `{ hls, dash, progressive }` shortcut. Every resource requires a `type` — `'hls'`, `'dash'`, or `'progressive'` — which routes it to the right technology. Listing multiple resources of different types lets PWX pick the one playable in the current environment (see multi-technology support), but a type only resolves if the bundle includes its technology (`Hls` bundle handles `hls`, `Dash` handles `dash`, `Playback`\u002F`BitmovinV8` handle both; progressive works in any playback bundle).\n\n## Available bundles\n\n| Bundle | Description | Use when |\n|--------|-------------|----------|\n| `playerx-hls.js` | HLS playback (TS + fMP4) | Most common use case |\n| `playerx-dash.js` | DASH playback (fMP4, preliminary) | DASH-only content |\n| `playerx-core.js` | Core only, add packages manually | Custom\u002Fminimal builds |\n| `playerx-bitmovin-v8.js` | HLS + v8 API compatibility layer | Migrating from v8 |\n| `playerx-bitmovin-v8-core.js` | Core + v8 API base compatibility | Custom v8-compat builds |\n\n## Using the v8 compatibility layer\n\nIf you want the familiar v8 API (`new Player()`, `player.load()`, etc.) with the PWX engine:\n\n```html\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@bitmovin\u002Fplayer-web-x@10\u002Fbundles\u002Fplayerx-bitmovin-v8.js\">\u003C\u002Fscript>\n\u003Cscript>\n  \u002F\u002F Same API as v8!\n  const player = new bitmovin.player.Player(\n    document.getElementById('player'),\n    {\n      key: 'YOUR_KEY',\n      playback: { autoplay: true, muted: true },\n      location: {\n        ui: 'https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fjs\u002Fbitmovinplayer-ui.js',\n        ui_css: 'https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fcss\u002Fbitmovinplayer-ui.css',\n      },\n    }\n  );\n  player.load({ hls: 'https:\u002F\u002Fexample.com\u002Fstream.m3u8' });\n\u003C\u002Fscript>\n```\n\nThis mirrors the current official v8-compat documentation more closely than manually calling `UIFactory.buildUI()`. Prefer the documented `location.ui` \u002F `location.ui_css` path unless you have a specific reason to own the UI bootstrap yourself.\n\n**Caveat:** Not all v8 APIs are implemented. Some methods are NOPs pending PWX feature completion. Check the live [support matrix](https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-support-matrix) before relying on specific features.\n\n### Loading v8 AND PWX v8-compat on the same page\n\nBoth bundles attach to `window.bitmovin.player.Player` — whichever loads second wins. To run both side-by-side (e.g. an A\u002FB demo), capture each reference between script loads:\n\n```html\n\u003C!-- 1) Load v8 -->\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player@8\u002Fbitmovinplayer.js\">\u003C\u002Fscript>\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fjs\u002Fbitmovinplayer-ui.js\">\u003C\u002Fscript>\n\u003Cscript>\n  window.V8Player = window.bitmovin.player.Player;      \u002F\u002F capture v8\n\u003C\u002Fscript>\n\n\u003C!-- 2) Load PWX v8-compat (this OVERWRITES window.bitmovin.player.Player) -->\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@bitmovin\u002Fplayer-web-x@10\u002Fbundles\u002Fplayerx-bitmovin-v8.js\">\u003C\u002Fscript>\n\u003Cscript>\n  window.PwxPlayer = window.bitmovin.player.Player;     \u002F\u002F capture PWX v8-compat\n\u003C\u002Fscript>\n\n\u003Cscript>\n  const v8 = new window.V8Player(v8Container, { key, ui: false });\n  const pwx = new window.PwxPlayer(pwxContainer, { key, ui: false });\n  bitmovin.playerui.UIFactory.buildUI(v8);\n  bitmovin.playerui.UIFactory.buildUI(pwx);             \u002F\u002F same UI on both\n  v8.load({ hls: url });\n  pwx.load({ hls: url });\n\u003C\u002Fscript>\n```\n\n`window.bitmovin.playerui` is a separate namespace from a separate script — it is not affected by the collision.\n\n## Measuring PWX vs v8 startup time\n\nBoth v8 and the PWX v8-compat bundle emit a `'playing'` event when the first frame renders. Measure time-to-first-frame like this:\n\n```typescript\nasync function measureStartup(player, source) {\n  const firstFrame = new Promise(resolve => player.on('playing', () => resolve(performance.now())));\n  const t0 = performance.now();\n  await player.load(source);\n  const t1 = await firstFrame;\n  return t1 - t0;  \u002F\u002F startup in ms\n}\n```\n\nRun both players in parallel (so network contention is equal) or sequentially (for isolated numbers) depending on what you're benchmarking. Do **not** hardcode an expected delta into your guidance: startup results vary significantly by browser, device, stream packaging, cache state, and SDK version.\n\n## Custom packages\n\nPWX's killer feature is its package system. You can extend, replace, or add functionality:\n\n```typescript\nimport { Player } from '@bitmovin\u002Fplayer-web-x';\n\nconst player = Player({\n  key: 'YOUR_KEY',\n  defaultContainer: document.getElementById('player'),\n});\n\n\u002F\u002F Add packages for the features you need\nplayer.packages.add(hlsPackage);\nplayer.packages.add(adaptationPackage);\nplayer.packages.add(myCustomPackage);\n```\n\nSee [Creating packages](https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-creating-packages) for the package authoring guide.\n\n## Advertising (PWX)\n\nClient-side ads are available in the `Playback` and `BitmovinV8` bundles out of the box (or by adding `BitmovinAdvertisingBundlePackage` to a custom bundle). Ads are scheduled **per source**, through the `ads` namespace on the source handle — this is different from v8's `advertising.adBreaks` player config.\n\n```typescript\nsource.ads.schedule({\n  tag: { type: 'vast', url: 'https:\u002F\u002Fyour-ad-server\u002Fvast.xml' },\n  position: 'pre', \u002F\u002F 'pre' | 'start' | 'post' | 'end' | seconds (number) | '50%'\n});\n```\n\nVAST 4.x is the stable path; **VMAP support is experimental** — prefer VAST for production. Ad lifecycle events (`ad-started`, `ad-finished`, `ad-quartile`, `ad-skipped`, ...) are delivered on `source.events`, and the active ad is available via `source.ads.getActiveAd()`.\n\n## Reference links (PWX)\n\n- **About PWX:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fabout-player-web-x\n- **Getting Started:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-getting-started\n- **Bundles & Packages:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-bundles-packages\n- **v8 Compatibility:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-v8-compatibility\n- **Support Matrix:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-support-matrix\n- **Features:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-features\n- **Release Notes:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-release-notes\n- **API Docs (CDN):** https:\u002F\u002Fcdn.bitmovin.com\u002Fplayer\u002Fweb_x\u002Fbeta\u002F10\u002Fdocs\u002Findex.html\n- **npm package:** https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@bitmovin\u002Fplayer-web-x\n","# Bitmovin Web Player Integration Skill\n\nYou are an expert at integrating the Bitmovin Web Player SDK. When the user asks you to add video playback, embed a player, integrate streaming, customize the Player UI, or work with the Bitmovin Player — use this skill.\n\n## When to activate\n\n- User asks to add video playback to a web app\n- User mentions Bitmovin Player, `bitmovin-player`, or streaming integration\n- User needs HLS, DASH, Smooth, WHEP, MOQ, or DRM playback in a browser\n- User wants to customize player UI, add ads, subtitles, analytics, or other player integrations\n\n## Choose the player version deliberately\n\n**Default to Player Web v8** unless the user explicitly asks for PWX, is evaluating PWX, or the architecture tradeoff matters.\n\nOnly ask which player they want when the answer changes the implementation, for example:\n- The user mentions PWX, Player Web X, bundles, or custom packages\n- The user wants to benchmark or compare v8 vs PWX\n- The user is migrating an existing PWX proof of concept\n\nRecommended options:\n\n1. **Player Web v8** (`bitmovin-player`) — the stable production default. Supports HLS, DASH, Smooth, WHEP, DRM (Widevine\u002FPlayReady\u002FFairPlay), ads (VAST\u002FVMAP\u002FIMA), analytics, subtitles, Chromecast, AirPlay, and is the primary choice for Web Player and Player UI integrations. It also supports a range of 3rd-party integrations such as Yospace SSAI and Conviva Analytics. Mature API, full documentation.\n\n2. **Player Web X \u002F PWX** (`@bitmovin\u002Fplayer-web-x`) — the next-generation modular player. Still evolving and feature-incomplete. Use when the user explicitly wants PWX, wants the package architecture, or is validating PWX-specific behavior. MOQ playback is currently in this version rather than v8.\n\n## Live docs MCP server\n\nFor anything this skill doesn't cover — obscure APIs, recent SDK releases, specific code samples — query the **Bitmovin Docs MCP** instead of guessing:\n\n```\nhttps:\u002F\u002Fagentic.bitmovin.com\u002Fdocumentation\u002Fmcp\n```\n\nIt indexes `developer.bitmovin.com` documentation and the official GitHub sample repositories (`bitmovin-player-web-samples`, `bitmovin-player-ios-samples`, `bitmovin-player-android-samples`, `bitmovin-player-roku-samples`, `bitmovin-api-sdk-examples`).\n\n**When to use it:**\n- You need a working code sample for a specific feature (DRM flavor, custom ad integration, subtitle styling, etc.)\n- The user asks about a feature not documented in this skill\n- You're unsure about a config field or API signature — verify against live docs before writing code\n- The user reports the SDK behavior differs from what this skill describes (SDK may have moved on)\n\nAdd it as an MCP connector in the chat client, or fetch URLs from `developer.bitmovin.com` directly. Prefer the MCP when it's available — it's faster than fetching individual doc pages.\n\n---\n\n# Player Web v8 (stable)\n\n## Installation\n\n```bash\nnpm install bitmovin-player\n```\n\nFor explicit UI customization or version pinning, also install the dedicated UI package:\n\n```bash\nnpm install bitmovin-player-ui\n```\n\nThe main package includes the player runtime and TypeScript types. The dedicated `bitmovin-player-ui` package ships the UI assets and type declarations for explicit UI work.\n\n### CDN alternative\n\n```html\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player@8\u002Fbitmovinplayer.js\">\u003C\u002Fscript>\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fjs\u002Fbitmovinplayer-ui.js\">\u003C\u002Fscript>\n\u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fcss\u002Fbitmovinplayer-ui.css\" \u002F>\n```\n\n**These are UMD bundles that attach to global namespaces — NOT ES modules.** Do not `import` from the CDN URL:\n- Player: `window.bitmovin.player.Player`\n- UI: `window.bitmovin.playerui.UIFactory`\n\nFor ES module `import` syntax, install via npm and use a bundler.\n\n### License key\n\nEvery Bitmovin Player instance needs a license key. `localhost` is auto-allowed; deployed domains must be added to the license allowlist.\n\nGet one of these ways:\n- **Bitmovin Dashboard**: https:\u002F\u002Fdashboard.bitmovin.com → Player → Licenses\n- **Bitmovin CLI** (if installed): `bitmovin player licenses list` — lists all licenses with their keys and allowed domains. `bitmovin player licenses get \u003Cid> --json` for details.\n\n## Basic integration\n\n```typescript\nimport { Player } from 'bitmovin-player';\n\nconst container = document.getElementById('player');\n\nconst player = new Player(container, {\n  key: 'YOUR_LICENSE_KEY',\n  playback: {\n    autoplay: false,\n    muted: false,\n  },\n});\n\n\u002F\u002F Load a source\nawait player.load({\n  hls: 'https:\u002F\u002Fexample.com\u002Fstream.m3u8',\n  title: 'My Video',\n  poster: 'https:\u002F\u002Fexample.com\u002Fposter.jpg',\n});\n```\n\n### UI guidance for current v8 releases\n\n**Last verified:** 2026-04-15 against Bitmovin Web release notes and UI v4 docs.\n\nFor new Web SDK integrations on current v8 releases, **do not assume you must wire `UIFactory` manually**. Since Web `8.245.0` (released 2026-02-09), Bitmovin's default Web UI integration migrated to UI v4 and standard setups can use the default UI without extra wiring.\n\nUse the explicit UI package only when you need one of these:\n- Pin a specific UI version\n- Customize the UI build process\n- Stay on an older player version where you still initialize the UI manually\n- Build a non-default UI variant intentionally\n\n### Explicit or custom UI integration\n\nFor explicit UI wiring, prefer the dedicated `bitmovin-player-ui` package because it ships type declarations.\n\n```typescript\nimport { Player } from 'bitmovin-player';\nimport { UIFactory } from 'bitmovin-player-ui';\nimport 'bitmovin-player-ui\u002Fdist\u002Fcss\u002Fbitmovinplayer-ui.css';\n\nconst player = new Player(document.getElementById('player'), {\n  key: 'YOUR_LICENSE_KEY',\n  ui: false,\n});\n\nUIFactory.buildUI(player);\n```\n\nWhen you want to pin the hosted UI assets explicitly, configure them through `PlayerConfig.location`:\n\n```typescript\nconst player = new Player(document.getElementById('player'), {\n  key: 'YOUR_LICENSE_KEY',\n  location: {\n    ui: 'https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fjs\u002Fbitmovinplayer-ui.js',\n    ui_css: 'https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fcss\u002Fbitmovinplayer-ui.css',\n  },\n});\n```\n\nIf you set `ui: false`, you are responsible for attaching a UI yourself.\n\n## Source configuration\n\nThe source object tells the player what to play. At least one of `hls`, `dash`, `smooth`, or `progressive` is required.\n\n**API reference:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-source-config.md\n\n```typescript\nawait player.load({\n  \u002F\u002F Stream URL — pick one:\n  hls: 'https:\u002F\u002Fcdn.example.com\u002Fstream.m3u8',       \u002F\u002F HLS\n  dash: 'https:\u002F\u002Fcdn.example.com\u002Fstream.mpd',        \u002F\u002F DASH\n  smooth: 'https:\u002F\u002Fcdn.example.com\u002Fstream.ism',      \u002F\u002F Smooth Streaming\n  progressive: 'https:\u002F\u002Fcdn.example.com\u002Fvideo.mp4',  \u002F\u002F Progressive MP4\u002FWebM\n\n  \u002F\u002F Metadata\n  title: 'Video Title',\n  description: 'Video description',\n  poster: 'https:\u002F\u002Fcdn.example.com\u002Fposter.jpg',\n\n  \u002F\u002F Subtitles\n  subtitleTracks: [\n    { url: 'https:\u002F\u002Fcdn.example.com\u002Fsubs_en.vtt', lang: 'en', label: 'English' },\n    { url: 'https:\u002F\u002Fcdn.example.com\u002Fsubs_de.vtt', lang: 'de', label: 'Deutsch' },\n  ],\n\n  \u002F\u002F Thumbnails (for seek preview)\n  thumbnailTrack: { url: 'https:\u002F\u002Fcdn.example.com\u002Fthumbs.vtt' },\n});\n```\n\n## Player configuration\n\nThe config object passed to `new Player(container, config)`.\n\n**API reference:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-player-config.md\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `key` | `string` | **Required.** License key from Bitmovin Dashboard. |\n| `playback.autoplay` | `boolean` | Start playback automatically. Default: `false`. |\n| `playback.muted` | `boolean` | Start muted (required for autoplay in most browsers). |\n| `ui` | `boolean` | Set to `false` when using UIFactory. |\n| `adaptation.startupBitrate` | `string` | Initial quality (e.g. `\"2000kbps\"`). |\n| `network.preprocessHttpRequest` | `function` | Modify requests before they're sent (add tokens, headers). |\n| `network.sendHttpRequest` | `function` | Completely replace the HTTP fetch mechanism. |\n| `buffer.video.forwardduration` | `number` | Forward buffer target in seconds. Default: 30. |\n| `style.width` | `string` | Player width. Default: `'100%'`. |\n| `style.aspectratio` | `string` | Aspect ratio. Default: `'16:9'`. |\n\n## Player API\n\n**API reference:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-player-api.md\n\n```typescript\n\u002F\u002F Playback control\nawait player.load(sourceConfig);\nplayer.play();\nplayer.pause();\nplayer.seek(120);         \u002F\u002F seek to 2:00\nplayer.setVolume(50);     \u002F\u002F 0-100\n\n\u002F\u002F State queries\nplayer.getCurrentTime();  \u002F\u002F seconds\nplayer.getDuration();     \u002F\u002F seconds (Infinity for live)\nplayer.isPlaying();\nplayer.isPaused();\nplayer.getVolume();\n\n\u002F\u002F Quality\nplayer.getAvailableVideoQualities();\nplayer.setVideoQuality('auto');\n\n\u002F\u002F Events\nplayer.on('play', () => { ... });\nplayer.on('pause', () => { ... });\nplayer.on('timechanged', () => { ... });\nplayer.on('error', (e) => { console.error(e.code, e.message); });\nplayer.on('ready', () => { ... });\nplayer.on('sourceloaded', () => { ... });\n\n\u002F\u002F Cleanup\nplayer.destroy();\n```\n\n## DRM (Digital Rights Management)\n\nAdd `drm` to the source config. The player handles license acquisition automatically.\n\n```typescript\nawait player.load({\n  dash: 'https:\u002F\u002Fcdn.example.com\u002Fencrypted.mpd',\n  drm: {\n    widevine: {\n      LA_URL: 'https:\u002F\u002Flicense.example.com\u002Fwidevine',\n      headers: { 'X-Auth': 'token123' },  \u002F\u002F optional\n    },\n    playready: {\n      LA_URL: 'https:\u002F\u002Flicense.example.com\u002Fplayready',\n    },\n    fairplay: {\n      LA_URL: 'https:\u002F\u002Flicense.example.com\u002Ffairplay',\n      certificateURL: 'https:\u002F\u002Flicense.example.com\u002Ffairplay\u002Fcert',\n    },\n  },\n});\n```\n\n**Key points:**\n- Widevine works on Chrome, Firefox, Edge, Android\n- PlayReady works on Edge (Windows), some Smart TVs\n- FairPlay works on Safari (macOS, iOS)\n- Provide multiple DRM configs for cross-browser support — the player auto-selects\n- HLS with FairPlay uses `hls` + `drm.fairplay`; DASH with Widevine uses `dash` + `drm.widevine`\n\n## Advertising\n\nSupports VAST, VMAP, and IMA SDK integration.\n\nAt minimum, configure `advertising: {}` if you want ad support enabled up front but plan to schedule ads dynamically later via `player.ads.schedule(...)`.\n\nBy default, the Google IMA SDK is used for ad playback. That is often the best choice when Google Ad Manager \u002F Google Ad Server is involved because it unlocks capabilities such as programmatic demand and ActiveView measurement. If you integrate with non-Google ad servers, consider switching to the Bitmovin Advertising Module (BAM) by importing `bitmovin-player\u002Fmodules\u002Fbitmovinplayer-advertising-bitmovin.js` and registering that module with the player. BAM can be preferable when you want Bitmovin's full ads UI and ads UI customization options.\n\n```typescript\nconst player = new Player(container, {\n  key: 'YOUR_KEY',\n  advertising: {\n    adBreaks: [\n      {\n        tag: { url: 'https:\u002F\u002Fexample.com\u002Fvast-preroll.xml', type: 'vast' },\n        position: 'pre',\n      },\n      {\n        tag: { url: 'https:\u002F\u002Fexample.com\u002Fvast-midroll.xml', type: 'vast' },\n        position: '50%',  \u002F\u002F midroll at 50%; other supported formats include time-based values like '90'\n      },\n      {\n        tag: { url: 'https:\u002F\u002Fexample.com\u002Fvast-postroll.xml', type: 'vast' },\n        position: 'post',\n      },\n    ],\n  },\n});\n```\n\nFor VMAP (server-defined ad schedule):\n```typescript\nadvertising: {\n  adBreaks: [\n    { tag: { url: 'https:\u002F\u002Fexample.com\u002Fvmap.xml', type: 'vmap' }, position: 'pre' },\n  ],\n}\n```\n\nFor mid-rolls, `position` supports multiple string formats, not just percentages. Common examples include percentage-based values such as `'50%'`, time-based values such as `'90'` for 1m30s, and other formats documented here: https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-advertising#position\n\n## Analytics\n\nBitmovin Analytics is bundled with the player. Enable by providing an analytics license key:\n\n```typescript\nconst player = new Player(container, {\n  key: 'YOUR_PLAYER_KEY',\n  analytics: {\n    key: 'YOUR_ANALYTICS_KEY',\n    title: 'My Video',\n    videoId: 'video-123',\n    customData1: 'category-sports',\n  },\n});\n```\n\nSet `analytics: false` to disable.\n\n## Network customization\n\n### Adding auth tokens to requests\n\n```typescript\nconst player = new Player(container, {\n  key: 'YOUR_KEY',\n  network: {\n    preprocessHttpRequest: (type, request) => {\n      if (type.startsWith('media\u002F') || type.startsWith('manifest\u002F')) {\n        request.headers['Authorization'] = 'Bearer ' + getToken();\n      }\n      return Promise.resolve(request);\n    },\n  },\n});\n```\n\n### Completely replacing the network layer\n\nUse `sendHttpRequest` to intercept ALL HTTP requests. Useful for proxying, offline playback, or running in restricted environments (like MCP App sandboxes).\n\n```typescript\nnetwork: {\n  sendHttpRequest: (type, request) => {\n    const isText = type.startsWith('manifest\u002F');\n    return {\n      getResponse: async () => {\n        const resp = await myCustomFetch(request.url);\n        return {\n          request,\n          url: request.url,\n          headers: {},\n          status: 200,\n          statusText: 'OK',\n          body: isText ? await resp.text() : await resp.arrayBuffer(),\n        };\n      },\n      setProgressListener: () => {},\n      cancel: () => {},\n    };\n  },\n}\n```\n\n## Framework integration patterns\n\n### React\n\n```tsx\nimport { useEffect, useRef } from 'react';\nimport { Player } from 'bitmovin-player';\n\nfunction BitmovinPlayer({ source, licenseKey }) {\n  const containerRef = useRef\u003CHTMLDivElement | null>(null);\n  const playerRef = useRef\u003CPlayer | null>(null);\n\n  useEffect(() => {\n    if (!containerRef.current) return;\n\n    const player = new Player(containerRef.current, {\n      key: licenseKey,\n    });\n    playerRef.current = player;\n\n    return () => {\n      playerRef.current?.destroy();\n      playerRef.current = null;\n    };\n  }, [licenseKey]);\n\n  useEffect(() => {\n    const player = playerRef.current;\n    if (!player) return;\n\n    let cancelled = false;\n\n    (async () => {\n      try {\n        await player.load(source);\n      } catch (error) {\n        if (!cancelled) {\n          console.error('Bitmovin load failed', error);\n        }\n      }\n    })();\n\n    return () => {\n      cancelled = true;\n    };\n  }, [source]);\n\n  return \u003Cdiv ref={containerRef} \u002F>;\n}\n```\n\n**Important:** Create the player once, then load new sources separately. Do not key the effect off `source.hls || source.dash || source.progressive` because that misses other source changes such as DRM, subtitles, poster, start offset, or analytics metadata.\n\n### Next.js\n\nThe player touches `window`\u002F`document` on import — it cannot SSR. Use dynamic import:\n\n```tsx\nimport dynamic from 'next\u002Fdynamic';\n\nconst BitmovinPlayer = dynamic(() => import('.\u002FBitmovinPlayer'), { ssr: false });\n```\n\n### Vue\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv ref=\"container\" \u002F>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\n  import { ref, onMounted, onUnmounted } from 'vue';\n  import { Player } from 'bitmovin-player';\n\n  const container = ref(null);\n  let player = null;\n\n  onMounted(async () => {\n    player = new Player(container.value, { key: 'YOUR_KEY' });\n    await player.load({ hls: 'https:\u002F\u002Fexample.com\u002Fstream.m3u8' });\n  });\n\n  onUnmounted(() => player?.destroy());\n\u003C\u002Fscript>\n```\n\n## Common mistakes\n\n1. **Assuming manual `UIFactory.buildUI()` is required on every v8 integration** — On current Web SDK releases, standard setups can use the default UI integration without manual wiring.\n\n2. **Using `UIFactory` or `UIManager` directly without disabling built-in UI handling** — If you wire the UI yourself, set `ui: false` in the player config so the player does not also try to manage the default UI.\n\n3. **Disabling the UI without attaching one** — If you set `ui: false`, the player will not create a default UI for you.\n\n4. **Not destroying on unmount** — The player creates DOM elements and event listeners. Always call `player.destroy()` when removing the player.\n\n5. **Autoplay without muted** — Browsers block unmuted autoplay. Set `playback: { autoplay: true, muted: true }` or handle the `play()` promise rejection.\n\n6. **SSR importing** — The player SDK accesses `window` and `document` at import time. Always use dynamic imports with `ssr: false` in Next.js\u002FNuxt.\n\n7. **Missing explicit UI assets when pinning or customizing the UI** — If you override `location.ui` \u002F `location.ui_css` or wire the UI package manually, load the matching JS and CSS assets together.\n\n8. **Loading multiple sources without awaiting** — `player.load()` returns a promise. Await it before calling `load()` again or querying state.\n\n9. **Hardcoding the license key** — Use environment variables. The key is exposed to the browser (client-side SDK) but shouldn't be in source control.\n\n## Modular builds\n\nReference: https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-modules.md\n\nFor smaller bundles, import only the modules you need:\n\n```typescript\nimport { Player } from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-core';\nimport EngineBitmovinModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-engine-bitmovin';\nimport MseRendererModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-mserenderer';\nimport HlsModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-hls';\nimport AbrModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-abr';\nimport ContainerTSModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-container-ts';\nimport ContainerMP4Module from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-container-mp4';\nimport PolyfillModule from 'bitmovin-player\u002Fmodules\u002Fbitmovinplayer-polyfill';\n\n[EngineBitmovinModule, MseRendererModule, HlsModule, AbrModule,\n  ContainerTSModule, ContainerMP4Module, PolyfillModule]\n  .forEach(m => Player.addModule(m));\n```\n\nThis gives you HLS playback at ~1.2MB instead of 2.2MB. Add `DashModule`, `DrmModule`, etc. as needed.\n\nIf you use a modular build at all — meaning you import at least `Player` from `bitmovin-player\u002Fmodules\u002Fbitmovinplayer-core` — then do **not** import anything from `bitmovin-player` directly anywhere in that bundle. Keep all Bitmovin player imports on the `bitmovin-player\u002Fmodules\u002F*` path. Mixing modular imports with `bitmovin-player` can cause both the modular modules and the full player build to end up in the application bundle, defeating the bundle-size benefit.\n\n## TVs & gaming consoles\n\nWhen targeting TV or console platforms, check whether Bitmovin provides platform-specific modules and add them explicitly where needed. Some platform integrations are not part of the generic full player build and are expected to be included separately for the relevant target platform. Examples include Samsung Tizen TVs, LG webOS TVs, and PlayStation 5. If the user is building for one of these environments, verify the required platform module set before proposing the final integration.\n\n## Test streams\n\nUse these public streams for development and testing:\n\n| Stream | URL | Type |\n|--------|-----|------|\n| Sintel (HLS) | `https:\u002F\u002Fbitmovin-a.akamaihd.net\u002Fcontent\u002Fsintel\u002Fhls\u002Fplaylist.m3u8` | HLS |\n| Sintel (DASH) | `https:\u002F\u002Fbitmovin-a.akamaihd.net\u002Fcontent\u002Fsintel\u002Fsintel.mpd` | DASH |\n| Art of Motion (DASH) | `https:\u002F\u002Fbitmovin-a.akamaihd.net\u002Fcontent\u002FMI201109210084_1\u002Fmpds\u002Ff08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd` | DASH |\n| Big Buck Bunny (MP4) | `https:\u002F\u002Fcommondatastorage.googleapis.com\u002Fgtv-videos-bucket\u002Fsample\u002FBigBuckBunny.mp4` | Progressive |\n| Tears of Steel (HLS) | `https:\u002F\u002Fdemo.unified-streaming.com\u002Fk8s\u002Ffeatures\u002Fstable\u002Fvideo\u002Ftears-of-steel\u002Ftears-of-steel.ism\u002F.m3u8` | HLS |\n\n## Reference links (v8)\n\n- **Player Config:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-player-config.md\n- **Source Config:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-source-config.md\n- **Player API:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Freference\u002Fweb-sdk-player-api.md\n- **Getting Started Guide:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fgetting-started-web\n- **Web Release Notes:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Frelease-notes-web\n- **What's New in UI v4:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fwhats-new-in-ui-v4\n- **npm package:** https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fbitmovin-player\n- **UI package:** https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fbitmovin-player-ui\n- **GitHub samples:** https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fbitmovin-player-web-samples\n\n---\n\n# Player Web X \u002F PWX (next-generation)\n\nPlayer Web X is Bitmovin's next-generation web player built on the **Phoenix Framework** — a from-scratch architecture with structured concurrency, an effect system, and a package-first design. It is modular, extensible, and produces smaller bundles than v8.\n\n**Last verified:** 2026-07-02 against PWX 10.2.0 (getting started guide, v8 compatibility guide, support matrix, and package docs).\n\n**Status:** PWX is in active development and its capability surface is volatile. Use v8 for production by default unless the user explicitly needs PWX's package architecture or is validating PWX itself.\n\n## What works in PWX today\n\n- HLS playback\n- DASH playback (partially supported)\n- Progressive playback (played by the native `\u003Cvideo>` element, e.g. MP4)\n- Multi-technology sources: a single source can list several resources of different types (`hls`\u002F`dash`\u002F`progressive`) and PWX selects the one it can play in the current environment\n- Sources API \u002F multi-source workflows\n- Load control and view mode packages\n- WebVTT subtitles\n- Advertising: VAST ad scheduling (VMAP is experimental)\n- Optional analytics package support\n- v8 API compatibility layer (partial)\n\n## What does NOT work yet in PWX\n\n- DRM is still incomplete enough that you should verify the live docs before promising it\n- Stable VMAP advertising — VMAP handling is experimental; VAST is the stable path\n- Smooth Streaming — not supported\n- WebRTC — not supported\n- Network API support is currently marked unsupported in the official support matrix\n- Quality and playback APIs are still marked `Next` in the official support matrix\n- Some v8 compatibility APIs are NOPs\n\n## Installation\n\n```bash\nnpm install @bitmovin\u002Fplayer-web-x\n```\n\n### CDN bundles\n\n```html\n\u003C!-- HLS bundle (most common) -->\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@bitmovin\u002Fplayer-web-x@10\u002Fbundles\u002Fplayerx-hls.js\">\u003C\u002Fscript>\n\n\u003C!-- DASH bundle (preliminary) -->\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@bitmovin\u002Fplayer-web-x@10\u002Fbundles\u002Fplayerx-dash.js\">\u003C\u002Fscript>\n\n\u003C!-- v8 compatibility bundle (use v8 API with PWX engine) -->\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@bitmovin\u002Fplayer-web-x@10\u002Fbundles\u002Fplayerx-bitmovin-v8.js\">\u003C\u002Fscript>\n```\n\n**These are UMD bundles — NOT ES modules.** Load via `\u003Cscript src>`, not `import`. Global namespace attachments:\n\n| Bundle | Global |\n|--------|--------|\n| `playerx-hls.js`, `playerx-dash.js`, `playerx-core.js` | `window.bitmovin.playerx.Player` |\n| `playerx-bitmovin-v8.js`, `playerx-bitmovin-v8-core.js` | `window.bitmovin.player.Player` (⚠️ overwrites v8!) |\n\nFor ES module `import` syntax, install via npm (`@bitmovin\u002Fplayer-web-x`) and use a bundler.\n\n## Basic integration (native PWX API)\n\nThe PWX API is **fundamentally different from v8**. Do not mix them. Minimum working example:\n\n```typescript\nimport { Player } from '@bitmovin\u002Fplayer-web-x\u002Fbundles\u002Fplayerx-hls';\n\u002F\u002F CDN: const { Player } = window.bitmovin.playerx;\n\nconst player = Player({\n  key: 'YOUR_LICENSE_KEY',\n  defaultContainer: document.getElementById('player'),\n});\n\n\u002F\u002F sources.add() returns a source HANDLE synchronously — it is NOT a promise.\n\u002F\u002F Do not `await` it. The handle is where playback control lives.\n\u002F\u002F Every resource requires a `type` (as of PWX 10.2.0): 'hls' | 'dash' | 'progressive'.\nconst source = player.sources.add({\n  resources: [{ url: 'https:\u002F\u002Fexample.com\u002Fstream.m3u8', type: 'hls' }],\n});\n\n\u002F\u002F Trigger playback via the source, not the player. Autoplay config is unreliable;\n\u002F\u002F call source.play() explicitly (muted so browsers allow it).\nsource.play({ isMuted: true });\n\n\u002F\u002F Listen for first-frame \u002F state changes on the SOURCE, not the player.\nsource.events.on('playing', () => console.log('first frame rendered'));\n\n\u002F\u002F Teardown\nplayer.dispose();  \u002F\u002F NOT player.destroy() — that's a v8 name\n```\n\n### Critical differences from v8 — read this before coding\n\n| Concern | v8 | PWX (native) |\n|---------|----|-----|\n| Construct player | `new Player(container, config)` | `Player({ defaultContainer, ...config })` (no `new`) |\n| Load a stream | `player.load({ hls: url })` returns a Promise | `player.sources.add({ resources: [{ url, type }] })` returns a source handle synchronously |\n| Play \u002F pause | `player.play()`, `player.pause()` | `source.play({ isMuted: true })`, `source.pause()` — lives on the source handle, NOT the player |\n| Subscribe to events | `player.on('playing', fn)` | `source.events.on('playing', fn)` OR `player.events.on(...)` — the player instance has no `.on()` |\n| Dispatch commands | N\u002FA | `player.events.dispatch({ type: 'play', ... })` (event-driven architecture) |\n| Autoplay config | `playback: { autoplay: true, muted: true }` works | Config does not autoplay — must call `source.play()` |\n| Teardown | `player.destroy()` | `player.dispose()` |\n| Player instance surface | rich API: play, pause, seek, on, load, destroy, ... | minimal: `{ packages, events, sources, dispose }` |\n\n**DO NOT** call `play()` on the underlying `\u003Cvideo>` element to work around missing playback. That races the player's internal start flow and produces \"play() request was interrupted by a new load request.\" Always use `source.play()`.\n\n### PWX config\n\n- `key` — license key (same as v8)\n- `defaultContainer` — DOM element to mount into (v8 passes this as a constructor arg; PWX puts it in config)\n- `playback.muted`, `playback.autoplay` — present in config, but unreliable in current PWX. Prefer explicit `source.play({ isMuted: true })`.\n\n**Resources and `type` (PWX 10.2.0+):** a source's `resources` array replaces v8's `{ hls, dash, progressive }` shortcut. Every resource requires a `type` — `'hls'`, `'dash'`, or `'progressive'` — which routes it to the right technology. Listing multiple resources of different types lets PWX pick the one playable in the current environment (see multi-technology support), but a type only resolves if the bundle includes its technology (`Hls` bundle handles `hls`, `Dash` handles `dash`, `Playback`\u002F`BitmovinV8` handle both; progressive works in any playback bundle).\n\n## Available bundles\n\n| Bundle | Description | Use when |\n|--------|-------------|----------|\n| `playerx-hls.js` | HLS playback (TS + fMP4) | Most common use case |\n| `playerx-dash.js` | DASH playback (fMP4, preliminary) | DASH-only content |\n| `playerx-core.js` | Core only, add packages manually | Custom\u002Fminimal builds |\n| `playerx-bitmovin-v8.js` | HLS + v8 API compatibility layer | Migrating from v8 |\n| `playerx-bitmovin-v8-core.js` | Core + v8 API base compatibility | Custom v8-compat builds |\n\n## Using the v8 compatibility layer\n\nIf you want the familiar v8 API (`new Player()`, `player.load()`, etc.) with the PWX engine:\n\n```html\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@bitmovin\u002Fplayer-web-x@10\u002Fbundles\u002Fplayerx-bitmovin-v8.js\">\u003C\u002Fscript>\n\u003Cscript>\n  \u002F\u002F Same API as v8!\n  const player = new bitmovin.player.Player(\n    document.getElementById('player'),\n    {\n      key: 'YOUR_KEY',\n      playback: { autoplay: true, muted: true },\n      location: {\n        ui: 'https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fjs\u002Fbitmovinplayer-ui.js',\n        ui_css: 'https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fcss\u002Fbitmovinplayer-ui.css',\n      },\n    }\n  );\n  player.load({ hls: 'https:\u002F\u002Fexample.com\u002Fstream.m3u8' });\n\u003C\u002Fscript>\n```\n\nThis mirrors the current official v8-compat documentation more closely than manually calling `UIFactory.buildUI()`. Prefer the documented `location.ui` \u002F `location.ui_css` path unless you have a specific reason to own the UI bootstrap yourself.\n\n**Caveat:** Not all v8 APIs are implemented. Some methods are NOPs pending PWX feature completion. Check the live [support matrix](https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-support-matrix) before relying on specific features.\n\n### Loading v8 AND PWX v8-compat on the same page\n\nBoth bundles attach to `window.bitmovin.player.Player` — whichever loads second wins. To run both side-by-side (e.g. an A\u002FB demo), capture each reference between script loads:\n\n```html\n\u003C!-- 1) Load v8 -->\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player@8\u002Fbitmovinplayer.js\">\u003C\u002Fscript>\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbitmovin-player-ui@4\u002Fdist\u002Fjs\u002Fbitmovinplayer-ui.js\">\u003C\u002Fscript>\n\u003Cscript>\n  window.V8Player = window.bitmovin.player.Player;      \u002F\u002F capture v8\n\u003C\u002Fscript>\n\n\u003C!-- 2) Load PWX v8-compat (this OVERWRITES window.bitmovin.player.Player) -->\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@bitmovin\u002Fplayer-web-x@10\u002Fbundles\u002Fplayerx-bitmovin-v8.js\">\u003C\u002Fscript>\n\u003Cscript>\n  window.PwxPlayer = window.bitmovin.player.Player;     \u002F\u002F capture PWX v8-compat\n\u003C\u002Fscript>\n\n\u003Cscript>\n  const v8 = new window.V8Player(v8Container, { key, ui: false });\n  const pwx = new window.PwxPlayer(pwxContainer, { key, ui: false });\n  bitmovin.playerui.UIFactory.buildUI(v8);\n  bitmovin.playerui.UIFactory.buildUI(pwx);             \u002F\u002F same UI on both\n  v8.load({ hls: url });\n  pwx.load({ hls: url });\n\u003C\u002Fscript>\n```\n\n`window.bitmovin.playerui` is a separate namespace from a separate script — it is not affected by the collision.\n\n## Measuring PWX vs v8 startup time\n\nBoth v8 and the PWX v8-compat bundle emit a `'playing'` event when the first frame renders. Measure time-to-first-frame like this:\n\n```typescript\nasync function measureStartup(player, source) {\n  const firstFrame = new Promise(resolve => player.on('playing', () => resolve(performance.now())));\n  const t0 = performance.now();\n  await player.load(source);\n  const t1 = await firstFrame;\n  return t1 - t0;  \u002F\u002F startup in ms\n}\n```\n\nRun both players in parallel (so network contention is equal) or sequentially (for isolated numbers) depending on what you're benchmarking. Do **not** hardcode an expected delta into your guidance: startup results vary significantly by browser, device, stream packaging, cache state, and SDK version.\n\n## Custom packages\n\nPWX's killer feature is its package system. You can extend, replace, or add functionality:\n\n```typescript\nimport { Player } from '@bitmovin\u002Fplayer-web-x';\n\nconst player = Player({\n  key: 'YOUR_KEY',\n  defaultContainer: document.getElementById('player'),\n});\n\n\u002F\u002F Add packages for the features you need\nplayer.packages.add(hlsPackage);\nplayer.packages.add(adaptationPackage);\nplayer.packages.add(myCustomPackage);\n```\n\nSee [Creating packages](https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-creating-packages) for the package authoring guide.\n\n## Advertising (PWX)\n\nClient-side ads are available in the `Playback` and `BitmovinV8` bundles out of the box (or by adding `BitmovinAdvertisingBundlePackage` to a custom bundle). Ads are scheduled **per source**, through the `ads` namespace on the source handle — this is different from v8's `advertising.adBreaks` player config.\n\n```typescript\nsource.ads.schedule({\n  tag: { type: 'vast', url: 'https:\u002F\u002Fyour-ad-server\u002Fvast.xml' },\n  position: 'pre', \u002F\u002F 'pre' | 'start' | 'post' | 'end' | seconds (number) | '50%'\n});\n```\n\nVAST 4.x is the stable path; **VMAP support is experimental** — prefer VAST for production. Ad lifecycle events (`ad-started`, `ad-finished`, `ad-quartile`, `ad-skipped`, ...) are delivered on `source.events`, and the active ad is available via `source.ads.getActiveAd()`.\n\n## Reference links (PWX)\n\n- **About PWX:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fabout-player-web-x\n- **Getting Started:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-getting-started\n- **Bundles & Packages:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-bundles-packages\n- **v8 Compatibility:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-v8-compatibility\n- **Support Matrix:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-support-matrix\n- **Features:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-features\n- **Release Notes:** https:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Fplayer-web-x-release-notes\n- **API Docs (CDN):** https:\u002F\u002Fcdn.bitmovin.com\u002Fplayer\u002Fweb_x\u002Fbeta\u002F10\u002Fdocs\u002Findex.html\n- **npm package:** https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@bitmovin\u002Fplayer-web-x\n",{"name":5,"description":7},"main","14b211bb07d3e4057b85fb9140369fc73b1eb33b","skills\u002Fbitmovin-player-web","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills","https:\u002F\u002Fgithub.com\u002Fbitmovin","https:\u002F\u002Fbitmovin.com","2026-11-12 23:46:20",{"source":8,"skillName":5,"commands":56,"prompt":66,"zipAvailable":67},[57,60,63],{"tool":58,"command":59},"npx","npx skills add bitmovin\u002Fskills --skill bitmovin-player-web",{"tool":61,"command":62},"bunx","bunx skills add bitmovin\u002Fskills --skill bitmovin-player-web",{"tool":64,"command":65},"pnpm","pnpm dlx skills add bitmovin\u002Fskills --skill bitmovin-player-web","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-player-web\u002FSKILL.md",true,[],[70,97,119,139,161,187],{"skillNo":71,"name":9,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":81,"publishedAt":43},"01M6XCZ365TGD9DCJG6Z45S0T4","Hub skill for Bitmovin video infrastructure. Use when the user mentions Bitmovin, asks about video playback, encoding, transcoding, streaming, player integration, video analytics, content delivery optimization, or video AI. Routes to the right Bitmovin product (Player, VOD\u002FLive Encoding, Observability, AI Scene Analysis, Streams, Stream Lab) and walks through MCP\u002FCLI\u002FSDK setup.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin\u002FSKILL.md",[75,78],{"categoryNo":76,"categoryName":77,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":79,"categoryName":80,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[9,82,83,84,85,86,87,88,89,90,39,35,91,92,93,94,95,25,96,28],"MCP","cli","analytics","infrastructure","product","observability","streaming","Optimization","analysis","sdk setup","delivery","live","streams","lab","for",{"skillNo":98,"name":99,"nameZh":6,"description":100,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":101,"categories":102,"keywords":103,"publishedAt":43},"01M6XCYRHQ60DC0YXWRCASET1X","bitmovin-player-ios","Integrate and troubleshoot the Bitmovin Player SDK for iOS, tvOS, and visionOS in Swift, SwiftUI, UIKit, and existing Objective-C apps, including playback setup, player lifecycle, source loading, DRM, ads, AirPlay, Google Cast \u002F Chromecast, PiP, platform-specific UX, logs, network diagnostics, and stream validation.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-player-ios\u002FSKILL.md",[],[104,25,105,9,106,107,108,109,110,38,111,112,34,113,114,115,96,116,117,118],"ios","bitmovin player ios","google","swift","validation","logs","lifecycle","swiftui","uikit","platform","apps","pip","Existing","stream","objective",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":125,"publishedAt":43},"01M6XCYM297KGT0QKD7J9DNC4G","bitmovin-player-android","Integrate and troubleshoot the Bitmovin Android Player SDK in Kotlin\u002FCompose Android apps, including playback setup, Web UI, PiP\u002Ffullscreen handlers, media session, cast, TV variants, and source-loading robustness.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-player-android\u002FSKILL.md",[],[126,25,127,9,128,26,41,129,130,131,34,132,133,114,134,135,115,136,137,138],"android","bitmovin player android","troubleshoot","Media","kotlin","session","compose","cast","web ui","loading","source","robustness","variants",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":146,"publishedAt":43},"01M6XCYCHP945BKSJ37BMDR0KR","bitmovin-observability-root-cause-analysis","Root-cause analysis for a video QoE metric that moved — startup time, rebuffer ratio, error rate, bitrate, video-start-failures — using the Bitmovin Observability MCP. Use whenever someone asks WHY a QoE metric changed, spiked, dropped, regressed, or \"looks off\", whether a release or CDN change hurt quality, or what drives a delta between two windows or cohorts. Trigger even without \"RCA\" — \"rebuffering is up in APAC\", \"did v11 regress startup?\", \"what happened to error rate Tuesday\" all belong here. Drives a disciplined Define→Gather→Reflect→Conclude investigation with mix-vs-rate (Simpson's paradox) protection and a falsification step, producing a structured findings object.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-observability-root-cause-analysis\u002FSKILL.md",[145],{"categoryNo":79,"categoryName":80,"taxonomyType":21,"taxonomyTypeDesc":22},[147,87,90,148,149,9,150,82,151,152,153,154,39,155,27,156,157,158,159,160],"root cause analysis","root","root cause","bitmovin observability root cause analysis","change","reflect","release","start","why","time","error","mix","metric","rate",{"skillNo":162,"name":163,"nameZh":6,"description":164,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":165,"categories":166,"keywords":175,"publishedAt":43},"01M6XCY63KD9ZQ5S5P4KSV881A","bitmovin-observability-collector-integration","Integrate the Bitmovin Observability (Analytics) collector into any video player on any platform. Use WHENEVER a developer wants to add Bitmovin Analytics \u002F Observability tracking, \"set up the collector\", \"enable analytics\", \"track playback \u002F QoE \u002F impressions\", wire up a player to the Bitmovin dashboard, or asks why their analytics data is missing, empty, or wrong after integrating. Covers Web, Android\u002FFire TV, iOS\u002FtvOS\u002FvisionOS, and Roku, for both the Bitmovin Player and third-party engines (HLS.js, Shaka, Video.js, HTML5, ExoPlayer, Media3, AVPlayer, THEOplayer, CAFv3). Use it even when the user only names their player (\"add analytics to my Shaka player\") without saying \"Bitmovin collector\". Knows that the Bitmovin Player has analytics pre-integrated while third-party players need a separate collector with a managed attach\u002Fdetach lifecycle.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-observability-collector-integration\u002FSKILL.md",[167,170],{"categoryNo":168,"categoryName":169,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000013","分析与可视化",{"categoryNo":171,"categoryName":172,"taxonomyType":173,"taxonomyTypeDesc":174},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[87,176,177,9,178,179,180,40,39,155,181,34,182,27,183,25,184,185,186,28],"integration","collector","bitmovin observability collector integration","dashboard","data","video player","only","attach","my","track","managed",{"skillNo":188,"name":189,"nameZh":6,"description":190,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":191,"categories":192,"keywords":198,"publishedAt":43},"01M6XCXZGGXA2YFFEVAC4B7PD3","bitmovin-observability-ad-report","Snapshot-style ad operations report for a Bitmovin Analytics license. Use whenever someone asks for an \"ad ops report\", \"ad health check\", \"ad funnel\", \"weekly ad review\", \"VAST error breakdown\", \"advertiser performance\", \"ad system comparison\", \"ad startup-time breakdown\", or \"how are our ads doing\" — even without the word \"report\". Builds the impression→start→quartile→ completion funnel, ranks advertisers and ad systems, flags VAST\u002Fad-server errors, evaluates pod-position cohorts, and summarizes ad startup latency as structured anomaly flags (AdOpsReport object). Does NOT explain why a metric moved between two windows — use the QoE RCA skill for that.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-observability-ad-report\u002FSKILL.md",[193,194,195],{"categoryNo":168,"categoryName":169,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":171,"categoryName":172,"taxonomyType":173,"taxonomyTypeDesc":174},{"categoryNo":196,"categoryName":197,"taxonomyType":173,"taxonomyTypeDesc":174},"01JBSEEDCAT000000000000034","行政与办公人员",[199,87,200,9,201,202,203,84,204,154,205,155,38,206,207,156,157,208,209,159],"report","ad","bitmovin observability ad report","review","performance","server","how","snapshot","flags","errors","weekly"]