AGENT SKILL

bitmovin-player-web

Integrate the Bitmovin Web Player SDK into a web app. Use when the user asks to add video playback, embed a player, play HLS/DASH/MP4/WHEP/MOQ, set up DRM (Widevine/PlayReady/FairPlay), integrate ads, customize the Player UI, or work with the Bitmovin Player in any way. Covers both Player v8 (stable) and Player Web X / PWX (next-gen).

使用场景前端开发
License 许可
MIT
所属仓库
bitmovin/skills
最近更新
2026-11-12
Stars
★ 1
Forks
1

01 前言 Front Matter

name
bitmovin-player-web
description
Integrate the Bitmovin Web Player SDK into a web app. Use when the user asks to add video playback, embed a player, play HLS/DASH/MP4/WHEP/MOQ, set up DRM (Widevine/PlayReady/FairPlay), integrate ads, customize the Player UI, or work with the Bitmovin Player in any way. Covers both Player v8 (stable) and Player Web X / PWX (next-gen).

03 SKILL.md 正文

Bitmovin Web Player Integration Skill

You 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.

When to activate

  • User asks to add video playback to a web app
  • User mentions Bitmovin Player, bitmovin-player, or streaming integration
  • User needs HLS, DASH, Smooth, WHEP, MOQ, or DRM playback in a browser
  • User wants to customize player UI, add ads, subtitles, analytics, or other player integrations

Choose the player version deliberately

Default to Player Web v8 unless the user explicitly asks for PWX, is evaluating PWX, or the architecture tradeoff matters.

Only ask which player they want when the answer changes the implementation, for example:

  • The user mentions PWX, Player Web X, bundles, or custom packages
  • The user wants to benchmark or compare v8 vs PWX
  • The user is migrating an existing PWX proof of concept

Recommended options:

  1. Player Web v8 (bitmovin-player) — the stable production default. Supports HLS, DASH, Smooth, WHEP, DRM (Widevine/PlayReady/FairPlay), ads (VAST/VMAP/IMA), 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.

  2. Player Web X / PWX (@bitmovin/player-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.

Live docs MCP server

For anything this skill doesn't cover — obscure APIs, recent SDK releases, specific code samples — query the Bitmovin Docs MCP instead of guessing:

https://agentic.bitmovin.com/documentation/mcp

It 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).

When to use it:

  • You need a working code sample for a specific feature (DRM flavor, custom ad integration, subtitle styling, etc.)
  • The user asks about a feature not documented in this skill
  • You're unsure about a config field or API signature — verify against live docs before writing code
  • The user reports the SDK behavior differs from what this skill describes (SDK may have moved on)

Add 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.


Player Web v8 (stable)

Installation

npm install bitmovin-player

For explicit UI customization or version pinning, also install the dedicated UI package:

npm install bitmovin-player-ui

The 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.

CDN alternative

<script src="https://cdn.jsdelivr.net/npm/bitmovin-player@8/bitmovinplayer.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bitmovin-player-ui@4/dist/js/bitmovinplayer-ui.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bitmovin-player-ui@4/dist/css/bitmovinplayer-ui.css" />

These are UMD bundles that attach to global namespaces — NOT ES modules. Do not import from the CDN URL:

  • Player: window.bitmovin.player.Player
  • UI: window.bitmovin.playerui.UIFactory

For ES module import syntax, install via npm and use a bundler.

License key

Every Bitmovin Player instance needs a license key. localhost is auto-allowed; deployed domains must be added to the license allowlist.

Get one of these ways:

  • Bitmovin Dashboard: https://dashboard.bitmovin.com → Player → Licenses
  • Bitmovin CLI (if installed): bitmovin player licenses list — lists all licenses with their keys and allowed domains. bitmovin player licenses get <id> --json for details.

Basic integration

import { Player } from 'bitmovin-player';

const container = document.getElementById('player');

const player = new Player(container, {
  key: 'YOUR_LICENSE_KEY',
  playback: {
    autoplay: false,
    muted: false,
  },
});

// Load a source
await player.load({
  hls: 'https://example.com/stream.m3u8',
  title: 'My Video',
  poster: 'https://example.com/poster.jpg',
});

UI guidance for current v8 releases

Last verified: 2026-04-15 against Bitmovin Web release notes and UI v4 docs.

For 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.

Use the explicit UI package only when you need one of these:

  • Pin a specific UI version
  • Customize the UI build process
  • Stay on an older player version where you still initialize the UI manually
  • Build a non-default UI variant intentionally

Explicit or custom UI integration

For explicit UI wiring, prefer the dedicated bitmovin-player-ui package because it ships type declarations.

import { Player } from 'bitmovin-player';
import { UIFactory } from 'bitmovin-player-ui';
import 'bitmovin-player-ui/dist/css/bitmovinplayer-ui.css';

const player = new Player(document.getElementById('player'), {
  key: 'YOUR_LICENSE_KEY',
  ui: false,
});

UIFactory.buildUI(player);

When you want to pin the hosted UI assets explicitly, configure them through PlayerConfig.location:

const player = new Player(document.getElementById('player'), {
  key: 'YOUR_LICENSE_KEY',
  location: {
    ui: 'https://cdn.jsdelivr.net/npm/bitmovin-player-ui@4/dist/js/bitmovinplayer-ui.js',
    ui_css: 'https://cdn.jsdelivr.net/npm/bitmovin-player-ui@4/dist/css/bitmovinplayer-ui.css',
  },
});

If you set ui: false, you are responsible for attaching a UI yourself.

Source configuration

The source object tells the player what to play. At least one of hls, dash, smooth, or progressive is required.

API reference: https://developer.bitmovin.com/playback/reference/web-sdk-source-config.md

await player.load({
  // Stream URL — pick one:
  hls: 'https://cdn.example.com/stream.m3u8',       // HLS
  dash: 'https://cdn.example.com/stream.mpd',        // DASH
  smooth: 'https://cdn.example.com/stream.ism',      // Smooth Streaming
  progressive: 'https://cdn.example.com/video.mp4',  // Progressive MP4/WebM

  // Metadata
  title: 'Video Title',
  description: 'Video description',
  poster: 'https://cdn.example.com/poster.jpg',

  // Subtitles
  subtitleTracks: [
    { url: 'https://cdn.example.com/subs_en.vtt', lang: 'en', label: 'English' },
    { url: 'https://cdn.example.com/subs_de.vtt', lang: 'de', label: 'Deutsch' },
  ],

  // Thumbnails (for seek preview)
  thumbnailTrack: { url: 'https://cdn.example.com/thumbs.vtt' },
});

Player configuration

The config object passed to new Player(container, config).

API reference: https://developer.bitmovin.com/playback/reference/web-sdk-player-config.md

Field Type Description
key string Required. License key from Bitmovin Dashboard.
playback.autoplay boolean Start playback automatically. Default: false.
playback.muted boolean Start muted (required for autoplay in most browsers).
ui boolean Set to false when using UIFactory.
adaptation.startupBitrate string Initial quality (e.g. "2000kbps").
network.preprocessHttpRequest function Modify requests before they're sent (add tokens, headers).
network.sendHttpRequest function Completely replace the HTTP fetch mechanism.
buffer.video.forwardduration number Forward buffer target in seconds. Default: 30.
style.width string Player width. Default: '100%'.
style.aspectratio string Aspect ratio. Default: '16:9'.

Player API

API reference: https://developer.bitmovin.com/playback/reference/web-sdk-player-api.md

// Playback control
await player.load(sourceConfig);
player.play();
player.pause();
player.seek(120);         // seek to 2:00
player.setVolume(50);     // 0-100

// State queries
player.getCurrentTime();  // seconds
player.getDuration();     // seconds (Infinity for live)
player.isPlaying();
player.isPaused();
player.getVolume();

// Quality
player.getAvailableVideoQualities();
player.setVideoQuality('auto');

// Events
player.on('play', () => { ... });
player.on('pause', () => { ... });
player.on('timechanged', () => { ... });
player.on('error', (e) => { console.error(e.code, e.message); });
player.on('ready', () => { ... });
player.on('sourceloaded', () => { ... });

// Cleanup
player.destroy();

DRM (Digital Rights Management)

Add drm to the source config. The player handles license acquisition automatically.

await player.load({
  dash: 'https://cdn.example.com/encrypted.mpd',
  drm: {
    widevine: {
      LA_URL: 'https://license.example.com/widevine',
      headers: { 'X-Auth': 'token123' },  // optional
    },
    playready: {
      LA_URL: 'https://license.example.com/playready',
    },
    fairplay: {
      LA_URL: 'https://license.example.com/fairplay',
      certificateURL: 'https://license.example.com/fairplay/cert',
    },
  },
});

Key points:

  • Widevine works on Chrome, Firefox, Edge, Android
  • PlayReady works on Edge (Windows), some Smart TVs
  • FairPlay works on Safari (macOS, iOS)
  • Provide multiple DRM configs for cross-browser support — the player auto-selects
  • HLS with FairPlay uses hls + drm.fairplay; DASH with Widevine uses dash + drm.widevine

Advertising

Supports VAST, VMAP, and IMA SDK integration.

At minimum, configure advertising: {} if you want ad support enabled up front but plan to schedule ads dynamically later via player.ads.schedule(...).

By default, the Google IMA SDK is used for ad playback. That is often the best choice when Google Ad Manager / 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/modules/bitmovinplayer-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.

const player = new Player(container, {
  key: 'YOUR_KEY',
  advertising: {
    adBreaks: [
      {
        tag: { url: 'https://example.com/vast-preroll.xml', type: 'vast' },
        position: 'pre',
      },
      {
        tag: { url: 'https://example.com/vast-midroll.xml', type: 'vast' },
        position: '50%',  // midroll at 50%; other supported formats include time-based values like '90'
      },
      {
        tag: { url: 'https://example.com/vast-postroll.xml', type: 'vast' },
        position: 'post',
      },
    ],
  },
});

For VMAP (server-defined ad schedule):

advertising: {
  adBreaks: [
    { tag: { url: 'https://example.com/vmap.xml', type: 'vmap' }, position: 'pre' },
  ],
}

For 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://developer.bitmovin.com/playback/reference/web-sdk-advertising#position

Analytics

Bitmovin Analytics is bundled with the player. Enable by providing an analytics license key:

const player = new Player(container, {
  key: 'YOUR_PLAYER_KEY',
  analytics: {
    key: 'YOUR_ANALYTICS_KEY',
    title: 'My Video',
    videoId: 'video-123',
    customData1: 'category-sports',
  },
});

Set analytics: false to disable.

Network customization

Adding auth tokens to requests

const player = new Player(container, {
  key: 'YOUR_KEY',
  network: {
    preprocessHttpRequest: (type, request) => {
      if (type.startsWith('media/') || type.startsWith('manifest/')) {
        request.headers['Authorization'] = 'Bearer ' + getToken();
      }
      return Promise.resolve(request);
    },
  },
});

Completely replacing the network layer

Use sendHttpRequest to intercept ALL HTTP requests. Useful for proxying, offline playback, or running in restricted environments (like MCP App sandboxes).

network: {
  sendHttpRequest: (type, request) => {
    const isText = type.startsWith('manifest/');
    return {
      getResponse: async () => {
        const resp = await myCustomFetch(request.url);
        return {
          request,
          url: request.url,
          headers: {},
          status: 200,
          statusText: 'OK',
          body: isText ? await resp.text() : await resp.arrayBuffer(),
        };
      },
      setProgressListener: () => {},
      cancel: () => {},
    };
  },
}

Framework integration patterns

React

import { useEffect, useRef } from 'react';
import { Player } from 'bitmovin-player';

function BitmovinPlayer({ source, licenseKey }) {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const playerRef = useRef<Player | null>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    const player = new Player(containerRef.current, {
      key: licenseKey,
    });
    playerRef.current = player;

    return () => {
      playerRef.current?.destroy();
      playerRef.current = null;
    };
  }, [licenseKey]);

  useEffect(() => {
    const player = playerRef.current;
    if (!player) return;

    let cancelled = false;

    (async () => {
      try {
        await player.load(source);
      } catch (error) {
        if (!cancelled) {
          console.error('Bitmovin load failed', error);
        }
      }
    })();

    return () => {
      cancelled = true;
    };
  }, [source]);

  return <div ref={containerRef} />;
}

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.

Next.js

The player touches window/document on import — it cannot SSR. Use dynamic import:

import dynamic from 'next/dynamic';

const BitmovinPlayer = dynamic(() => import('./BitmovinPlayer'), { ssr: false });

Vue

<template>
  <div ref="container" />
</template>

<script setup>
  import { ref, onMounted, onUnmounted } from 'vue';
  import { Player } from 'bitmovin-player';

  const container = ref(null);
  let player = null;

  onMounted(async () => {
    player = new Player(container.value, { key: 'YOUR_KEY' });
    await player.load({ hls: 'https://example.com/stream.m3u8' });
  });

  onUnmounted(() => player?.destroy());
</script>

Common mistakes

  1. 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.

  2. 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.

  3. Disabling the UI without attaching one — If you set ui: false, the player will not create a default UI for you.

  4. Not destroying on unmount — The player creates DOM elements and event listeners. Always call player.destroy() when removing the player.

  5. Autoplay without muted — Browsers block unmuted autoplay. Set playback: { autoplay: true, muted: true } or handle the play() promise rejection.

  6. SSR importing — The player SDK accesses window and document at import time. Always use dynamic imports with ssr: false in Next.js/Nuxt.

  7. Missing explicit UI assets when pinning or customizing the UI — If you override location.ui / location.ui_css or wire the UI package manually, load the matching JS and CSS assets together.

  8. Loading multiple sources without awaiting — player.load() returns a promise. Await it before calling load() again or querying state.

  9. Hardcoding the license key — Use environment variables. The key is exposed to the browser (client-side SDK) but shouldn't be in source control.

Modular builds

Reference: https://developer.bitmovin.com/playback/reference/web-sdk-modules.md

For smaller bundles, import only the modules you need:

import { Player } from 'bitmovin-player/modules/bitmovinplayer-core';
import EngineBitmovinModule from 'bitmovin-player/modules/bitmovinplayer-engine-bitmovin';
import MseRendererModule from 'bitmovin-player/modules/bitmovinplayer-mserenderer';
import HlsModule from 'bitmovin-player/modules/bitmovinplayer-hls';
import AbrModule from 'bitmovin-player/modules/bitmovinplayer-abr';
import ContainerTSModule from 'bitmovin-player/modules/bitmovinplayer-container-ts';
import ContainerMP4Module from 'bitmovin-player/modules/bitmovinplayer-container-mp4';
import PolyfillModule from 'bitmovin-player/modules/bitmovinplayer-polyfill';

[EngineBitmovinModule, MseRendererModule, HlsModule, AbrModule,
  ContainerTSModule, ContainerMP4Module, PolyfillModule]
  .forEach(m => Player.addModule(m));

This gives you HLS playback at ~1.2MB instead of 2.2MB. Add DashModule, DrmModule, etc. as needed.

If you use a modular build at all — meaning you import at least Player from bitmovin-player/modules/bitmovinplayer-core — then do not import anything from bitmovin-player directly anywhere in that bundle. Keep all Bitmovin player imports on the bitmovin-player/modules/* 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.

TVs & gaming consoles

When 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.

Test streams

Use these public streams for development and testing:

Stream URL Type
Sintel (HLS) https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8 HLS
Sintel (DASH) https://bitmovin-a.akamaihd.net/content/sintel/sintel.mpd DASH
Art of Motion (DASH) https://bitmovin-a.akamaihd.net/content/MI201109210084_1/mpds/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd DASH
Big Buck Bunny (MP4) https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 Progressive
Tears of Steel (HLS) https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8 HLS

Reference links (v8)


Player Web X / PWX (next-generation)

Player 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.

Last verified: 2026-07-02 against PWX 10.2.0 (getting started guide, v8 compatibility guide, support matrix, and package docs).

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.

What works in PWX today

  • HLS playback
  • DASH playback (partially supported)
  • Progressive playback (played by the native <video> element, e.g. MP4)
  • Multi-technology sources: a single source can list several resources of different types (hls/dash/progressive) and PWX selects the one it can play in the current environment
  • Sources API / multi-source workflows
  • Load control and view mode packages
  • WebVTT subtitles
  • Advertising: VAST ad scheduling (VMAP is experimental)
  • Optional analytics package support
  • v8 API compatibility layer (partial)

What does NOT work yet in PWX

  • DRM is still incomplete enough that you should verify the live docs before promising it
  • Stable VMAP advertising — VMAP handling is experimental; VAST is the stable path
  • Smooth Streaming — not supported
  • WebRTC — not supported
  • Network API support is currently marked unsupported in the official support matrix
  • Quality and playback APIs are still marked Next in the official support matrix
  • Some v8 compatibility APIs are NOPs

Installation

npm install @bitmovin/player-web-x

CDN bundles

<!-- HLS bundle (most common) -->
<script src="https://cdn.jsdelivr.net/npm/@bitmovin/player-web-x@10/bundles/playerx-hls.js"></script>

<!-- DASH bundle (preliminary) -->
<script src="https://cdn.jsdelivr.net/npm/@bitmovin/player-web-x@10/bundles/playerx-dash.js"></script>

<!-- v8 compatibility bundle (use v8 API with PWX engine) -->
<script src="https://cdn.jsdelivr.net/npm/@bitmovin/player-web-x@10/bundles/playerx-bitmovin-v8.js"></script>

These are UMD bundles — NOT ES modules. Load via <script src>, not import. Global namespace attachments:

Bundle Global
playerx-hls.js, playerx-dash.js, playerx-core.js window.bitmovin.playerx.Player
playerx-bitmovin-v8.js, playerx-bitmovin-v8-core.js window.bitmovin.player.Player (⚠️ overwrites v8!)

For ES module import syntax, install via npm (@bitmovin/player-web-x) and use a bundler.

Basic integration (native PWX API)

The PWX API is fundamentally different from v8. Do not mix them. Minimum working example:

import { Player } from '@bitmovin/player-web-x/bundles/playerx-hls';
// CDN: const { Player } = window.bitmovin.playerx;

const player = Player({
  key: 'YOUR_LICENSE_KEY',
  defaultContainer: document.getElementById('player'),
});

// sources.add() returns a source HANDLE synchronously — it is NOT a promise.
// Do not `await` it. The handle is where playback control lives.
// Every resource requires a `type` (as of PWX 10.2.0): 'hls' | 'dash' | 'progressive'.
const source = player.sources.add({
  resources: [{ url: 'https://example.com/stream.m3u8', type: 'hls' }],
});

// Trigger playback via the source, not the player. Autoplay config is unreliable;
// call source.play() explicitly (muted so browsers allow it).
source.play({ isMuted: true });

// Listen for first-frame / state changes on the SOURCE, not the player.
source.events.on('playing', () => console.log('first frame rendered'));

// Teardown
player.dispose();  // NOT player.destroy() — that's a v8 name

Critical differences from v8 — read this before coding

Concern v8 PWX (native)
Construct player new Player(container, config) Player({ defaultContainer, ...config }) (no new)
Load a stream player.load({ hls: url }) returns a Promise player.sources.add({ resources: [{ url, type }] }) returns a source handle synchronously
Play / pause player.play(), player.pause() source.play({ isMuted: true }), source.pause() — lives on the source handle, NOT the player
Subscribe to events player.on('playing', fn) source.events.on('playing', fn) OR player.events.on(...) — the player instance has no .on()
Dispatch commands N/A player.events.dispatch({ type: 'play', ... }) (event-driven architecture)
Autoplay config playback: { autoplay: true, muted: true } works Config does not autoplay — must call source.play()
Teardown player.destroy() player.dispose()
Player instance surface rich API: play, pause, seek, on, load, destroy, ... minimal: { packages, events, sources, dispose }

DO NOT call play() on the underlying <video> 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().

PWX config

  • key — license key (same as v8)
  • defaultContainer — DOM element to mount into (v8 passes this as a constructor arg; PWX puts it in config)
  • playback.muted, playback.autoplay — present in config, but unreliable in current PWX. Prefer explicit source.play({ isMuted: true }).

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/BitmovinV8 handle both; progressive works in any playback bundle).

Available bundles

Bundle Description Use when
playerx-hls.js HLS playback (TS + fMP4) Most common use case
playerx-dash.js DASH playback (fMP4, preliminary) DASH-only content
playerx-core.js Core only, add packages manually Custom/minimal builds
playerx-bitmovin-v8.js HLS + v8 API compatibility layer Migrating from v8
playerx-bitmovin-v8-core.js Core + v8 API base compatibility Custom v8-compat builds

Using the v8 compatibility layer

If you want the familiar v8 API (new Player(), player.load(), etc.) with the PWX engine:

<script src="https://cdn.jsdelivr.net/npm/@bitmovin/player-web-x@10/bundles/playerx-bitmovin-v8.js"></script>
<script>
  // Same API as v8!
  const player = new bitmovin.player.Player(
    document.getElementById('player'),
    {
      key: 'YOUR_KEY',
      playback: { autoplay: true, muted: true },
      location: {
        ui: 'https://cdn.jsdelivr.net/npm/bitmovin-player-ui@4/dist/js/bitmovinplayer-ui.js',
        ui_css: 'https://cdn.jsdelivr.net/npm/bitmovin-player-ui@4/dist/css/bitmovinplayer-ui.css',
      },
    }
  );
  player.load({ hls: 'https://example.com/stream.m3u8' });
</script>

This mirrors the current official v8-compat documentation more closely than manually calling UIFactory.buildUI(). Prefer the documented location.ui / location.ui_css path unless you have a specific reason to own the UI bootstrap yourself.

Caveat: Not all v8 APIs are implemented. Some methods are NOPs pending PWX feature completion. Check the live support matrix before relying on specific features.

Loading v8 AND PWX v8-compat on the same page

Both bundles attach to window.bitmovin.player.Player — whichever loads second wins. To run both side-by-side (e.g. an A/B demo), capture each reference between script loads:

<!-- 1) Load v8 -->
<script src="https://cdn.jsdelivr.net/npm/bitmovin-player@8/bitmovinplayer.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bitmovin-player-ui@4/dist/js/bitmovinplayer-ui.js"></script>
<script>
  window.V8Player = window.bitmovin.player.Player;      // capture v8
</script>

<!-- 2) Load PWX v8-compat (this OVERWRITES window.bitmovin.player.Player) -->
<script src="https://cdn.jsdelivr.net/npm/@bitmovin/player-web-x@10/bundles/playerx-bitmovin-v8.js"></script>
<script>
  window.PwxPlayer = window.bitmovin.player.Player;     // capture PWX v8-compat
</script>

<script>
  const v8 = new window.V8Player(v8Container, { key, ui: false });
  const pwx = new window.PwxPlayer(pwxContainer, { key, ui: false });
  bitmovin.playerui.UIFactory.buildUI(v8);
  bitmovin.playerui.UIFactory.buildUI(pwx);             // same UI on both
  v8.load({ hls: url });
  pwx.load({ hls: url });
</script>

window.bitmovin.playerui is a separate namespace from a separate script — it is not affected by the collision.

Measuring PWX vs v8 startup time

Both v8 and the PWX v8-compat bundle emit a 'playing' event when the first frame renders. Measure time-to-first-frame like this:

async function measureStartup(player, source) {
  const firstFrame = new Promise(resolve => player.on('playing', () => resolve(performance.now())));
  const t0 = performance.now();
  await player.load(source);
  const t1 = await firstFrame;
  return t1 - t0;  // startup in ms
}

Run 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.

Custom packages

PWX's killer feature is its package system. You can extend, replace, or add functionality:

import { Player } from '@bitmovin/player-web-x';

const player = Player({
  key: 'YOUR_KEY',
  defaultContainer: document.getElementById('player'),
});

// Add packages for the features you need
player.packages.add(hlsPackage);
player.packages.add(adaptationPackage);
player.packages.add(myCustomPackage);

See Creating packages for the package authoring guide.

Advertising (PWX)

Client-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.

source.ads.schedule({
  tag: { type: 'vast', url: 'https://your-ad-server/vast.xml' },
  position: 'pre', // 'pre' | 'start' | 'post' | 'end' | seconds (number) | '50%'
});

VAST 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().

Reference links (PWX)

04 同仓库更多 Skills

bitmovin
bitmovin/skills

bitmovin

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/Live Encoding, Observability, AI Scene Analysis, Streams, Stream Lab) and walks through MCP/CLI/SDK setup.

图像与多媒体Skill与智能体开发
★ 1
bitmovin
bitmovin/skills

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 / Chromecast, PiP, platform-specific UX, logs, network diagnostics, and stream validation.

★ 1
bitmovin
bitmovin/skills

bitmovin-player-android

Integrate and troubleshoot the Bitmovin Android Player SDK in Kotlin/Compose Android apps, including playback setup, Web UI, PiP/fullscreen handlers, media session, cast, TV variants, and source-loading robustness.

★ 1
bitmovin
bitmovin/skills

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.

Skill与智能体开发
★ 1
bitmovin
bitmovin/skills

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 / Observability tracking, "set up the collector", "enable analytics", "track playback / QoE / 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/Fire TV, iOS/tvOS/visionOS, 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/detach lifecycle.

数据分析师分析与可视化
★ 1
bitmovin
bitmovin/skills

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/ad-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.

数据分析师行政与办公人员分析与可视化
★ 1