# Dolby OptiView Player > Dolby OptiView Player (formerly THEOplayer) enables you to deploy cutting-edge video playback experiences on any device. Use the basic default UI, customize your player with Open Video UI's ready-made components, or build a complete UI from scratch with the chromeless player. # OptiView Player > **OptiView Rebranding** > > OptiView Player is the new name for THEOplayer as part of the OptiView product suite. During the transition, you may still see references to THEOplayer. OptiView Player and THEOplayer refer to the same product. Dolby OptiView Player is the universal video player solution from Dolby. It offers support for HLS, MPEG-DASH, advertisements, DRM and much more. On these pages, you'll learn how to get started with Dolby OptiView Player, how to use the various features, and explore the many examples. ## [OptiView Player Web SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/web.md) [For desktop and mobile web browsers, and smart TVs like Tizen and WebOS](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/web.md) ## [OptiView Player Android SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/android.md) [For smartphones, tablets and Android TVs](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/android.md) ## [OptiView Player iOS & tvOS SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/ios.md) [For iPhone, iPad and Apple TV](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/ios.md) ## [OptiView Player React Native SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/react-native.md) [For cross-platform apps targeting web, Android and iOS](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/react-native.md) ## [OptiView Player Flutter SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/flutter.md) [For cross-platform apps targeting web, Android and iOS](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/flutter.md) ## [OptiView Player Chromecast SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/chromecast.md) [For custom Chromecast receiver apps](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/chromecast.md) ## [OptiView Player Roku SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/roku.md) [For Roku smart TVs](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/roku.md) --- # Changelog These are the release notes for THEOplayer 11.0.0 and higher. For older versions, see: * [Version 10.x](https://optiview.dolby.com/docs/theoplayer/v10/changelog/) * [Version 9.x](https://optiview.dolby.com/docs/theoplayer/v9/changelog/) * [Version 8.x](https://optiview.dolby.com/docs/theoplayer/v8/changelog/) * [Version 7.x](https://optiview.dolby.com/docs/theoplayer/v7/changelog/) * [Version 5.x and 6.x](https://optiview.dolby.com/docs/theoplayer/v6/changelog/) * [Version 2.x, 3.x and 4.x](https://optiview.dolby.com/docs/theoplayer/v4/changelog/) ## 🚀 11.11.0 (2026/09/10) ### General #### ✨ Features * Added the `THEO_LIVE_DISCOVERY_ERROR` (13004) error code, which is reported when an OptiView Live channel could not be played because the discovery request failed or returned an invalid response. ### Web #### ✨ Features * Added Google IMA SDK for webOS and Tizen to enable beacons and UI for serverside ads on LG and Samsung TVs. * Added CMCD v2 buffer starvation reporting (`bs`, `bsa`, `bsd`), media start delay (`msd`) and media resume delay (`com.dolby.optiview-mrd`) to make tracking rebuffering easier. #### 🐛 Issues * Fixed an issue where playback would fail when the selected DRM key system (e.g. Widevine) failed after selection, instead of falling back to an alternative configured key system (e.g. PlayReady). * Fixed an issue where latency management stayed disabled for low-latency HLS streams without an explicit `latencyConfiguration` on the source, such as the LL-HLS playback of a THEOlive channel, causing playback to never catch up with the live edge. * Fixed an issue where playback of a low-latency HLS stream could fail with an 'attempted to download an incomplete segment' error. * Fixed an issue where the player would keep targeting the primary channel's latency after switching to the backup channel. * Fixed an issue where an HLS stream with a Dolby Vision enhancement layer failed to play on webOS devices without Dolby Vision support, instead of falling back to its base layer. * Fixed an issue where the CMCD session ID (sid) was not regenerated when a new source was set, so separate playback sessions were reported as one. A new session ID is now generated each time a source is set and stays stable for that source, including OptiView Live endpoint switches, fallbacks and OptiView Ads interstitials. * Fixed an issue where an unnecessary FairPlay license request was sent on every quality switch when playing HESP content on Safari. * Report fatal player errors to CMCD before the first media request. ### Android #### 💥 Breaking Changes * The Google IMA and Google DAI integrations now require `minSdk` 24 or higher, because the Google IMA SDK 3.40.0 dependency requires it. The core THEOplayer SDK still supports `minSdk` 23. Applications that use the Google IMA or Google DAI integration must raise their `minSdk` to 24. #### 🐛 Issues * Fixed a stray `pause` event being dispatched when switching between two back-to-back OptiView ad breaks. * Fixed unbounded memory growth during THEOlive playback when the network delivers data faster than the player consumes it, which could cause `OutOfMemoryError` crashes. * Fixed a NullPointerException that could occur when Google IMA delivered a null AdMediaInfo to the ad player. * Fixed an issue where the player would keep targeting the primary channel's latency after switching to the backup channel. * Fixed an issue where Google DAI live streams could fail to start with a `MANIFEST_LOAD_ERROR` after a 15 second timeout, caused by a bug in Google IMA SDK 3.39.0. The Google IMA SDK dependency was updated to 3.40.0, which requires `minSdk` 24 or higher for applications that use the Google IMA or Google DAI integration. * The `MANIFEST_LOAD_ERROR` that is dispatched when a Google DAI stream request fails now contains the original Google IMA `AdError` as its cause. * Report fatal player errors through CMCD when they occur before the first media request. * Fixed missing CMCD headers and playback state reporting on THEOlive HLS segment requests since 11.8.0. ### iOS #### 🐛 Issues * Fixed an issue where the CMCD channel name (`theo-cn`) reported the raw distribution ID. It is now only reported once the human-readable name is known, and is left unset until then rather than falling back to the ID. * Fixed an issue where the player would keep targeting the primary channel's latency after switching to the backup channel. * Fixed an issue where a seek that was cancelled by AVFoundation left the player stuck in the seeking state, so `player.seeking` stayed `true` and no `seeking` or `seeked` events were dispatched for any later seek. A seek issued before a player item exists now invokes its completion handler with an error instead of never calling back. * Fixed an issue where the CMCD-States header could grow past CDN log limits: state reason and error strings are now capped at 128 characters, and when more than 20 states are pending the oldest are dropped and their count is reported on the first shipped state. * Report fatal player errors through CMCD when they occur before the first media request. ### tvOS #### 🐛 Issues * Fixed an issue where a seek that was cancelled by AVFoundation left the player stuck in the seeking state, so `player.seeking` stayed `true` and no `seeking` or `seeked` events were dispatched for any later seek. A seek issued before a player item exists now invokes its completion handler with an error instead of never calling back. * Fixed an issue where the CMCD-States header could grow past CDN log limits: state reason and error strings are now capped at 128 characters, and when more than 20 states are pending the oldest are dropped and their count is reported on the first shipped state. ### Chromecast #### 🐛 Issues * Fixed an issue where sources with erroring IMAGE-STREAM URLs would fail to play on Chromecast receiver apps. ### Roku #### ✨ Features * Added CMCD reporting of whether the device can present Dolby Vision and decode Dolby Digital Plus (EC-3), and of the video and audio codecs which are actually being decoded. #### ⚡ Improvements * Added the reason a playback state was entered to the reported CMCD state, so a session that restarted because an OptiView Live channel fell back to another stream can now be told apart from one that started normally, matching the other platforms. #### 🐛 Issues * Fixed an issue where a fatal error could be reported in the CMCD data without its error code or message, leaving the failure unclassifiable. * Fixed an issue where adding an unsupported integration reported an unrelated error code and no error message. * Fixed an issue where an HLS stream mixing Dolby Vision and SDR variants could fail to play on devices whose display output is SDR. * Report fatal player errors through CMCD before the first media request. ## 🚀 11.10.2 (2026/09/03) ### Web #### 🐛 Issues * Fixed an issue where an unnecessary FairPlay license request was sent on every quality switch when playing HESP content on Safari. * Fixed an issue where an OptiView Live stream could get stuck while starting up, showing a black screen for 15 seconds before falling back to its HLS stream. * Fixed an issue where an OptiView Live stream continued without audio after it fell back to its HLS stream. ## 🚀 11.10.1 (2026/08/28) ### Web #### 🐛 Issues * Fixed an issue with OptiView Ads where only the first ad of an ad pod would play when the player was in native fullscreen or native picture-in-picture on iOS Safari. ## 🚀 11.10.0 (2026/08/27) ### Web #### ✨ Features * Added a 'c2pametadata' player event exposing raw C2PA metadata from ISOBMFF 'uuid' boxes in HLS and DASH streams, enabled through the 'c2paMetadata' source property. * Added support for configuring `google.ima.AdsRenderingSettings.useStyledNonLinearAds` and `useStyledLinearAds` via `player.ads.googleIma.useStyledNonLinearAds` and `player.ads.googleIma.useStyledLinearAds`. When left `undefined`, the Google IMA SDK default is used. #### 🐛 Issues * Fixed an issue for DASH streams with no `SegmentTimeline` where the manifest would sometimes fail to load if the `Period` duration was an exact multiple of the segment duration. * Fixed an issue where a scheduled Google IMA VAST ad would be requested twice, causing the ad server to return an empty VAST response (IMA error 303) for the second ad break. * Fixed an issue where the content would briefly appear and play between consecutive Google IMA ad breaks scheduled at the same time offset. ### Android #### 🐛 Issues * Fixed an issue where removing a caching task before its initialization completed, for example right after the cache reported INITIALISED, would mark the task as EVICTED but leave the cached Media3 files and download entry on the device. * Fixed an issue where an expired caching task would not clean up its offline DRM license data, and where removing a caching task could remove offline DRM sessions still used by another task caching the same content. * Fixed an issue where playback of a fully cached source could select a rendition that was not cached, causing a source error or endless waiting during offline playback. ### iOS #### ⚡ Improvements * Improved error reporting specifically when playback fails to play to end time. In this case the `ErrorEvent` will ensure to contain an `errorObject`. #### 🐛 Issues * Fixed an issue where `bytesCached` and `bytes` properties would return 0 for cached sources pre 10.12.0 * Fixed an issue where the player would not exit `lShape` layout when an ad error was thrown. Note: the issue was observed specifically for `LSHAPE_CONTENT` and not `LSHAPE_AD`. * Fixed an issue where the player would pause without dispatching a `PauseEvent` when pressing on "Learn more" during an ad break. This would result in unexpected state management when designing a player controls UI. ### Roku #### ✨ Features * Updated the AEP SDK to 1.3.1. This adds in the ability to set user consent for data collection and also the ability to set custom identities. #### 🐛 Issues * Fixed an issue where OptiView Live analytics reported uninterrupted playback sessions as never having started. The playing state is now reported throughout a session instead of only when playback changes state, matching the other platforms. * Fixed an issue where the CMCD measured throughput was reported as zero before any throughput had actually been measured. The field is now omitted until a real measurement is available, matching the other platforms. * Made live stall errors not report as fatal for Mux analytics. Also fixed an issue where a live stall would be incorrectly reported on a repeated timestamp. * Fixed an issue where fatal playback errors were reported to OptiView analytics without an error code or message. * Fixed issue where CMCD event mode would not activate for Google IMA SSAI streams. ## 🚀 11.9.1 (2026/08/22) ### Web #### ⚡ Improvements * Improved the content protection error when the CDM reports an output protection (HDCP) violation: instead of error 7012 with the generic 'Unknown CDM error' message, the player now reports error 7011 (CONTENT\_PROTECTION\_OUTPUT\_RESTRICTED) explaining that the required HDCP level cannot be guaranteed, e.g. because AirPlay screen mirroring, screen recording, screen sharing or a non-HDCP display is active. The error now also exposes the CDM's `systemCode`. #### 🐛 Issues * Fixed an issue where playback with Google IMA could stall indefinitely when a VMAP ad break resolved to an empty VAST response (VAST error 303). The player now dispatches a non-fatal `aderror` event and resumes content playback. * Fixed an issue where playback of PlayReady-protected Dolby Vision content could fail on Microsoft Edge on Windows. * Fixed an issue where a content protection error on a OptiView channel triggered several endpoint fallback attempts before surfacing a generic error. Content protection errors are now treated as non-recoverable and are surfaced immediately. ## 🚀 11.9.0 (2026/08/12) ### Web #### ✨ Features * Added support to the OptiView Ads break manifest for `double`, `lshape_ad`, `lshape_content` and `overlay` formats as well as device type support. * Added the `adclicked` event, for the `csai`and `google-ima` CSAI ads integrations, for Google DAI ads (SSAI) and OptiView Ads (SGAI). It is dispatched when the user clicks on an ad clickthrough element. * Added `network.useStreamingFetch` to the `PlayerConfiguration` API to explicitly disable consuming fetch responses as streams on devices with a broken streaming fetch implementation. * Added CMCD reporting of whether the platform can decode Dolby Vision and Dolby Digital Plus (EC-3), and of the video and audio codecs which are actually being decoded. #### ⚡ Improvements * Ads preloading for Google IMA now works consistently for both VMAP ad schedules and individually scheduled VAST ads. When preloading is enabled (using `AdsConfiguration.preload`), VAST midrolls are fetched 8 seconds before their scheduled start time. #### 🐛 Issues * Fixed an issue where completed SGAI interstitials could be re-added and emit repeated interstitial events on live streams. * Fixed an issue where PlayReady HLS key formats specified as a `urn:uuid:` were not recognized. * Fixed an issue where playback would fail for Google DAI sources because CMCD headers on manifest requests would trigger CORS errors. CMCD now operates in event mode only for Google DAI sources. * Fixed an issue where an ad scheduled after a non-linear Google IMA ad would never start, because Google IMA does not fire any events after LOADED when a non-linear ad is the last ad of its ads manager. * Fixed an issue where the player could get stuck when starting playback of a new source with a Google IMA pre-roll ad, if the previous source also had a Google IMA pre-roll ad. * Fixed an issue where a Google IMA pre-roll ad of a new source could start loading and playing immediately after changing the source, even when autoplay was disabled and the player was paused. ### Android #### ✨ Features * Added support to the OptiView Ads break manifest for `double`, `lshape_ad`, `lshape_content` and `overlay` formats as well as device type support. * Added CMCD reporting of whether the device can decode Dolby Vision and Dolby Digital Plus (EC-3), and of the codecs of the video and audio formats fed to the decoders. * Added `TheoLive.switchToHesp()`, which allows switching an OptiView Live stream back to HESP playback after it has fallen back to another format. * Added `Player.getCurrentSource()`, which returns the `TypedSource` that is currently being played. The source is the same as expected in the existing `CurrentSourceChangeEvent`. #### 🐛 Issues * Fixed an issue where the `overrideLayout` property would incorrectly override the `lshape_content` layout of an OptiView Ads ad break. * Fixed an issue where OptiView Ads PTS breaks might not play. * Fixed an issue where the first playback of a DRM-protected stream could fail on a device that had not played DRM-protected content before, and would keep failing when retried. ### iOS #### ✨ Features * Added support to the OptiView Ads break manifest for `double`, `lshape_ad`, `lshape_content` and `overlay` formats as well as device type support. * Added `currentBandwidthEstimate` to the player's `metrics` API, reporting the estimated available bandwidth in bits per second for HESP streams. * Added Dolby Vision and EC-3 capability reporting (`com.dolby.optiview-cap-dvc`, `com.dolby.optiview-cap-ec3`) to the CMCD session data, and active video and audio codec reporting (`com.dolby.optiview-vc`, `com.dolby.optiview-ac`) to the CMCD status data. #### 🐛 Issues * Fixed an issue where the player would exit fullscreen presentation mode when setting a new source. * Fixed an issue where OptiView Ads PTS breaks might not be played. * Fixed an issue that caused OptiView Ads to end abruptly. * Fixed an issue where skipping OptiView Ads would cause playback failure. * Fixed an issue where seeking into a range of an interstitial would cause playback failure. * Fixed an issue where legacy `CachingTask`s encoded without `bytes` and `bytesCached` properties would fail to restore. ### tvOS #### ✨ Features * Added display criteria matching: when enabled through the new `THEOplayer.manageContentMatching` property (disabled by default), and while in fullscreen presentation mode, the player matches the display mode (frame rate and dynamic range) of the TV with the content before playback starts. * The player will now set its preferred video format when a new source is loading. This allows tvOS to change the format on the fly when a user enables content matching in "Settings" > "Video and Audio" > "Match Content". * Added Dolby Vision and EC-3 capability reporting (`com.dolby.optiview-cap-dvc`, `com.dolby.optiview-cap-ec3`) to the CMCD session data, and active video and audio codec reporting (`com.dolby.optiview-vc`, `com.dolby.optiview-ac`) to the CMCD status data. #### 🐛 Issues * Fixed an issue where the player would exit fullscreen presentation mode when setting a new source. ### Roku #### 🐛 Issues * Fixed an issue where the CMCD-States payload could grow unbounded during repeated playback errors. The payload now contains at most the 20 newest states, and CMCD values are serialized strictly according to the CMCD specification (CTA-5004), matching the other platforms. * Fixed issue where streams would fail to start playback on less powerful devices. ## 🚀 11.8.1 (2026/08/05) ### Android #### ✨ Features * Added `DRMConfiguration.preferredKeySystems` to control the key system selection order. * Added `SourceAbrConfiguration.preferredVideoCodecs` and `SourceAbrConfiguration.preferredAudioCodecs` to prefer specific codecs (e.g. Dolby Vision) during track selection. #### ⚡ Improvements * Content protection errors now name the selected key system, and a clear error is reported when none of the configured key systems are supported by the device instead of failing with an unrelated Widevine error. * The player now prefers Dolby Vision and HEVC variants over other codecs by default on streams with a mixed-codec ladder, when the platform supports them. Set `SourceAbrConfiguration.preferredVideoCodecs` to an empty list to disable this preference. * Playback errors during THEOlive playback now report their own error code instead of `THEO_LIVE_UNKNOWN_ERROR`, so content protection failures can be told apart from stalls and manifest errors. * Added the `CONTENT_PROTECTION_PROVISIONING_ERROR` and `CONTENT_PROTECTION_DEVICE_REVOKED` error codes. A Widevine provisioning failure and a revoked device are now reported with their own code instead of the generic `CONTENT_PROTECTION_ERROR`. * Added the `MEDIA_INTERNAL_ERROR` error code. An unexpected internal failure is now reported with its own code instead of `MEDIA_DECODE_ERROR`. #### 🐛 Issues * Fixed an issue where playback of a DRM-protected stream with a mixed-codec ladder (e.g. HEVC and AVC) could fail on devices that lack a secure decoder for one of the codecs. The player now excludes formats that require an unavailable secure decoder from track selection, so playback continues on a supported codec. * Fixed an issue where the first playback of a DRM-protected stream could fail on a device that had not played DRM-protected content before, and would keep failing when retried. * Fixed an issue where a playback error could be reported with an unrelated error code, hiding what actually went wrong. * Fixed an issue where variant streams that the device cannot decode could be selected for playback, for example when `VideoTrack.targetQualities` includes a quality with an unsupported codec. This also applies to qualities that exceed the advertised decoder capabilities, which are now only used when no other quality is supported. * Fixed an issue where playback failed with a fatal `Decoder failed` error when a decoder could not handle the selected variant stream despite claiming support for it. The player now excludes the failing codec from track selection, so playback continues on another codec when the stream provides one. ## 🚀 11.8.0 (2026/07/31) ### Web #### 💥 Breaking Changes * Removed the deprecated Service Worker-based playback pipeline for HESP sources on iOS Safari. HESP playback on iOS Safari now requires the `ManagedMediaSource` API, which is available on iOS 17.1 and higher. On older iOS versions, OptiView Streaming (THEOlive) sources fall back to their HLS endpoint instead. The `theoplayer.sw.js` file is still shipped as an intentionally empty file so that existing build pipelines keep working, and it is safe to stop serving it. #### ✨ Features * Added `PlayerConfiguration.overrideAutoplayCapability` to override the player's automatic detection of the platform's autoplay capabilities. This is an advanced API that can be used on platforms where the auto-detection is known to be slow or to fail, such as lower-end smart TVs and set-top boxes. * Added the `title` property to the `Ad` interface for the `csai` integration. The title is parsed from the VAST `` element. * Added support for `loadVideoTimeout` and `bitrate` in the `csai` ad configuration. #### ⚡ Improvements * Improved transitions between Google IMA ads when scheduling multiple VAST ads back-to-back using `player.ads.schedule()`. * The player now prefers Dolby Vision and HEVC variant streams over other codecs during the initial variant stream selection for HLS streams when the platform supports them. Set `SourceAbrConfiguration.preferredVideoCodecs` to an empty list to disable this preference. #### 🐛 Issues * Fixed an issue where the live stream latency (`ltc`) in CMCD reports could sometimes contain an incorrect or impossibly large value. The field is now omitted when no valid latency measurement is available. * Fixed several CMCD reporting issues: VOD sessions now correctly report stream type as `st=v`, the startup flag (`su`) is no longer incorrectly reported as `false`, and bitrate fields are no longer reported when no valid value is available. * Fixed an issue where requesting picture-in-picture could hang indefinitely on iOS/iPadOS Safari 26+ when another player was already in picture-in-picture, causing the player state to become incorrect. * Fixed an issue where the `adTagParameters` from the ad source configuration were not reported in the CMCD interstitial events for OptiView Ads ad breaks. * Fixed an issue where the THEOads DOUBLE/L-shape layout animation would still activate even when both the backdrop and the ad failed to load during preloading (e.g. due to a DNS blocker). The player now correctly skips the layout transition when a preload error has already been reported. * Fixed an issue where the player did not always correctly switch back to the content after playing a Google IMA ad on iOS Safari with `allowNativeFullscreen` enabled. * Fixed an issue for OptiView Streaming (THEOlive) where HESP endpoints would be attempted on platforms that cannot play HESP, such as iOS Safari without `ManagedMediaSource` support, resulting in a delayed fallback to HLS. * Fixed an issue where captions could be rendered twice while in native picture-in-picture on Chromium-based browsers. * Fixed an issue where playback of a DRM-protected HLS stream with a mixed-codec ladder (e.g. HEVC and AVC) failed on platforms that support only some of those codecs for encrypted playback. * Fixed an issue where CEA-608 closed captions were not shown when playing an HLS stream with HEVC video. * Fixed an issue where the player would not select a variant stream whose codecs are supported by the browser when using a DRM pipeline, even though those codecs worked in a clear pipeline. This enables Dolby Vision with PlayReady to play on Windows Edge, which otherwise does not support clear Dolby Vision. * Fixed an issue where `DRMConfiguration.preferredKeySystems` entries using key system aliases such as `widevine` or `playready` were not honored for HLS streams. Previously only full key system URNs were matched. ### Android #### ✨ Features * OptiView Ads can now be used with any valid player license, and no longer needs to be enabled specifically for your license. #### 🐛 Issues * Fixed an issue where the `adTagParameters` from the ad source configuration were not reported in the CMCD interstitial events for OptiView Ads ad breaks. * Fixed an issue where playback might stall on low-performance devices after a reboot. * Fixed an issue where loading an OptiView Live V3 distribution without a CMCD configuration would cause a crash. ### iOS #### ✨ Features * OptiView Ads can now be used with any valid player license, and no longer needs to be enabled specifically for your license. #### 🐛 Issues * Fixed several CMCD reporting issues: the live stream latency (`ltc`) could sometimes be reported as an impossibly large value, unmeasured fields such as `ltc`, `bl` and `mtp` were incorrectly reported as zero rather than being omitted, and the buffer length reported in CMCD states was always zero. * Fixed an issue where the `AD_BREAK_END` event was sometimes not dispatched. * Fixed an issue where the `adTagParameters` from the ad source configuration were not reported in the CMCD interstitial events for OptiView Ads ad breaks. * Fixed a bug on iOS 15 where playback could stall indefinitely after returning from an IMA ad. * Fixed an issue where recovering an OptiView Live stream after a failover or restart could fail to reload the stream. ### Roku #### ✨ Features * Added the ability to set the ad system and provider from OptiView Live. #### 🐛 Issues * Fixed an issue where OptiView Live streams wouldn't autoplay after failing over to a new endpoint. * Fixed an issue where the player would sometimes not start playing when repeatedly changing sources and calling `play()`. ## 🚀 11.7.0 (2026/07/15) ### Web #### ✨ Features * Added support for the HLS `SUPPLEMENTAL-CODECS` attribute. #### 🐛 Issues * Fixed an issue where an HESP stream playing in iOS web views could appear frozen while the current time kept progressing. The player now detects this and recovers. * Fixed an issue where seeking immediately to the middle of an MPEG-DASH stream and then later seeking back to the start of the stream could stall the player indefinitely. * Fixed an issue where the distributed `.js` files were referencing internal `.js.map` files through a `//# sourceMappingURL` comment, which could raise warnings when bundling with e.g. webpack. These comments have been removed. * Fixed an issue where the `id` property of a `csai` ad contained the VAST creative ID instead. `Ad.id` now returns the ad's ID, while `Ad.creativeId` returns the creative's ID. * Fixed an issue where a PTS break would sometimes not be played when using the OptiView Ads manifest. * Fixed a regression that could break playback on legacy engines (such as Tizen 2.4) when using certain polyfills that define a `Symbol.iterator` getter on non-iterable objects. * Fixed an issue where the video track would report no active quality when playing an HLS stream that duplicates its variant streams across multiple audio codec groups, such as OptiView Live streams with both a Dolby and an AAC ladder. Qualities that only reference variant streams with unsupported codecs are now marked as unavailable. * Fixed an issue where ABR would switch to a variant stream with incompatible codecs when the network bandwidth was insufficient for all variant streams, causing an unnecessary playback interruption, e.g. leaving the Dolby Vision ladder of an HLS stream instead of selecting its lowest Dolby Vision variant stream. ### Android #### 🐛 Issues * Fixed an issue where the OptiView Ads manifest could fail to load. * Fixed an issue where the `InterstitialEndEvent` was not dispatched in OptiView Ads. ### iOS #### ✨ Features * Added support for the `contentprotectionerror` event, which is dispatched when the DRM flow fails. #### 🐛 Issues * Fixed an issue where the clickthrough info could not be updated through an `SSAIIntegration`. * Fixed an issue where the `readyState` would revert from `HAVE_ENOUGH_DATA` to `HAVE_CURRENT_DATA` when playing an MP4 stream with sideloaded VTT text tracks. ### Roku #### 🐛 Issues * Fixed an issue where internal observers were not removed properly when the player was destroyed, which could cause a crash when rapidly creating and destroying the player. ## 🚀 11.6.1 (2026/07/07) ### Android #### 🐛 Issues * Fixed an issue where a caching task would download all qualities instead of only the selected ones when the source had sideloaded text tracks. ### iOS #### 🐛 Issues * Fix a bug on iOS 18 where playback could stall indefinitely after returning from an IMA ad. ## 🚀 11.6.0 (2026/07/03) ### General #### ✨ Features * Added `externalId` to OptiView Live distribution metadata exposed by the `distributionloaded` event. ### Web #### 🐛 Issues * Fixed an issue where an OptiView Live stream was incorrectly flagged as offline during slow startup. * Fixed an issue where ID3 text tracks did not initially have a `TextTrack.type` set when playing inside a `WKWebView` on iOS. * Fixed an issue where the player would error on some specific HLS streams with TTML subtitles. * Fixed an issue where the player would create gaps in the buffer around discontinuities in certain HLS streams, which could result in undesirable gap jumps or short freezes during playback. * Fixed an issue where the player sometimes would play only audio after a stall when playing an HESP stream. * Fixed an issue where the device motion controls were not working in VR on mobile Chrome browsers. * Fixed an issue where the device motion orientation in VR was inverted on mobile Chrome browsers. * Fixed an issue where VR videos were being rendered upside down on iPad Safari. ### Android #### ✨ Features * CMCD ad event reporting is now supported for all ad integrations (and no longer limited to just OptiView Ads). #### ⚡ Improvements * Added optional provider information to OptiView Live endpoints. #### 🐛 Issues * Fixed an issue where an OptiView Ads overlay stays onscreen indefinitely after it has expired. * Fixed an issue where Double Box and L-shape layouts were incorrectly shown in Picture-in-Picture mode with OptiView Ads. * Fixed an issue where the player would crash if OptiView Ads playback was started while in Picture-in-Picture mode. * Fixed an issue where the ads UI could get stuck after playing two consecutive ad breaks with OptiView Ads. ### iOS #### ✨ Features * Added support for customData on the Ad and AdBreak protocols. #### 🐛 Issues * Fixed an issue where the duration of OptiView Break Manifest Ads were not respected. * Fixed an issue where changing or clearing `player.source` while playing an HESP stream could cause the app to crash due to an internal race condition. * Fixed an issue where transitioning between IMA prerolls would cause the PiP window to close. * Fixed an issue where sources with multiple IMA prerolls would load trailing prerolls after the main content. * Fixed an issue where setting a source with an IMA ad while another source with an ad is playing would cause a crash. * Fixed an issue where scheduling IMA ads causes a momentary black screen in main content. * Fixed an issue where ads from different systems would overlap with OptiView Ads. * Fixed an issue in OptiView Ads where the layout would switch to single momentarily in between companion ads. * Fixed an issue where during ad playback in PiP, the original player view continues to playback main content. * Fixed an issue where double box and L-shape ads don't show in PiP. * Fixed an issue where Ads would remain in double box layout after finishing. ### Roku #### ✨ Features * Added client-side ad beaconing for MediaKind SSAI streams. * Added session beacons for the MediaKind connector. #### ⚡ Improvements * Removed automatic use of `START=Live` for live MediaKind streams. The client now can specify the live stream URL params. #### 🐛 Issues * Fixed an issue where during fallback on an OptiView Live stream the wrong endpoint was selected. ## 🚀 11.5.3 (2026/08/13) ### iOS #### 🐛 Issues * Fixed an issue where `bytesCached` and `bytes` properties would return 0 for cached sources pre 10.12.0. ## 🚀 11.5.2 (2026/08/10) ### iOS #### 🐛 Issues * Fixed an issue where legacy `CachingTask`s encoded without `bytes` and `bytesCached` properties would fail to restore. ## 🚀 11.5.1 (2026/07/03) ### iOS #### 🐛 Issues * Fixed an issue when playing cached sources where the player could switch to audio/video renditions that were not cached. ## 🚀 11.5.0 (2026/06/16) ### General #### ⚡ Improvements * Changed the CMCD event report MIME type to `application/cmcd` per the CTA-5004-B specification. ### Web #### ✨ Features * Added support for OptiView Discovery V3 distributions. * Added support for `ignoreEmbeddedTextTrackTypes` for HESP streams. #### ⚡ Improvements * Improved ABR performance on HESP streams, which should result in fewer stalling and increased stable playback at higher quality. #### 🐛 Issues * Fixed an issue where calling `player.destroy()` while playing an ad would throw an uncaught `TypeError`. * Fixed an issue where [ID3 metadata carried inside CMAF](https://aomediacodec.github.io/id3-emsg/) would end up in separate ID3 text tracks if the `scheme_id_uri` or `value` of the encapsulating `emsg` boxes were different. All ID3 cues will now always end up in a single ID3 text track, and the `value` of the `emsg` is ignored. This matches the existing behavior on Android and iOS. * Fixed an issue where an HESP stream failed to load on an iOS WebView. * Fixed an issue where `emsg` cues were never removed while playing an HESP stream. * Fixed a regression where the "exit fullscreen" button was no longer visible when the player encounters a fatal error while playing in fullscreen mode using the legacy UI. * Fixed an issue where the `encrypted` event was not properly dispatched when playing a DRM-protected stream. * Fixed an issue where the player stalls for a while when switching back from ad to content with an OptiView Ads stream. #### 👎 Deprecations * `player.theoLive.badNetworkMode` is no longer supported. The player's ABR has been improved to quickly switch to a lower quality when the network conditions worsen, even when playing at very low latency. This API is now a no-op, and will be removed in the next major version. ### Android #### ✨ Features * Added `Abr.preferredMaximumResolution` to cap the maximum video resolution selected at runtime. Also `player.abr.abrStrategy` is now used for Millicast playback when `MillicastSource.connectOptions` is not set. * Added `hlsDateRange` property to `THEOplayerConfig` to enable HLS date range parsing at the player level. #### ⚡ Improvements * Updated the Millicast SDK to version 2.6.0 which adds `maxWidth` and `maxHeight` constraints to viewer layer selection, allowing resolution-based filtering without selecting a specific simulcast layer. * `THEOplayerConfig` now fully supports using Kotlin property syntax. #### 🐛 Issues * Fixed an issue where an ad with Double Box layout shifted the content and ad players outside the video frame. * Fixed an issue when `CastStrategy.AUTO` had no effect on startup and a Cast device was not re-connected due to regression in Cast SDK. Cast Framework has been downgraded to the latest stable version `21.5.0`. * Fixed an issue where a `NullPointerException` could be thrown if the player enters picture-in-picture mode when its `THEOplayerView` is not properly attached to a parent `Activity`. * Fixed a crash when `setSource` was called on a destroyed player. ### iOS #### 💥 Breaking Changes * For non OptiView Live sources, a `CMCDConfiguration` or `CMCDSourceConfiguration` is now expected to enable CMCD reporting. #### ✨ Features * Added support for OptiView Discovery V3 distributions. * Introduce configuring ABR strategy and maximum resolution for Millicast sources. #### 🐛 Issues * Fixed an issue where the startTimes of DateRangeCues are incorrect if the player applies a seek while the cues are being processed. * Fixed an issue in OptiView Ads where no `AD_BREAK_END` error was dispatched after an interstitial error. * Fixed an EXC\_BREAKPOINT crash in CMCDState.Builder.build() caused by NaN or infinite values in CMCD measurements during DVR seek operations. * Fixed an issue where CMCD would be active even when not configured. * Fixed an issue where daterange cues with identical startTimes were not processed correctly. * Fixed an issue where OptiView Live specific CMCD status would not be reported. ### Roku #### ✨ Features * Added basic support for CMCD event mode reporting of DRM and ad events. * Added in support for custom SSAI integrations and SSAI ad descriptions that use ad tag parameters. * Added support for OptiView Live discovery v3 distributions. * Added MediaKind connector with support for playback of DRM streams. * Added the Capabilities API to the SDK for help getting device capabilities. #### 🐛 Issues * Fixed an issue with tag remover's handling of URIs that started with "data:". ## 🚀 11.4.0 (2026/06/03) ### Web #### ✨ Features * Added basic support for CMCD event mode reporting of DRM and ad events. * Added `Quality.enabled` to enable or disable a quality. When a quality is disabled, it will be ignored during ABR selection or when setting `MediaTrack.targetQuality`. * Added support for `breakManifestUrl` for OptiView Ads streams. #### 🐛 Issues * Fixed an issue with OptiView Ads where setting overlay positions (top/bottom/left/right) to 0 was not supported. * Fixed an issue where the player could crash on Sky Glass/Puck when `autoplay` is set to `true`. * Fixed an issue where using WebXR on iOS Safari was preventing VR video from being rendered inline. * Fixed an issue where clicking the "LIVE" button on an HLS live stream would sometimes seek to the start of the DVR window instead of the live edge when using native HLS playback on macOS/iOS Safari. * Fixed an issue on the FireTV AFTM model where playback stalls were made worse by trying to recover too fast. * Fixed an issue where HESP streams with PlayReady DRM would repeatedly stall while playing in Edge on Windows. * Fixed an issue where VAST ads containing both an ad pod and an ad buffet failed to play through the Google IMA integration. #### ⚠ Known Limitations * When starting a Chromecast session while playing a Google IMA ad break with multiple ads, the Chromecast receiver may start playing from the first ad in the ad break even if it was already played before. ### Android #### ✨ Features * Added basic support for CMCD event mode reporting of DRM and ad events. * Added support for OptiView Discovery V3 distributions. * Added support for Break Manifest in OptiView Ads. * Added `CustomAdDescription`, allowing custom ad integrations to create custom ad descriptions that can be passed through `SourceDescription.ads`. #### 🐛 Issues * Fixed an issue when the player suddenly switched to the highest possible quality right after a stall while playing an HESP stream. * Fixed an issue when player crashed if there was a cache download background task and the system required to stop it. ### iOS #### ✨ Features * Enabled OptiView Ads overlays to be signaled through SSE. * Added basic support for CMCD event mode reporting of DRM and ad events. #### ⚡ Improvements * Improved OptiView Streaming (THEOlive) endpoint fallback selection logic. * Improved fallback recovery speed for OptiView Streaming (THEOlive) on iOS 26. #### 🐛 Issues * Fixed an issue where IMA ad errors caused content playback to fail completely. * Fixed an issue in the IMA integration where pausing ad playback did not work when scheduled via `requestAds`. * Fixed an issue where no `error` event was sent during playback on iOS 26 with a bad network connection. * Fixed an issue in OptiView Ads where dismissing the player would cause a crash. ### Chromecast CAF #### 🐛 Issues * Fixed an issue with OptiView Ads where setting overlay positions (top/bottom/left/right) to 0 was not supported. ### Roku #### ✨ Features * Added `currentsourcechange` event when the currently playing stream changes. * Added ability to call `setContentId`, `setContentLength`, and `setContentGenre` on RAF via methods of the same names on `player.ads.rafProxy`. Also added the `setRafMetadata` method to `player.ads.dai` for setting the same data during DAI playback. #### 🐛 Issues * Fixed the order of events on startup so they emit at the correct times. ## 🚀 11.3.0 (2026/05/18) ### Web #### ✨ Features * Added support for OptiView Live Streams with JWT token security on iOS Safari. Note that this requires a long-lived token that remains valid for the entire playback session. For short-lived tokens, we recommend [using a service worker](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/theolive/token-based-security.md#short-lived-tokens-using-service-worker). * Added support for DRM-protected OptiView Live Streams with JWT token security on macOS Safari. Note that this requires a long-lived token that remains valid for the entire playback session. #### 🐛 Issues * Fixed an issue where date range cues were not correctly created when playing an HLS media playlist (without multi-variant playlist) on iOS Safari. * Fixed an issue where the player would still switch to an ad layout when seeking into the ad while the ad is not available for an OptiView Ads Stream. ### Android #### ⚡ Improvements * Modify the Millicast SDK dependency in the Millicast Integration to start accepting patch version updates (2.5.x) #### 🐛 Issues * Fixed an issue where `TextTrackStyle.setFont()` did not override the font family defined in the subtitle within the stream. ### iOS #### ⚡ Improvements * Modify the Millicast SDK dependency in THEOplayerMillicastIntegration to allow patch version updates (2.5.x). ### Roku #### 🐛 Issues * Added `adexitrequested` event to Ads API to allow applications to handle back button presses during clientside ad playback. ## 🚀 11.2.0 (2026/05/07) ### Web #### ✨ Features * Exposed `traffickingParametersString` for ads in `google-dai` sources. * Added support for `#EXT-X-KEY` tags for PlayReady with CENC encryption for HLS streams. * Added support for `replaceContent` for OptiView Ads streams. #### 🐛 Issues * Fixed an issue where the player would not have the correct `seekable`, `buffered` and `duration` during ad playback in a VOD OptiView Ads stream. * Fixed an issue where the player would stutter when playing a muxed HLS stream on MacOS Safari unmuted. * Fixed an issue where switching to an OptiView Live stream with JWT token security enabled while casting to Chromecast did not correctly pass along the JWT token to the receiver. * Fixed an issue where the playhead would snap back during scrubbing on mobile browsers. ### Android #### 💥 Breaking Changes * Ads insertion metadata request will now be postponed from `setSource` call till the `play` call happens as the default `PreloadType` is `NONE`. Changing `PreloadType` to `AUTO` or `METADATA` brings the old behaviour back: `setSource` will fetch ads insertion metadata. #### ✨ Features * Added support for loading Google DAI metadata in a lazy manner. Use `PreloadType.NONE` to delay loading metadata until `Player.play()` is called. If `PreloadType` is `AUTO` or `METADATA` the behaviour stays unchanged: the load request will be executed as soon as `setSource` is called. #### 🐛 Issues * Fixed an issue where the `PLAYING` event was fired again after the end of an ad break when using OptiView Ads. ### iOS #### ⚡ Improvements * Updated the minimum required Google IMA SDK version to 3.31 to include the latest improvements. #### 🐛 Issues * Fixed an issue where subtitles were not correctly positioned when changing the player `aspectRatio` property. * Fixed an issue where no duration change event was dispatched before an HLS live source ended. * Fixed an issue where the player does not retain picture-in-picture mode when main content is resumed after an ad playback. * Fixed an issue where non-fatal coreMediaError surfaced as fatal errors. ### tvOS #### ⚡ Improvements * Updated the minimum required Google IMA SDK version to 4.16 to include the latest improvements. ### Chromecast CAF #### ✨ Features * Added support for playing Axinom DRM streams on Chromecast. #### ⚡ Improvements * Updated all CAF Chromecast applications to use Shaka 4.15.36 by default to enable HLS-CMAF streams and improve OptiView Live playback. Also introduced an MPL-only CAF Chromecast application that does not use Shaka. #### 🐛 Issues * Fixed an issue with OptiView Live streams reloading after being cast. * Fixed an issue with keyOS DRM streams where the `customdata` property was lost when casting to the Chromecast CAF app. ### Roku #### ✨ Features * Added in support for DRM on THEOlive streams. Also added new configuration options for content protection. #### 🐛 Issues * Fixed an issue with no playback when switching between two SSAI streams. * Fixed an issue where the Adobe analytics task node was not being properly cleaned up when the connector is destroyed. Added a param to the connector's `destroy` method to force the Adobe analytics task node to be destroyed when the connector is destroyed. ## 🚀 11.1.0 (2026/04/28) ### Web #### ✨ Features * Added `Player.version` as an alias for the global `THEOplayer.version`. #### 🐛 Issues * Fixed an issue where `player.hesp.latencies` would return `undefined` for some OptiView Live streams. * Fixed an issue where `preferredKeySystems` was ignored when using the `keyos` pre-integration. * Fixed an issue where the player would error or stall when playing an HLS MPEG-TS stream with a small audio-only segment before a discontinuity. * Fixed an issue where an incorrect segment URL was being requested in a DASH live stream when the player receives an MPD update that re-adds a previously removed segment in a ``. ### Android #### 🐛 Issues * Fixed an issue where the `durationchange` event was not fired before the `ended` event when an HLS live stream ends with `#EXT-X-ENDLIST`. * Fixed an issue where switching between two DRM-enabled HESP OptiView Live streams with identical DRM configurations could crash the player. * Fixed an issue where Cast state events are not fired if `CastStrategy.MANUAL` is used. The issue resulted in Cast button not being shown. ### iOS #### 💥 Breaking Changes * As announced in the [11.0.0 release changelog](#-1100-20260416), THEOplayer iOS SDK and its integrations will now be built with Xcode 26 instead of Xcode 16. #### 🐛 Issues * Fixed an issue where the aspect ratio was not properly updated while playing an OptiView Real-time stream. * Fixed an issue where `THEOliveAPI.latencies` would return `nil` for some OptiView Live streams. * Fixed an issue where OptiView Ads triggered through ID3 would fail to load correctly. * Fixed an issue where MP3 sources with the `audio/mpeg` mimetype were not recognized correctly. ### Chromecast CAF #### ✨ Features * Added `Player.version` as an alias for the global `THEOplayer.version`. ### Roku #### 🐛 Issues * Fixed an issue where using an array of URLs as `source.sources` would cause an error. * Fixed an issue where the license expired text would show momentarily while renewing a license. * Fixed issue where VMAP ads would not play. * Made stall detection logic less prone to false positives. ## 🚀 11.0.0 (2026/04/16) We are happy to announce the eleventh major version of THEOplayer, releasing all the improvements and developments our team has achieved since version 10.0. THEOplayer 11.0 includes **some breaking changes per SDK**. Please review them carefully in the respective changelog for your SDK. * The iOS SDK now supports iOS 15 and tvOS 15 and higher, dropping support for iOS/tvOS 13 and 14. * On Android, the Google IMA integration has been updated to support version 3.39.0 of the Google IMA SDK. This requires core library desugaring to be enabled in your app. For more info on navigating our breaking changes, take a look at our migration guides for [Web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/migrating-to-theoplayer-11.md), [Android](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/android/migrating-to-theoplayer-11.md), [iOS](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/ios/migrating-to-theoplayer-11.md) and [React Native](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/frameworks/react-native/migrating-to-react-native-theoplayer-11.md). ### Web #### 💥 Breaking Changes * Removed `preloadPublications` in THEOlive API. ### Android #### 💥 Breaking Changes * All methods on `Player` and `THEOplayerView` must only be called from the main thread and are annotated with `@MainThread`. Calling these methods from a different thread will throw an `IllegalStateException`. * The Google IMA SDK integration now requires [core library desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring) to be enabled. See [our updated guide for Google IMA](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/android/ads/google-ima.md#updating-the-google-ima-sdk) for instructions. * Removed `preloadChannels` in THEOlive API. * Changed `MediaTailorAdAvail.id` to return a `String` instead of an `Int`, to align with `AdBreak.id`. * Removed `TheoAdsErrorEvent`, use `AdErrorEvent` instead. * Removed `TheoAdsEventTypes.THEOADS_ERROR`, use `AdsEventTypes.AD_ERROR` instead. #### ✨ Features * Added `AdBreak.id`, to replace the (now deprecated) `AdBreak.idAsString` property. #### ⚡ Improvements * Updated Google IMA SDK to version 3.39.0. #### 🐛 Issues * Fixed `Event`, `EventListener` and `EventDispatcher` to not use raw types in their generic type parameters. * Fixed a `NullPointerException` fired by the IMA SDK on ad request. #### 👎 Deprecations * Deprecated `AdBreak.idAsString`, use `AdBreak.id` instead. ### iOS #### 💥 Breaking Changes * The minimum supported iOS/tvOS version is now 15.0, dropping support for iOS/tvOS 13 and 14. * This update aligns with current Xcode tooling requirements and enables us to maintain a high standard of performance, security, and long-term support for the SDK. All devices that support iOS/tvOS 13 and 14 are capable of upgrading to iOS/tvOS 15 or later, meaning no active hardware is excluded by this change. Additionally, based on our internal analytics, fewer than 1% of end users remain on versions below iOS/tvOS 15, and those users are predominantly on older SDK versions. This change allows us to focus development efforts on modern platform capabilities while minimizing impact to production environments. * THEOplayer iOS SDK and its integrations will be built with Xcode 26 instead of Xcode 16 in an upcoming minor 11.x version. * This aligns with [Apple's announcement](https://developer.apple.com/news/?id=ueeok6yw) that apps must be built with the iOS 26 & tvOS 26 SDKs starting April 28, 2026. * Please note that this is only an announcement, but not an immediate change for THEOplayer SDK. Please check upcoming release changelogs for more information. * Removed deprecated `ManifestInterceptor` and `DeveloperSettings` APIs in favor of `NetworkAPI`. * Renamed `playerMetrics` API to `metrics`. * Changed `Ad.adBreak` type to be optional. * Removed deprecated `MediaTrack.activeQualityBandwidth` property in favor of `MediaTrack.activeQuality.bandwidth`. * Removed deprecated `SourceDescription.enableStreamingDVR` property. * Errors in `CachingTaskErrorStateChangeEvent` now dispatch cache related error codes instead of `NETWORK_ERROR` code. For more details, check the `cause` property of the error. * Removed `preloadPublications` in THEOlive API. #### ✨ Features * Starting with 11.0.0, the THEOplayer iOS SDK will be released in a public self-hosted spec repo. * To get the SDK from our hosted repo, simply add `source 'https://github.com/THEOplayer/cocoapods-specs'` to your `Podfile`. * This decision comes as a solution to [the planned read-only change to the CocoaPods public spec repo at the end of 2026](https://blog.cocoapods.org/CocoaPods-Specs-Repo/). * For now, we will keep publishing to both sources, but we highly recommended switching to THEOplayer's spec repo. Starting with 12.0.0 later this year, we plan to stop publishing to the CocoaPods public spec repo. #### 👎 Deprecations * Deprecated the old Chromecast pipeline in favor of the new experimental pipeline. * The new Chromecast pipeline introduced in version 10.6.0 offers new features and improved performance. Over time and with more positive feedback, we are heading towards switching to the new pipeline by default. * The old pipeline is still enabled by default. However, we highly recommend customers switch to the new pipeline by setting `enableExperimentalPipeline` to `true` in `CastConfiguration`. * Starting with 12.0.0 later this year, we plan to retire the `enableExperimentalPipeline` property together with the old pipeline, making the new pipeline the default for all customers. * Changed the return types of `player.cast` and `player.cast.airplay` to be non-optional. ### Roku #### 💥 Breaking Changes * Removed deprecated `bitratechange` event. --- # Connectors for THEOplayer ## [Connectors for Web SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/connectors/web.md) ## [Connectors for Android SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/connectors/android.md) ## [Connectors for iOS & tvOS SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/connectors/ios.md) ## [react-nativeConnectors for React Native SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/connectors/react-native.md) ## [Connectors for Roku](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/connectors/roku.md) --- # THEOPlayer 🤝 Sideloaded TextTracks Samples We provide source code of two sample applications that illustrate how to use the connector with different package managers: * [Sample using **Cocoapods**](https://github.com/THEOplayer/iOS-Connector/blob/-/Code/Sideloaded-TextTracks-Examples/Cocoapod) (requires Cocoapods installation through terminal) --- # @theoplayer/react-native-analytics-npaw ## 1.1.0 ### ✨ Features * * Restricted the supported OptiView (THEOplayer) player versions to v10 and v11. On Android, the default `THEOplayer_sdk` version range is now `[10.0.0, 12.0.0)` for every connector. ## 1.0.0 * Initial release. --- # THEOplayer React Native NPAW Connector An [NPAW](https://npaw.com/) (formerly YOUBORA) analytics connector for `@theoplayer/react-native`. This connector is JavaScript-only and works across iOS, Android and web through the unified `react-native-theoplayer` player, using only the `npaw-plugin-react-native` plugin — no native iOS/Android modules are required. > **Note:** This connector targets **NPAW v7+** (it depends on `npaw-plugin-react-native` `^7.3.x`). ## Installation ```sh npm install @theoplayer/react-native-analytics-npaw ``` ## Usage ### Configuring the connector Create the connector with the `useNpaw` hook, passing a config object and initializing it once the `THEOplayer` instance is ready: ```tsx import { LogLevel, useNpaw } from '@theoplayer/react-native-analytics-npaw'; const App = () => { const [npaw, initNpaw] = useNpaw({ accountCode: 'your-account-code', analytics: { 'content.title': 'Big Buck Bunny', 'content.isLive': false, // Free-form custom metadata shown in the NPAW "Metadata" panel: 'content.metadata': { genre: 'animation' }, }, // Optional NpawPlugin options (host, sessionRecovery, ...): plugin: { sessionRecovery: 'auto' }, logLevel: LogLevel.DEBUG, }); const onPlayerReady = (player: THEOplayer) => { initNpaw(player); }; return ; }; ``` Alternatively, construct the `NpawConnector` class directly: ```ts import { NpawConnector } from '@theoplayer/react-native-analytics-npaw'; const connector = new NpawConnector(player, { accountCode: 'your-account-code' }); ``` ### Configuration | Field | Description | | ------------- | --------------------------------------------------------------------------------------------------------- | | `accountCode` | Your NPAW account code. **Required.** | | `analytics` | Per-video analytics options, e.g. `content.title`, `content.isLive`, `content.metadata`, `user.name`, ... | | `plugin` | `NpawPlugin` options such as `host`, `sessionRecovery` and `balancerAnalyticsEnabled`. | | `logLevel` | Initial `LogLevel`. | ### What is tracked automatically Once initialized, the connector registers a video adapter (and an ads adapter) against the player and reports playback automatically: * Playback events (start, join, pause/resume, buffering, seeking, end and errors) mapped from the unified `PlayerEventType` events. * Playhead, duration and live/VOD state, resource, rendition and bitrate. * The active subtitle/caption track language (`getSubtitles`) and audio language (`getVideoLanguage`). * Ads: ad breaks, ad start/join/stop, quartiles, skip, click and errors, with ad position (`pre`/`mid`/`post`), duration and skippability. ### Updating options at runtime ```ts // Update options for the current video (e.g. late-arriving metadata): npaw.current?.setVideoOptions({ 'content.title': 'New Title' }); // Update global analytics options: npaw.current?.setAnalyticsOptions({ 'user.name': 'viewer-1' }); // Change the log level: npaw.current?.setLogLevel(LogLevel.SILENT); // Stop analytics and close all sessions: npaw.current?.destroy(); ``` See the [NPAW integration documentation](https://documentation.npaw.com/integration-docs/docs/) for the full set of plugin and analytics options. --- # Getting started > **OptiView Rebranding** > > OptiView Player is the new name for THEOplayer as part of the OptiView product suite. During the transition, you may still see references to THEOplayer. OptiView Player and THEOplayer refer to the same product. OptiView Player offers a rich portfolio of video SDKs for many platforms and frameworks across desktop, mobile and TV devices. Our dedicated guides help you get started right away. ## [OptiView Player Web SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md) ## [OptiView Player Android SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/android/getting-started.md) ## [OptiView Player iOS & tvOS SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/ios/getting-started.md) ## [OptiView Player React Native SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/frameworks/react-native/getting-started.md) ## [OptiView Player Flutter SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/frameworks/flutter/getting-started.md) ## [OptiView Player Chromecast SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/chromecast/getting-started.md) ## [OptiView Player Roku SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/roku/getting-started.md) --- # How to update an SDK? This how-to guide describes how to update to a new version of the THEOplayer SDK. > **Info** > > Starting from version 2.79.0, you can make use of License as a Configuration with Web SDK. This allows you to reuse the same library when changing your license. It also allows you to make use of our NPM modules instead of using the THEOplayer zip-file. In order to use this functionality, there is a change needed in your THEOplayer integration. You can read more about this [here](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/license/introduction.md). > > We highly recommend using this new license system due to all the advantages it brings. ## Update Web SDK Typically, next to using [NPM](https://www.npmjs.com/package/theoplayer), you grab a CDN URL or a downloadable ZIP file through , which gives you access to URLs like those below: * LIBRARY LOCATION: `https://cdn.myth.theoplayer.com//` * CDN ZIP LINK: `https://portal.theoplayer.com/download/?h=1643658986` Configuring THEOplayer through the "Library Location" implies a **cloud-hosted (CDN)** approach. Downloading (and configuring) the ZIP (or using NPM) implies a **self-hosted (ZIP file)** approach. ![Update SDK](/pr-873/assets/images/update-sdk-5f00e7ff6ef3854952cf07f2562b3d08.png) ### 1. Cloud-hosted Every SDK that you create through is associated with a specific THEOplayer version. When you're doing cloud hosting, you're referring to THEOplayer's CDN to load the relevant JavaScript, for example `https://cdn.myth.theoplayer.com//THEOplayer.js`. To change the version of this specific SDK, you use the GUI at to change it to a different version, and hit the "Save & Publish" button after making your changes. Your video player will now automatically start using this new version, but do note that the CDN and browser cache might still be referring to your older version for a little while. If your browser is still not fetching the new JS libraries, try hard refreshing your browser tab first. ![Save \& Publish](/pr-873/assets/images/republish-7d7f211f07d91032ae93414a82528d26.png) ### 2. Self-hosted Alternatively, you could extract the included zip file and host the library yourself. In this case, the steps are similar to the above steps, but you'll also need to re-download your SDK to grab the new THEOplayer files. When you extract the ZIP file, you will see the following 'helper files': * `theoplayer.d.js` * `theoplayer.e.js` * `theoplayer.p.js` It is **important** these files are placed in the **same directory as the core library (THEOplayer.js)**. **It is crucial to also update the libraryLocation as seen in the snippet above. Not doing will result in playback failure.** If you're using [THEOplayer through NPM](https://www.npmjs.com/package/theoplayer), then you should replace the version of THEOplayer in your `package.json` file. ## Other SDKs Updating the iOS SDK, Android SDK and Roku SDK is similar with regards to self-hosting the SDK. The iOS SDK and Android SDK can also be managed through CocoaPods and Maven respectively. You cannot "cloud host" these SDKs. --- # SDKs Dolby OptiView Player provides a set of feature-rich video player software development kits (SDKs). Each SDK targets a set of platforms. For example, a developer integrates the iOS SDK in their iOS app. Dolby OptiView Player offers the following SDKs. (Clicking the link takes you to the respective getting started guide.) * HTML5/Tizen/webOS SDK * [Web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md) targets (desktop and mobile) browsers, HTML5-based smart TVs, progressive web apps, and other HTML5-based platforms. * [webOS](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/webos/getting-started.md) targets LG webOS apps. * [Tizen](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/tizen/getting-started.md) targets Samsung Tizen apps. * [Chromecast](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/chromecast/getting-started.md) targets (custom) Chromecast apps. * Android/Android TV/Fire TV SDK * [Android](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/android/getting-started.md) targets Android, Android TV and Fire TV apps. It may also be used for other Android-based platforms. * iOS/tvOS SDK * [iOS & tvOS](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/ios/getting-started.md) targets iOS, iPadOS and tvOS apps. * Roku SDK * [Roku](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/roku/getting-started.md) targets Roku apps. * [React Native](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/frameworks/react-native/getting-started.md) SDK Want to update an existing SDK instead? Follow [this guide](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/how-to-update-a-sdk.md). > **Looking for the Legacy Android and iOS/tvOS (4.12.x) SDKs?** > > The legacy Android and iOS/tvOS SDKs (version 4.12.x) are no longer supported. However, you can still access their documentation through the following links: > > * Android/Android TV/Fire TV Legacy (4.12.x) SDK > > * [Android](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v4/getting-started/sdks/android/getting-started) targets Android apps, and may be used for other Android-based platforms. > * [Android TV](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v4/getting-started/sdks/android-tv/getting-started) targets Android TV apps. > * [Fire TV](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v4/getting-started/sdks/fire-tv/getting-started) targets Fire TV apps. > > * iOS/tvOS Legacy (4.12.x) SDK > > * [iOS](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v4/getting-started/sdks/ios/getting-started) targets iOS and iPadOS apps. > * [tvOS](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v4/getting-started/sdks/tvos/getting-started) targets tvOS (i.e. Apple TV) apps. --- # OptiView Hosted Player Dolby hosts a special version of the OptiView video player for use with customers intended to be used for ` ``` --- # OptiView Player Web SDK > **OptiView Rebranding** > > OptiView Player is the new name for THEOplayer as part of the OptiView product suite. During the transition, you may still see references to THEOplayer. OptiView Player and THEOplayer refer to the same product. Dolby OptiView Player is the universal video player solution from Dolby. It offers support for HLS, MPEG-DASH, advertisements, DRM and much more. On these pages, you'll learn how to get started with Dolby OptiView Player, how to use the various features, and explore the many examples. ## [🚀Getting started](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md) [Set up your first THEOplayer in just a few minutes!](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md) ## [📖How-to guides](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides.md) [Learn how to implement our rich set of features and integrations.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides.md) ## [🎨UI](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/introduction.md) [Choose a ready-made UI, customize with Open Video UI, or build from scratch.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/introduction.md) ## [🛝Examples](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/examples.md) [See the player in action!](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/examples.md) ## [🔌Connectors](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/connectors/web.md) [Integrate third-party solutions with THEOplayer using our pre-built connectors.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/connectors/web.md) ## [📰Changelog](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/changelog.md) [Find out what's new in the OptiView Player.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/changelog.md) ## [\*️⃣API references](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web) [Discover all properties and functions of THEOplayer.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web) ## [🧠Knowledge base](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/knowledge-base.md) [Learn about topics in the video industry from our experts.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/knowledge-base.md) ## [❔FAQ](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq.md) [Answers to common questions we've received from our customers over the years.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq.md) --- # Getting started on Web > **OptiView Rebranding** > > OptiView Player is the new name for THEOplayer as part of the OptiView product suite. During the transition, you may still see references to THEOplayer. OptiView Player and THEOplayer refer to the same product. Dolby OptiView Player is the universal video player solution from Dolby. The HTML5/Tizen/webOS Player SDK offers support for HLS, MPEG-DASH, advertisements, DRM and much more. This article is your starting point if you are using Dolby OptiView Player for the first time. It takes you through the required steps to achieve a basic manual implementation of the Player SDK on Web. The outcome of this article is the template below. ```html THEOplayer
``` > **Note** > > When copy-pasting the above code, make sure to replace the placeholders `YOUR-LICENSE-STRING` and `SDK-LIBRARY-LOCATION` with your license and library location respectively. [See below for more information.](#build-configure-and-publish-sdk) ## Prerequisites Before we can start using THEOplayer, you'll need a few things: 1. A web server and domain name to host your code and provide access to viewers. 2. A THEOplayer SDK and license to implement the THEOplayer library and access the THEOplayer API. 3. A video stream (e.g. an `.m3u8` or `.mpd` file) to show to your viewers through the THEOplayer API. ### Installing THEOplayer Web SDK The THEOplayer Web SDK is available on npm: . Installation can be done using your favorite package manager for Node (such as `npm` or `yarn`): ```bash npm install theoplayer ``` You can also install a specific version instead: ```bash npm install theoplayer@7.0.0 ``` Different THEOplayer packages are available on npm with their own included features. Choose the package which best fits your requirements. In the rest of the guide we reference `SDK-LIBRARY-LOCATION` which should become the path to the installed theoplayer package in your node modules, similar to: `path/to/node_modules/theoplayer/`. > **Custom Web SDK** > > We do recommend downloading the THEOplayer Web SDK through npm, but if you need a custom SDK with a different combination of features it is possible to create one on [the Portal](https://portal.theoplayer.com/login). Skip to [Create an SDK through Portal](#create-an-sdk-through-portal) for a detailed explanation. ### Getting a THEOplayer License Creating a Web SDK on the Portal can be achieved by the following steps: 1. [Register and sign in](#register-and-sign-in) 2. [Initiate license creation](#initiate-license-creation) 3. [Build, configure and publish license](#build-configure-and-publish-license) #### Register and sign in You register with your GitHub or Google account through the [sign-in page](https://portal.theoplayer.com/login), or complete the [signup form](https://portal.theoplayer.com/register). You sign in with your GitHub or Google account, or with your credentials, through the sign-in page. #### Initiate license creation On the THEOplayer Developer Portal dashboard, click the Create button in the licenses panel, as annotated with a red arrow in the following screenshot. ![THEOplayer Developer Portal\'s dashboard](/pr-873/assets/images/portal-dashboard-b6d20084c1941c5e96902bd8c3a4a92e.png) Alternatively, you create a license through the SDKs page. * You click the SDKs item in the side navigation panel to navigate to the overview of your existing SDKs. * The content panel of the SDKs page presents action buttons. Click the Create button to initiate the license creation process, as highlighted in the screenshot below. ![THEOplayer Developer Portal SDK Page](/pr-873/assets/images/portal-sdk-b439507beb50e04557eefb9e763db28b.png) #### Build, configure and publish license Building your license involves three steps. 1. Choose platform – Select the HTML5 icon highlighted with the red circle in the following screenshot. HTML5 applications (i.e. websites, progressive web apps) use the THEOplayer Web SDK. 2. Configure license – Name the license and configure your domain whitelisting settings. The latter allows your license to only function for some websites and/or streams. 3. Review and publish – Validate your configuration and click "Publish", or go back. ![Choose platform](/pr-873/assets/images/portal-choose-platform-23ce30a0d4c2ea5364a6429c679bfb39.png) After you complete the third step, "Review and Publish", you will get your unique LICENSE which should replace `YOUR-LICENSE-STRING` in the examples. ![License generated](/pr-873/assets/images/portal-license-86831573290e72f6ab522fba40572050.png) ### Create an SDK through Portal > **Info** > > We recommend [downloading the THEOplayer SDK through npm](#installing-theoplayer-web-sdk) for most use cases. If you need an SDK with custom features, you can follow the next steps. Otherwise, skip to [Basic setup](#basic-setup). Creating a Web SDK on the Portal can be achieved by the following steps: 1. [Register and sign in to Portal](#register-and-sign-in-to-portal) 2. [Initiate SDK creation](#initiate-sdk-creation) 3. [Build, configure and publish SDK](#build-configure-and-publish-sdk) #### Register and sign in to Portal You register with your GitHub or Google account through the sign-in page, or complete the signup form. You sign in with your GitHub or Google account, or with your credentials, through the sign-in page. #### Initiate SDK creation To create an SDK through Portal similar steps should be followed as generating a license only. On the dashboard or in the SDKs menu, also click the Create button. Important here is to make sure you are on the CUSTOM tab as this will allow creating a custom SDK with a license instead of only a license. ![THEOplayer Developer Portal\'s dashboard custom](/pr-873/assets/images/portal-custom-sdk-5d9ea6fb504a6ebe49b949014e1dc9a6.png) Alternatively, when following the steps to generate only a license there will be redirect link to the steps for creating a custom THEOplayer SDK. ![Custom SDK redirect](/pr-873/assets/images/portal-sdk-redirect-fd80f250b0a7d315392169ff414ea16e.png) #### Build, configure and publish SDK Building your Web SDK involves four steps. 1. Choose platform – Select the HTML5 icon highlighted with the red circle in the following screenshot. HTML5 applications (i.e. websites, progressive web apps) use the THEOplayer Web SDK. 2. Configure SDK – Name the SDK and configure your domain whitelisting settings. The latter allows you to restrict your SDK to only function on your own websites and/or streams. 3. Select features – Enable certain features (e.g. HLS advertising) and disable others (e.g. Chromecast). 4. Review and publish – Validate your configuration and click "Publish", or go back. ![Build SDK](/pr-873/assets/images/portal-build-sdk-246e4b4384d6eb310935338612b5a560.png) After you complete the fourth step, "Review and Publish", it'll take the portal a couple of minutes to generate your unique SDK. Once you see the Ready! button, as the screenshot below highlights with the red rectangle, you know that your SDK is available. ![SDK ready](/pr-873/assets/images/portal-sdk-ready-5d3aa3e1aed98d820b77eb79e0f4523f.png) The following sections of this guide reference `SDK-LIBRARY-LOCATION` which should be replaced with the SDK library location URL. Similarly, `YOUR-LICENSE-STRING` should be replaced with the LICENSE ## Basic setup Now, we're ready to set up THEOplayer on your website. This guide explains how you implement THEOplayer in four steps: 1. We'll [start from a basic HTML file](#step-1-set-up-an-html-file), with no mention of THEOplayer. 2. In the [second step](#step-2-including-the-theoplayer-library), we'll explain what the THEOplayer library is and how you can include it. We'll do the same for the default THEOplayer UI. 3. In the [third step](#step-3-the-video), we'll describe how you play a video stream through THEOplayer. 4. In the [fourth step](#step-4-the-result), we'll give a final overview. ### Step 1: Set up an HTML file When THEOplayer is used as a web video player, we will need a web page (i.e. HTML file) to embed THEOplayer and the code. Note that a THEOplayer SDK license can only be used on whitelisted domains. You configure these domains when you create a THEOplayer license at . Let's start with the following HTML file: ```html THEOplayer ``` Next, we’ll include the THEOplayer SDK. ### Step 2: Including the THEOplayer library To access the THEOplayer API and default UI, you must first include the THEOplayer library files. The JavaScript library exposes the THEOplayer API and the CSS library contains the default UI. > **Customize your player with Open Video UI** > > The Web SDK includes a basic default UI. When you need a more customized player, Open Video UI provides ready-made components that you can compose and style instead of building every control from scratch. [Get started with Open Video UI for Web.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/getting-started.md) #### THEOplayer JavaScript library In a previous step, we downloaded the [THEOplayer npm package](#installing-theoplayer-web-sdk) or created a [custom THEOplayer SDK](#create-an-sdk-through-portal). Now we'll include it in our HTML file. There are two options for this: * Option 1: in your HTML ```html ``` The ` ``` Let's break down the above snippet. #### Create video player container This `
` element creates a video player container. ```html
``` * The `theoplayer-container` class is used to fetch the container through JavaScript, as demonstrated by the snippet below. * The `video-js theoplayer-skin vjs-16-9` classes load the default THEOplayer UI. You may omit `vjs-16-9` if you don't want a 16:9 container. Setting the size of the container There are other approaches to setting the size of your container instead of using `vjs-16-9`. For example, you could omit the `vjs-16-9` class, and instead configure the width and height of container class through CSS. ```css .theoplayer-container { width: 200px; height: 200px; } ``` ![screenshot from a sample minimal page with THEOplayer, showing how to use the width and height to change the size of the player](/pr-873/assets/images/container-height-percent-f3c137751a4bcaf899cbe1717cfe5363.png) Alternatively, you set your container to be responsive to its parent node by setting `fluid: true` in your configuration, as demonstrated by the following screenshot. ```js var player = new THEOplayer.Player(element, { fluid: true // ...other options go here }); ``` ![screenshot from a sample minimal page with THEOplayer, showing how to use the playerConfig property fluid to regulate the player size](/pr-873/assets/images/container-height-fluid-8b4b6a4d519bed788ed2231a26a8b1ab.png) There is no wrong or right. It's up to you to decide which approach is best for your use-case. ```javascript var element = document.querySelector('.theoplayer-container'); ``` #### Create THEOplayer instance The snippet below creates a THEOplayer instance for a container with a specific [Player Configuration](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerConfiguration). ```js var player = new THEOplayer.Player(element, { // instantiates the video player libraryLocation: 'SDK-LIBRARY-LOCATION', // references folder containing your THEOplayer library files (theoplayer.p.js, THEOplayer.js, ...) license: 'YOUR-LICENSE-STRING', // references your THEOplayer SDK license }); ``` Similar to the JavaScript and CSS library, you must swap out `SDK-LIBRARY-LOCATION` and `YOUR-LICENSE-STRING`. You can read more about configuring the `license` at the [License section](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/license/introduction.md). #### Configure video stream The snippet below configures a HLS video stream for the THEOplayer instance. ```js player.source = { sources: [ { src: '//cdn.theoplayer.com/video/star_wars_episode_vii-the_force_awakens_official_comic-con_2015_reel_(2015)/index.m3u8', // sets HLS source type: 'application/x-mpegurl', // sets type to HLS }, ], }; ``` To configure a DASH stream instead, you must set `type` to `'application/dash+xml'` instead of `'application/x-mpegurl'`, as demonstrated by commented out code in one of the earlier snippets. In the next step we'll put everything together. ### Step 4: The result Use the snippet below on your web server. ```html THEOplayer
``` ## Bundler setup When using a [bundler](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview) such as [Webpack](https://webpack.js.org/), [Rollup](https://rollupjs.org/), [Vite](https://vitejs.dev/) or [Parcel](https://parceljs.org/) you may need some additional configuration to make your bundler copy all files needed by the THEOplayer Web SDK and set your `libraryLocation` correctly. > **Just give me the code!** > > If you prefer starting from a fully working example rather than copy-pasting these snippets into your code, check out [our examples on GitHub](https://github.com/THEOplayer/samples-npm-html5-sdk)! What is a `libraryLocation`? The THEOplayer Web SDK uses [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) to off-load several CPU-intensive tasks to a separate worker thread. By moving these tasks off the main thread, THEOplayer ensures that your app remains fast and responsive. The code for these workers is located in separate JavaScript files, outside the main THEOplayer library. The THEOplayer Web SDK ships with several of these files, such as: * `THEOplayer.transmux.js`: responsible for transmuxing MPEG-TS and AAC files to fragmented MP4. * `theoplayer.d.js`: responsible for decrypting HLS segments encrypted with full-segment AES-128 encryption. Additionally, some of these workers leverage [WebAssembly](https://developer.mozilla.org/en-US/docs/WebAssembly) for improved performance: * `THEOplayer.transmux.wasm`: the WebAssembly binary for transmuxing MPEG-TS and AAC files. These files need to be hosted on your app's server, so the main THEOplayer library can locate and load them when needed. You tell THEOplayer where you put those files by setting the `libraryLocation` in your [player configuration](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerConfiguration). JavaScript bundlers work by traversing your app's dependencies through `import` or `require()` statements in your source code. As such, they can't auto-magically detect that THEOplayer needs these extra files, since they are never directly imported. You'll need to configure your bundler to *also* copy these extra files to your app's output directory. ### Webpack Use [`copy-webpack-plugin`](https://webpack.js.org/plugins/copy-webpack-plugin/) to copy the THEOplayer files to the output directory. webpack.config.js ```js const CopyPlugin = require('copy-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: __dirname + '/dist', }, plugins: [ new CopyPlugin({ patterns: [ { context: 'node_modules/theoplayer/', from: './*.(css|js|html|wasm)', info: { minimized: true }, to: 'vendor/theoplayer/', }, ], }), ], }; ``` Then, configure your library location: src/index.js ```js import * as THEOplayer from 'theoplayer'; var player = new THEOplayer.Player(element, { libraryLocation: '/vendor/theoplayer/', license: 'YOUR-LICENSE-STRING', }); ``` ### Rollup Use [`rollup-plugin-copy`](https://webpack.js.org/plugins/copy-webpack-plugin/) to copy the THEOplayer files to the output directory. rollup.config.js ```js import copy from 'rollup-plugin-copy'; export default { input: { bundle: './src/index.js', }, output: { dir: './dist', }, plugins: [ copy({ targets: [ { src: './node_modules/theoplayer/*.(js|css|html|wasm)', dest: './dist/vendor/theoplayer', }, ], }), ], }; ``` Then, configure your library location: src/index.js ```js import * as THEOplayer from 'theoplayer'; var player = new THEOplayer.Player(element, { libraryLocation: '/vendor/theoplayer/', license: 'YOUR-LICENSE-STRING', }); ``` ### Vite Use [`vite-plugin-static-copy`](https://www.npmjs.com/package/vite-plugin-static-copy) to copy the THEOplayer files to the output directory. vite.config.js ```js import { defineConfig } from 'vite'; import { viteStaticCopy } from 'vite-plugin-static-copy'; export default defineConfig({ plugins: [ viteStaticCopy({ targets: [ { src: 'node_modules/theoplayer/*.(css|js|html|wasm)', dest: 'vendor/theoplayer/', }, ], }), ], }); ``` Then, configure your library location: src/index.js ```js import * as THEOplayer from 'theoplayer'; var player = new THEOplayer.Player(element, { libraryLocation: `${import.meta.env.BASE_URL}vendor/theoplayer/`, license: 'YOUR-LICENSE-STRING', }); ``` ### Parcel Use [`parcel-reporter-static-files-copy`](https://www.npmjs.com/package/parcel-reporter-static-files-copy) to copy the THEOplayer files to the output directory. .parcelrc ```json { "extends": ["@parcel/config-default"], "reporters": ["...", "parcel-reporter-static-files-copy"] } ``` package.json ```json { "name": "my-app", "staticFiles": { "staticPath": "node_modules/theoplayer/", "distDir": "dist/vendor/theoplayer/" } } ``` Then, configure your library location: src/index.js ```js import * as THEOplayer from 'theoplayer'; var player = new THEOplayer.Player(element, { libraryLocation: '/vendor/theoplayer/', license: 'YOUR-LICENSE-STRING', }); ``` ## Next steps Ready to learn more? * Go through our [how-to guides](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides.md) to learn about [UI customization](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/introduction.md), [advertising](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ads/introduction.md), [DRM](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/drm/introduction.md), [casting](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/cast/chromecast/introduction.md), [analytics](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/analytics/introduction.md) and other topics. * Check out our [API reference](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer). * Try out samples from our [GitHub](https://github.com/THEOplayer). --- # API examples This page contains an overview of examples, demonstrating the capabilities of the THEOplayer API and custom JavaScript. With these examples, we try to explain the fundamentals of how to use our API: * Accessing **player properties** and using **methods** (e.g. *currentTime*, *duration*, ...) , * Hooking to **events**, * Inserting DOM-elements (buttons, text, ...) in the **UI** * ... The examples are sorted per subject, and a link can re-appear if it also relates to another subject. New examples will be added from time to time, when new use cases arise, or when new developments happen. If you are new to THEOplayer, please read our [Getting Started on Web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md) or our [API pages](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web). ## Properties and methods * — Demonstrates how to use basic player properties and the *play()* method to create your own bookmarks, and load those settings into your active player. * — Demonstrates a use case of the *currentTime*, *played* and *buffered* properties. * — Demonstrates how to use ad block detection. * — Demonstrates how to schedule an advertisement. ## Events * — Demonstrates how to listen to player events as *playing* and *pause*, and ad events as *adbegin*. * — Demonstrates a use case of the *timeupdate* and *loadeddata* event. * — Demonstrates how to save the playhead position and restore it, using cookies. * — Demonstrates how to use ad block detection. ## UI * — Demonstrates how to insert a button in the control bar, overlay a container and overlay text. * — Demonstrates how to change some elements (e.g. re-color progress bar, change control bar icon) of the default UI. * — Demonstrates how to create a minimal chromeless UI. * - Demonstrates how to insert buttons which can jump back/forth in time. * - Demonstrates how to configure language localization. * - Demonstrates how to create custom centered control bar * - Demonstrates how to create a custom social control bar which is only visible when the player is in fullscreen mode * - Demonstrates how to build a playlist which can preload upcoming content * - Demonstrates how to set a background image for the player * - Demonstrates how to display custom error messages --- # Getting started with Accessibility Integrating accessibility features on the web is crucial to accommodating viewers with limitations, optimize the general viewer experience and maximize overall reach. The basis of video accessibility means that viewers with disabilities can perceive, understand, navigate, and engage with video content equally, and without obstacles or barriers. Additionally, accessibility can apply to all viewers, regardless of disability or not (i.e. situational limitations, slow internet connection, temporary disabilities, etc.). In this guide we discuss what guidelines to follow to make your content accessible, the legal bodies in place, why accessibility is important, as well as what features are available for OptiView player to make video content accessible. ## Web Content Accessibility Guidelines *The Web Content Accessibility Guidelines (WCAG)* are created by both individuals and organizations, and are seen as the standard for accessible web content internationally. Those organizations are formally known as the Accessibility Guidelines Working Group (AG WG) which is part of the World Wide Web Consortium (W3C) Web Accessibility Initiative (WAI). The guidelines are created to help individuals and organizations across the world make their online content accessible to all viewers, including those with disabilities. Online content is seen by the WCAG as information such as text, image, video, audio, and also as any code that determines structure, UI presentation, and more. You can find the full overview of WCAG guidelines [on their website](https://www.w3.org/WAI/standards-guidelines/wcag/). There are three levels to the WCAG: A, AA, and AAA: * A: the basic/bare minimum accessibility functionalities * AA: decently accessible, where most organizations tend to stand * AAA: very accessible, top tier accessibility What is new in WCAG? WCAG 2.1 was updated in 2018. The success criteria from WCAG 2.0 are included verbatim in WCAG 2.1, but 17 additions were included in 2.1 to address mobile accessibility, viewers with low vision and viewers with cognitive and learning disabilities. WCAG 2.0 and 2.1 are backwards compatible, or in other words, if an organization adheres to 2.1 they also adhere to 2.0. The additions in 2.1 require online content to be adaptable, distinguishable, compatible, keyboard accessible, have functional modalities, be safe for those with seizures or physical reactions, and have ‘enough time’ capabilities. To view the technical documentation of the WCAG 2.1 and use case examples, [visit their website here](https://www.w3.org/WAI/standards-guidelines/). WCAG 2.2 was updated in 2024. The 2.0 and 2.1 success criteria are essentially the same in 2.2, with one exception: Parsing is obsolete and removed from WCAG 2.2. OptiView Player is a single player across all major mobile devices, platforms and browsers, and our video player solution is upgraded with accessibility capabilities to adhere to WCAG across all of them, out of the box. ## User Agent Accessibility Guidelines (UAAG) Under the WCAG is the User Agent Accessibility Guidelines (UAAG), which encompasses web browsers, browser extensions and media players, and the UAAG targets developers. The UAAG was created to primarily provide alternative information for those who want to remain compliant with WCAG, but are using a variety of different technologies and platforms. For example, some accessibility needs are met through the browser the viewer is using, while others are better met through the media content. UAAG is seen as relevant for the future of accessibility, especially for mobile device accessibility. Mobile devices show the need for accessibility within a combination of UI, the content and applications used. UAAG also highlights the need for more accessibility features that work seamlessly across devices, platforms, and browsers. If you would like to explore more about UAAG specifically, check out the documentation with [standards and guidelines from WCAG](https://www.w3.org/WAI/standards-guidelines/wcag/). ## Why is WCAG important for Video? Complying with the WCAG is crucial for all publishers of online content, particularly video content. By ignoring accessibility, you are losing a large chunk of your audience. By a large chunk, we mean 15% of the world’s population who have at least one disability. By implementing accessibility features you are including everyone, reaching more viewers and increasing your traffic and SEO rankings. Not only is accessibility important for rankings and to those with disabilities, but it is also adds to the viewer experience and usability for non-disabled viewers as well. For example, if a viewer is in a noisy or quiet environment, or the viewer is a non-native speaker of the language used in the video, subtitles will allow them to still enjoy the video content. Keyboard accessibility can be of great use to those viewers whom may not have a mouse or have a temporary injury. # Accessibility Features OptiView Player integrates the following features for accessibility: ### Text Tracks Text Tracks (i.e. video captions or subtitles) are the audio from a video, but in text form. This includes words that are spoken, who is speaking (if it isn’t clear) and important noises such as music. This can help viewers who are hearing impaired in any capacity, or those with cognitive or learning disabilities. These captions can also benefit those general viewers whom, for example, may be in a loud, or silent, viewing environment. You can seamlessly integrate this feature into your OptiView Player. For more information on integrating Text Tracks into OptiView player and the supported formats, see our [guide on text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/introduction.md). ### Text Track Style Text track style allows for the text in video captions to be displayed in a way the text is readable. This involves adapting the text color, font, size, spacing and other text properties. This makes the audio and video captions accessible to those with low vision, dyslexia, and/or cognitive or learning disabilities. This feature also makes your content more adaptable to different devices and browsers, content translation and personal preferences for caption viewing. This feature goes hand-in-hand with Text Tracks, and can be easily integrated in your OptiView player. For more on this feature, see this section of our [guide on text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/insert-subtitles.md#subtitle-styling). ### Accessibility Roles for Tracks The OptiView player can expose the type of text or audio track programmatically through the API via `AccessibilityRole`. These roles allow assistive technologies to recognize the purpose of each track automatically. See more in our [API Reference](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/types/AccessibilityRole). Common roles are: * *Captions*: speech + non-speech audio cues * *Subtitles*: translation, no non-speech audio cues * *Audio Descriptions*: narration of visual content) * *Sign Language*: describes a sign-language track ### Focus Style Focus Style allows the buttons on the video player (the control bar, menu items, close buttons, etc.) to be outlined in order to make them more visible when they are clicked or hovered over. This feature makes it easier for all viewers to see and understand which buttons are in use and which one they have selected. Also while hovered over, the Tooltips feature can make it easier for users by giving them a text description of the button on the UI. For example, when hovered over the mute button, it is outlined and a readers also see the word "Mute" appear on their UI. You can change the look of the default UI with our [UI Skinning Guide](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/ui-skinning.md) or fully customize every element of your video player with our [Open Video UI](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui.md). ### User Interface Customization User Interface, or UI, is the aspect of the video content that the viewer engages with. For viewers with disabilities, it is important that colors have sufficient contrast with the text and its background. The contrast is not limited to captions, but also includes the text on images, buttons and icons. This feature is especially helpful for viewers with color blindness or low contrast visibility, which is common in aging viewers, as well as viewers in difficult light conditions. It is also important for the video player to have a clear layout and design, to make it as easy and accessible to all viewers as possible. You can change the look of the default UI with our [UI Skinning Guide](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/ui-skinning.md) or fully customize every element of your video player with our [Open Video UI](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui.md). ### Keyboard Compatibility This allows for viewers to navigate the UI (i.e. buttons, links, player functions) via the keyboard. This feature is helpful for viewers who are physically unable to use a mouse, or those viewers who are visually impaired and unable to see the player UI. To customize keyboard presses, refer to our [guide on keyboard hotkeys](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/how-to-implement-hotkeys.md). Our universal player is focused on the mandatory guidelines, and is compliant with those guidelines set out by the WCAG 2.2, the EU Web Accessibility Directive, as well as other guidelines for regions across the world. It is also important to note that different countries and regions of the world can have different guidelines and regulations. For an extensive list of guidelines and regulations per country or region, and all of the standards and specifications related to accessibility, please visit the [WCAG website](https://www.w3.org/WAI/standards-guidelines/wcag/). ### Screen Reader Support The OptiView player supports screen readers across different platforms including VoiceOver for Mac/iOS and Narrator for Windows. For more detail on the WCAG success criterion, refer to [SC 1.1.1](https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html). ### Bookmarks, Chapter Markers, and Thumbnails The player supports thumbnails, bookmarks and chapter markers as alternative navigation aids for time-based media. * [Reference for Bookmarks](https://optiview.dolby.com/docs/theoplayer/getting-started/sdks/web/api-examples/#properties-and-methods) * [Reference for Chapter Markers](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/knowledge-base/playback/what-are-chapter-markers.md) * [Reference for Thumbnails](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/preview-thumbnails.md) ### Other Features There are a few other features of the OptiView player which adhere to the WCAG guidelines. Specifically: * *orientation*: the player adapts itself out of the box to different device orientations (and advanced controls for this are also available). * *audio controls*: Audio controls are available, including a mute button, and a pause button (also during ads); player adheres to browsers autoplay policies * *speed*: the player speed can be customized, so fast-paced subtitles can be showed for longer times --- # How to implement Keyboard Hotkeys This how-to guide describes how you can implement keyboard shortcuts in THEOplayer. Hotkeys allow viewers to also control THEOplayer functionality by using their keyboard. ## TLDR: 1. Add an id attribute to the player element in your HTML. 2. **Copy, Paste** and **Link** [this script](#full-javascript-code-example) in your HTML to enable the following set of hotkeys. ## List of Hotkeys Keyboard hotkeys are not an 'out-of-the-box' feature provided by THEOplayer. This means you'll have to insert a bit of custom code to enable this functionality. The script below will implement the following shortcuts: * Spacebar : Toggles between Pause and Play. * Left Arrow: Rewinds the video. (default: 5s) * Right Arrow: Forwards the video. (default: 5s) * Up Arrow: Increases the volume. * Down Arrow: Lowers the volume. * ' F ' Key: Toggles fullscreen mode. * ' M ' Key: Toggles audio mute. ## Step-by-step Guide ### 1. THEOplayer-wrapper Because the hotkeys we're about to include also have default browser functionality (e.g. space bar skipping down the page), we have two options: 1. Disable default browser hotkeys and overwrite them with our custom player hotkeys. 2. Enable THEOplayer hotkeys only when the viewer's mouse is inside the player area or when the player is focused. Start by adding an id attribute to the player element in your HTML: ```html
``` ### 2. JavaScript Make a new JavaScript file and include it to your project with the rest of your scripts. First off we're going to make a variable that holds the element in which we want to check for user interaction. ```javascript const wrapper = document.getElementById('yourPlayerId'); ``` Next up are the individual functions: ### 2.1 Disabling Default browser hotkeys #### preventStandardHotKeyActions() With event[.preventDefault()](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) we prevent the pressed key from performing their default browser functionality. In our case this will disable the 'keydown' event if conditions are met. ```javascript function preventStandardHotKeyActions(event) { event.stopPropagation(); event.preventDefault(); } ``` ### 2.2 Pause and Play #### togglePlay() Switches between pausing and playing the video. This function is triggered by pressing the spacebar. ```javascript function togglePlay() { if (player.paused) { player.play(); } else { player.pause(); } } ``` ### 2.3 Rewinding and Skipping #### rewind() Rewinds the video 5 seconds (default) back in time when triggered. ```javascript function rewind() { player.currentTime -= 5; //Subtracts 5 seconds } ``` #### forward() Skips the video 5 seconds (default) forward in time when triggered. ```javascript function forward() { player.currentTime += 5; //Adds 5 seconds } ``` ### 2.4 Volume Settings #### increaseVolume() Increases the THEOplayer's volume in 5% increments (default). ```javascript function increaseVolume() { player.volume = Math.min(player.volume + 0.05, 1); //Increases volume by 5% } ``` #### decreaseVolume(): Lowers the THEOplayer's volume in 5% increments (default). ```javascript function decreaseVolume() { player.volume = Math.max(player.volume - 0.05, 1); //Lowers volume by 5% } ``` ### 2.5 Toggle Fullscreen #### toggleFullScreen() Checks what presentation mode THEOplayer is in and switches the presentation mode to 'fullscreen' or 'inline' accordingly. ```javascript function toggleFullScreen() { if (player.presentation.currentMode === 'fullscreen') { player.presentation.requestMode('inline'); } else { player.presentation.requestMode('fullscreen'); } } ``` ### 2.6 Toggle Mute #### toggleMute() Turns THEOplayer's audio on or off. ```javascript function toggleMute() { player.muted = !player.muted; } ``` ### 2.7 KeyPressed Event #### getPressedKey() This function gets the 'key' property of the event object that triggered it (In our case a 'keydown' event). [List of Key values](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values) Start by initiating a variable for any key's that are pressed ('pressedKey') and a variable called 'action' that's going to call one of the functions we made earlier. Next we make a switch case where we check the event's 'key' property for the ones we want to catch (e.g. ArrowLeft, " " for space, ... etc). If there's a match, we assign the corresponding function to 'action' to call after our conditions have been met. ```javascript function getPressedKey(event) { const pressedKey = event.key; let action; switch (pressedKey) { case ' ': action = togglePlay(); //Pauses or Unpauses with Space break; case 'ArrowLeft': action = rewind(); //Rewinds the video with the Left Arrow Key break; case 'ArrowRight': action = forward(); //Forwards the video with the Right Arrow Key break; case 'ArrowUp': action = increaseVolume(); //Increases volume with the Up Arrow Key break; case 'ArrowDown': action = decreaseVolume(); //Lowers volume with the Down Arrow Key break; case 'f': action = toggleFullScreen(); //Toggle Fullscreen mode with the 'F' Key break; case 'm': action = toggleMute(); //Toggle Mute with the 'M' Key break; } if (action && pressedKey !== 'Control' && pressedKey !== 'Alt' && pressedKey !== 'Shift') { action(); preventStandardHotKeyActions(event); //Stops the default key behavior like jumping the page with space. } } ``` ### 2.8 Enable hotkeys with focus #### playerFocused(): Next we will allow the use of the THEOplayer hotkeys whenever the active element on the site is an element inside the player. Any element that the user interacts with is set to be the active element. (e.g.: pressing a button with the mouse or by selecting something with tab for example.) The snippet below will catch when the active element changes. If this element is part of the THEOplayer, we will enable the use of player hotkeys. ```javascript function playerFocused() { if (wrapper.contains(document.activeElement)) { document.addEventListener('keydown', getPressedKey); } else { document.removeEventListener('keydown', getPressedKey); } } ``` ### 2.9 Enable Hotkeys on mouseEnter Since browsers have default hotkey functionality (like skipping down the page by pressing space), you may only want to allow the use of the hotkeys whenever the viewer is actively watching the player. The following functions will handle that use-case. #### mouseInPlayer() Gets called when a viewer enters the specified wrapper area with their cursor. Creates an eventListener for both 'keydown' events and when the viewer's mouse leaves the wrapper area we specified at the beginning. ```javascript function mouseInPlayer() { //Adds listeners for the keydown event and mouse leave event document.addEventListener('keydown', getPressedKey); wrapper.addEventListener('mouseleave', mouseOutPlayer); } ``` #### mouseOutPlayer() Gets called when a viewer exits the wrapper area. When the viewer has left the wrapper area, the unused eventListeners are removed. ```javascript function mouseOutPlayer() { //Checks if the mouse leaves the player wrapper area wrapper.removeEventListener('mouseleave', mouseOutPlayer); document.removeEventListener('keydown', getPressedKey); } ``` ### 3.0 Enabling the Hotkeys #### enableShortcuts() This function lets you choose between enabling the hotkeys site-wide, or only enabling them when the viewer's mouse is inside the player or the player is focused. When calling this function you can use: 1. `enableShortcuts();` -To enable THEOplayer hotkeys site-wide. 2. `enableShortcuts('mouseOver');` -To enable THEOplayer hotkeys when the viewer's mouse is inside the specified area. ```javascript function enableShortcuts(method) { switch (method) { case 'mouseOver': //If the param reads 'mouseOver' shortcuts are only enabled when the mouse is inside of the player. wrapper.addEventListener('mouseenter', mouseInPlayer); break; case 'playerFocused': //If the param reads 'playerFocused' shortcuts are only enabled when the player is focused. document.addEventListener('focus', playerFocused, true); break; default: document.addEventListener('keydown', getPressedKey); //Else player shortcuts are always enabled. } } enableShortcuts('mouseOver'); ``` *** ## Full JavaScript Code Example ```javascript const wrapper = document.getElementById('yourPlayerId'); const element = document.querySelector('.video-js'); const player = new THEOplayer.Player(element, { ui: { fluid: true, }, libraryLocation: '/YourTHEOplayerLibraryLocation/', license: 'your-license-here', }); player.source = { sources: ['https://example.mpd'], metadata: { images: [ { src: 'https://thumbnail.jpg', }, ], releaseDate: '2014-10-31', releaseYear: 2014, subtitle: 'My movie subtitle', title: 'My movie', type: 'movie', }, }; function togglePlay() { if (player.paused) { player.play(); } else { player.pause(); } } function rewind() { player.currentTime -= 5; //Subtracts 5 seconds } function forward() { player.currentTime += 5; //Adds 5 seconds } function increaseVolume() { player.volume = Math.min(player.volume + 0.05, 1); //Increases volume by 5% } function decreaseVolume() { player.volume = Math.max(player.volume - 0.05, 1); //Lowers volume by 5% } function toggleFullScreen() { if (player.presentation.currentMode === 'fullscreen') { player.presentation.requestMode('inline'); } else { player.presentation.requestMode('fullscreen'); } } function toggleMute() { player.muted = !player.muted; } function preventStandardHotKeyActions(event) { event.stopPropagation(); event.preventDefault(); } function getPressedKey(event) { const pressedKey = event.key; let action; switch (pressedKey) { case ' ': action = togglePlay(); //Pauses or Unpauses with Space break; case 'ArrowLeft': action = rewind(); //Rewinds the video with the Left Arrow Key break; case 'ArrowRight': action = forward(); //Forwards the video with the Right Arrow Key break; case 'ArrowUp': action = increaseVolume(); //Increases volume with the Up Arrow Key break; case 'ArrowDown': action = decreaseVolume(); //Lowers volume with the Down Arrow Key break; case 'f': action = toggleFullScreen(); //Toggle Fullscreen mode with the 'F' Key break; case 'm': action = toggleMute(); //Toggle Mute with the 'M' Key break; } if (action && pressedKey !== 'Control' && pressedKey !== 'Alt' && pressedKey !== 'Shift') { action(); preventStandardHotKeyActions(event); //Stops the default key behavior like jumping the page with space. } } function playerFocused() { if (wrapper.contains(document.activeElement)) { document.addEventListener('keydown', getPressedKey); } else { document.removeEventListener('keydown', getPressedKey); } } function mouseInPlayer() { //Checks if the mouse is inside the player wrapper area document.addEventListener('keydown', getPressedKey); wrapper.addEventListener('mouseleave', mouseOutPlayer); } function mouseOutPlayer() { //Checks if the mouse leaves the player wrapper area wrapper.removeEventListener('mouseleave', mouseOutPlayer); document.removeEventListener('keydown', getPressedKey); } function enableShortcuts(method) { switch (method) { case 'mouseOver': //If the param reads 'mouseOver' shortcuts are only enabled when the mouse is inside of the player. wrapper.addEventListener('mouseenter', mouseInPlayer); break; case 'playerFocused': //If the param reads 'playerFocused' shortcuts are only enabled when the player is focused. document.addEventListener('focus', playerFocused, true); break; default: document.addEventListener('keydown', getPressedKey); //Else player shortcuts are always enabled. } } //enableShortcuts('mouseOver'); //Enable player shortcuts only when mouse inside the player area. enableShortcuts(); //Always enable shortcuts. ``` ## Additional Resources * [preventDefault()](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) * [stopPropagation()](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation) * [List of Key values](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values) --- # How to embed an iframe Embedding a video player through an ` ``` The code snippet above represent an actual ` ``` On top of this, you want to configure the [`allow`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-allow)-attribute and track its evolution. ```html ``` Through this attribute, you can allow your iframe to access a set of [features](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy#Directives), like autoplay, Picture-in-Picture, DRM, and more. ### Deciding approaches The previous section explains how you can make THEOplayer compatible with an ` ``` However, more common is to rely on responsive styling, and not knowing your specific width and height. On top of wanting a responsive iframe, you usually want to respect the aspect ratio of your video. Basically, you want your ` ``` The above snippet demonstrates how you can achieve this. * You want to set an `aspect-ratio` in the `style` of your ` ``` If we had configured a different `src`-value, like `https://cdn.theoplayer.com/demos/iframe/theoplayer.html?autoplay=false&muted=false&preload=none&src=//cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny_metadata.m3u8`, then it would have loaded a different video. Note that unmuted autoplay may not be possible, and that you may have to [combat autoplay policies](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq/how-to-combat-autoplay-policies.md). You can see the code of the iframe HTML page at , or in the code snippet below. ```html
``` ## Resources * [Player API - PlayerConfiguration](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerConfiguration) * [iframe demo code](https://cdn.theoplayer.com/demos/iframe/theoplayer.html?autoplay=false\&muted=false\&preload=none\&src=//cdn.theoplayer.com/video/elephants-dream/playlist.m3u8) * [AMP](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/frameworks/amp/getting-started.md) (consider translating your learning to the AMP framework) * [MDN: iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) * [ottball.com: lazy loading](https://ottball.com/lazy-loading-video/#embedded-players) (consider lazy loading for your iframes) --- # How to implement a seamless transition between videos? You may need an answer to this question if you want to play one video after the other without letting the user know the source has changed (to the user they will seem as if playing just one video). Similar questions may be: * Is it possible to make 2 video files play one after the other without pause? * How can I play a second video file after the first without the user notice what happened? * How to make a forking video, one where the user can choose his/her own path? **This result is achieved taking advantage of a HTML5 canvas element to cover the source change.** An example can be seen on [this page](http://cdn.theoplayer.com/demos/confluence/seamless-video-transition.html). You can find the code of the page here below, opportunely commented to clarify how it works (hint: all interesting comments start with "COMMENT:" to aid the search). Seamless video transitionExpand source ```html Seamless video transition

Seamless video transition

``` The result on your page can also be improved implementing [cache](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Cache) for your videos. --- # How to work around browser cache with new THEOplayer SDK libraries? In some cases you may experience problems related to cached versions of the THEOplayer library (e.g. if you are dealing with users who are getting expired license errors even though you already deployed new SDK libraries). Related questions may be: * Can I implement a cachebuster on the THEOplayer library load? * Can I force a file to be called always from the server to prevent it from being fetched from the cache? * How can I make sure that users always get the newest version of THEOplayer libraries I deployed? ## Cache To reduce the internet usage and increase the responsiveness of a webpage, browsers use caching. In simple terms, it may take a while to download a file (e.g. `THEOplayer.js`). The browser will therefore keep files stored locally in its cache for a certain amount of time for later usage. After some time, the browser will remove the file and update it the next time you visit that page. By keeping it locally, the browser can use the local file instead of having to fetch a new one. This makes your page faster, but the downside is that the browser may not get the most up-to-date file. If you really need users to use the latest library you serve or renew at the expiration date of your contract, you may still run into caching issues. Luckily there are some ways to ensure that the browser updates the file. ## Ways to force the browser to update ### Change the file path A good way to ensure that the file gets updated, is by changing the file path. If your file path is different, the browser will see all the files within that new directory as new files and download them again. You could, for example, keep track of the version of THEOplayer or the deploy date by using different directories: For example: ```html /resource/file/path/5.5.0/THEOplayer.js /resource/file/path/5.5.1/THEOplayer.js /resource/file/path/31-01-2023/THEOplayer.js ``` ### Add a variable at the end of the file Another way to trick the browser into updating the file immediately, is by adding a variable at the end of the filename. This variable on its own, in the case of `THEOplayer.js`, won't change anything to the file. By adding something like the version or the date, you can be sure that the browser updates the file. For example: ```html /resource/file/path/THEOplayer.js?d=10012023 /resource/file/path/THEOplayer.js?v=5.5.1 ``` **Note**: For HLS playback, the player uses worker files. These workers must be present in the `libraryLocation` set on the player configuration. You do not need to link to them in the webpage as they are dynamically called from the `libraryLocation`. The downside of adding a variable at the end of the filename is that these worker files will not get this variable and therefore may still be cached, resulting in a `THEOplayer.js` file from a newer version, while the workers are still on the old version from the cache. In this case you may see an error from the player like the following: `THEOplayer workers could not be loaded. please check that the worker's version matches THEOplayer's version (5.5.1)` You can make sure that the variable is a different one for each call, thus requesting the server version of the file each time (and ignoring the cached version). Here's an example on how to dynamically generate such a cachebuster variable in JavaScript (taken from [this original article](https://www.virendrachandak.com/techtalk/cachebuster-code-in-javascript/)): ```html ``` ## Related articles * [How to update an SDK](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/how-to-update-a-sdk.md) * [License](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/license/introduction.md) * [How to reduce data usage on mobile devices on mobile web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/media-tracks/reduce-data-usage.md) --- # How to customize quality selection and labels on the default UI (MP4) Some of our customers prefer MP4 assets over HLS/MPEG-DASH ones. One major downside to this approach is that Adaptive Bitrate Streaming (ABR) is not possible with such streams, as they don't have a playlist with multiple qualities. To counter this, we've made it possible to pass on a list of MP4 qualities & switch between them manually, to improve the overall UX. What is nice about this piece of integration code, is that it can be tweaked and applied to customize other parts of the default UI as well! ## SDKs | Web SDK | Android SDK | iOS SDK | tvOS SDK | Android TV SDK | Chromecast SDK | | ------- | ----------- | ------- | -------- | -------------- | -------------- | | Yes | N/A | N/A | N/A | N/A | N/A | ## Customizing the quality selector and labels This example explains how to customize quality selection & labels for MP4 assets. ##### Web SDK First of all, we need to [set up the player](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md). Then, the different MP4 qualities of the same asset need to be stated, along with the corresponding resolutions: ```js // set your resolutions and sources var resolutions = [ { name: '360p', src: 'https://example.com/demos/sample-videos/low.mp4' }, { name: '540p', src: 'https://example.com/demos/sample-videos/medium.mp4' }, { name: '720p', src: 'https://example.com/demos/sample-videos/high.mp4' }, ]; ``` Now for the slightly trickier part: Removing current & adding custom menu ```js var currentTime, wasPaused; var settingsMenu = player.ui.getChild('ControlBar').getChild('SettingsButton').menu; // Remove built-in quality sub menu settingsMenu.removeMenuItem(settingsMenu.menuItems[0]); // Create custom quality sub menu var qualityMenuItem = settingsMenu.addSubMenuItem({ index: 0, }); var qualityMenuTitle = document.createElement('span'); qualityMenuTitle.className = 'theo-settings-control-menu-item-title'; qualityMenuTitle.innerHTML = 'Quality'; var qualityMenuValue = document.createElement('span'); qualityMenuValue.className = 'theo-settings-control-menu-item-value'; qualityMenuItem.el().appendChild(qualityMenuTitle); qualityMenuItem.el().appendChild(qualityMenuValue); qualityMenuItem.subMenu.menuHeader.el().innerHTML = 'Quality'; var qualityItems = resolutions.map(function (resolution) { var qualityItem = qualityMenuItem.subMenu.addMenuItem(); qualityItem.el().innerHTML = resolution.name; qualityItem.on('click', function () { selectQuality(resolution); }); return qualityItem; }); // Getting the player/UI in the correct state function selectQuality(resolution) { qualityMenuValue.innerHTML = resolution.name; currentTime = player.currentTime; wasPaused = player.paused; player.src = resolution.src; player.currentTime = currentTime; if (!wasPaused) { player.play(); } var qualityItem = qualityItems[resolutions.indexOf(resolution.name)]; qualityItems.forEach(function (item) { item.selected(item === qualityItem); }); } selectQuality(resolutions[0]); ``` ##### Android SDK Android SDK does not have a built-in UI. It also does not support JavaScript injection. However, you can implement a similar custom quality selection menu using [Open Video UI for Android](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/android/ui/open-video-ui.md) and following the same logic for Web SDK. ##### iOS SDK iOS SDK does not have a built-in UI. It also does not support JavaScript injection. However, you can implement a similar custom quality selection menu on your custom UI following the same logic for Web SDK. You can now select multiple qualities of the same MP4 asset! ![Customize quality selection](/pr-873/assets/images/customize-quality-selection-e65dfce41a9308baad45748bb40589d8.png "Customize quality selection") --- # How to get frame-accurate currentTime display in the UI Control bar This article will show you how to display a frame-accurate currentTime on your THEOplayer instance UI control bar. For more information regarding the currentTime itself, please refer to the [reference API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer). ## SDKs | Web SDK | Android SDK | iOS SDK | tvOS SDK | Android TV SDK | Chromecast SDK | | ------- | ----------- | ------- | -------- | -------------- | -------------- | | Yes | Yes | Yes | No | Yes | N/A | ## Changing the currentTime UI display At this moment there is no official THEOplayer feature to display a frame-accurate currentTime in the UI Controlbar. The following snippets rewrite the innerHTMLText of the currentTime element. ##### Web SDK ```js function secondsToHms(d) { d = Number(d); console.log(player.currentTime); var ms = d % 1; var h = Math.floor(d / 3600); var m = Math.floor((d % 3600) / 60); var s = Math.floor((d % 3600) % 60); var hDisplay = h < 10 ? '0' + h : h; var mDisplay = m < 10 ? '0' + m : m; var sDisplay = s < 10 ? '0' + s : s; var msDisplay = ms.toFixed(3); //var msDisplay = d.toPrecision() -player.currentTime.toFixed(); return hDisplay + ':' + mDisplay + ':' + sDisplay + '.' + String(msDisplay).split('.')[1]; } setInterval(function () { document.querySelector('.vjs-current-time-display').innerText = secondsToHms(player.currentTime); }, 1); player.addEventListener('durationchange', function (e) { document.querySelector('.vjs-duration').innerText = secondsToHms(Math.floor(e.duration)); }); ``` ##### Android & iOS SDK This can be done through JavaScript injection: [How to add CSS or JavaScript files to an Android/iOS project](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v4/faq/how-to-add-css-or-javascript-files-to-android-ios) ## Remarks With the same logic, slightly modified, you could also decide to show the frame number for that second instead of the milliseconds. ## Resources * [API reference - player properties](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer) --- # How to play an LCEVC source with THEOplayer This article will show you how to play an LCEVC encoded source in THEOplayer. If you are not familiar with THEOplayer, we strongly recommend reading the [getting started documentation for web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md) first. ## SDKs | Web SDK | Android SDK | iOS SDK | tvOS SDK | Android TV SDK | Chromecast SDK | | ------- | ----------- | ------- | -------- | -------------- | -------------- | | Yes | No | No | No | No | No | ## Prerequisites #### 1. Your THEOplayer SDK needs to have the 'lcevc' feature enabled. You can quickly check using `THEOplayer.features`. The feature is only available from THEOplayer 4.0 onwards. #### 2. You need to include V-Nova's [LCEVC Decoder for Web](https://docs.v-nova.com/v-nova/lcevc/sdk/lcevc-decoder-for-web-lcevcdecjs) on the page. This example below uses the latest version of the LCEVCdecJS library that is maintained by V-Nova: ```js ``` ## Setting the source After your player is correctly configured you can set your LCEVC source, but you need to also include `lcevc: true` on the SourceDescription so that the player knows how to configure the correct playback pipeline: ##### DASH example ```js player.source = { sources: { type: 'application/dash+xml', src: 'path/to/your/source/master.mpd', lcevc: true, }, }; ``` ##### HLS example ```js player.source = { sources: { type: 'application/x-mpegurl', src: 'path/to/your/source/master.m3u8', lcevc: true, }, }; ``` ## Remarks LCEVC enhanced playback only works on modern browsers that support Media Source Extensions (MSE). If the platform does not support MSE (for example: Safari on iOS), the player will fall back to native playback without LCEVC enhancements. --- # How to use THEOplayer as a JavaScript module As of THEOplayer 6.0, THEOplayer can also be used as a [JavaScript module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). Instead of loading the library through a ` ``` ## Prerequisites See [the prerequisites in our getting started guide](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md#prerequisites). ## Basic setup Now, we're ready to set up THEOplayer on your website. This guide explains how you implement THEOplayer in four steps: 1. We'll [start from a basic HTML file](#step-1-set-up-an-html-file), with no mention of THEOplayer. 2. In the [second step](#step-2-including-the-theoplayer-library), we'll explain what the THEOplayer library is and how you can include it. We'll do the same for the default THEOplayer UI. 3. In the [third step](#step-3-the-video), we'll describe how you play a video stream through THEOplayer. 4. In the [fourth step](#step-4-the-result), we'll give a final overview. ### Step 1: Set up an HTML file When THEOplayer is used as a web video player, we will need a web page (i.e. HTML file) to embed THEOplayer and the code. Note that a THEOplayer SDK license can only be used on whitelisted domains. You configure these domains when you create a THEOplayer Web SDK at . Let's start with the following HTML file: ```html THEOplayer ``` Pay attention to the `type="module"` attribute on the ` ``` The path of the `import` statement is the URI of the THEOplayer library that you wish to use. This could be something like: `/path/to/THEOplayer.esm.js`, where the URL is a link to your `THEOplayer.esm.js` file. You must swap `/path/to/` with your CDN URL. For example, if your SDK is hosted on `https://example.com/vendor/theoplayer/`, then you'd write the following import: ```js import * as THEOplayer from 'https://example.com/vendor/theoplayer/THEOplayer.esm.js'; ``` #### THEOplayer CSS library To use the default THEOplayer UI, you need to reference the THEOplayer CSS library: ```html ``` The `` tag includes a CSS file on a web page. Similar to the JavaScript library, you must swap `/path/to/` with your CDN URL. Next, we’ll create a THEOplayer instance and configure a video stream. ### Step 3: The video To play a video through the THEOplayer Web SDK, you must: 1. Create a video player container, 2. fetch this container through JavaScript, 3. create a THEOplayer instance associated with that container, 4. and configure a video stream for this instance. The snippet below implements these 4 steps. ```html
``` Let's break down the above snippet. #### Create video player container This `
` element creates a video player container. ```html
``` * The `theoplayer-container` class is used to fetch the container through JavaScript, as demonstrated by the snippet below. * The `video-js theoplayer-skin vjs-16-9` classes load the default THEOplayer UI. You may omit `vjs-16-9` if you don't want a 16:9 container. ```javascript let element = document.querySelector('.theoplayer-container'); ``` #### Create THEOplayer instance The snippet below creates a THEOplayer instance for a container with a specific [Player Configuration](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerConfiguration). ```js let player = new THEOplayer.Player(element, { // instantiates video player libraryLocation: '/path/to/your-theoplayer-folder/', // references folder containing your THEOplayer library files (theoplayer.p.js, THEOplayer.js, ...) license: 'your_license_string', // references your THEOplayer SDK license }); ``` Similar to the JavaScript and CSS library, you must swap out `'/path/to/your-theoplayer-folder/'` and `'your_license_string'`. For example, if your SDK is hosted on `https://example.com/vendor/theoplayer/` and your license string is `ABCD1234`, then you would instead configure the snippet below: ```js var player = new THEOplayer.Player(element, { libraryLocation: 'https://example.com/vendor/theoplayer/', license: 'ABCD1234', }); ``` You can read more about configuring the `license` at the [License section](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/license/introduction.md). #### Configure video stream The snippet below configures a HLS video stream for the THEOplayer instance. ```js player.source = { sources: [ { src: '//cdn.theoplayer.com/video/star_wars_episode_vii-the_force_awakens_official_comic-con_2015_reel_(2015)/index.m3u8', // sets HLS source type: 'application/x-mpegurl', // sets type to HLS }, ], }; ``` To configure a DASH stream instead, you must set `type` to `'application/dash+xml'` instead of `'application/x-mpegurl'`, as demonstrated by commented out code in one of the earlier snippets. In the next step we'll put everything together. ### Step 4: The result We're done! Here's the complete code again, which you can put on your web server: ```html THEOplayer
``` ## Next steps Ready to learn more? * Go through our [how-to guides](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides.md) to learn about [UI customization](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/introduction.md), [advertising](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ads/introduction.md), [DRM](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/drm/introduction.md), [casting](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/cast/chromecast/introduction.md), [analytics](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/analytics/introduction.md) and other topics. * Check out our [API reference](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer). * Try out samples from our [GitHub](https://github.com/THEOplayer). --- # How to use MultiView This article will go over how to work with THEOplayer's MultiViewPlayer. As the setup is very similar to using normal THEOplayer, we strongly recommend reading the [getting started documentation for web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md) first. ## SDKs | Web SDK | Android SDK | iOS SDK | tvOS SDK | Android TV SDK | Chromecast SDK | | ------- | ----------- | ------- | -------- | -------------- | -------------- | | Yes | No | No | No | No | No | ## Prerequisites #### 1. Your THEOplayer SDK needs to have the 'multiview' feature enabled. You can quickly check using `THEOplayer.features`. The feature is only available from THEOplayer 4.3 and onwards. This feature is included by default in the [@theoplayer/extended](https://www.npmjs.com/package/@theoplayer/extended) npm package. #### 2. You need to have a THEOplayer license which is compatible with MultiView This can be done through . ## Basic setup To make a `MultiViewPlayer`, add the following code to your page: ```html
``` Now that the `MultiViewPlayer` is instantiated, you'll need to load each view separately: ```js // set the name of the source player.load('Star Wars', { sources: [ { src: '//cdn.theoplayer.com/video/star_wars_episode_vii-the_force_awakens_official_comic-con_2015_reel_(2015)/index.m3u8', // sets HLS source type: 'application/x-mpegurl', // sets type to HLS }, ], }); // set the name of the source player.load('Big Buck Bunny', { sources: [ { src: '//cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny.m3u8', // sets HLS source type: 'application/x-mpegurl', // sets type to HLS }, ], }); ``` The `MultiViewPlayer` has a slightly different API from the general THEOplayer, so for more information we refer to the [documentation](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/MultiViewPlayer). --- # Migrating to THEOplayer Web SDK 11.x This article will guide you through updating to THEOplayer Web SDK version 11 (from version 10), and the changes needed in your code. ## Update THEOplayer Run the following command to install THEOplayer Web SDK version 11: ```bash npm install theoplayer@11 ``` If you're using the [Open Video UI for Web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui.md) or one of [our connectors](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/connectors/web.md), make sure to update them to the latest version too to ensure proper support for THEOplayer version 11. ```bash npm update @theoplayer/web-ui npm update @theoplayer/conviva-connector-web ``` ## Replace or remove usages of deprecated APIs Some properties and methods that were previously deprecated have been removed from the API. Update your code to use the new APIs instead. * Removed `preloadPublications` in THEOlive API. --- # Getting Started on webOS This guide will help you integrate your Dolby OptiView Player SDK on webOS. Since webOS IDE is [no longer available](https://webostv.developer.lge.com/develop/tools/ide-introduction) as of February 2022, you will need to set up [webOS CLI](https://webostv.developer.lge.com/develop/tools/cli-installation), optionally in combination with the [webOS TV VS Code Extension](https://webostv.developer.lge.com/develop/tools/vsce-installation). The focus of this article is using our [sample apps for webOS](https://github.com/THEOplayer/samples-webos). You may also find the [Getting Started on Web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md) guide useful. ## Prerequisites 1. Make sure that the [webOS CLI](https://webostv.developer.lge.com/develop/tools/cli-installation) is correctly installed on your system. 2. Validate that the CLI is correctly set up on your environment by invoking `ares` at the command line of a new terminal. If the command is recognized, you are correctly set up. If not, please go over the steps on the CLI installation guide again. 3. Optionally you can also install the official VS Code extension listed [here](https://marketplace.visualstudio.com/items?itemName=webostvsdk.webostv) to quickly connect/deploy to your devices. # Download the starter files * Download the starter code from [our sample repository](https://github.com/THEOplayer/samples-webos). * The folder contains all the necessary files to quickly deploy a THEOplayer solution to a webOS device. * The packager will use the `app` folder to build an IPK package * the `app` folder contains a crude solution with entry point `index.html`; the only thing left is to put your THEOplayer library files in the `libs` folder. * The folder `example-with-UI` contains a more elaborate example, * To use it, optionally modify the code and run the following commands: ```shell cd example-with-ui npm i npm run build ``` * Next, copy the `js` folder, the `css` folder and the `index.html` file to the app folder (overwriting the existing files). ## Setting up the physical device ### Prerequisites 1. Make sure you have created an [LG Developer Account](https://webostv.developer.lge.com/login). You will need this to login to the Developer Tools app on the physical device. 2. Download the Developer Tools app to the TV from the LG Content Store, login with your LG account and follow the instructions to set the device in developer mode (a restart is required). ### Make a connection * In case you downloaded the webOS TV VS Code extension, you can use it to link your development environment to the physical device (from the webOS TV Devices menu on the left-hand toolbar). * Manually set up (use the guideline provided by LG): 1. Open terminal and enter `ares-setup-device` 1. Choose `add`. 2. Enter device name: tv (feel free to use another name). 3. Enter the IP address of the LG TV (open the Developer Tools app on the device to find out). 4. Use port `9922` for physical devices (for a simulator the port is 6622). 5. SSH user: this should be set to `prisoner` (**important note**: do not use the proposed `root`, this will not work). 6. Enter an optional description. 7. Authentication: choose option `password`. 8. Leave the actual password blank. 9. Save. 2. Next, you need to acquire a key from the physical device to be able to securely interact: 1. Enter `ares-novacom -d tv --getkey` (make sure to use the device name you chose in case you changed it). 2. You will be prompted for a key; the key will be visible in the Developer Tools app on the physical device. ## Simulator **Important Note**: the use of a simulator is not recommended as simulators do not implement all features (e.g. DRM). If you decide to use a simulator, follow the guide [here](https://webostv.developer.lge.com/develop/tools/simulator-installation) to install one. # Running the app **Note**: please visit the [LG webOS developer site](https://webostv.developer.lge.com/) to get more info on commands, tools and guidelines. First make sure to `cd` into the root folder (this is the parent of app folder). Next, run the following commands. ### Packaging ```shell ares-package ./app --outdir ./ ``` ### Installation Default name for the created package is *webos.theo\_1.0.0\_all.ipk*, *tv* is the device name that was given when setting up the device in the previous step. This step will fail if there is no connection to the device, e.g. the device is offline, not on the same network. ```shell ares-install webos.theo_1.0.0_all.ipk -d tv ``` ### Launch App ID is by default *web.theo* (you can change this in `appinfo.json`) ```shell ares-launch webos.theo -d tv ``` ### Debug ```shell ares-inspect webos.theo -d tv --open ``` --- # Getting started on Tizen This how-to guide describes how to set up a Tizen application using the Dolby OptiView Player SDK on Tizen. You may also find the [Getting Started on Web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md) to be useful. ## Set-up For brevity, the set-up is split up over multiple pages: * [Setting up Tizen Studio](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/tizen/installing-tizen-studio.md) * [Generating a certificate](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/tizen/generating-a-certificate.md) * [Setting up and deploying on a physical device (recommended)](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/tizen/setting-up-physical.md) * [Setting up and deploying on an emulator (less recommended)](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/tizen/setting-up-emulator.md) ## Getting started with the THEOplayer Tizen reference app ### Prerequisites * Generate a Tizen SDK build via the [THEOplayer portal](https://portal.theoplayer.com/). * Download the [reference app project](https://github.com/THEOplayer/samples-tizen) * Read the README to get started. There is one at the root level and one in the two examples. * Import the project in Tizen Studio. Go to File -> Open Projects from File System. ![](/pr-873/assets/images/import-1-8aaecab1b94a589fec742e47cd9bd394.jpg) Select the directory, then press Finish. ![](/pr-873/assets/images/import-2-abd40c64a1537775af117e7460865ac6.jpg) ### Reference app structure structure * **example-with-ui/** * Contains a Tizen project with a UI. Follow the readme for more instructions. * **css/** * CSS styles as used in the app. * **js/** * JavaScript files as used in the app. The compiled result of `src/` is put in here. * **src/** * The source code of the UI application. Change or replace as desired. * **libs/** * (Insert your THEOplayer Tizen SDK here.) * **index.html** * Entry point of application. * **config.xml** * Permissions of the app. * **package.json** * Contains the commands required to build the UI. * **minimum-example/** * Contains a very minimal Tizen project example. No UI included. * **libs/** * (Insert your THEOplayer Tizen SDK here.) * **index.html** * Entry point of application. * **config.xml** * Permissions of the app. --- # Installing the Tizen developer tools * Check if your device meets the [Tizen developer tools prerequisites](https://docs.tizen.org/application/tizen-studio/setup/prerequisites/). * Download the Tizen developer tools at [Tizen development tools](https://developer.tizen.org/development/tizen-studio/download). You can decide to either install the IDE or CLI, but in this guide we assume you use the IDE. * In Tizen Studio, make sure to install the Tizen package extensions: Go to Tools, then Package Manager ![](/pr-873/assets/images/tizen-extensions-1-832ce052ecfc2dd8d85a0d3a9b9a5b5c.jpg) Install everything under Tizen SDK tools under Main SDK. **Note:** Make sure to close Tizen Studio before installing, otherwise an empty warning message might show up that stops you from completing the installation. ![](/pr-873/assets/images/tizen-extensions-2-e020322a3a7e70ab03067a6c823f80fd.jpg) Install the latest tv extensions under Extension SDK. ![](/pr-873/assets/images/tizen-extensions-3-d03d447b80a0502d74d51d1de6b5bcdf.jpg) --- # Setting up a Tizen device for debugging Tizen requires a certificate to launch an application on a (physical) device. Below are the steps on how to create one: Go to Tools → Certificate Manager ![](/pr-873/assets/images/tizen-certificate-1-5dc8da6607b5532e09b09217eef7d97d.jpg) In the Certificate Manager, click the Add (plus) button to generate a new certificate. ![](/pr-873/assets/images/tizen-certificate-2-4e3672ee2e2fa8cbdcc9d518f3b8ce6a.jpg) Generate a Tizen Certificate. ![](/pr-873/assets/images/tizen-certificate-3-f5c7f5477ef3c33c0438c1f3be6aa5df.jpg) Give your certificate a profile name (name is free to pick). ![](/pr-873/assets/images/tizen-certificate-4-e8aad0838f338202f6ff8872dd43bbe1.jpg) Pick “Create a new author certificate” if you have never used one. If you already have one, then you can choose to select an existing author certificate instead. ![](/pr-873/assets/images/tizen-certificate-5-c08251931d95a82bbd050cd021acc7d5.jpg) If you decided to create a new author certificate, you will get this screen. Fill in the fields as requested with your own. Fill in a password. You can add additional fields when opening the “More details” component. ![](/pr-873/assets/images/tizen-certificate-6-c6150763460504d017b394943dbaf60d.jpg) Pick “Use the default Tizen distributor certificate”. ![](/pr-873/assets/images/tizen-certificate-7-81c3b7cc09f35744d96bcedf37a7e2ee.jpg) If everything went well, a pop-up will appear indicating that the certificate profile has been created. ![](/pr-873/assets/images/tizen-certificate-8-dc745be45feb631cdeb6a54185c9194c.jpg) Your certificate should now be available for selection. ![](/pr-873/assets/images/tizen-certificate-9-1cc2cd3b3d2d3d75e8ccd0160942aff2.jpg) --- # Deploying a test app on a physical Tizen device Both the physical Tizen device as the development computer must be on the same network. ## Set-up ### Tizen device * Go to the home menu (home button) and navigate to apps. Launch it. * Use the 123-key (or numbers) to type 12345. A menu will prompt. * Enable Developer mode. * Type in the local IP-address of the laptop you want to deploy a build from. They must be on the same network. * Reboot Tizen device by long-pressing (about 2 seconds) the power button on the remote. ### Set-up on development device * Go to Tizen Studio. Open Tools -> Device Manager. * Open Remote Device Manager (the icon with a pc and a smart tv) * Add Tizen device via its IP-address, connect and confirm. * The Tizen IP-address can be found under Settings -> General -> Network -> Network Status -> IP Settings. * Enable connection with remote device. ## Launching on a physical Tizen device * Ensure the connection is established. You can check this under Tools → Devices. * In Tizen Studio, click right on the project (under Project Explorer). Pick either "Run as" or "Debug as". --- # Deploying a test app on a Tizen emulator We strongly recommend using a physical Tizen device. If this is not possible, using an emulator can be a back-up. ## Set up * If you haven't done so, make sure to install the Tizen TV Emulator: * In Tizen Studio: Tools -> Package Manager -> Extension SDK -> TV Extensions 5.5 * Create the emulator by pressing the "Create" button and following the steps. ## Launching * In Tizen Studio, click right on the project (under Project Explorer). Pick either: * Run As -> Tizen Web Simulation Application * Debug As -> Tizen Application ## Known limitations of emulators: * AVPlay is unavailable. * DRM is unavailable due to a missing Content Decryption Module (CDM). * `manifest.json` permissions are ignored for emulators. Make sure to test on a physical device, as they are more strict. --- # Getting started with Angular *A brief guide on how to build a THEOplayer in an Angular environment.* Note that this information is for a basic player in a local testing environment. Additional information may be needed for specific cases. ## Step-by-step guide: 1. Make sure you have the Angular CLI installed ```shell npm install -g @angular/cli ``` 2. If needed, create a new workspace and initial application ng new appname 3. In the root project, create a new component for the player (in this case appname) ```shell cd appname ng generate component theoplayer ``` 4. add the THEOplayer.js files to the root project 5. In index.html add the /THEOplayer.js as a script tag and /ui.css as a stylesheet 6. In theoplayer.component.html add ```html
``` 7. In theoplayer.component.ts add ```typescript import { Component, OnInit } from '@angular/core'; import * as THEOplayer from '../../../THEOplayer.js'; @Component({ selector: 'app-theoplayer', templateUrl: './theoplayer.component.html', styleUrls: ['./theoplayer.component.css'], }) export class TheoplayerComponent implements OnInit { constructor() {} ngOnInit() { this.createPlayer(); } createPlayer() { const element = document.querySelector('.theoplayer-container'); const player = new THEOplayer.Player(element, { libraryLocation: '', }); player.source = { sources: [ { src: '//cdn.theoplayer.com/video/elephants-dream/playlist.m3u8', type: 'application/x-mpegurl', // sets type to MPEG-DASH }, ], }; } } ``` Then ```typescript import * as THEOplayer from '../../../THEOplayer.js'; ``` will make sure to get everything needed from our sdk. 9. In app.component.html add the following code to display the player in the app component. ```html ``` 10. Profit. ## Related articles * --- # Getting started with React There is a sample app using THEOplayer as a React component available [on our GitHub](https://github.com/THEOplayer/samples-react-js). ## Project setup ```shell git clone https://github.com/THEOplayer/samples-react-js.git ``` Creates a local clone of the project ```shell npm install ``` Installs the necessary dependencies. This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). ```shell npm start ``` Compiles and runs the app in the development mode. Open to view it in the browser. The page will reload if you make edits. You will also see any lint errors in the console. ## Creating and using a component ### `src/THEOplayerWrapper.js` is an example implementation of a React Component that wraps the THEOplayer.js module. You can extend this wrapper to support whatever THEOplayer API that you want to use. ### `src/App.js` uses the `Player` component exported by `THEOplayerWrapper.js` and specifies a source for the player to play. ## Going to Production The THEOplayer library provided in this sample is currently targeted to the latest version and is set to run on **localhost only** ### Getting a license To run THEOplayer in production you will need a valid THEOplayer HTML5 SDK set up to run on your domain. You can use your existing license or get one for free on [THEOportal](http://portal.theoplayer.com). ### Updating the project You will need to update some files that reference the THEOplayer.js and THEOplayer.css files: * `src/THEOplayerWrapper.js`, -- Please replace the URL of the `libraryLocation` to the URL or folder where your THEOplayer files (THEOplayer.js, THEOplayer.d.js, etc.) are residing. * `src/App.js` * `public/index.html` -- Please replace the links to the THEOplayer assets with your own URLs. --- # Getting started with NuxtJS This how-to guide describes how to integrate THEOplayer with NuxtJS framework. ## Instructions 1. Open your favorite IDE and then a terminal and install Vue CLI component: ```shell npx create-nuxt-app theoplayer-nuxtjs-sample ``` 2. Follow the installation process (please note these instructions assume you’ve chosen npm as a module packager) 3. Once the app is created, execute the following commands: ```shell cd theoplayer-nuxtjs-sample npm install npm run dev ``` 4. The default application should be served under: 5. Reference the THEOplayer Web SDK by editing file `nuxt.config.js` like the following: ```js export default { mode: 'spa', /* ** Headers of the page */ head: { title: process.env.npm_package_name || '', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: process.env.npm_package_description || '', }, ], link: [ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, { rel: 'stylesheet', type: 'text/css', href: '//cdn.theoplayer.com/dash/theoplayer/ui.css', }, ], script: [ { type: 'text/javascript', src: '//cdn.theoplayer.com/dash/theoplayer/THEOplayer.js', }, ], }, /* ** Customize the progress-bar color */ loading: { color: '#fff' }, /* ** Global CSS */ css: [], /* ** Plugins to load before mounting the App */ plugins: [], /* ** Nuxt.js dev-modules */ buildModules: [ // Doc: https://github.com/nuxt-community/eslint-module '@nuxtjs/eslint-module', ], /* ** Nuxt.js modules */ modules: [], /* ** Build configuration */ build: { /* ** You can extend webpack config here */ extend(config, ctx) {}, }, }; ``` 6. Add the new component named Player.vue under this path: components/Player.vue ```html ``` 7. Please note that vuex is used to pass the player source, for this purpose add index.js file under this path: store/index.js with following content: ```js export const state = () => ({ source: null, }); export const mutations = { setSource(state, source) { state.source = source; }, }; ``` 8. Now, we are ready to reference the player component in the index.vue like following: ```html ``` 9. This should result in a page which includes the THEOplayer component. ## Build Setup ```bash # install dependencies $ npm run install # serve with hot reload at localhost:3000 $ npm run dev # build for production and launch server $ npm run build $ npm run start # generate static project $ npm run generate ``` For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). --- # Getting started with VueJS This how-to guide describes how to integrate THEOplayer with VueJS framework. ### Customize configuration See [Configuration Reference](https://cli.vuejs.org/config/). ## A step-by-step guide: 1. Open your favorite IDE and then a terminal and install Vue CLI component: ```shell npm install vue ``` 2. If needed, create a new workspace and initial application: ```shell vue create theoplayer-vuejs-sample ``` 3. Choose your preferred configuration by choosing one of the presets from the list. 4. Once the project is created, you should see a successful message. 5. Go to root project and start application using instructions: ```shell cd theoplayer-vuejs-sample npm run serve ``` 6. As a result you should be able to browse the default page under the following URL: 7. Reference the THEOplayer files in index.html like following: ```html theoplayer-vuejs-sample
``` 8. In the src/components/ add a new file and name it Player.vue, then add the following snippet: ```html ``` 9. Then you can reference the Player component in different parts of the app and pass the source, for instance you can change App.vue like following: ```html ``` ## Including THEOplayer library assets 1. Create a new folder named i.e. libs under public folder and copy the THEOplayer WebSDK files there. 2. Change the index.html to reference local files: ```html theoplayer-vuejs-sample
``` 3. In the Player.vue modify libraryLocation like following: ```js const player = new window.THEOplayer.Player(this.$refs.theoplayer, { fluid: true, libraryLocation: './libs/THEOplayer', }); ``` --- # Getting started with AMP *A brief guide on how to build a THEOplayer in an AMP environment.* Note that this information is for a basic player in a testing environment. Additional information may be needed for specific cases. ## AMP Accelerated Mobile Pages (AMP) is an open-source HTML framework. More information about AMP is available at . ## Step-by-step guide: 1. Have a [valid AMP page](https://validator.ampproject.org/). *(Let's call this page A.)* 2. Prepare an embeddable page which can be served through an [``](https://amp.dev/documentation/components/amp-iframe/?format=websites). *(Let's call this page B.)* Read more about THEOplayer and iframes [here](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/how-can-we-embed-iframe.md). It's your responsibility to decide the most optimal approach for your use-case. You can use as a starting point to prepare your page. 3. Upload page B to a different (sub) domain than page A in order to adhere to the [iframe origin policy](https://github.com/ampproject/amphtml/blob/main/docs/spec/amp-iframe-origin-policy.md). 4. Embed page B into page A by using an ``. It's your responsibility to decide the most optimal approach for your use-case. You can use the snippet below as an example. ```html amp-iframe ``` 5. Once again: upload page A to a different (sub) domain than page B in order to adhere to the [iframe origin policy](https://github.com/ampproject/amphtml/blob/main/docs/spec/amp-iframe-origin-policy.md). For example, you can put page B on and page A on An example application of this step-by-step guide is available [here](https://f.hubspotusercontent20.net/hubfs/2163521/Demo_zone/amp-iframe-example.html). ## Alternatives Some video players offer a **custom AMP tag**. For example, YouTube offers this through [``](https://amp.dev/documentation/components/amp-youtube/?format=websites). Under the hood, these video players also leverage an `', }, ]; ``` ## Resources * [Social sharing API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/SocialSharing) * [Online demo](https://demo.theoplayer.com/social-sharing) --- # Up next on Web The up next feature adds a component to the default UI which enables viewers to navigate to the upcoming asset, and also loads this asset automatically. This can be useful if you want to increase the engagement and consumption of your users. ![Up next](/pr-873/assets/images/up-next-b95f9436365130312d8f0070857eceaa.png "Up next") The up next feature loads new web pages (rather than just changing the video source), so it is only available on the Web SDK. Make sure your Web SDK includes the `upnext` feature. ## Usage The snippet below demonstrates how you configure the up next API. ```js player.upnext.source = { image: '//cdn.theoplayer.com/video/vr/poster.jpg', title: '360/VR in THEOplayer', duration: '2:14', link: '//demo.theoplayer.com/vr-and-360', }; player.upnext.bar.offset = 106; ``` ## Resources * [Up next API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/Player#upnext) * [Online demo](https://www.theoplayer.com/theoplayer-demo-up-next) --- # VR and 360° video on Web 360-degree video is a type of video where every angle from a single viewpoint is recorded and can be played back. It offers a great sense of immersion for panoramic imagery or simulation purposes. The input should be a video source in an equirectangular format, and the output is a container where viewers can navigate around the scene. Next to spherical or 360° video playback, THEOplayer also offers integration with VR devices through a stereoscopic view, dubbed "stereo mode". VR is only available on the Web SDK. Make sure your Web SDK includes the `vr` feature. ## Initialization The Web SDK uses the [WebXR API](https://developer.mozilla.org/en-US/docs/Web/API/WebXR_Device_API) for virtual reality playback. For platforms that do not natively support WebXR, you can add the [WebXR polyfill](https://github.com/immersive-web/webxr-polyfill) to your web page. > **Bug in official polyfill** > > The official polyfill has a bug in its rendering, see [issue 167](https://github.com/immersive-web/webxr-polyfill/issues/167). Our team has already [proposed and submitted a fix](https://github.com/immersive-web/webxr-polyfill/pull/168), but this fix has not yet been published in a new release of the polyfill. > > In the meantime, you can use a patched build from our CDN. We highly recommend you to download and host this build on your own web server, since it will no longer be available once the official polyfill has been fixed. Add the polyfill to your page: ```html ``` And enable it: ```js const POLYFILL_CONFIG = { allowCardboardOnDesktop: true, }; new WebXRPolyfill(POLYFILL_CONFIG); ``` To indicate that your stream contains 360° content, pass a valid `VRConfiguration` as `vr` property when setting `player.source`. ```js const element = document.querySelector('.theoplayer'); const player = new THEOplayer.Player(element, { fluid: true, }); player.vr.useDeviceMotionControls = true; player.source = { sources: { type: 'application/x-mpegurl', src: 'https://example.com/example-stream.m3u8', }, vr: { 360: true, }, }; ``` ## Manipulating the viewing direction Below you can find an example querying the VR viewing direction and one setting the viewing direction. ```js /* reading the current position */ const currentViewingDirection = player.vr.direction; // e.g. {pitch: 0, yaw: 0, roll: 0} /* setting the position */ player.vr.direction = { pitch: 0, yaw: 180, roll: 0 }; /* example of how you can update only one direction property */ const newViewingDirection = player.vr.direction; /* e.g. {pitch: 0, yaw: 30, roll: 0} */ newViewingDirection.pitch = 180; /* {pitch: 180, yaw: 30, roll: 0} */ player.vr.direction = newViewingDirection; ``` ## Setting the stereo mode The snippet below enables stereo mode by setting the `stereoMode` property to `horizontal` or `vertical`. ```js player.source = { sources: [ { src: 'https://example.com/example-stream.m3u8', type: 'application/x-mpegurl', }, ], vr: { 360: true, stereoMode: 'horizontal', // or 'vertical' }, }; ``` Note that the device must have the automatic rotation feature enabled for stereo mode to work. ## Listening to VR related events The following code sample listens to the `stereochange` and `directionchange` events thrown by THEOplayer. ```js player.vr.addEventListener('stereochange', () => { const isStereoEnabled = player.vr.stereo; // (boolean) // do something with it }); player.vr.addEventListener('directionchange', () => { const { yaw, roll, pitch } = player.vr.direction; // (object) // do something with it }); ``` ## Requesting permissions Since iOS 13, access to device orientation and motion data is disabled by default. You need to request the necessary permission, triggered by a user action. ```js function requestPermissions() { DeviceMotionEvent.requestPermission() .then((response) => { if (response == 'granted') { window.addEventListener('devicemotion', (e) => { console.log('Device motion permissions granted'); }); } }) .catch(console.error); DeviceOrientationEvent.requestPermission() .then((response) => { if (response == 'granted') { window.addEventListener('deviceorientation', (e) => { console.log('Device orientation permissions granted'); }); } }) .catch(console.error); } ``` ## Resources * [VR API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/VR) * [Online demo](https://www.theoplayer.com/theoplayer-demo-virtual-reality-360-degree-view) --- # Web Audio on Web THEOplayer integrates with the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API), which allows you to do some quite nifty stuff with the player's audio output. If you are unfamiliar with the ideas behind the Web Audio API, Mozilla has an excellent [introduction to the basic concepts](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Basic_concepts_behind_Web_Audio_API). This guide helps you set up a demo just like the one in our [demo zone](https://demo.theoplayer.com/web-audio-api). The Web Audio API is only available on the Web SDK. Make sure your Web SDK includes the `webaudio` feature. ## A very basic Web Audio graph The first step in working with Web Audio is to create an `AudioContext`. Once you have one, you can create an `AudioNode` which contains the player's audio output, and connect it to the context's destination. ```js const AudioContext = window.AudioContext || window.webkitAudioContext; const audioCtx = new AudioContext(); const sourceNode = player.audio.createAudioSourceNode(audioCtx); // Without this connection, the player's audio is piped into the graph but not connected to any output, // resulting in no sound from your speakers or headphones. sourceNode.connect(audioCtx.destination); ``` ## Playing with the audio gain A first useful thing we could do is play with the gain, i.e. manipulate the player volume through the Web Audio API. ```js const AudioContext = window.AudioContext || window.webkitAudioContext; const audioCtx = new AudioContext(); const sourceNode = player.audio.createAudioSourceNode(audioCtx); const gainNode = audioCtx.createGain(); sourceNode.connect(gainNode); gainNode.connect(audioCtx.destination); ``` This sets up a gain filter on top of the player audio output, which you can manipulate using the `gainNode.gain.value` property. As an example, you could set up a callback which monitors mouse movement: ```js document.onmousemove = function (e) { gainNode.gain.value = (screen.height - e.screenY) / screen.height; }; ``` The gain is adjusted to the ratio of the distance of the mouse pointer position from the bottom of the screen and the screen height. The farther you move the mouse pointer down, the quieter the output of the video becomes, and the opposite effect when you move the mouse pointer up. ## Playing with stereo panning Similarly, you can play with the stereo panning of the player audio output through the `pan.value` property of a `StereoPanner` node. ```js const AudioContext = window.AudioContext || window.webkitAudioContext; const audioCtx = new AudioContext(); const sourceNode = player.audio.createAudioSourceNode(audioCtx); const pannerNode = audioCtx.createStereoPanner(); sourceNode.connect(pannerNode); pannerNode.connect(audioCtx.destination); document.onmousemove = function (e) { pannerNode.pan.value = (2 * e.screenX) / screen.width - 1; }; ``` Moving the pointer left and right changes the stereo panning of the player audio, with balanced audio at the exact middle of the screen. ## Combining both The Web Audio API allows you to pipe the output of one node as input for another node (in most cases). This way, you can combine the two tricks above: modifying the volume by moving the mouse pointer up and down, and modifying the panning by moving the mouse left and right. ```js const AudioContext = window.AudioContext || window.webkitAudioContext; const audioCtx = new AudioContext(); const sourceNode = player.audio.createAudioSourceNode(audioCtx); const gainNode = audioCtx.createGain(); const pannerNode = audioCtx.createStereoPanner(); sourceNode.connect(pannerNode); pannerNode.connect(gainNode); gainNode.connect(audioCtx.destination); document.onmousemove = function (e) { gainNode.gain.value = (screen.height - e.screenY) / screen.height; pannerNode.pan.value = (2 * e.screenX) / screen.width - 1; }; ``` ## Manipulating two players at once Assume that you have two players set up, bound to the variables `player1` and `player2`. You can set up exactly the same audio pipeline for both players, and reroute their audio to a single output. ```js const sourceNode1 = player1.audio.createAudioSourceNode(audioCtx); const gainNode1 = audioCtx.createGain(); const pannerNode1 = audioCtx.createStereoPanner(); sourceNode1.connect(pannerNode1); pannerNode1.connect(gainNode1); gainNode1.connect(audioCtx.destination); const sourceNode2 = player2.audio.createAudioSourceNode(audioCtx); const gainNode2 = audioCtx.createGain(); const pannerNode2 = audioCtx.createStereoPanner(); sourceNode2.connect(pannerNode2); pannerNode2.connect(gainNode2); gainNode2.connect(audioCtx.destination); ``` From there, you can do the inverse for the second player's audio: moving the mouse pointer up decreases the volume for the second player, and the panning is inverted as well. ```js document.onmousemove = function (e) { gainNode1.gain.value = (screen.height - e.screenY) / screen.height; pannerNode1.pan.value = (2 * e.screenX) / screen.width - 1; gainNode2.gain.value = e.screenY / screen.height; pannerNode2.pan.value = 1 - (2 * e.screenX) / screen.width; }; ``` ## Resources * [Basic guide to the Web Audio API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/knowledge-base/playback/basic-guide-web-audio-api.md) * [MDN - Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) * [Online demo](https://demo.theoplayer.com/web-audio-api) --- # Clipping on Web THEOplayer supports playing only a selected part of a video-on-demand stream, rather than playing it from start to finish. This can be used for example to watch a single game-changing event from a sports match, highlight an important part of a long video presentation, or skip straight to the best part of a music video. ## Usage The clip API helps you configure a clip within your content, through a `startTime` and an `endTime`, expressed in seconds. These properties are optional, and you don't need to specify both. For example, you can set just `startTime = 30` to have the video start at 30 seconds and play the rest of the video normally. The following example shows a video clipped to start at 30 seconds and end at 60 seconds. ```js player.source = { sources: [ { src: '//cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny_metadata.m3u8', type: 'application/x-mpegurl', }, ], }; // The clipping is applied in an event listener to make sure it isn't called before the source is set player.addEventListener('durationchange', function () { player.clip.startTime = 30; player.clip.endTime = 60; }); ``` ## Remarks * The event listener in the code sample is not strictly necessary, but it ensures that the clipping is applied after the source is set. If your source has not been set, or is being set but the process is not complete, clipping does not have any effect. ## Resources * [Clip API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Clip) * [Frame accurate clipping in HLS](https://www.theoplayer.com/blog/frame-accurate-clipping-in-hls) --- # Picture-in-Picture on Web The Picture-in-Picture (PiP) feature allows you to create a floating player. This is commonly used to let the video remain playing on screen even if: * The user scrolls to another section of the page (causing the original player to no longer be visible). * The user sends the application to the background. * The user opens a new page within the same application. THEOplayer distinguishes two flavors of Picture-in-Picture: * With **in-app** Picture-in-Picture, the visibility of the PiP window is contained to the inside of the app. In other words, it goes to background and foreground together with the application. * With **out-of-app** Picture-in-Picture, the visibility of the PiP window is not contained to the inside of the app. It can remain visible while the user navigates to other views, pages or apps. Picture-in-Picture is a presentation mode of the player. As such, you can listen for a `presentationmodechange` event, or read the player's presentation mode, to find out what the presentation mode is at a given moment or to detect a change. The Web SDK supports in-app Picture-in-Picture. Out-of-app Picture-in-Picture is not supported. ## Usage Picture-in-Picture is enabled as soon as the player configuration contains a `pip` configuration, so there is no need for any additional code to enable it. ![Picture-in-Picture](/pr-873/assets/images/picture-in-picture-778462f302e24a4b44a176d0fa345cd1.png "Picture-in-Picture") The PiP configuration has three properties: * `position`: (optional, possible values: `"top-left"`, `"top-right"`, `"bottom-left"`, `"bottom-right"`)
The corner in which the player should be shown while in PiP mode. Defaults to the bottom right corner. * `visibility`: (optional, a number from 0 to 1)
The maximum percentage of the original player position that should be visible to enable PiP automatically. If not configured, PiP can only be turned on by setting [presentationMode](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/types/PresentationMode) to `"picture-in-picture"`. * `retainPresentationModeOnSourceChange`: (optional, `true` or `false`)
If set to `true`, the previous [presentationMode](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/types/PresentationMode) is retained even as the source changes. It is set to `false` by default. ```js const playerConfig = { /* ... */ pip: { visibility: 0.7, position: 'bottom-left', retainPresentationModeOnSourceChange: true, }, }; ``` ## Remarks * Support for native Picture-in-Picture (i.e. where the player is draggable) is experimental. While support is widely adopted, the feature is most notably not supported by Firefox. ## Resources * [PiPConfiguration API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PiPConfiguration) * [PlayerConfiguration API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerConfiguration) * [PresentationMode API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/types/PresentationMode) * [Online demo](https://demo.theoplayer.com/picture-in-picture): start the video and scroll down on the page to see PiP in action. --- # HLS date ranges on Web The `#EXT-X-DATERANGE` tag associates a date range (i.e. a range of time defined by a starting and ending date) with a set of attribute/value pairs. It is a way of providing timed metadata in an HLS manifest. A possible use case is defining timed metadata for interstitial regions such as advertisements, but it can be used for any timed metadata needed by your stream. Date ranges are not parsed by default. To enable them, set `hlsDateRange` to `true`, either on the player configuration (for all sources) or on an individual source. Once enabled, the player exposes the date ranges as cues on a metadata text track, which you can consume through the text tracks API. ## Enabling date ranges ```js // in the player configuration const element = document.querySelector('.theoplayer-container'); const player = new THEOplayer.Player(element, { libraryLocation: libraryLocation, license: 'your-license-here', hlsDateRange: true, }); // or in the source description player.source = { sources: { src: 'https://example.com/example-stream.m3u8', type: 'application/x-mpegurl', hlsDateRange: true, }, }; ``` ## Reading date range cues Date ranges are exposed as cues on a metadata text track. Listen for the metadata track being added, and for its cues, to interact with the date range attributes. ```js player.textTracks.addEventListener('addtrack', (event) => { const track = event.track; if (track.kind !== 'metadata') { return; } // By default, metadata tracks are disabled and do not expose cues track.mode = 'hidden'; track.addEventListener('entercue', (enterCueEvent) => { console.log('Date range started', enterCueEvent.cue.content); }); track.addEventListener('exitcue', (exitCueEvent) => { console.log('Date range ended', exitCueEvent.cue.content); }); }); ``` See "[How to detect active text track cues](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-active-text-track-cues.md)" for more information. ## Resources * [HLS specification: EXT-X-DATERANGE](https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.2.7) --- # Preloading on Web The aim of preloading is to improve the user experience by loading parts of the video before the play button is hit, when the user is likely to play the video. Choosing to preload your video content reduces the start-up time of your videos. It also makes some data regarding the current source (video duration, timeline thumbnails, etc.) available before the user initiates playback. On the other hand, when the video is less likely to be played, or when it is less beneficial (live video, autoplay), you can configure the player to not preload it and save bandwidth. ## Preloading settings | Value | Description | | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `none` | Do not preload anything. After setting the source, the player does not load the manifest nor any media files until the play method is invoked on the player. | | `metadata` | Preload the metadata of the video. After setting the source, the player automatically loads the manifest file and waits for loading media files until the play method is invoked on the player. For static videos (e.g. MP4 files), this translates to preloading data from the start of the video in order to acquire e.g. the video's width and height and the initial video frames. | | `auto` | Preload enough data to allow for smooth initial playback. After setting the source, the player loads both the manifest files and enough media files until a sufficient playback buffer has been obtained. | ## Usage In this example, we are preloading metadata only. ```js player.preload = 'metadata'; ``` ## Remarks * THEOplayer always prefers downloading data for the currently playing video over downloading data for a preloading video. Only when the currently playing video has enough data to play to the end of the video, does the player start preloading other videos. * When preloading the first segment(s) (preload `auto`), the first frame of the video is shown under the big play button, if there is no poster. ## Resources * [What are the benefits of preloading?](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq/what-are-the-benefits-of-preloading.md) * [PreloadType API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/types/PreloadType) * [HTML specification](https://html.spec.whatwg.org/multipage/media.html#attr-media-preload) * [Online demo](https://demo.theoplayer.com/preloading-vod) --- # C2PA metadata on Web [C2PA](https://c2pa.org/) (Coalition for Content Provenance and Authenticity) defines a standard for cryptographically signed provenance metadata, allowing viewers to verify the origin and integrity of media content. Live streams signed according to the [C2PA specification](https://spec.c2pa.org/) carry this metadata inside the stream itself: * **ISOBMFF `uuid` boxes** (usertype `d8fec3d6-1b0e-483c-9297-5828877ec481`) in fMP4/CMAF segments: * the initialization segment carries the C2PA manifest store (purpose `manifest`), * each media segment can carry per-segment integrity data such as Merkle tree data (purpose `merkle`). * **`emsg` (event message) boxes** in media segments, carrying timed metadata. THEOplayer exposes this metadata to your application for both HLS and MPEG-DASH streams, so you can validate it with a C2PA-compatible library of your choice. > **Note** > > THEOplayer does **not** validate C2PA signatures, evaluate trust lists, or make any trust decisions. It only exposes the raw metadata to your application. > **Note** > > C2PA `uuid` box extraction is only available for fMP4/CMAF content played through THEOplayer's own HLS and MPEG-DASH pipelines (using Media Source Extensions). No `c2pametadata` events are dispatched for native HLS playback (e.g. on Safari with `useNativePlayback`), MPEG-TS content, or HESP/THEOlive streams. ## Enabling C2PA metadata extraction C2PA `uuid` box extraction is disabled by default and must be enabled per source with the `c2paMetadata` source property. C2PA presence is not advertised in the HLS or DASH manifest, so the player cannot detect it up front — enable it explicitly when you intend to validate the stream: ```js player.source = { sources: [ { src: 'https://example.com/signed-stream/master.m3u8', c2paMetadata: true, }, ], }; ``` ## Listening for C2PA `uuid` box metadata Whenever the player appends a segment containing a C2PA `uuid` box, it fires a `c2pametadata` event with the raw box contents: ```js player.addEventListener('c2pametadata', (event) => { console.log(event.purpose); // 'manifest' (initialization segment) or 'merkle' (media segment) console.log(event.segmentType); // 'initialization' or 'media' console.log(event.mediaType); // 'video' or 'audio': the type of media track in which the box was found console.log(event.startTime, event.endTime); // segment time range, or undefined for initialization segments console.log(event.data); // Uint8Array with the raw box payload (e.g. a JUMBF-serialized C2PA manifest store) // Forward the raw data to a C2PA-compatible validation library of your choice. myC2paValidator.addSegmentData(event.purpose, event.data); }); ``` For initialization segments, `startTime` and `endTime` are `undefined`. For media segments, the segment's time range is available in `startTime` and `endTime`. When audio and video are carried in separate tracks, each track is a separate C2PA-signed stream with its own manifest and Merkle data. Use `event.mediaType` to associate each event with the correct track, for example to validate the audio and video streams independently. The player dispatches every C2PA `uuid` box it encounters, without deduplication. Your application may receive events with identical contents more than once, for example when the same initialization segment is buffered again after a quality switch or a seek. Deduplicate in your application if needed. ## Listening for C2PA `emsg` metadata Timed C2PA metadata carried in `emsg` boxes is exposed through the player's generic [metadata text track](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-active-text-track-cues.md) support. The player creates a metadata text track of type `emsg`, and each `emsg` box becomes a cue with the raw message data as its content: ```js player.textTracks.addEventListener('addtrack', (event) => { const track = event.track; if (track.kind === 'metadata' && track.type === 'emsg') { track.mode = 'hidden'; track.addEventListener('addcue', (event) => { const cue = event.cue; console.log(cue.schemeIDURI); // e.g. 'https://c2pa.org/streaming' console.log(cue.startTime, cue.endTime); console.log(cue.content); // Uint8Array with the raw emsg message data }); } }); ``` ## Validating the metadata The exposed data is the raw, unmodified payload from the stream. To verify the provenance of the content, pass it to a C2PA-compatible library (for example, [c2pa-js](https://github.com/contentauth/c2pa-js)) together with the downloaded segment data, and apply your own trust policy to the validation result. --- # Context menu on Web The default UI of the Web SDK shows a THEOplayer context menu when the viewer right-clicks the player. This section explains how to change or remove it. * [How to disable the right click](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/miscellaneous/context-menu/disable-right-click.md) * [How to change the context menu](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/miscellaneous/context-menu/change-context-menu.md) --- # How to disable the right click on Web There are two ways to disable the right-click menu on the player. ## Disable the context menu feature By default, the `contextmenu` feature is included in the Web SDK. If you build your SDK with this feature disabled, your player does not have a context menu. ## Add a CSS rule to your page If you do not want to rebuild your player, you can add the following CSS rule after including THEOplayer's `ui.css`. You can add this rule in a ` ``` --- # How to change the context menu on Web You may want to change the logo in the context menu, change the default text, or add some items. The snippet below does exactly that. ```js function customizeContextMenu(container) { const contextMenu = container.querySelector('.theo-context-menu'); // change context menu text const contextMenuFirstItem = contextMenu.querySelector('.theo-context-version'); contextMenuFirstItem.innerText = 'Brand X'; // change mini logo const contextLogo = contextMenu.querySelector('.theo-0logo'); contextLogo.style.backgroundImage = "url('https://example.com/logo.png')"; contextLogo.style.backgroundSize = 'cover'; contextLogo.querySelector('svg').style.visibility = 'hidden'; // add a line const contextMenuSecondItem = document.createElement('li'); contextMenuSecondItem.innerText = '24/07/2018'; contextMenu.appendChild(contextMenuSecondItem); } const element = document.querySelector('.video-container'); const player = new THEOplayer.Player(element, playerConfig); customizeContextMenu(element); ``` Note that `customizeContextMenu` is called immediately after initializing a THEOplayer instance. The context menu can also be removed altogether, since it is one of the feature flags you can toggle when building your Web SDK. To verify whether the feature is enabled, you can execute the query below. ```js const contextMenuEnabled = THEOplayer.features.indexOf('contextmenu') > -1; ``` ## Related articles * [How to disable the right click](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/miscellaneous/context-menu/disable-right-click.md) --- # Errors on Web We live in an imperfect world, and sometimes things go wrong. A viewer might not be able to watch a video stream for a variety of reasons. Some example reasons are: * The video stream is unavailable, * The video stream is not supported on the selected platform, * The THEOplayer license is incorrectly configured. **When an issue occurs, an error should be triggered**. When a fatal playback issue occurs, THEOplayer exposes information about the error through the THEOplayer API. This allows you to detect errors: as a developer, you can use an event handler to catch (and handle) `error` events. This feature (i.e. being able to intercept errors and act on it) may be mandatory to you to achieve a use-case: * You want to log the error on an analytics stack, * You want to show your own error message to the viewer, * ... THEOplayer associates error codes with errors to categorize types of issues. As a developer, these error codes may help you to log an error on an analytics stack, or may help you to generate a custom error message towards your viewer. When a fatal playback issue occurs, THEOplayer's default UI overlays an error message on top of the video player. This error message indicates to the viewer that playback is unsuccessful. ## Related resources * [How to do error handling](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/miscellaneous/error/error-handling.md): this article explains how you use event listeners to intercept the `error` event. * [Error codes](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/miscellaneous/error/error-codes.md): this article explains what error codes are, and why they are useful. --- # How to do error handling on Web As a developer, you could be interested in error handling for any of the following reasons: * You want to render human-friendly error messages inside the video player. Reading this error message, * the user can either remediate the issue by themselves (e.g. by refreshing the page, by checking again in some time, ...), * or contact a support service with this error code. * You want to send error-data to an analytics service. (And this service helps you to identify frequent issues.) * You want to self-remediate the issue. For example, if a stream is unavailable, you want to retry to load the stream, or load a different stream. * ... THEOplayer exposes different types of errors, and you can interact with them in two ways: 1. Through an event listener, and attaching an event handler to this listener. 2. Through a getter, which holds data when an error has occurred. The sections below describe the "top-level", "generic", "player" `error` event. This event is fatal and prevents playback. There are also other `error` events, for example the one dispatched by the Chromecast API. You handle those `error` events in the same fashion. ## Event listener Subscribe to the [`error`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#error) event of the player, and attach a callback function which implements the error handling. ```js player.addEventListener('error', function (errorEvent) { console.log(errorEvent.errorObject); }); ``` Other interfaces dispatch their own `error` event, for example the [`error`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/ChromecastEventMap#error) event of the Chromecast API. You handle those in the same fashion. ## Getter You can query an occurred "generic player error" through the [`errorObject`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#errorObject) property of the player. ```js player.addEventListener('error', function () { console.log(player.errorObject); }); ``` Note that other types of errors are not available through this approach. ## Types of errors "[Which error related events does the player expose](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq/which-error-related-events-does-player-expose.md)" lists which types of errors there are. Note that generally speaking only the "generic error event" is fatal. Other events, like the "Chromecast error event" and the "ad error event", are considered non-fatal because some type of playback can still continue. "[Error codes](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/miscellaneous/error/error-codes.md)" lists which error codes are available for the "generic error event". ## Remarks * We encourage you to use event listeners to handle errors. The data in this event can be processed (e.g. stringified) and transferred (e.g. sent to an analytics service). * You could render your own error messages inside the video player. The demo at offers some help on this. --- # Error codes on Web THEOplayer has different types of error events. One of those error events is the "generic player error event". This error event indicates a fatal error, and can be intercepted through the player. This error event exposes an "error code" to explain the occurred error. An error code may be more interesting to track than a verbose error message. For example, if you are logging errors through an analytics service, it could be useful to filter them by their error code. An error code is a number (e.g. 2001), and it belongs to a category. The format of an error code is "\[category]\[sub-code]". For example, error "2001" belongs to category 2, and has sub-code 001. If the error is a generic sub-code for its category, then the sub-code is `000`. In other words, error `11000` is the "catch-all" for category `11`. ## Categories Each error code belongs to an error category. At the time of writing, there are 11 categories. 1. Configuration error: the integrator misconfigured the player. 2. License error: the player license is no longer valid, incorrect or not provided. 3. Source invalid: the source is not valid, or cannot be played on the current platform. 4. Manifest load error: the manifest cannot be loaded or parsed. 5. Media not supported: the media cannot be played on the current platform. 6. Network error: the network timed out, or is not available. 7. Content protection error: anything DRM related. Key system is not supported, invalid license, license expired, key missing, output restricted... 8. Subtitle load error: the subtitles cannot be loaded or parsed... 9. VR not supported: VR is not available on the current platform, VR presentation cannot be switched. 10. Ad blocker detected: an ad has been blocked (or the third party CSAI library was not loaded). 11. Fullscreen error: switching to fullscreen is not possible (note: this may be the case when fullscreen was not triggered by user interaction). Refer to "[How to do error handling](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/miscellaneous/error/error-handling.md)" to learn how to detect the "generic player error event". ## Requesting categories and codes You can programmatically request the list of available error categories through `THEOplayer.ErrorCategory`, as illustrated by the screenshot below. ![ErrorCategory](/pr-873/assets/images/error-code-ErrorCategory-6f53a43777395769d34029b30d5ecade.png "ErrorCategory") You can also request the category of an error code through `THEOplayer.ErrorCategory.fromCode()`. Meaning: `THEOplayer.ErrorCategory.fromCode(11000)` returns `11`. Similarly, you can request the list of all available error codes through `THEOplayer.ErrorCode`, as illustrated by the screenshot below. ![ErrorCode](/pr-873/assets/images/error-code-ErrorCode-198ffee2db62e071b0f3be03b5e2d95d.png "ErrorCode") ## API references * [`ErrorCategory`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/enums/ErrorCategory) * [`ErrorCategory.fromCode()`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/functions/ErrorCategory.fromCode) * [`ErrorCode`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/enums/ErrorCode) --- # Network on Web THEOplayer makes a number of network requests, and receives responses, while it is loading and playing a stream. The Network API allows you to intercept those requests and responses, so you can inspect them, modify them, redirect them, or respond with your own data. Common use cases are adding authentication headers or query parameters to segment requests, redirecting requests to another CDN, or rewriting a manifest before the player parses it. The Web SDK exposes this API through [`player.network`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Network). On iOS and iPadOS browsers, the Network API can only be used to intercept and alter DRM-related requests and responses. ## Detecting a lost connection To detect that the player lost its connection, listen for the [`manifesterror`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#manifesterror) and [`segmenterror`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#segmenterror) events, which are dispatched when the player fails to load the manifest or a segment. The [`manifestupdate`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#manifestupdate) event indicates that the player successfully loaded the manifest again, so the connection is back: ```js player.addEventListener('manifesterror', handleConnectionLost); player.addEventListener('segmenterror', handleConnectionLost); player.addEventListener('manifestupdate', handleConnectionRestored); ``` Event listeners can be removed similarly: ```js player.removeEventListener('manifesterror', handleConnectionLost); ``` ## Related articles * [How to add and remove request and response interceptors](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/network/request-response-interceptors.md) * [Why does the Network API not work on iOS devices?](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq/why-does-network-api-not-work-on-ios-devices.md) --- # How to add and remove request and response interceptors on Web A *request interceptor* runs before the player opens the connection, so it can change properties of the request such as its URL, method, headers or body. It can also respond to the request immediately, without contacting the server. A *response interceptor* runs before the player consumes the response, so it can change the status, the headers or the body of the response. Interceptors are kept in a stack: the interceptor which was added last is the first one to intercept. ## Prerequisites This guide assumes that you already have a THEOplayer instance in your page. If not, start with our [getting started guide](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md). ## Adding and removing request interceptors A [`RequestInterceptor`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/types/RequestInterceptor) receives the intercepted request, and can modify it with the [`redirect`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/InterceptableRequest#redirect) or [`respondWith`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/InterceptableRequest#respondWith) method. ```js const interceptor = (request) => { if (request.type === 'manifest') { request.redirect({ url: 'https://cdn.theoplayer.com/video/elephants-dream/448/chunklist_w370587926_b688000_vo_slen_t64TWFpbg==.m3u8', method: 'GET', headers: { 'Content-Type': 'application/x-mpegurl', }, }); } }; ``` The code sample above intercepts manifest requests made by the player, and redirects them to the given URL. If properties of the given [`RequestInit`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/RequestInit) object are missing, the API uses the values of the original request. Instead of redirecting the request, you can also respond to it yourself: ```js const interceptor = (request) => { if (request.type === 'manifest') { request.respondWith({ body: '#EXTM3U\n#EXTINF:6,\nhttps://cdn.theoplayer.com/video/elephants-dream/448/media_w370587926_b688000_vo_slen_t64TWFpbg==_0.ts\n#EXT-X-ENDLIST', status: 200, statusText: 'OK', }); } }; ``` The code sample above intercepts manifest requests made by the player, and responds with a manifest which contains a single segment. Add the interceptor to the player as follows: ```js player.network.addRequestInterceptor(interceptor); ``` Interceptors can be removed similarly: ```js player.network.removeRequestInterceptor(interceptor); ``` ## Adding and removing response interceptors A [`ResponseInterceptor`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/types/ResponseInterceptor) receives the intercepted response, and can modify it with the [`respondWith`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/InterceptableResponse#respondWith) method, which accepts a [`ResponseInit`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/ResponseInit) object. ```js const interceptor = (response) => { response.respondWith({ body: '#EXTM3U\n#EXTINF:6,\nhttps://cdn.theoplayer.com/video/elephants-dream/448/media_w370587926_b688000_vo_slen_t64TWFpbg==_0.ts\n#EXT-X-ENDLIST', status: 200, statusText: 'OK', }); }; ``` The code sample above intercepts responses received by the player, and responds with a manifest which contains a single segment. Add the interceptor to the player as follows: ```js player.network.addResponseInterceptor(interceptor); ``` Interceptors can be removed similarly: ```js player.network.removeResponseInterceptor(interceptor); ``` ## Waiting for asynchronous work The `waitUntil` method allows you to run asynchronous work before the request or the response is closed. It throws an error if the request or the response was already closed by the same interceptor. It accepts a done callback, a function which returns a promise, or a promise. ### Done callback When you pass a done callback, the request or the response is only closed once you call `done`. Failing to call it means that the request or the response is never delivered. ```js const interceptor = (request) => { request.waitUntil((done) => { setTimeout(() => { request.redirect({ url: 'https://cdn.theoplayer.com/video/elephants-dream/448/chunklist_w370587926_b688000_vo_slen_t64TWFpbg==.m3u8', method: 'GET', headers: modifyRequestHeaders(request.headers), }); done(); }, 100); }); }; ``` The code sample above waits for 100 milliseconds, redirects to another URL, and then closes the request. ### Function which returns a promise When you pass a function which returns a promise, the request or the response is only closed once that promise resolves. ```js const interceptor = (request) => { request.waitUntil(() => delay(100).then(() => { request.redirect({ url: 'https://cdn.theoplayer.com/video/elephants-dream/448/chunklist_w370587926_b688000_vo_slen_t64TWFpbg==.m3u8', method: 'GET', headers: modifyRequestHeaders(request.headers), }); }) ); }; ``` ### Promise You can also pass a promise directly. The request or the response is closed once that promise resolves. ```js const interceptor = (request) => { const promise = delay(100).then(() => { request.redirect({ url: 'https://cdn.theoplayer.com/video/elephants-dream/448/chunklist_w370587926_b688000_vo_slen_t64TWFpbg==.m3u8', method: 'GET', headers: modifyRequestHeaders(request.headers), }); }); request.waitUntil(promise); }; ``` ## HTTP errors If the status code is set to a code between 200 and 299, the player treats the response as successful. In any other case, the player reports an HTTP error. An interceptor can turn an HTTP error into a successful response, and vice versa. ## Related articles * [Network introduction](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/network/introduction.md) * [Why does the Network API not work on iOS devices?](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq/why-does-network-api-not-work-on-ios-devices.md) --- # Player on Web The Player API is the top-level API. This API exposes: * basic functions to manipulate video playback, such as start and pause; * properties to check states and values, such as the playhead position; * events to track changes. ## Events As a developer, you can programmatically attach event listeners to different APIs. Event listeners are often used to implement a custom UI, or to implement a custom analytics integration. The THEOplayer API exposes many events across different interfaces, as can be seen in the graphic below. Furthermore, most events exist in all SDKs (Web, iOS-based, Android-based and Roku). ![THEOplayer API Interfaces and Events](/pr-873/assets/images/analytics-events-185d6ab52b556af08d8300b545ae5876.png "THEOplayer API Interfaces and Events") On Web, event listeners are for example used to [build a chromeless UI](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/build-chromeless-ui.md), or to [implement a custom analytics integration](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/analytics/custom-analytics-integration.md). The table below gives an overview of a subset of the available events per interface in the THEOplayer Web SDK. | Interface | Event | Description | | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | [Player](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap) | [canplay](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#canplay) | Fired when the player can resume playback of the media data. | | Player | [canplaythrough](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#canplaythrough) | Fired when the player can resume playback of the media data and buffering is unlikely. | | Player | [contentprotectionerror](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#contentprotectionerror) | Fired when an error related to content protection occurs. | | Player | [contentprotectionsuccess](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#contentprotectionsuccess) | Fired when the key is usable for decryption. | | Player | [currentsourcechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#currentsourcechange) | Fired when the current source, which is chosen from ChromelessPlayer.source.sources, changes. | | Player | [destroy](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#destroy) | Fired when the the player is destroyed. | | Player | [dimensionchange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#dimensionchange) | Fired when the dimensions of the HTML element changes. | | Player | [durationchange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#durationchange) | Fired when ChromelessPlayer.duration changes. | | Player | [emptied](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#emptied) | Fired when the player's source is cleared. | | Player | [encrypted](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#encrypted) | Fired when the player encounters key system initialization data in the media data. | | Player | [ended](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#ended) | Fired when playback has stopped because the end of the media resource was reached. | | Player | [error](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#error) | Fired when an error occurs. | | Player | [loadeddata](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#loadeddata) | Fired when the player can render the media data at the current playback position for the first time. | | Player | [loadedmetadata](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#loadedmetadata) | Fired when the player determines the duration and dimensions of the media resource. | | Player | [loadstart](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#loadstart) | Fired when the player starts loading the manifest. | | Player | [manifestupdate](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#manifestupdate) | Fired when the manifest is updated. | | Player | [pause](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#pause) | Fired when ChromelessPlayer.paused changes to true. | | Player | [play](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#play) | Fired when ChromelessPlayer.paused changes to false. | | Player | [playing](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#playing) | Fired when playback is ready to start after having been paused or delayed due to lack of media data. | | Player | [progress](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#progress) | Fired when the player loaded media data. | | Player | [ratechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#ratechange) | Fired when ChromelessPlayer.playbackRate changes. | | Player | [readystatechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#readystatechange) | Fired when ChromelessPlayer.readyState changes. | | Player | [representationchange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#representationchange) | Fired when the current representation changes. | | Player | [resize](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#resize) | Fired when either ChromelessPlayer.videoWidth or ChromelessPlayer.videoHeight changes. | | Player | [seeked](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#seeked) | Fired when ChromelessPlayer.seeking changes to false after the current playback position was changed. | | Player | [seeking](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#seeking) | Fired when ChromelessPlayer.seeking changes to true, and the player has started seeking to a new position. | | Player | [segmentnotfound](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#segmentnotfound) | Fired when a segment can not be found. | | Player | [sourcechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#sourcechange) | Fired when ChromelessPlayer.source changes. | | Player | [timeupdate](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#timeupdate) | Fired when the current playback position changed as part of normal playback or in an especially interesting way, for example discontinuously. | | Player | [volumechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#volumechange) | Fired when ChromelessPlayer.volume changes. | | Player | [waiting](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#waiting) | Fired when playback has stopped because the next frame is not available, but the player expects that frame to become available in due course. | | Player | [currentTime](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#currenttime) | The current playback position of the media, in seconds. | | Player | [currentProgramDateTime](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#currentprogramdatetime) | The current playback position of the media, as a timestamp. | | Player | [muted](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#muted) | Whether audio is muted. | | Player | [...](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer) | ... | | [Ads (Client-Side)](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap) | [adbegin](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adbegin) | Fired when an ad begins. | | Ads (Client-Side) | [adbreakbegin](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adbreakbegin) | Fired when an ad break begins. | | Ads (Client-Side) | [adbreakchange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adbreakchange) | Fired when an ad break changes. | | Ads (Client-Side) | [adbreakend](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adbreakend) | Fired when an ad break ends. | | Ads (Client-Side) | [adbuffering](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adbuffering) | Fired when the ad has stalled playback to buffer. | | Ads (Client-Side) | [addad](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#addad) | Fired when an ad is added. | | Ads (Client-Side) | [addadbreak](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#addadbreak) | Fired when an ad break is added. | | Ads (Client-Side) | [adend](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adend) | Fired when an ad ends. | | Ads (Client-Side) | [aderror](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#aderror) | Fired when an ad errors. | | Ads (Client-Side) | [adfirstquartile](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adfirstquartile) | Fired when an ad reaches the first quartile. | | Ads (Client-Side) | [adimpression](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adimpression) | Fired when an ad counts as an impression. | | Ads (Client-Side) | [adloaded](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adloaded) | Fired when an ad is loaded. | | Ads (Client-Side) | [admetadata](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#admetadata) | Fired when an ads list is loaded. | | Ads (Client-Side) | [admidpoint](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#admidpoint) | Fired when an ad reaches the mid point. | | Ads (Client-Side) | [adskip](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adskip) | Fired when an ad is skipped. | | Ads (Client-Side) | [adthirdquartile](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adthirdquartile) | Fired when an ad reaches the third quartile. | | Ads (Client-Side) | [removeadbreak](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#removeadbreak) | Fired when an ad break is removed. | | Ads (Client-Side) | [updatead](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#updatead) | Fired when an ad is updated. | | Ads (Client-Side) | [updateadbreak](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#updateadbreak) | Fired when an AdBreak is updated. | | [Vendor Cast](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/VendorCastEventMap) | [statechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/VendorCastEventMap#statechange) | Fired when the state changes. | | [Chromecast](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/ChromecastEventMap) | [error](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/ChromecastEventMap#error) | Fired when an error occurs while casting or trying to cast. | | [Track List](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap) | [addtrack](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap#addtrack) | Fired when a track is added. | | Track List | [change](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap#change) | Fired when a track is activated or deactivated. | | Track List | [removetrack](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap#removetrack) | Fired when a track is removed. | | [Track](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackEventMap) | [change](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackEventMap#change) | Fired when a media track's enabled or a text track's changes. | | Track | [update](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackEventMap#update) | Fired when the track updates. | | [Media Track](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/MediaTrackEventMap) | [activequalitychanged](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/MediaTrackEventMap#activequalitychanged) | Fired when the media track's active quality changes. | | Media Track | [qualityunavailable](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/MediaTrackEventMap#qualityunavailable) | Fired when a quality of the track becomes unavailable. | | Media Track | [targetqualitychanged](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/MediaTrackEventMap#targetqualitychanged) | Fired when the media track's target quality changes. | | [Text Track](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap) | [addcue](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#addcue) | Fired when a cue is added to the track. | | Text Track | [cuechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#cuechange) | Fired when a cue of the track changes. | | Text Track | [entercue](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#entercue) | Fired when a cue of the track enters. | | Text Track | [error](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#error) | Fired when an error occurred while loading or parsing the track. | | Text Track | [exitcue](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#exitcue) | Fired when a cue of the track exits. | | Text Track | [readystatechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#readystatechange) | Fired when the text track's changes. | | Text Track | [removecue](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#removecue) | Fired when a cue of the track is removed. | | Text Track | [typechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#typechange) | Fired when the text track's changes. | | [Text Track Cue](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap) | [enter](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap#enter) | Fired when the cue is entered. | | Text Track Cue | [exit](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap#exit) | Fired when the cue is exited. | | Text Track Cue | [update](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap#update) | Fired when the cue is updated. | | [Presentation](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PresentationEventMap) | [error](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PresentationEventMap#error) | Fired when an error occurs. | | Presentation | [presentationmodechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PresentationEventMap#presentationmodechange) | Fired when the presentation mode changes. | | [VR](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/VREventMap) | [directionchange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/VREventMap#directionchange) | Fired when the VR.direction changes. | | VR | [error](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/VREventMap#error) | Fired when an error occurs. | | VR | [statechange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/VREventMap#statechange) | Fired when the VR.state changes. | | VR | [stereochange](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/VREventMap#stereochange) | Fired when the VR.stereo changes. | | [Uplynk](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkEventMap) | [assetinforesponse](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkEventMap#assetinforesponse) | Fired when an asset info response is received. | | Uplynk | [pingerror](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkEventMap#pingerror) | Fired when an error or invalid response is received from the Ping API. | | Uplynk | [pingresponse](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkEventMap#pingresponse) | Fired when a Ping response is received. | | Uplynk | [preplayresponse](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkEventMap#preplayresponse) | Fired when a Preplay response is received. | | Uplynk | [ads](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Uplynk#ads) | The Uplynk SSAI API. | | Uplynk | [assets](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Uplynk#assets) | List of Uplynk assets. | | [Uplynk Ad Break List](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakListEventMap) | [addadbreak](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakListEventMap#addadbreak) | Fired when the ad break is added. | | Uplynk Ad Break List | [removeadbreak](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakListEventMap#removeadbreak) | Fired when the ad break is removed. | | [Uplynk Ad Break](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakEventMap) | [adbreakbegin](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakEventMap#adbreakbegin) | Fired when the ad break begins. | | Uplynk Ad Break | [adbreakend](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakEventMap#adbreakend) | Fired when the ad break ends. | | Uplynk Ad Break | [adbreakskip](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakEventMap#adbreakskip) | Fired when the ad break is skipped. | | Uplynk Ad Break | [updateadbreak](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakEventMap#updateadbreak) | Fired when the ad break is updated. | | [Uplynk Ad](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap) | [adbegin](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap#adbegin) | Fired when an ad begins. | | Uplynk Ad | [adcomplete](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap#adcomplete) | Fired when the ad is completed. | | Uplynk Ad | [adend](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap#adend) | Fired when the ad ends. | | Uplynk Ad | [adfirstquartile](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap#adfirstquartile) | Fired when the ad reaches the first quartile. | | Uplynk Ad | [admidpoint](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap#admidpoint) | Fired when the ad reaches the mid point. | | Uplynk Ad | [adthirdquartile](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap#adthirdquartile) | Fired when the ad reaches the third quartile. | Our APIs are continuously evolving, and so is our list of events. Refer to [the Web SDK API reference](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web) to go through the latest set of events. ## Related articles * [How do you know when a stream or an advertisement is playing?](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/player/detect-stream-or-ad-playing.md) * [How to measure the time-to-first-frame](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/player/measure-time-to-first-frame.md) * [How to track the first play(ing) event](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/player/track-first-playing-event.md) * [How to track player states](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/player/track-player-states.md) --- # How do you know when a stream or an advertisement is playing on Web? This guide explains how to determine programmatically when the player is playing, and whether it is playing a stream or a client-side advertisement. This is useful when you want different behavior, or a different UI, for content and for advertisements. The `playing` event tells you when content or an advertisement starts playing. Its event handler is the right place to check whether an advertisement is playing. This approach only works for client-side advertisements, not for server-side advertisements. ## Usage The Web SDK exposes whether an advertisement is playing through [`player.ads.playing`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Ads#playing). ```js player.addEventListener('playing', (event) => { const adIsPlaying = player.ads.playing; console.log('PLAYING', adIsPlaying ? 'Advertisement' : 'Content', event); }); ``` ## Related articles * [How to track the first play(ing) event](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/player/track-first-playing-event.md) --- # How to measure the time-to-first-frame on Web The time-to-first-frame, also known as the start-up time, is the time between the moment where playback is requested and the moment where the first frame is rendered. To measure it, measure the time between the `play` event and the `playing` event. ## Usage ```js let playEventRegisteredAt; function playEventHandler() { player.removeEventListener('play', playEventHandler); playEventRegisteredAt = Date.now(); } function playingEventHandler() { player.removeEventListener('playing', playingEventHandler); const timeToFirstFrame = Date.now() - playEventRegisteredAt; console.log('Time-to-first-frame:', timeToFirstFrame); } player.addEventListener('play', playEventHandler); player.addEventListener('playing', playingEventHandler); ``` --- # How to track the first play(ing) event on Web Sometimes you want to know when a new stream starts playing for the first time, for example to forward a `firstplay` event to an analytics back-end. THEOplayer does not expose such an event, but you can implement it yourself, which gives you more control over its logic. The implementation below listens for the first `playing` event after every source change. If the viewer pauses and resumes the stream, the event is not dispatched again. If the viewer switches to another stream, it is dispatched once more. You can swap out the `playing` event for a `play` event, or for any other event for that matter. ## Usage ```js function firstPlayingHandler(event) { player.removeEventListener('playing', firstPlayingHandler); console.log('First playing event!', event); } player.addEventListener('sourcechange', () => { player.removeEventListener('playing', firstPlayingHandler); player.addEventListener('playing', firstPlayingHandler); }); ``` Instead of `sourcechange`, you can also listen for `loadedmetadata`, which is dispatched once the player has loaded the metadata of the new stream. ## Related articles * [How do you know when a stream or an advertisement is playing?](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/player/detect-stream-or-ad-playing.md) --- # How to track player states on Web A video player has states. As a developer, you should understand these states, as each state grants you access to different data, and might imply a re-render of your UI. For example, if your video player is in the "Paused" state, you want to show a play-button instead of a pause-button. The objective of this guide is to define some video player states, and how to track the transition between states. Although there's no official list of video player states, we'll define 11 states for learning purposes. These 11 states are visualized in the state diagram below. ![Video Player States](/pr-873/assets/images/player-video-player-states-4f4f37f92d4b03a2c750ce7fe03d0a5a.png "Video Player States") The table below provides the definitions of these 11 states. | State | Description | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | A: Empty | A state where the player is created, but has no information or data retained on the source which is to be played. The player is idle and not undertaking any interaction with the system. | | B: Initialized | A state where the player knows the source which is to be played, but no media data is loaded. The player is idle and not undertaking any interaction with the system. | | C: Playing - HAVE\_NOTHING | A state where the player wants to start playing a source, but has no media data available. The player is actively loading media data in order to initiate playback. | | D: Paused - HAVE\_NOTHING | A state where the player wants to fill its buffer without playing, but has no media data available. The player is actively loading media data in order to fill its buffer. | | E: Playing - HAVE\_METADATA | A state where the player wants to start playing a source, but only metadata is available and the entire buffer is still empty. The player knows which tracks and qualities are available. The player is actively loading media data in order to initiate playback. | | F: Paused - HAVE\_METADATA | A state where the player wants to fill its buffer without playing, but only metadata is available and the entire buffer is still empty. The player knows which tracks and qualities are available. The player is actively loading media data in order to fill its buffer. | | G: Playing - HAVE\_CURRENT\_DATA | A state where the player wants to start playing a source and has media data in its buffer for the current timestamp, but this data is insufficient to start playback and guarantee a smooth viewer experience. The player is actively loading media data in order to initiate playback. | | H: Paused - HAVE\_CURRENT\_DATA | A state where the player wants to fill its buffer without playing and has media data in the buffer for the current timestamp, but this data is insufficient to start playback and guarantee a smooth viewer experience. The player is actively loading media data in order to fill its buffer. | | I: Playing | A state where the player is actively rendering out media data. The current timestamp is progressing at the speed of the current playback rate and media buffer data is being consumed and loaded in parallel. timeupdate events will be dispatched regularly during playback and progress events will be dispatched as additional data is loaded. | | J: Paused | A state where the player has sufficient media data in its buffer in order to guarantee a smooth viewer experience when playback is started, but it is in a paused state. The player is not consuming any media data, but might be loading additional media data to grow its buffer in the background. | | K: Error | A state where the player has observed a fatal, non recoverable error. The player cannot continue playback and needs to be initialized again in order to restart playback. A player can get into this state from any state other than the Empty-state. | The table below explains what triggers each of the 26 transitions between states, and what video player events are related. | Transition | Triggers | Events | | ---------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | `new THEOplayer.Player` | | | 2 | `player.source = X` | `sourcechange`, `currentsourcechange` | | 3 | `player.play()` | `play`, `loadstart` | | 4 | `player.load()` | `loadstart` | | 5 | `player.pause()` | `pause` | | 6 | `player.play()` | `play` | | 7 | The stream manifest being loaded | `readystatechange`, `loadedmetadata`, `durationchange` | | 8 | The stream manifest being loaded | `readystatechange`, `loadedmetadata`, `durationchange` | | 9 | `player.pause()` | `pause` | | 10 | `player.play()` | `play` | | 11 | Stream media data is loaded | `readystatechange`, `loadeddata`, `progress`, `canplay` | | 12 | Stream media data is loaded | `readystatechange`, `loadeddata`, `progress`, `canplay` | | 13 | `player.pause()` | `pause` | | 14 | `player.play()` | `play` | | 15 | Sufficient media data is loaded to start playback fluently. | `progress`, `canplaythrough`, `readystatechange`, `playing`
*A `seeked` event can be dispatched if the entrance of this state was caused by a seek.* | | 16 | Sufficient media data is loaded to start playback fluently. | `canplaythrough`, `readystatechange`
*A `seeked` event can be dispatched if the entrance of this state was caused by a seek.* | | 17 | `player.pause()` | `pause` | | 18 | `player.play()` | `play`, `playing` | | 19 | `player.currentTime = X` and there is some media data at the target time, but not enough to play fluently. | `seeking`, `readystatechange` | | 20 | The player buffer is not being filled enough, causing a stall. | `waiting`, `readystatechange` | | 21 | `player.currentTime = X` and there is no media data at the target time. | `seeking`, `readystatechange` | | 22 | Playback has ended. | `ended`, `readystatechange` | | 23 | `player.currentTime = X` and there is some media data at the target time, but not enough to play fluently. | `seeking`, `readystatechange` | | 24 | `player.currentTime = X` and there is no media data at the target time. | `seeking`, `readystatechange` | | 25 | An error occurred. | `error` | | 26 | `player.source = null` | `emptied` | ## Implementation The code samples below demonstrate how you track some of the 18 events related to the state transitions. * `sourcechange` * `currentsourcechange` * `play` * `loadstart` * `pause` * `readystatechange` * `loadedmetadata` * `durationchange` * `loadeddata` * `progress` * `canplay` * `canplaythrough` * `playing` * `waiting` * `seeking` * `ended` * `error` * `emptied` All 18 events are dispatched by the `Player` interface. The snippet below demonstrates how you can track some of the related events through the Web SDK. All other events can be tracked similarly. You can find the event names in [the `PlayerEventMap` API reference](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap), and the properties of the player in [the `ChromelessPlayer` API reference](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer). This approach is valid for all web-based platforms, including Tizen and webOS. ```js player.addEventListener('sourcechange', (event) => { console.log(event); }); // ... player.addEventListener('ended', (event) => { console.log(event); }); ``` --- # Text tracks on Web The articles in this section describe how to implement common use-cases related to text tracks, subtitles, closed captions and metadata. These use-cases can range from programmatically detecting new metadata, to side-loading subtitles. THEOplayer supports * subtitle and closed caption formats like WebVTT, CEA-608, DVB Subs and TTML; * metadata formats like ID3, emsg, EventStream and `EXT-X-DATERANGE`. The TextTrack API allows you to programmatically switch between text tracks, detect events related to text tracks (e.g. a new active cue) and more. ## Related articles * [How to dynamically change the visible captions](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/change-visible-captions.md) * [How to programmatically detect text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-text-tracks.md) * [How to programmatically detect text track changes](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-text-track-changes.md) * [How to programmatically enable or disable text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) * [How to detect active text track cues](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-active-text-track-cues.md) * [How to insert subtitles](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/insert-subtitles.md) * [How to implement preview thumbnails](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/preview-thumbnails.md) * [How to track ID3 cues/tags](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/track-id3-cues.md) --- # How to dynamically change the visible captions on Web This article describes how you can dynamically change the currently selected subtitle or closed captions language, or how you can set a default language. This article assumes that you are using text tracks that are loaded with the manifest. If you add the text track separately, you can also mark it as the default track when you configure your source. The examples below match tracks on their `language` property, which contains the ISO language code of the track. If that is not sufficient for your use case, for example when a stream contains multiple tracks with the same language, you can match on the track's `label` instead, which contains the human-readable name shown in your UI. ## Usage The function below disables all text tracks which are currently active, and then enables the text track with the requested language. ```js function setLanguage(player, language) { // Disable all text tracks that are currently active player.textTracks .filter(function (track) { return track.mode !== 'disabled'; }) .forEach(function (track) { track.mode = 'disabled'; }); // Enable the text track for a specific language const requestedTrack = player.textTracks.find(function (track) { return track.language === language; }); if (requestedTrack) { requestedTrack.mode = 'showing'; } } ``` ## Related articles * [How to programmatically detect text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-text-tracks.md) * [How to programmatically enable or disable text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) * [How to insert subtitles](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/insert-subtitles.md) --- # How to programmatically detect text tracks on Web This article describes how you can use the TextTrack API to detect text tracks, which can be subtitles, closed captions or metadata. Implementing this functionality is a common use-case for developers who want to build their own UI to visualize the available subtitle languages. ## Usage Listen for the [`addtrack`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap#addtrack) event on [`player.textTracks`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#texttracks) to detect every [`TextTrack`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrack) that is added to the player. ```js function handleAddTrackEvent(addTrackEvent) { const textTrack = addTrackEvent.track; } player.textTracks.addEventListener('addtrack', handleAddTrackEvent); ``` ## Related articles * [How to programmatically enable or disable text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) * [How to programmatically detect text track changes](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-text-track-changes.md) * [How to build a chromeless UI](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/build-chromeless-ui.md) --- # How to programmatically enable or disable text tracks on Web This article describes how you can use the TextTrack API to enable or disable subtitles, closed captions or metadata. Implementing this functionality is a common use-case for developers who want to build their own UI to toggle subtitles. It is advised to disable the text tracks which you do not want to display, in order to avoid issues with overlapping text. ## Usage Set the [`mode`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrack#mode) of a [`TextTrack`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrack). ```js // disable all text tracks player.textTracks.forEach(function (track) { track.mode = 'disabled'; }); // enable a specific text track player.textTracks[indexOfRequestedTextTrack].mode = 'showing'; ``` ## Related articles * [How to dynamically change the visible captions](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/change-visible-captions.md) * [How to programmatically detect text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-text-tracks.md) * [How to build a chromeless UI](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/build-chromeless-ui.md) --- # How to insert subtitles on Web This article describes how you can side-load subtitles or closed captions, i.e. how you can add text tracks which are not part of your HLS or MPEG-DASH manifest. Every source description has a text tracks property, which accepts an array of side-loaded text tracks. All valid tracks are available for playback as long as the player's source is not set again. Text track descriptions can also be used for other purposes, e.g. for including preview thumbnails and chapters. ## Usage Each text track is described by a [`TextTrackDescription`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackDescription). ```js player.source = { sources: [ { src: '//path/to/your-manifest.m3u8', type: 'application/x-mpegurl', }, ], textTracks: [ { default: true, // optional kind: 'subtitles', // optional label: 'English subs', // optional - this appears in your UI src: 'path/to/your-subs-track1.vtt', srclang: 'en', }, ], }; ``` ## Subtitle styling To style your subtitles, look into the player's [`textTrackStyle`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackStyle) property. ## Related articles * [How to dynamically change the visible captions](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/change-visible-captions.md) * [How to programmatically enable or disable text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) * [How to implement preview thumbnails](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/preview-thumbnails.md) --- # How to implement preview thumbnails on Web This article describes how you can enable preview thumbnails, which THEOplayer implements through the TextTrack API. The thumbnails are loaded as a separate text track, by side-loading them as a text track in your source description. A demo of the preview thumbnails on the THEOplayer Web SDK is available at . ## Usage ```js player.source = { sources: { type: 'application/x-mpegurl', src: 'https://cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny.m3u8', }, textTracks: [ { default: true, src: 'https://cdn.theoplayer.com/dash/theoplayer/thumbnails/big_buck_bunny_thumbnails.vtt', label: 'thumbnails', kind: 'metadata', }, ], }; ``` If you use the [Open Video UI for Web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui.md) or [THEOplayer's default UI](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/introduction.md), the thumbnails are shown automatically when hovering or scrubbing the seek bar. ## Related articles * [How to insert subtitles](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/insert-subtitles.md) --- # How to track ID3 cues and tags on Web [ID3](https://en.wikipedia.org/wiki/ID3) is a type of metadata which can be inserted in HTTP live streams. Once an ID3 cue is inserted, it is added to a THEOplayer text track. Developers commonly track ID3 cues because they want to introduce a certain behavior depending on the metadata contained by the ID3 cues, for example: * to schedule advertisements dynamically by using information passed on by the ID3 metadata; * to overlay certain text on top of the player (e.g. the score of a football match). The demo at demonstrates a usage of ID3 metadata. Just before the song changes, an `enter` event is dispatched. The song information (title, album, etc.) is contained within this `enter` event, and can be used to update the UI. This article describes how you can listen for timed metadata events, and how you can track the `enter` event. ## Listening for timed metadata events Listen for the [`cuechange`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#cuechange) event of a [`TextTrack`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrack). ```js player.textTracks.addEventListener('addtrack', function (addTrackEvent) { const track = addTrackEvent.track; if (track.kind !== 'metadata') { return; } track.addEventListener('cuechange', function (cueChangeEvent) { // here you can access the cues of the track, and display the metadata to the outside }); }); ``` ## Tracking the enter event The `enter` event, which is part of the TextTrack API, maps to the moment in time when the ID3 cue becomes relevant. Listen for the [`entercue`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#entercue) event of the track, which is fired for every cue of that track that becomes active. The entered cue is available as [`event.cue`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEnterCueEvent#cue). ```js player.textTracks.addEventListener('addtrack', function (addTrackEvent) { const track = addTrackEvent.track; if (track.kind !== 'metadata') { return; } track.addEventListener('entercue', function (enterCueEvent) { // log entercue event console.log(enterCueEvent); }); }); ``` Alternatively, you can listen for the [`enter`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap#enter) event on each individual cue. Note that you have to add a listener to every cue, including the cues that are added to the track later on. ```js function enterListener(event) { // log enter event console.log(event); } function handleTrackCreation(addTrackEvent) { const track = addTrackEvent.track; if (track.kind !== 'metadata') { return; } track.cues.forEach(function (cue) { cue.addEventListener('enter', enterListener); }); // detect cues being added to the track track.addEventListener('addcue', function (addCueEvent) { // detect a cue being shown from a track addCueEvent.cue.addEventListener('enter', enterListener); }); } player.textTracks.addEventListener('addtrack', handleTrackCreation); ``` ## Resources * : a demo which illustrates the use of ID3 in production. * : ID3.org home page. * : Wikipedia - ID3. * : Sourcing In-band Media Resource Tracks from Media Containers into HTML. ## Related articles * [How to detect active text track cues](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-active-text-track-cues.md) * [How to programmatically detect text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-text-tracks.md) --- # How to programmatically detect text track changes on Web This article describes how you can use the TextTrack API to detect text track changes. A text track "change" is triggered by enabling (or disabling) a subtitle or closed captions track. Implementing this functionality is a common use-case for developers who want to build their own UI, and annotate the subtitle (or closed captions) track that is currently active. ## Usage The implementation of the Web SDK applies to all web-based platforms, including Tizen and webOS. The Web SDK exposes the TextTrack API through [`player.textTracks`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#texttracks). This `textTracks` property is a [`TextTrackList`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTracksList) that inherits from the [`TrackList`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackList). This `TrackList` dispatches the events from the [`TrackListEventMap`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap). This `TrackListEventMap` contains the [`change`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap#change) event, as well as the `addtrack` and `removetrack` event. The code below allows you to detect text track changes. ```js player.textTracks.addEventListener('change', function (event) { const track = event.track; const isEnabled = track.mode === 'showing'; console.log(track, track.label, track.kind, track.type, isEnabled); }); ``` The properties of a text `track` (e.g. `mode`, `kind`) are described in [the `TextTrack` API reference](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrack). ## Related articles Are you reading this article because you are interested in subtitles and closed captions? Continue reading below. * [How to programmatically detect text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-text-tracks.md) * [How to dynamically change the visible captions](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/change-visible-captions.md) * [How to programmatically enable or disable text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) * [How to insert subtitles](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/insert-subtitles.md) Refer to [how to track ID3 cues](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/track-id3-cues.md) if you are interested in timed metadata (ID3, emsg, EventStream, `EXT-X-DATERANGE`, ...). Are you reading this article because you are implementing a custom UI? Then you will find the following articles interesting: * [How to build a chromeless UI](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/build-chromeless-ui.md) * [How to detect video track quality changes](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/media-tracks/detect-video-track-quality-changes.md) * [How to detect audio track changes](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/media-tracks/detect-audio-track-changes.md) --- # How to detect active text track cues on Web This article describes how you can use the TextTrack API to detect the active text track cues, by subscribing to the `enter` and `exit` events of a cue, or to the `cuechange` event of a text track. Implementing this functionality can be a use-case for developers who want to build their own UI, and insert and style their subtitles with maximum freedom. Alternatively, you may need to access the active cues for analytics purposes, or to render them outside of the video player. Another common use-case is to detect the active text track cue of timed metadata. This article also discusses this use-case. ## Implementation for subtitles and closed captions Note that this section focuses on detecting active cues for subtitles and closed captions. Go to the section on ["implementation for timed metadata"](#implementation-for-timed-metadata) if you rather want to track timed metadata like ID3, emsg, `EXT-X-DATERANGE` and EventStream. The Web SDK exposes the TextTrack API through [`player.textTracks`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#texttracks). This `textTracks` property is a [`TextTrackList`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTracksList) that inherits from the [`TrackList`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackList). This `TrackList` dispatches the events from the [`TrackListEventMap`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap). This `TrackListEventMap` contains the [`addtrack`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap#change) event, as well as the `change` and `removetrack` event. In the callback of your `addtrack` event, you want to track the [`addcue`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#addcue) event through the [`TextTrack`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrack) interface. Then, in the callback of your `addcue` event, you want to track the [`enter`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap#enter) and [`exit`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap#exit) events through the [`TextTrackCue`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackCue) interface. The `enter` event is dispatched when a cue becomes active and the `exit` event is dispatched when a cue becomes inactive. You can fetch the actual content in the callback of your `enter` event through its [`content`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackCue#content) property. Alternatively, in the callback of your `addtrack` event, you could track the [`cuechange`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#cuechange) event through the `TextTrack` interface. Then, in the callback of your `cuechange` event, you want to iterate over the active cues. For each active cue, you could also query its `content` property. The code below allows you to detect the active text track cues. ```js player.textTracks.addEventListener('addtrack', function (addTrackEvent) { const track = addTrackEvent.track; track.addEventListener('addcue', function (addCueEvent) { const cue = addCueEvent.cue; cue.addEventListener('enter', function (enterEvent) { console.log(enterEvent, enterEvent.cue.content); }); cue.addEventListener('exit', console.log); }); track.addEventListener('cuechange', function (cueChangeEvent) { const cues = cueChangeEvent.track.activeCues; for (let i = 0; i < cues.length; i++) { console.log('cuechange active cue', i, cues[i]); } }); }); // ... // player.source = ... ``` You should invoke these event handlers before you configure your stream, because the player might have already dispatched the event before you were able to subscribe to it. ## Implementation for timed metadata The implementation for timed metadata is identical to the one for subtitles and closed captions, except for three things. 1. You do not necessarily use the `enter` event. For cues that span a period of time, for example `EXT-X-DATERANGE` cues, you might want to perform your action when the cue ends, so on the `exit` event instead. 2. You might need to set the `mode` of the relevant text track to `hidden`, as documented in [how to programmatically enable or disable text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md). Some types of timed metadata, for example `EXT-X-DATERANGE` and EventStream, are `disabled` by default. You should add a condition to your `addtrack` callback to decide whether you want to set your track to `hidden`. 3. You should set [`hlsDateRange`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/PlayerConfiguration#hlsDateRange) to `true` in your player configuration or stream configuration, if you want to detect `EXT-X-DATERANGE` tags in an HLS stream. The article on [how to track ID3 cues](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/track-id3-cues.md) might be useful to learn more about detecting ID3 tags specifically. ## Related articles * [How to programmatically detect text track changes](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/detect-text-track-changes.md) * [How to programmatically enable or disable text tracks](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) * [How to track ID3 cues](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/text-tracks/track-id3-cues.md) --- # License on Web A THEOplayer license is required to use THEOplayer. The licensing schema was previously based on the built-in license from the SDK build, but THEOplayer now allows you to update the license without rebuilding or re-integrating the player SDK. This article explains how to configure the license at runtime. ## Passing the license through the player configuration This can be done in two ways: * Pass a license string for the player by setting it on the `license` property: ```js const player = new THEOplayer.Player(element, { libraryLocation: '/path/to/your-theoplayer-folder/', license: 'your_license', }); ``` * Pass a valid URL that can be used to fetch the license for the player by setting it on the `licenseUrl` property: ```js const player = new THEOplayer.Player(element, { libraryLocation: '/path/to/your-theoplayer-folder/', licenseUrl: 'your_license_url', }); ``` If the player is configured with either `license` or `licenseUrl`, the player will first validate the `license` string. If the license is either undefined or expired, the player will attempt to request the `licenseUrl` instead. If no `licenseUrl` is defined, then a default license URL will be used. If neither of these options is provided, the player will default to the built-in license in the SDK if it was built on THEOportal. If the player is [from npm](https://www.npmjs.com/package/theoplayer), it will throw a license error. If no valid license can be found, the player will throw a license error. ## FAQ ### Why do I have to configure a license? In order to provide THEOplayer insights if a stream can be played, it needs to have access to an active license. You can choose to either configure this license, or configure a URL where this license can be retrieved. We recommend everyone to configure an active license or a license URL. ### How do I avoid introducing any dependencies on THEOplayer servers? There are two important aspects which can introduce a dependency on THEOplayer servers: 1. **Loading a THEOplayer SDK from the THEOplayer CDN.** This issue is only relevant for the Web SDK. In order to avoid this dependency, we recommend to self-host your THEOplayer SDK. The easiest approach is to load the player through npm, or download the player SDK as a zip file from the developer portal. More information on how to self-host your player can be found in "[Self-hosting THEOplayer](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq/self-hosting-theoplayer.md)". 2. **Loading a THEOplayer license from the THEOplayer license system.** In order to avoid this dependency, it is important to always keep your license up to date, either by configuring an updated license or by configuring your own license URL. When your THEOplayer license expires, a new player instance will actively check against THEOplayer's license system if there is an updated license available. This system should however only serve as a fallback for applications which cannot be updated. ### Should I pass a license string or a license URL? We recommend our customers to use the license string whenever possible. In special cases it is possible to use the license URL instead. Use cases include contractual or legal (for example privacy) requirements. For more information, please contact . If the player is configured with both a license string and a license URL, it will first try to validate the license string. If the license string is either not configured or expired, the player will attempt to request the license URL instead. If no license URL is defined, then a default license URL will be used. If no valid license can be found, the player will throw a license error. ### Should I update my application upon license renewal? Yes. After renewing your license, you should make sure to include the new THEOplayer license in your next app update. ### What happens if I don't timely update the license in my application? The player will attempt to update the license up to 72 hours before expiration. For this, the player will request a new license either from the configured license URL, or as a fallback (when there is no license URL configured) from `validate.theoplayer.com`. Either way, if the request is successful, the player will store the license locally. We highly recommend you to self-host the license using the license URL in the player configuration. For self-hosting, the license file with the latest license string for your application should be hosted. The full URL to this file should then be configured as the license URL in the player configuration. ### Will I still be able to play offline content with my THEOplayer SDK if my license expires? When your license expires, THEOplayer will attempt to load an updated license. If an updated license is available, offline playback will still function. If no valid license is available, and no license server can be reached (due to the client being offline), the player will throw an error and stop playback. For this reason, it is important to always keep your license up to date. ### What should I do to protect my license? The THEOplayer license you get from THEOportal is already an obfuscated string. If you want to protect your license, you need to make sure to configure your [page and source domains](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq/page-and-source-domains.md) to guarantee that your license string cannot be used anywhere other than the specified pages and/or with those source domains. --- # Introduction THEOplayer and Uplynk are partners in the streaming industry. [Uplynk](https://www.uplynk.com/) offers services like encoding, transcoding, server-side ad-insertion, a CDN and more. > **Verizon Media / Edgio** > > Uplynk is formerly known as **Verizon Media** and **Edgio**. We are moving away from any Verizon Media references in our API. These are now marked as deprecated and should be replaced by the Uplynk API. Uplynk offers services which can enhance the API, UI and UX of video players and applications. The most popular services are called *Preplay*, *Ping* and *Asset Info*. THEOplayer integrates these services so that developers and viewers can benefit from them. For example, * Viewers can skip over ads; * Viewers can see when an ad is playing (and stopping); * Developers can define the behavior when viewers try to seek over an ad; * Developers can query metadata to render a playlist in their application. A live demo of THEOplayer's integration of Uplynk is available [here](https://cdn.theoplayer.com/demos/verizon-media/index.html). ![UplynkDemo](/pr-873/assets/images/VerizonMediaDemo-57db7aa1e91a449a9a1d708b999251d5.png "Uplynk Demo") For more information on the integration and available APIs, continue your read on the pages below. ## [📄️Preplay](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/preplay.md) [This article explains how to leverage the Uplynk Preplay API through THEOplayer. This API allows developers to pre-integrate with Uplynk's Preplay service.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/preplay.md) ## [📄️Uplynk Ads](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ads.md) [Uplynk offers support for server-side ad-insertion (SSAI).](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ads.md) ## [📄️Ping](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ping.md) [This article explains how to use Uplynk's Ping API.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ping.md) ## [📄️Uplynk configuration](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/configuration.md) [This article explains what is possible with an Uplynk configuration when setting up a THEOplayer instance.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/configuration.md) ## [📄️Uplynk interface](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/interface.md) [This article explains how developers can hook into the Uplynk interface. This interface allows developers to query the available assets and ads in an Uplynk stream, its properties, and subscribe to its events.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/interface.md) ## [📄️What are the Uplynk challenges](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq/what-are-the-uplynk-challenges.md) [The purpose of this article is to group common challenges application developers might encounter when using Uplynk (formerly known as Verizon Media, Edgecast or Edgio) as the back-end.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/faq/what-are-the-uplynk-challenges.md) Sample Git projects are available on the following links: * Web: * Android: * iOS: * Roku: --- # Preplay This article explains how to leverage the Uplynk Preplay API through THEOplayer. This API allows developers to pre-integrate with Uplynk's Preplay service. Uplynk users can use an API (i.e. `Preplay`) to generate a streaming playlist given one or more assets or advertisements. The Uplynk back-end stitches the content together and allows for server-side ad-insertion (SSAI) when applicable. This document describes how THEOplayer users should configure their source to leverage this Preplay service. Assumptions: * THEOplayer assumes the availability of the Preplay API and Uplynk content servers to be 100%, since these identify and provide the necessary streams for playback with this feature. * THEOplayer assumes that developers who are interested in this feature have a basic understanding of the Preplay API. Uplynk documents this service in the [API docs](https://api-docs.uplynk.com/#Develop/Preplayv2.htm). * THEOplayer assumes that developers provide correct asset identifiers, as well as the proper content protection level. ## Stream Configuration (without ads) The examples below demonstrate how to configure a stream through the Uplynk pre-integration. ```js player.source = { sources: [ { integration: 'uplynk', id: '', preplayParameters: {}, // Optional, defaults to an empty object. These will be added as query parameters to the Preplay API call. assetType: 'asset', // Optional, defaults to 'asset'. Can also be 'channel' or 'event', following the Uplynk semantics, where 'asset' is On-demand content. contentProtected: false, // Optional, defaults to false. }, ], }; // Source example for an external id (with optional properties omitted) player.source = { sources: [ { integration: 'uplynk', id: { userId: '', externalId: '', }, }, ], }; // Source example for a regular asset id with e.g. 'delay' parameter and 'sig' token parameter (with optional properties omitted) player.source = { sources: [ { integration: 'uplynk', id: '', preplayParameters: { delay: '7200', rays: 'dcba', sig: '2ff94739b021912712adafeccd6fa291f11eef0648c3b18b30224b84e0590b4f', }, }, ], }; ``` The snippet above gives a quick overview of the structure of an Uplynk-specific source, also known as an [UplynkSource](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkSource). More information on certain properties: * `id`: The ID field identifies the asset. Instead of a single string, developers can also specify an array of asset IDs as strings, which will be stitched into a continuous stream. The same goes for the `externalId` property in case an external ID is used in the source. * `preplayParameters`: The `preplayParameters` object should have string-key-string-value combinations, which will be used as query parameters for the Preplay API call. Nested objects are not supported. * `contentProtected`: Boolean value which will internally set any necessary content-protection information. No content-protection details have to be specified by the customer. * **A Preplay request must include all parameters defined within the playback request, hence these parameters must be included in the THEOplayer source**. This request must also include a digital signature if the 'Require a token for playback' option is enabled in the back-end on the corresponding live channel. (See also : [Signing a Playback URL Tutorial](https://api-docs.uplynk.com/#Tutorials/Signed-Playback-URL-Tutorial.htm)) * Uplynk specific documentation on the available playback URL query parameters can be found on the Uplynk documentation site under [Customizing Playback via Parameters](https://api-docs.uplynk.com/#Setup/Customizing-Playback.htm). * Some parameters can be excluded from the digital signature calculation. These parameters may enable certain use cases (e.g. DVR) and would for this reason be excluded from the digital signature. ## Stream Configuration (with ads) The examples below demonstrate how to configure a stream with server-side ads through an [UplynkSource](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkSource). ```js player.source = { sources: [{ integration: 'uplynk', id: [ '', '', '', '', '' ], preplayParameters: { // Parameters here should specify the necessary ad parameters for the Preplay API ad.param1: 'param_val1', ad.param2: 'param_val2' }, }] } ``` More information on certain properties: * `preplayParameters`: The `preplayParameters` object should have string-key-string-value combinations, which will be used as query parameters for the Preplay API call. Nested objects are not supported. * **A Preplay request must include all parameters defined within the [playback request](https://api-docs.uplynk.com/#Setup/Playback-URLs.htm#LiveChannelPURLs), hence these parameters must be included in the THEOplayer source**. This request must also include a digital signature if the 'Require a token for playback' option is enabled in the back-end on the corresponding live channel. (See also : [Signing a Playback URL Tutorial](https://api-docs.uplynk.com/#Tutorials/Signed-Playback-URL-Tutorial.htm)) > Note the confusion that the provided link only mentions signing the **playback URL**, even though we are describing using tokens for the call to the Preplay API. However, this process is also valid for signing the `preplayParameters`. * Uplynk specific documentation on the available playback URL query parameters can be found on the Uplynk documentation site under [Customizing Playback via Parameters](https://api-docs.uplynk.com/#Setup/Customizing-Playback.htm). * Some parameters can be excluded from the digital signature calculation. These parameters may enable certain use cases (e.g. DVR) and would for this reason be excluded from the digital signature. ## Preplay Response THEOplayer automatically interprets the response returned by the Preplay service. However, if a developer wants to perform their own logic with the Preplay API response, then they can intercept the `preplayresponse` event. ```js const listener = (event) => { console.log('Do something with the raw response of the Preplay API call', event.response); }; player.uplynk.addEventListener('preplayresponse', listener); ``` ## Using Tokens Uplynk users often leverage URL signatures (i.e. tokens) to further secure their content -- on top of AES-128 encryption or studio DRM. A token is (typically) generated on back-end using an API key and a set of parameters, as described on [Signing a Playback URL Tutorial](https://api-docs.uplynk.com/#Tutorials/Signed-Playback-URL-Tutorial.htm). > Note the confusion that the provided link only mentions signing the **playback URL**, even though we are describing using tokens for the call to the Preplay API. However, this process is also valid for signing the `preplayParameters`. This set of parameters should also be passed along to the `preplayParameters` in an [UplynkSource](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkSource). If you do not configure the `preplayParameters` correctly in relation to your signature parameters, then the Preplay request made by THEOplayer will most likely return an invalid Preplay response, preventing THEOplayer from setting up your stream. Let's consider a set-up where an Uplynk customer uses both URL signatures and multi-DRM. [When you do DRM, you must specify the `rmt` and `manifest` parameter](https://api-docs.uplynk.com/#Develop/Preplayv2.htm?Highlight=rmt), **or** you can set `allowrmt` to `1` instead. We recommend using the latter, as demonstrated in our Node.js reference project at [GitHub](https://github.com/THEOplayer/theoplayer-verizon-media-node-js). So, let's say you want to generate a signature on your back-end that is compatible with HLS + FairPlay, MPEG-DASH + Widevine and MPEG-DASH + PlayReady, then it could look like the snippet below using `allowrmt`. ```js let params = { v: '2', tc: '1', exp: 36000, rn: 12345, ct: 'a', cid: '', allowrmt: 1, }; let query = Object.keys(params) .map((key) => key + '=' + params[key]) .join('&'); let sig = CryptoJS.HmacSHA256(query, '').toString(); params['sig'] = sig; ``` Now you want to communicate this entire `params` object to your client-side, and set it as the value for `preplayParameters`: ```js player.source = { sources: { integration: 'uplynk', id: '', preplayParameters: params, assetType: 'asset', contentProtected: true, }, }; ``` When there's a mismatch between signature parameters and Preplay parameters, the Preplay response usually returns `Invalid token signature` and playback is not possible. Having difficulties figuring out tokens, DRM and THEOplayer? Check out the Node.js reference project at [GitHub](https://github.com/THEOplayer/theoplayer-verizon-media-node-js). # Related articles * [Uplynk - Ads](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ads.md) * [Uplynk - Ping](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ping.md) --- # Uplynk Ads Uplynk offers support for [server-side ad-insertion](https://docs.uplynk.com/docs/ads) (SSAI). This article explains how to use the Uplynk Ads API exposed by THEOplayer to hook into these SSAI advertisements. Additionally, this API allows developers to manipulate the ad-related UX and UI. ## Global Configuration The examples below demonstrate how to pass along global configuration for the Uplynk ads. These settings can be overwritten. ##### Web SDK ```js var player = new THEOplayer.Player(element, { uplynk: { defaultSkipOffset: 2, // Ad breaks added by SSAI are by default skippable after two seconds. Optional and defaults to unskippable. onSeekOverAd: 'play-last', // When seeking over ad breaks, this configuration decides how to snapback and play any missed ad breaks. // Possible values are currently 'play-none', 'play-last' and 'play-all' where 'play-none' is the default. }, // ... Other configuration goes here }); ``` ## Uplynk Ads API > **Deprecation** > > You can use the regular `player.ads` API for subscribing to Uplynk ad breaks, as described on [How to subscribe to ad events](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ads/subscribe-to-ad-events.md). The implementation below can be expected to be removed in the future. Specifying a source which contains server-side ads configured through the Uplynk pre-integration will have the player expose an Ads API specific to Uplynk, i.e. on the `player.uplynk.ads` API: * `player.uplynk.ads.adBreaks`: Specifies all ad breaks that are currently in the stream by using SSAI through Preplay. * `player.uplynk.ads.currentAdBreak`: If applicable, returns the ad break that is currently being played. * `player.uplynk.ads.currentAds`: If applicable, returns a list of the current ads being played. * `player.uplynk.ads.skip()`: If there is an ad break playing, and it is skippable, skip the ad break to the next content (or next seek point in case of snapback). Calling the `player.uplynk.ads.skip()` function on a live stream will not have any effect. In addition, event listeners are available to listen to new ad breaks: * For adbreaks, the following events are defined: * *adbreakbegin* * *adbreakend* * *adbreakskip* * *addadbreak* * *removeadbreak* * For ads, the following events are defined: * *adbegin* * *adchange* * *adend* * *adfirstquartile* * *admidpoint* * *adthirdquartile* * *adcomplete* ```js // Sample source snippet player.uplynk.ads.adBreaks.addEventListener('addadbreak', (event) => { // do something with event.adBreak as it is an ad break added through SSAI, an example: const adBreak = event.adBreak; if (adBreak.duration <= 15) { adBreak.skipOffset = -1; // Do not allow skipping of short ad breaks } else if (adBreak.duration > 60) { adBreak.skipOffset = 10; // Allow skipping of long ad breaks after ten seconds } else { adBreak.skipOffset = 0; // For all other ad breaks, allow immediate skipping } // subscribe to additional events event.adBreak.addEventListener('adbreakbegin', console.log); event.adBreak.addEventListener('adbreakend', console.log); for (var i = 0; i < event.adBreak.ads.length; i++) { event.adBreak.ads[i].addEventListener('adbegin', console.log); event.adBreak.ads[i].addEventListener('adend', console.log); } }); ``` ## Related articles * [Uplynk - Preplay](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/preplay.md) * [Uplynk - Ping](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ping.md) --- # Ping This article explains how to use Uplynk's Ping API. The Ping API by Uplynk allows users of their CMS to receive updates about SSAI on running live-streams or provide better playback position beaconing for Video on demand. The Ping API is used together with the Preplay API because the latter provides a session to be used with the Ping API. This pre-integration allows the integrator to specify a source for Preplay as integrated in other tickets (RDMP-287), and enable or disable certain features of the Ping API. The player will then perform these Ping calls internally, without the integrator having to write their own Ping client. The response of Ping calls will be exposed for external handling if necessary. The updates about SSAI received through the Ping API will be reflected in the player API, where information about SSAI through the Verizon Media Platform is already present. **Version 3 of the Ping API will be implemented.** **Playhead Position** When Ping mentions time (and time mentioned below) relative compared to 0, the 0-point will be the start of playback. This might be a mismatch with the normal `player.currentTime`. Instead, this will be comparable with the time passed since `player.played.start(0)`. **Feature Assumptions** We assume the SSAI information returned through the Ping API to be of a certain format, which we compile from examples and our own testing. The documentation does not define the structure of this payload very well and only states that it is "like the object returned in Preplay requests", though the formats are not a one-on-one match. This feature currently excludes client-side ad tracking and VPAID support. **Assumptions** * THEOplayer assumes the availability of the Ping API and Uplynk content servers to be 100%, since these identify and provide the necessary streams for playback with this feature. * THEOplayer assumes application developers have a notion of the Ping API, namely any extra parameters to be appended to the requests to Uplynk (e.g. for correct ad insertion). Uplynk documents this service in the [API docs](https://api-docs.uplynk.com/#Develop/Pingv3.htm). * THEOplayer assumes application developers provide proper ID's of the Assets they want to play back, as well as the proper content protection level. ## Configuring Ping The player allows specification of the desired features of the Ping API as listed [in the official Ping API documentation](https://api-docs.uplynk.com/#Develop/Pingv3.htm#Features). By default, the ping API is disabled for all sessions. To enable it, the `ad.cping=1` parameter must be added to your preplay request. If you attempt to call the API without passing in the `ad.cping` parameter, you can throw off the server's ability to make ad event calls correctly. In addition to enabling the API, you must also notify the server of the features you want to support for this viewing session. To specify which features you'd like to enable, you add the `ad.pingf={some value}` parameter to the playback token. The value of the parameter is detailed in the official Ping Documentation. Sample playback URL with `cping`: `https://content.uplynk.com/preplay/cc829785506f46dda4c605abdf65392b.json?ad=adserver&ad.cping=1&ad.pingf=3` In the THEOplayer API, these features can simply be activated through the `ping` property in the source configuration: ```js player.source = { sources: [{ integration: 'uplynk', ..., ping: { linearAdData: true, adImpressions: false, freeWheelVideoViews: false }, ... }] } ``` ## Ping requests When the Ping API is enabled, THEOplayer will call the Ping URL located at: ```text {prefix}/session/ping/{sessionID}.json?v=3&pt={currentTime}&ev={event}&ft={freeWheelTime} ``` Where: * `{prefix}`: the prefix URL from the Preplay response. * `{sessionID}`: the session ID from the Preplay response. * `{currentTime}`: mandatory parameter. This is the current player time in seconds. * `{event}`: the current Ping event. An event should only be passed when playback starts or when a viewer seeks to a new position. Valid values are: * **start**: Pass this event, along `withpt=0`, when the player starts playback. This lets the server know where playback starts and allows the server to fire start events as needed. * **seek**: Pass this event when a viewer seeks. This resets the timeline to prevent inadvertently firing events for skipped ads. * `{freeWheelTime}`: indicates the playback position, in seconds, right before a viewer seeks to a different position in the timeline. This property is mandatory when the freeWheelVideoViews is used. ## Ping Response When performed correctly, a Ping request will return a JSON response. THEOplayer will interpret this response according to the following principles: * `next_time`: a new beacon will be scheduled when the player's currentTime passes this value. In case the value is -1, no further beacons will be scheduled. * `currentBreakEnd`: will be used to update the end time of an ongoing ad-break in case the breakEnd was previously unknown. (Note Verizon Media by default requests 240s of ads in case the break length is unknown and will return all ads in the Ping response.) * `ads`: will be interpreted in order to display markers in the timeline as well as expose ad information through the player.verizonMedia.ads property. * `ads.breaks.timeOffset` will be used in order to determine the start time of the ad break (in seconds). * `ads.breaks.ads` will be looped in order to extract the ad information to be exposed in the Uplynk Ad on the THEO API: * `ad.duration` will serve as duration in the ad (in seconds) * `ad.mimeType` will serve as mimeType in the ad * `ad.apiFramework` will serve as apiFramework in the ad * `ad.companions` will serve as companions in the ad * `ad.creative` will serve as creative in the ad * `ad.width` will serve as width in the ad * `ad.height` will serve as height in the ad * `ad.events` will serve as events in the ad * `ad.fw_parameters` will serve as freeWheelParameters in the ad * `ads.breaks.breakEnd` will be used in order to determine the end of the ad break. Note that this property is optional, and the duration of an ad break can be unknown and updated at a later point. * `ads.breaks.duration` will be ignored by the player. For all ads and breaks added, `adbreakbegin`, `adbreakend`, `adbegin` and `adend` events will be dispatched. Additionally, when `breakEnd` gets updated, an `updateadbreak` event will be dispatched. * extensions: Will be ignored by THEOplayer. * error: Errors will be reported back through the THEOplayer API. The raw ping response can be retrieved by adding an event listener for the `pingresponse` event on the `player.uplynk` property. | Event | Description | Event object | | ------------ | --------------------------------------- | ----------------------------------------------------------------------- | | pingresponse | Fired when a Ping response is received. | event.response will contain the raw JSON response from the Ping server. | ```js player.uplynk.addEventListener('pingresponse', function (event) { console.log(event.response); // Will print the raw Ping response. }); ``` ## Related articles * [Uplynk - Preplay](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/preplay.md) * [Uplynk - Ads](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ads.md) --- # Uplynk configuration This article explains what is possible with an Uplynk configuration when setting up a THEOplayer instance. ## Features Developers can set an Uplynk configuration when creating their video player instance. Through this configuration, developers can set some UX and UI preferences related to the Uplynk integration. * `defaultSkipOffset`: specifies when an ad break can be skipped. The default value is `-1`, which means that the ad break cannot be skipped. * `onSeekOverAd`: specifies what should happen when viewers try to seek over an ad. By default, no ad are played when seeking over ads. As an alternative, you can 1. only play the last ad break (before the requested playhead position), or 2. play all ad breaks (before the requested playhead position). * `ui` * `adBreakMarkers`: specifies that a (red) ad break marker should be rendered in the scrub bar. (Enabled by default.) * `assetMarkers`: specifies that a (black) asset marker should be rendered in the scrub bar. (Enabled by default.) * `adNotification`: specifies that a count-down timer must be rendered when an ad is playing. (Enabled by default.) * `contentNotification`: specifies that a count-down timer to the next asset must be rendered when the viewer is close to the transition of another asset of a multi-asset stream. (Enabled by default.) You can toggle this features on [our demo page](https://cdn.theoplayer.com/demos/verizon-media/index.html). *(Note: technically, this demo doesn't use `defaultSkipOffset` to configure the skip offset. Instead, this demo uses `player.uplynk.ads.adBreaks[].skipOffset` to adjust this dynamically at run-time.)* ![UplynkDemo](/pr-873/assets/images/VerizonMediaConfiguration-e6131ae47761b09919322b8c26ee3d15.png "Uplynk Configuration") The examples below demonstrate how to use an Uplynk configuration per platform. ```js const player = new THEOplayer.Player(element, { libraryLocation: '...', uplynk: { // optional onSeekOverAd: 'play-all', // default to 'play-none'. Other values: 'play-all' and 'play-last' ui: { contentNotification: true, // optional; defaults to true adNotification: true, // optional; defaults to true assetMarkers: true, // optional; defaults to true adBreakMarkers: true, // optional; defaults to true }, defaultSkipOffset: 5 // ad break can be skipped after 5 seconds. (defaults to -1) }, ... }); ``` Refer to the [UplynkConfiguration API reference](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/UplynkConfiguration) for more information. # Related articles * [Uplynk - Ads](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ads.md): this article zooms in on `defaultSkipOffset` and \`\`onSeekOverAd\`. * [Uplynk - Introduction](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/introduction.md): this article links to sample Git projects which implement an `UplynkConfiguration`. --- # Uplynk interface This article explains how developers can hook into the Uplynk interface. This interface allows developers to query the available assets and ads in an Uplynk stream, its properties, and subscribe to its events. ## Using the Uplynk interface The `Uplynk` interface is exposed through your THEOplayer instance (e.g.: `player.uplynk` for the Web SDK). Through this interface, * You can retrieve information (e.g. start and stop time) on the available assets (because THEOplayer extracts this information from its integration with the Asset Info and Preplay service offered by Uplynk). * You can also subscribe `addasset` and `removeasset` events, which can be useful when you need to be informed when the assets become available. * You can intercept Preplay, Asset Info and Ping responses (because THEOplayer integrates with these services offered by Uplynk). * You can retrieve information on the available advertisements (because THEOplayer extracts this information from its integration with the Preplay and Ping service offered by Uplynk). * You can also subscribe to `addadbreak`, `addad`, `removeadbreak`, ... events, which can be useful if you want to know when which state is applicable (e.g. to overlay a custom advertisement countdown-timer). * You can also adjust the ad skip offset of an ad break. If you're looking for more information on how to query the available advertisements and how to subscribe to ad events, then refer to [Uplynk Ads](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ads.md). The examples below provide a basic demonstration of how to use the `Uplynk` interface. The GitHub projects linked at [Uplynk - Introduction](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/introduction.md) provide more code samples. ```js const player = new THEOplayer.Player(element, {...}); // uplynk events [ 'preplayresponse', 'pingresponse', 'assetinforesponse' ].forEach(function(e) { player.uplynk.addEventListener(e, console.log); }); // uplynk.assets events [ 'addasset', 'removeasset' ].forEach(function(e) { player.uplynk.assets.addEventListener(e, console.log); }); // uplynk.ads events [ 'addadbreak', 'removeadbreak' ].forEach(function(e) { player.uplynk.ads.adBreaks.addEventListener(e, function (e1) { if (e1.type === "addadbreak") { // uplynk.ads.adBreak[i] events [ 'adbreakbegin', 'adbreakend', 'adbreakskip', 'updateadbreak' ].forEach(function(e) { e1.adBreak.addEventListener(e, console.log); // uplynk.as.adBreak.ads[i] events for (let i = 0; i < e1.adBreak.ads.length; i++) { [ 'adbegin', 'adend', 'adfirstquartile', 'admidpoint', 'adthirdquartile', 'adcomplete' ].forEach(function(e) { e1.adBreak.ads[i].addEventListener(e, console.log); }); } }); } }); }); ``` Refer to the [Uplynk API reference](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Uplynk) for more information. Additionally, reviewing [the player configuration in the demo app](https://github.com/THEOplayer/samples-html5-sdk/blob/master/reference-apps/verizon-media-app/src/player.js) helps to understand the `Uplynk` interface. ## Related articles * [Uplynk - Introduction](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/introduction.md): this article links to sample Git projects which query (and subscribe to) the various properties and events offered by the `Uplynk` interface. * [Uplynk - Preplay](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/preplay.md): this article explains how you can use the Uplynk interface to intercept Preplay responses. * [Uplynk - Ads](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ads.md): this article offers more information on the `ads` parts of the Uplynk interface. * [Uplynk - Ping](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/uplynk/ping.md): this article explains how you can use the Uplynk interface to intercept Ping responses. --- # Getting started with CMCD on Web Media player clients can transmit useful information to Content Delivery Networks (CDNs) with each object request. This implementation is planned to fully support Common Media Client Data (CMCD) as defined in [CTA-5004-B](https://cta-wave.github.io/Resources/common-media-client-data--cta-5004-b.html), published in April 2026. CMCD supports two modes of transmission: * **Request mode**: CMCD data is sent as HTTP headers or query parameters on manifest and media segment requests. * **Event mode** (available since v11.4.0): CMCD events are POSTed to configured HTTP endpoints. ## Request Mode For request mode, use the [CMCD connector for Web](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/connectors/web/cmcd/getting-started.md). ## Event Mode Event mode allows posting CMCD events to configured HTTP endpoints. > **Info** > > Event mode is supported starting from version 11.4.0. ### Player-level configuration ```javascript const player = new THEOplayer.Player(element, { cmcd: { externalSessionId: 'YOUR-EXTERNAL-SESSION-ID', // optional userId: 'YOUR-USER-ID', // optional eventEndpoints: [{ url: 'https://example.com/cmcd-event-endpoint' }], }, }); ``` ### Source-level configuration ```javascript player.source = { sources: [{ src: 'https://example.com/stream.m3u8' }], cmcd: { externalSessionId: 'YOUR-EXTERNAL-SESSION-ID', // optional sessionId: 'YOUR-SESSION-ID', // optional userId: 'YOUR-USER-ID', // optional eventEndpoints: [{ url: 'https://example.com/cmcd-event-other-endpoint' }], }, }; ``` ### Merging behavior * Source-level values take precedence for `externalSessionId` and `userId`. * `eventEndpoints` from both levels are **merged** (both player and source endpoints receive events). > **Warning** > > Event mode reporting currently only supports DRM and ad events. --- # MPEG-DASH Content Steering This guide shows you how to use MPEG-DASH Content Steering in the THEOplayer Web SDK. MPEG-DASH Content Steering is a server-driven mechanism that allows content providers to dynamically control CDN selection across large groups of clients. While players can independently respond to local network conditions and switch between CDNs, Content Steering enables centralized decision-making at scale. By communicating with a steering server, the player receives a prioritized list of CDN endpoints that reflects real-time information about CDN performance, capacity, and cost across your entire user base. This allows you to proactively route traffic away from overloaded or underperforming CDNs, balance load across multiple providers, or implement cost optimization strategies—all without requiring player-side logic or client updates. ## Enabling/Disabling Content Steering By default, Content Steering in MPEG-DASH is enabled, however, it can be explicitly enabled or disabled using a flag in your `SourceDescription`. You can also override the `clientRequirement="false"` property in the MPEG-DASH manifest to enable Content Steering for a specific playback session by explicitly setting it to `true`. ```javascript player.source = { sources: { src: 'https://your-dash-source-with-content-steering.mpd', dash: { contentSteering: true, // Default is true; set to false to disable. }, }, }; ``` ## Events for Content Steering THEOplayer provides events to monitor the Content Steering process. For more details, check the [API documentation](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web#ContentSteeringStartEvent). ```javascript player.addEventListener('contentsteeringstart', console.log); player.addEventListener('contentsteeringstop', console.log); player.addEventListener('contentsteeringlocationchange', console.log); player.addEventListener('contentsteeringupdate', console.log); player.addEventListener('contentsteeringerror', console.log); ``` ## Known Limitations * `PATHWAY-CLONE` from the Content Steering server response is not supported yet. * Extended HTTP GET request parametrization instructions are not supported yet. You can add custom parameters to Content Steering requests by using a `RequestInterceptor` to intercept and modify network requests with the type `content-steering`. * Targeting a specific set of clients as part of a ServiceDescription element is not supported. ## More information * [DASH-IF: Content Steering for DASH](https://www.etsi.org/deliver/etsi_ts/103900_103999/103998/01.01.01_60/ts_103998v010101p.pdf) --- # Getting started ## Usage 1. Follow [our Getting Started guide](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/getting-started/sdks/web/getting-started.md) to set up THEOplayer in your web app or website. 2. Add an OptiView Live source to your player's source. ### Add an OptiView Live source After setting up your THEOplayer on your web page, set its source to a `SourceDescription` containing a `THEOliveSource`. You'll need an OptiView Live channel ID: ```javascript const player = new THEOplayer.Player(element, configuration); player.source = { sources: { type: 'theolive', src: 'your-channel-id', }, }; ``` ### Add configuration Optionally, you can provide additional configuration to the player, specific for OptiView Live streams. To configure these settings, add a `theolive` property to the player configuration. For an exhaustive list of these options, please visit the [documentation](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TheoLiveConfiguration). ```javascript var player = new THEOplayer.Player(element, { license: 'your-license', // This is needed for fallback to work properly retryConfiguration: { maxRetries: 6, }, theoLive: { externalSessionId: 'my-external-session-id', fallbackEnabled: true, }, }); ``` ## More information * [API references](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TheoLiveSource) --- # Basic Playback Guide After following our [Getting Started on Web guide](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/theolive/getting-started.md) or the [Player Setup](#player-setup) section below, you will be ready to programmatically perform common playback actions. These include increasing/decreasing the volume, playing/pausing the stream, enabling full screen, and selecting quality layers. ## Player Setup To get started with OptiView Live playback using THEOplayer, you must first initialize your player with reference to your OptiView Live channel as the source. Here is an example of initializing a THEOplayer instance for OptiView Live: ```javascript const player = new THEOplayer.Player(element, configuration); player.source = { sources: { type: 'theolive', src: 'your-channel-id', }, }; ``` Once done, your player variable will reference the returned THEOplayer instance and have access to all of its [properties and methods](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/Player). ## Adjust Volume To adjust the volume of your stream during playback, you can set the [`volume`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/Player#volume) property to a floating point number in the range of `0.0` (0%) minimum and `1.0` (100%) maximum. The following code sets the player's volume to 60%: ```javascript // the stream's volume will now be 60% player.volume = 0.6; ``` ## Play/Pause Stream To play or pause your stream, you can use the [play](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/Player#play) and [pause](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/Player#pause) methods available on your player variable. To pause your stream: ```javascript player.pause(); ``` To play your stream: ```javascript player.play(); ``` ## Enable Full Screen To make your stream take up the full screen of the device it is being viewed upon, you must use the [`requestMode`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Presentation#requestMode) method available on the [`presentation`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/Player#presentation) property of your player variable. Before doing so, check that the player supports other presentation modes (besides the default `'inline'`) for the device which the stream is viewed on by using the [`supportsMode`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Presentation#supportsMode) method on the presentation property. Both the `requestMode` and `supportsMode` methods require a [`PresentationMode`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/types/PresentationMode) parameter that can accept the following arguments: * `inline`: The player is shown in its original location on the page. (default presentation mode) * `fullscreen`: The player fills the entire screen. * `picture-in-picture`: The player is shown on top of the page (see PiPConfiguration for more options). * `native-picture-in-picture`: (Experimental) The player requests out-of-app picture-in-picture mode. Not supported on Firefox. Check if fullscreen is supported with the following code: ```javascript // returns a boolean value of true or false player.presentation.supportsMode('fullscreen'); ``` If the `supportsMode` method returns true for the presentation mode you passed into it, then you can move on to the steps below. To make the current stream take up the full screen, pass a presentation mode of `'fullscreen'` to the `requestMode` method with the following code: ```javascript // stream will take up the full screen which it is being viewed on player.presentation.requestMode('fullscreen'); ``` To confirm that the change has occurred, check the [`currentMode`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Presentation#currentMode) property to ensure it now returns `'fullscreen'` as its value: ```javascript // should return a value of 'fullscreen' to the console console.log('this is the current presentation mode being used -->', player.presentation.currentMode); ``` ## Select Quality Layer To select a specific quality layer during the playing of your stream, you must set the [`targetQuality`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/MediaTrack#targetQuality) property which is accessible on the [`videoTracks`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/Player#videoTracks) property of your player variable. The `videoTracks` property leverages the [MediaTrack API](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/MediaTrack). To see what qualities you have available, use the following code: ```javascript // put all available quality layers in an array and log them to the console const qualities = player.videoTracks[0].qualities; console.log('this is an array containing all available qualities --> ', qualities); ``` Once you know what options you have available, you can set the stream to be a specific quality by doing the following: ```javascript // set a specific quality layer by using its index in the qualities array player.videoTracks[0].targetQuality = qualities[indexOfDesiredQualityLayer]; ``` ## Tracking Streaming Protocols In OptiView Live, an [`Endpoint`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/Endpoint) can contain sources for multiple streaming protocols. For instance, it can be configured to load a WebRTC source for real time streaming but load a HLS source to allow seeking into a DVR window. Another use case is one where HESP is used as the primary streaming protocol, but the player could fall back to a HLS source if the platform doesn't support HESP playback. To track switches between the possible streaming protocols, you can listen for the `currentsourcechange` event. The `currentSource` property in the payload shows which source is being used under the hood. ```javascript player.addEventListener('currentsourcechange', console.log); ``` ## More information * [API references](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/Player) --- # Airplay OptiView Live streams have support for Airplay. However, a limitation for now is that the latency is higher while casting (about 12 seconds). Configuring it is exactly the same as for other streams you would play with THEOplayer. The instructions are described in the [Airplay guide](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/cast/airplay/introduction.md). --- # Chromecast OptiView Live streams have support for Chromecast. However, a limitation for now is that the latency is higher while casting (about 12 seconds). Configuring it is exactly the same as for other streams you would play with THEOplayer. The instructions are described in the [Chromecast guide](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/cast/chromecast/enable-chromecast-on-the-sender.md). --- # Metadata Metadata that travels through your OptiView Live stream — whether [pushed via the API or embedded as SEI](https://docs-preview.optiview.dolby.com/pr-873/theolive/channel/metadata-insertion.md) — surfaces on the player as cues on a text track. The general approach is the same in every case: 1. Listen for `addtrack` on `player.textTracks` so you find out when a new metadata track shows up. 2. Subscribe to a cue event on that track. 3. Inspect the cue to make sure it carries the metadata you care about. 4. Read the payload from `cue.content`. The properties you match on, the cue event you subscribe to, and the shape of `cue.content` all depend on which kind of metadata is being delivered. ## User-data-unregistered SEI and API push UDU SEI and API-pushed payloads surface as ID3 `PRIV` frames inside an ID3 v2.4 tag embedded in an `emsg` box, carried by the CMAF segments of the stream. * The owner identifier of the `PRIV` frame will be set to: `optiview.live:meta:` * The frame's private data will contain your metadata as raw binary data. (See [section 4.27 of the ID3 v2.4 Native Frames specification](https://id3.org/id3v2.4.0-frames) and [ID3 in CMAF](https://aomediacodec.github.io/id3-emsg/) for more details.) The OptiView Player Web SDK exposes these ID3 frames on a text track with [`TextTrack.type`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrack#type) equal to `'id3'`. Listen for the cue event that matches when you want to react: * `addcue` fires as soon as a cue becomes available on the track, before playback reaches it — useful when you want to prefetch data or update application state ahead of time. * `entercue` fires when playback enters the cue — useful when you want to act in sync with the video, for example to render an overlay at the moment the cue was inserted. The ID3 frame will be contained in `cue.content` as a [ID3PrivateFrame](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/ID3PrivateFrame) with `ownerIdentifier` equal to `optiview.live:meta:`. > **Important** > > Make sure to always check the frame's `id` and `ownerIdentifier` before using it, to avoid processing unrelated ID3 frames. Suppose your UUID is `11111111-1111-1111-1111-111111111111`: ```javascript player.textTracks.addEventListener('addtrack', function trackListener(event) { const track = event.track; if (track.type === 'id3') { // ID3 track was added. Listen for incoming ID3 cues. track.addEventListener('addcue' /* or 'entercue' */, (e) => { const frame = e.cue.content; if (frame.id === 'PRIV' && frame.ownerIdentifier === 'optiview.live:meta:11111111-1111-1111-1111-111111111111') { console.log('metadata', frame.data); } }); track.removeEventListener('typechange', trackListener); } else if (track.type === '') { // Track type is not yet known. Check again when it becomes known. track.addEventListener('typechange', trackListener); } else { // Track type is known, but is not ID3. Stop listening. track.removeEventListener('typechange', trackListener); } }); ``` Here the `frame.data` carries the binary payload exactly as it was sent — your application is responsible for parsing it (for example UTF-8 JSON, protobuf, or your own format). Using third-party players When using a third-party player SDK, please refer to their respective documentation on how to retrieve ID3 from the stream. For example, with [Shaka Player](https://github.com/shaka-project/shaka-player), you can listen for the [`metadata` event](https://shaka-player-demo.appspot.com/docs/api/shaka.Player.html#.event:MetadataEvent): ```javascript player.addEventListener('metadata', (event) => { if (event.metadataType === 'org.id3') { const frame = event.payload; // see https://shaka-player-demo.appspot.com/docs/api/shaka.extern.html#.MetadataFrame if (frame.key === 'PRIV' && frame.description === 'optiview.live:meta:11111111-1111-1111-1111-111111111111') { console.log('metadata', frame.data); } } }); ``` With [hls.js](https://github.com/video-dev/hls.js/), you can listen for the `FRAG_PARSING_METADATA` event. However, hls.js doesn't come with a built-in ID3 parser, so you'll need to parse the ID3 tag manually: ```javascript hls.on(Hls.Events.FRAG_PARSING_METADATA, (event, data) => { for (const sample of data.samples) { if (sample.type === Hls.MetadataSchema.emsg) { const tag = sample.data; // TODO Parse ID3 tag and look for ID3 PRIV frame with correct owner identifier } } }); ``` ## Picture timing SEI Picture timing SEI cues surface on a text track with [`TextTrack.id`](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TextTrack#id) equal to `'timecode'`. Each cue is anchored to a frame, so `entercue` is usually the most useful event: it fires when playback reaches the cue, letting you react in sync with the video. ```javascript player.textTracks.addEventListener('addtrack', (event) => { const track = event.track; if (track.id === 'timecode') { track.mode = 'showing'; // Setting the mode to showing will enable the entercue events track.addEventListener('entercue', onTimeCode); } }); ``` The cue `content` is a structured `TimeCode` object: ```typescript export interface TimeCode { readonly hours: number; readonly minutes: number; readonly seconds: number; readonly frames: number; } ``` A handler can then turn that timecode into whatever action your app needs: ```javascript const onTimeCode = (event) => { const timeCode = event.cue.content; // Insert other code here }; ``` A full example could be as follows: you distribute sports matches and want to display an overlay on the player when the score changes. Every time a score change happens, you add a picture timing SEI message to the stream and store in your backend that this time corresponds with this score. (You could also just add the timing message to all frames if this is easier, but this requires more processing both server and player side.) This information can then be retrieved by the application running on the device of a viewer. You then add the event listener and listen for the `entercue` events. In the listener you check whether the `TimeCode` corresponds with a score change event you recorded earlier and if so, display an overlay over the player. --- # Token based security OptiView Live offers the option to enable JWT token security on a distribution (formerly referred to as an alias) level. This can be interesting if you only want valid users to access your stream. Read more about the feature and configuring it on your channels on the [token based security guide](https://docs-preview.optiview.dolby.com/pr-873/theolive/distribution/security/token-based-security.md). This page will demonstrate how to configure the Web SDK for playback of channels with token based security enabled. ## Setting up the Web SDK for OptiView Live Refer to the [getting started guide](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/theolive/getting-started.md) for the prerequisite steps in getting the Web SDK up and running for OptiView Live playback. ## Configuring the player to pass the token Use the `player.theoLive.authToken` API to have the player pass your JWT token along with all HTTP requests it makes while playing your OptiView Live distribution: ```javascript const token = getToken(); // Generate or request your token, for more information check the token based security guide linked above. player.theoLive.authToken = token; ``` This will ensure the player includes the correct `Authorization` HTTP request header on all subsequent requests. ## Dealing with token expiry and rotation If your tokens are short-lived, you want to make sure to update the token being passed to the player and requests before it expires, to allow playback to continue beyond expiry. This can be done by simply updating the `authToken` property. For example, one could check on an interval that makes sense for your token lifespan whether the token is about to expire and update when necessary, for example: ```javascript let token; // Helper function to check whether your token will expire within one minute from now function tokenWillExpireSoon() { const payload = JSON.parse(atob(token.split('.')[1])); const exp = payload.exp; // in seconds const now = Math.floor(Date.now() / 1000); // current time in seconds return exp - now <= 60; } function maybeUpdateToken() { if (!token || tokenWillExpireSoon(token)) { token = getToken(); // Generate or request your token, for more information check the token based security guide linked above. player.theoLive.authToken = token; } } maybeUpdateToken(); setInterval(maybeUpdateToken, 30000); // Check every 30 seconds ``` ## Clearing the token If the token isn't needed anymore, e.g. when switching to an unprotected distribution or a non-OptiView Live source altogether, the header can be simply removed as follows: ```javascript player.theoLive.authToken = undefined; ``` ## Known limitations ### Safari on iOS <17 Apple devices running an iOS version lower than 17.1 do not support [MSE](https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API), therefore the player must fall back to Safari's built-in HLS support. Since the player is no longer making the HTTP requests by itself, it cannot automatically append the necessary `Authorization` header. To continue using token based security on those devices, we recommend either switching to long-lived tokens, or using a service worker to intercept the HTTP requests and append the header in your code. #### Long-lived tokens For JWT tokens that are valid for the entire duration of a typical playback session, you can continue using the `authToken` API as normal. When the player uses Safari's native HLS support, it will automatically append the token as a *query parameter* to the URL, and the OptiView Live backend will use that instead of the `Authorization` header to authenticate the viewer. Note that the player **cannot change** the query parameter on the stream without interrupting playback. Hence, the token must not expire before the end of the playback session. > **Note** > > Long-lived tokens are supported as of version 11.3.0 of the OptiView Player Web SDK. #### Short-lived tokens using service worker For JWT tokens with a short validity duration, you will need to register a service worker that intercepts the HTTP requests made by Safari's native HLS playback and append the necessary `Authorization` header. This requires the player to make all HTTP requests with [CORS](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/knowledge-base/cors/introduction.md) enabled. Make sure to set [the `crossOrigin` property](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TypedSource#crossOrigin) on your OptiView Live source: ```javascript player.source = { sources: { type: 'theolive', src: 'your-channel-id', crossOrigin: 'anonymous', }, }; ``` A code snippet for the service worker code is shared below. > **Note** > > Service worker registration is only possible in a secure environment (`https://`) or on `localhost`. There can also only be one service worker active, so if your environment or application already has a service worker active, you will need to include the additional functionality in that service worker. iOS Safari service worker ```javascript self.addEventListener('install', () => { console.log('Service worker installed!'); self.skipWaiting(); }); self.addEventListener('activate', (event) => { console.log('Service Worker activated'); // Claim clients so the service worker is in effect immediately event.waitUntil(self.clients.claim()); }); // Intercept the fetch event and add in your JWT self.addEventListener('fetch', (event) => { try { const url = new URL(event.request.url); if (!url.origin.endsWith('theo.live')) { // Requests not made by the player for playback of the OptiView Live distribution should not be modified. return; } const token = getToken(); // Generate or request your token. For more information, check the token based security guide linked above. // Clone the request and add the JWT header const modifiedHeaders = new Headers(event.request.headers); modifiedHeaders.set('Authorization', `Bearer ${token}`); const modifiedRequest = new Request(event.request, { headers: modifiedHeaders, }); return event.respondWith(fetch(modifiedRequest)); } catch (error) { console.error('Error in fetch handler:', error); return event.respondWith(new Response('Service Worker Error', { status: 500 })); } }); ``` > **Note** > > In order for the service worker to be able to intercept requests made from the player library, its scope must include the player SDK files. That means the player and service worker must be hosted on the same path or the player must be hosted in a subfolder of the path where the service worker is hosted. To register this service worker in to your code, you can attach it this way. Register service worker ```javascript async function registerServiceWorker() { if (!('serviceWorker' in navigator)) { console.error('Service worker not supported!'); return; } const serviceWorkerScope = '/service/worker/path'; // Replace with your own path to the service worker location. const serviceWorkerPath = '${root}/service-worker.js'; // Replace with the filename of your service worker. // We recommend unregistering actively before (re-)registering as we've seen issues where hard reloads could cause issues if the service worker wasn't unregistered. const oldRegistration = await navigator.serviceWorker.getRegistration(serviceWorkerPath); await oldRegistration?.unregister(); try { const registration = await navigator.serviceWorker.register(serviceWorkerPath, { scope: serviceWorkerScope, }); if (registration.active) console.log('Service worker registered!'); } catch (err) { console.error('Could not register service worker!', err); } } // Initialise the service worker some time early in the process. if (!(window.MediaSource || window.ManagedMediaSource)) { registerServiceWorker(); } ``` The snippet above assumes the existence of a function `getToken()` that generates a JSON Web Token (JWT) and signs it using the default HMAC SHA-256 algorithm. [(Refer to docs on jwt.io)](https://jwt.io/introduction). In pseudocode: ```javascript const getToken = async () => { const YOUR_JWT_SIGNING_KEY = 'YOUR-SIGNING-KEY-GOES-HERE'; const payload = { exp: Math.floor(Date.now() / 1000) + 300, // 5 minutes }; return await sign(payload, YOUR_JWT_SIGNING_KEY); }; ``` You can use an existing library like [Jose](https://github.com/panva/jose), or you can use our example implementation: JWT `sign` function ```js const base64UrlEncode = (str) => { return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); }; const utf8ToUint8Array = (str) => { return new TextEncoder().encode(str); }; const sign = async (payload, secret) => { const header = { alg: 'HS256', typ: 'JWT' }; const encodedHeader = base64UrlEncode(JSON.stringify(header)); const encodedPayload = base64UrlEncode(JSON.stringify(payload)); const data = `${encodedHeader}.${encodedPayload}`; const secretBytes = utf8ToUint8Array(secret); const key = await crypto.subtle.importKey('raw', secretBytes, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign']); const signature = await crypto.subtle.sign({ name: 'HMAC' }, key, utf8ToUint8Array(data)); const signatureStr = String.fromCharCode(...new Uint8Array(signature)); return `${data}.${base64UrlEncode(signatureStr)}`; }; ``` > **Info** > > You can validate the token generated by inspecting it using the JWT decoder on [JWT.io](https://jwt.io/). The header and payload will be decoded immediately. If you provide your HMAC key, you can validate that the signature is also valid. > **Note** > > If you are using a bundler such as Vite or Rollup etc, you will need to ensure that your service worker also gets copied to your output directory and is `registered` from the correct path. ### FairPlay-protected DRM streams on Safari Currently, the OptiView Player Web SDK always uses Safari's built-in HLS support for FairPlay-protected DRM streams. As such, the same limitations apply as for [Safari versions without MSE](#safari-on-ios-17). ### Chromecast and AirPlay When the player casts an OptiView Live stream to a remote receiver (such as Chromecast or AirPlay), it is no longer in control of the HTTP requests for that stream. As such, it cannot append the necessary `Authorization` header by itself, similar to [when using Safari's native HLS playback](#safari-on-ios-17). We recommend using [long-lived tokens](#long-lived-tokens) to support remote playback. The player will automatically append the token as a query parameter to the URL when starting the remote session. --- # Migrating from deprecated THEOlive player to THEOplayer on Web The goal of this guide is to help you migrate from the deprecated [theolive player](https://www.npmjs.com/package/@theolive/player) to the recommended [THEOplayer](https://www.npmjs.com/package/theoplayer). If you haven't integrated with any player, please follow the [recommended](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/theolive/getting-started.md) guide instead of this one. ## Dependencies First remove the old player dependency: ```text npm uninstall @theolive/player ``` And replace with theoplayer using: ```text npm install theoplayer ``` ## Setting up a player Player creation is quite similar, instead of ```javascript const player = new THEOLive.Player(element); ``` Create the player like this: ```javascript var player = new THEOplayer.Player(element, { license: 'your-license', // This is needed for fallback to work properly retryConfiguration: { maxRetries: 6, }, theoLive: { // these are optional externalSessionId: 'my-external-session-id', fallbackEnabled: true, }, }); ``` Note that you now need a license, either use the THEOplayer license you had before, or use the one provided in the [OptiView Live dashboard](https://dashboard.optiview.dolby.com) when creating a channel. ## Loading a channel After setting up your THEOplayer on your web page, we will set the source to a channel. The old way was calling loadChannel on the player. ```javascript player.loadChannel('').catch(console.error); ``` With THEOplayer you just set a [SourceDescription](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TheoLiveSource). ```javascript player.source = { sources: { type: 'theolive', src: 'your-channel-id', }, }; ``` ## Calling methods and listening to events Most of the old functionality is right there on THEOplayer and can be found in the [THEOplayer docs](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/classes/ChromelessPlayer). For OptiView Live specific API, check out [player.theolive](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TheoLiveApi). ## More information * [API references](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/v11/api-reference/web/interfaces/TheoLiveSource) --- # UI on Web Choose the approach that fits your requirements: * Use Open Video UI when you want a custom experience without building common player components yourself. * Use the chromeless player when you want to start with no UI and build every control and interaction from scratch. * Use the basic default UI when its ready-made controls meet your needs and one is available. | Approach | Starting point | Flexibility | Development effort | | ------------- | ------------------------------ | ----------- | ------------------ | | Open Video UI | Ready-made, modular components | High | Moderate | | Chromeless | Player API without a UI | Complete | Highest | | Default UI | Complete basic player UI | Basic | Lowest | Building a chromeless UI means using the OptiView Player API and your own application code without a ready-made UI. You are responsible for adding every control, such as a play button, and connecting it to the player API, such as `player.play()`. This approach gives you total control over the UI and UX, but requires an understanding of video playback and the OptiView Player API. ## Choose your UI approach The Web SDK supports three approaches, depending on how much you want to customize: ## [🧩Open Video UI](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui.md) [Compose and style ready-made components for a custom, branded experience.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui.md) ## [🛠️Chromeless](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/chromeless.md) [Start with no UI and implement every control and interaction yourself.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/chromeless.md) ## [▶️Default UI](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/default-ui.md) [Start quickly with the basic, responsive controls included with the Web SDK.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/default-ui.md) --- # Open Video UI for Web > **OptiView Rebranding** > > OptiView Player is the new name for THEOplayer as part of the OptiView product suite. During the transition, you may still see references to THEOplayer. OptiView Player and THEOplayer refer to the same product. Open Video UI is the OptiView Player customization toolkit. It provides open-source, out-of-the-box components that you can compose and style to create a player UI that matches your product and brand. Open Video UI has dedicated libraries for each supported platform, so your customized player feels native in your app. These pages explain how to get started and tailor the components to your requirements. ## [🚀Getting started](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/getting-started.md) [Start building your UI in just a few minutes!](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/getting-started.md) ## [📖How-to guides](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/guides/custom-ui.md) [Learn how to integrate our rich set of features into your own UI.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/guides/custom-ui.md) ## [🛝Examples](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/examples/default-ui.md) [Find inspiration for your UI from our live demos!](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/examples/default-ui.md) ## [🗃React](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/react.md) [7 items](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/react.md) ## [📰Changelog](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/changelog.md) [Find out what's new in Open Video UI.](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/changelog.md) ## [\*️⃣API reference](https://theoplayer.github.io/web-ui/api/) [Discover all components provided by Open Video UI for Web.](https://theoplayer.github.io/web-ui/api/) ## [![](/pr-873/img/github.svg)![](/pr-873/img/github_dark.svg)GitHub](https://github.com/THEOplayer/web-ui/) [Browse the source code or report issues on GitHub.](https://github.com/THEOplayer/web-ui/) --- # Getting started ## Installation 1. This project requires the THEOplayer Web SDK to be installed. ```sh npm install theoplayer ``` You can also install a different variant of the THEOplayer npm package if you don't need all features, as long as it's aliased as `theoplayer`. ```sh npm install theoplayer@npm:@theoplayer/basic-hls ``` 2. Install the Open Video UI for Web. ```sh npm install @theoplayer/web-ui ``` 3. Add `@theoplayer/web-ui` to your app: * Option 1: in your HTML. ```html ``` * Option 2: in your JavaScript. ```js import { DefaultUI } from '@theoplayer/web-ui'; ``` Open Video UI will import THEOplayer from `theoplayer/chromeless` and [Lit](https://lit.dev/) from `lit`. If you're using a bundler such as Webpack or Rollup, these dependencies should automatically get bundled with your web app. Alternatively, you can use an [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) to let the browser resolve it: ```html ``` ## Usage ### Default UI `` provides a fully-featured video player experience with minimal setup, and allows for small customizations such as changing colors or fonts. * Option 1: in your HTML. ```html ``` * Option 2: in your JavaScript. ```js import { DefaultUI } from '@theoplayer/web-ui'; const ui = new DefaultUI({ libraryLocation: '/path/to/node_modules/theoplayer/', license: 'your_theoplayer_license_goes_here' }); // Set a source for the player to play ui.source = { sources: { src: 'https://example.com/stream.m3u8' } }; // Optionally, access the underlying THEOplayer player instance ui.player.addEventListener('playing', () => console.log('THEOplayer is now playing')); ``` See [this page](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/examples/default-ui.md) for a complete example. ### Custom UI If you want to fully customize your video player layout, you can use a `` instead. ```html ``` See [this page](https://docs-preview.optiview.dolby.com/pr-873/theoplayer/how-to-guides/web/ui/open-video-ui/examples/custom-ui.md) for a complete example. ### Legacy browser support By default, Open Video UI for Web targets modern browsers that support modern JavaScript syntax (such as [async/await](https://caniuse.com/async-functions)) and native [Custom Elements](https://caniuse.com/custom-elementsv1). This keeps the download size small, so your viewers can spend less time waiting for your page to load and start watching their video faster. On older browsers (such as Internet Explorer 11 and older smart TVs), you need to load a different version of the Open Video UI that uses older JavaScript syntax. You also need to load additional polyfills for missing features such as `Promise`, `Symbol.iterator` or Custom Elements: * For ES2015 features like `Promise` and `Symbol.iterator`, we recommend [the Cloudflare mirror of Polyfill.io](https://cdnjs.cloudflare.com/polyfill/). * For Custom Elements, we recommend loading our polyfill bundle from `@theoplayer/web-ui/polyfills`. Alternatively, you can load the [Web Components Polyfills](https://github.com/webcomponents/polyfills) along with [Lit's `polyfill-support` module](https://lit.dev/docs/v2/tools/requirements/#polyfills). * Option 1: in your HTML. This uses [differential serving](https://css-tricks.com/differential-serving/) so modern browsers will load the modern build (with `type="module"`), while legacy browsers will load the legacy build (with `nomodule`). ```html ``` * Option 2: in your JavaScript. This will load the legacy build on both modern and legacy browsers, which is suboptimal. Instead, we recommend configuring your bundler to produce a modern and legacy build of your entire web app, and to import the appropriate version of Open Video UI for each build flavor. ```js import '@theoplayer/web-ui/polyfills'; import { DefaultUI } from '@theoplayer/web-ui/es5'; // note the "/es5" suffix ``` --- # Making a custom UI Although the default UI was designed to support a variety of usage scenarios, you may still run into a case that it doesn't handle very well. Perhaps you want to move some buttons around, or add like and dislike buttons to the control bar, or perhaps integrate a text chat component inside your player. In these situations, you may want to build a custom player UI to create a truly unique experience for your viewers. In this guide, you will build a custom player from scratch, starting from a basic player and gradually adding more features. Finally, we will look at some more complete examples of custom players, which you can use as reference for your own UI. ## A basic player We'll start by making the most basic player possible: one with just a play button. ```html ``` The `` element is the main container of our custom UI. It handles creating and managing the underlying THEOplayer instance, using the `configuration` and `source` attributes set on the element. It also provides a skeleton layout for plugging in our playback controls, using [slots](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots#adding_flexibility_with_slots) such as `"top-chrome"` or `"centered-chrome"`. We place a `` in the `"centered-chrome"` slot, so it will appear in the center of our player. ![Screenshot](/pr-873/assets/images/custom-ui-basic-3ae76115ff4424c1b7a6f79007843701.png) Note that by default, the UI hides its controls when the user becomes inactive (i.e. does not interact with the player). If we want the play button to always remain visible, we can add a `no-auto-hide` attribute to it: ```html ``` We can add more components so the user can mute or seek the player, or go fullscreen. ```html ``` * `` is a circular loading spinner that is automatically shown when the player is waiting for more data. It is placed in the `"centered-loading"` slot, which appears in the center of the player but behind the `"centered-chrome"` elements. The `no-auto-hide` attribute ensures that it is visible even when all other controls are hidden. * `` is a horizontal container for playback controls. It's not required that all controls are inside a ``, you can use any element such as a `
` or a `
`. This element does not have a `slot` attribute, so it is placed in the "default slot", at the bottom of the player. * `` is a seek bar, showing the player's current position in the video, and allowing the user to seek to a different time. * `` is a button to mute or unmute the player's audio. * `` is a text showing the player's current position in hours, minutes and seconds. By adding the `show-duration` attribute, the text will also show the player's total video duration. * `` is a button to make the player take up the entire screen. * The `` with `flex-grow: 1` acts as a "spacer": it will stretch to fill the remaining space left by the other controls inside the ``. This works because `` is a flexbox container, i.e. it has `display: inline-flex`. The result is that the `` will be aligned to the right side of the player. ![Screenshot](/pr-873/assets/images/custom-ui-seekbar-6b279b099c28754aa634a6a181b8c949.png) ## A mobile-ready player All components are designed to work great on all device types. However, sometimes you may want to make certain buttons a little bigger on mobile, or move them around a bit to make them easier to reach with the touch of a finger instead of the click of a mouse cursor. `` adds a `mobile` attribute to itself when it detects that the user is on a mobile device. You can use this attribute in your own CSS to change the size of all controls: ```css /* Make controls bigger */ theoplayer-ui[mobile] { --theoplayer-control-height: 32px; } ``` You can also use it to control the visibility of certain controls. To avoid having a CSS rule for each individual component, you can use a custom class or an attribute on those components instead: ```css /* Hide mobile-hidden elements on mobile, and mobile-only elements on desktop */ theoplayer-ui[mobile] [mobile-hidden], theoplayer-ui:not([mobile]) [mobile-only] { display: none !important; } ``` ```html ``` > **Why do I need to add the styles for `[mobile-hidden]` and `[mobile-only]` to my own CSS?** > > Due to limitations of the `::slotted()` CSS selector, ``'s own CSS rules can only affect *directly* slotted elements. Descendant elements of a slotted element, such as a `` inside of a slotted `` cannot be targeted by such selectors. | ![Screenshot on desktop](/pr-873/assets/images/custom-ui-desktop-81909d06ea425103824bb1035e0b80df.png) | ![Screenshot on mobile](/pr-873/assets/images/custom-ui-mobile-c33fba60bea7b749b93527d3fd408ae0.png) | | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | | Desktop | Mobile | ## A player for livestreams Not every control makes sense for every stream. A livestream without a DVR window cannot be seeked, so a seek bar or a playback speed setting would be useless. Conversely, a button to seek back to the live point is only useful for a livestream. `` detects the stream type of its current source, and reflects it as a `stream-type` attribute with value `"vod"`, `"live"` (a livestream without DVR) or `"dvr"` (a livestream with a DVR window). Elements can declare in which stream types they want to be shown using the `stream-type-only` and `stream-type-hidden` attributes, which take a space-separated list of stream types: ```html ``` For the most common cases, there are shorthand boolean attributes: | Attribute | Equivalent to | Element is hidden for | | ------------- | ------------------------------- | ------------------------------- | | `live-only` | `stream-type-only="live dvr"` | VOD streams | | `live-hidden` | `stream-type-hidden="live dvr"` | all livestreams | | `dvr-only` | `stream-type-only="dvr"` | VOD and livestreams without DVR | | `dvr-hidden` | `stream-type-hidden="dvr"` | livestreams with DVR | An element is hidden as soon as one of its attributes says it should be hidden, so combining attributes narrows down the stream types in which the element is shown. For example, `live-only dvr-hidden` shows the element for livestreams without DVR only. (Note that `live-hidden dvr-only` therefore hides the element for *every* stream type. To show an element for VOD and DVR streams only, use `stream-type-hidden="live"` instead.) As with `mobile-hidden` and `mobile-only`, you need to copy these rules into your own CSS if you use them on elements nested inside a slotted element, such as a control bar: ```css /* Hide elements which are not meant for the current stream type */ theoplayer-ui[stream-type='live'] [stream-type-hidden~='live'], theoplayer-ui[stream-type='live'] [stream-type-only]:not([stream-type-only~='live']) { display: none !important; } ``` ## More examples ### Default UI It may sound odd to mention the default UI when we're talking about custom UIs. But in fact, the default UI is built in *exactly the same way* as a custom one! The default UI comes with all the bells and whistles that you'd expect from a fully-featured player: buttons to play or mute the video, a seek bar to seek through the video, menus to change the audio language and subtitles, and support for casting to Chromecast and AirPlay. It also has an adaptive layout that changes when used on desktop or on mobile, to provide the best experience for that device type. Have a look at [`DefaultUI.html`](https://github.com/THEOplayer/web-ui/blob/main/src/DefaultUI.html) and [`DefaultUI.css`](https://github.com/THEOplayer/web-ui/blob/main/src/DefaultUI.css) to learn how the default layout is implemented, and perhaps copy some ideas for your own UI. ### Nitflex theme The "Nitflex" theme shows an example of a fully customized player UI, inspired by a popular streaming platform. It has a custom layout for its playback controls, custom colors to match the brand's style, and even custom icons for its buttons. Have a look at its [HTML](https://github.com/THEOplayer/web-ui/blob/main/docs/static/open-video-ui/v2/examples/web/nitflex/demo.html) and [CSS](https://github.com/THEOplayer/web-ui/blob/main/docs/static/open-video-ui/v2/examples/web/nitflex/nitflex.css) to see how it's done. ![Screenshot](/pr-873/assets/images/nitflex-theme-939d70606ef41c085fca43b882ecf806.png) --- # Making a custom component This guide demonstrates how to make your own custom components that integrate with the player, and which you can use in your own custom UI. > **Note** > > We use [Lit](https://lit.dev/) to build all components in Open Video UI for Web, and we highly recommend it for your own custom components. > > You can also use a different library (like [Svelte](https://svelte.dev/docs/svelte/custom-elements)), or you can use the [Custom Element](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements) and [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) APIs directly. ## A custom play button First, we'll build a simple button that plays or pauses the player when clicked. > **Tip** > > The built-in [``](https://theoplayer.github.io/web-ui/api/classes/PlayButton.html) also provides this functionality. ### Step 1: Create a custom button Create a new JavaScript file called `my-play-button.js` with the following code: [my-play-button.js (step 1)](https://github.com/THEOplayer/web-ui/blob/main/docs/static/open-video-ui/v2/guides/web/custom-component/my-play-button-step1.js) ```js import { Button } from '@theoplayer/web-ui'; import { html } from 'lit'; // Define a new class for our custom element. // We extend the `Button` class from Open Video UI for Web, // so we can inherit some logic and styles. export class MyPlayButton extends Button { // This method is called whenever the button is (re-)rendered. // See: https://lit.dev/docs/components/rendering/ render() { return html`Play`; } // This method is called whenever the button gets clicked. handleClick() { alert('My play button was clicked!'); } } // Define our class as the constructor for our custom element. // This allows us to use anywhere in our HTML. // If you're using TypeScript, you can also use // Lit's @customElement decorator on the MyPlayButton class. // See: https://lit.dev/docs/components/defining/ customElements.define('my-play-button', MyPlayButton); ``` Now add your new button to your custom UI. In our example, we'll place it in the `centered-chrome` slot, so it'll appear in the center of the player: ```html ``` It should look something like this: Try clicking the "Play" button in the middle of the screen. You should see an alert window popping up saying `My play button was clicked!`. ### Step 2: Integrate with the backing player Of course, we want the player to start playing instead of showing an alert! For this, we need to get access to the backing THEOplayer instance. Open Video UI for Web provides a built-in mechanism to automatically inject dependencies into UI components, such as the player instance, or other fullscreen state of the UI. When a UI component is added as a child (or descendant) of a `` or ``, the parent UI will automatically inject those dependencies. First, the UI component needs to opt into this mechanism by [mixing in](https://www.typescriptlang.org/docs/handbook/mixins.html) `StateReceiverMixin` into its superclass (see [API documentation](https://theoplayer.github.io/web-ui/api/functions/StateReceiverMixin.html)). This mixin takes the original superclass, and an array of dependencies which need to be injected: ```js import { Button, StateReceiverMixin } from '@theoplayer/web-ui'; export class MyPlayButton extends StateReceiverMixin(Button, ['player']) { // ... } ``` > **For TypeScript users** > > If you're using TypeScript, you can also use the `@stateReceiver` decorator instead. > > ```typescript > import { Button, stateReceiver } from '@theoplayer/web-ui'; > > @stateReceiver(['player']) > export class MyPlayButton extends Button { > // ... > } > ``` Once this button is added to a ``, it'll automatically receive the backing THEOplayer instance in its `player` property. If you want to do some custom logic when this happens, you can implement a setter for this property: ```js export class MyPlayButton extends StateReceiverMixin(Button, ['player']) { set player(player) { this._player = player; console.log('My play button received a player!'); } } ``` Change your `handleClick()` method to call `play()` or `pause()` on the player: [my-play-button.js (step 2)](https://github.com/THEOplayer/web-ui/blob/main/docs/static/open-video-ui/v2/guides/web/custom-component/my-play-button-step2.js) ```js import { Button, StateReceiverMixin } from '@theoplayer/web-ui'; import { html } from 'lit'; export class MyPlayButton extends StateReceiverMixin(Button, ['player']) { constructor() { super(); this._player = undefined; } get player() { return this._player; } set player(player) { this._player = player; console.log('My play button received a player!'); } render() { return html`Play`; } handleClick() { if (!this._player) { // Not (yet) attached to a player. return; } // Toggle the player's playing state. if (this._player.paused) { this._player.play(); } else { this._player.pause(); } } } customElements.define('my-play-button', MyPlayButton); ``` It should look something like this: Try clicking the "Play" button in the middle of the screen. The player starts playing!
Clicking it again should pause the player. ### Step 3: Update the button text Notice that the button text always says "Play", even when the player is already playing. To fix this, we will need to add a [reactive property](https://lit.dev/docs/components/properties/) that we can use inside our `render()` method. [my-play-button.js (step 3)](https://github.com/THEOplayer/web-ui/blob/main/docs/static/open-video-ui/v2/guides/web/custom-component/my-play-button-step3.js) ```js import { Button, StateReceiverMixin } from '@theoplayer/web-ui'; import { html } from 'lit'; export class MyPlayButton extends StateReceiverMixin(Button, ['player']) { // Define our reactive properties. // See: https://lit.dev/docs/components/properties/ static properties = { _paused: { state: true } }; constructor() { super(); this._player = undefined; this._paused = true; } get player() { return this._player; } set player(player) { this._player = player; console.log('My play button received a player!'); } render() { // Show a different label depending on the paused state. return html`${this._paused ? 'Play' : 'Pause'}`; } handleClick() { if (!this._player) { // Not (yet) attached to a player. return; } // Toggle the player's playing state, // and update our paused state to trigger a re-render. if (this._player.paused) { this._player.play(); this._paused = false; } else { this._player.pause(); this._paused = true; } } } customElements.define('my-play-button', MyPlayButton); ``` > **For TypeScript users** > > If you're using TypeScript, you can also use the `@state` decorator instead. > > ```typescript > import { Button } from '@theoplayer/web-ui'; > import { state } from 'lit'; > > export class MyPlayButton extends Button { > @state() > private accessor _paused: boolean = true; > } > ``` It should look something like this: Congratulations, you've built your very own play button! 🎉 ## A custom quality label Next, let's create a label that displays the resolution of the player's current video quality. > **Tip** > > The built-in [``](https://theoplayer.github.io/web-ui/api/classes/ActiveQualityDisplay.html) also provides this functionality. ### Step 1: Create a custom component Create a new JavaScript file called `my-quality-label.js` with the following code: [my-quality-label.js (step 1)](https://github.com/THEOplayer/web-ui/blob/main/docs/static/open-video-ui/v2/guides/web/custom-component/my-quality-label-step1.js) ```js import { css, html, LitElement } from 'lit'; // This style will be applied to every . const myQualityLabelStyle = css` /* This rule targets the element itself, i.e. */ :host { /* Use the same text and background color as the rest of the UI controls */ color: var(--theoplayer-text-color, #fff); background: var(--theoplayer-control-background, transparent); /* Add some padding */ padding: var(--theoplayer-control-padding, 10px); } `; // Define a new class for our custom element. export class MyQualityLabel extends LitElement { static styles = [myQualityLabelStyle]; render() { // Show something (for testing) return html`1080p`; } } // Define our class as the constructor for our custom element. // This allows us to use anywhere in our HTML. customElements.define('my-quality-label', MyQualityLabel); ``` Now add your new label to your custom UI. In our example, we'll place it inside a `` in the default slot, so it'll appear at the bottom of the player: ```html ``` It should look something like this: ### Step 2: Listen to quality changes Right now, the quality label is static, it doesn't actually update when the player's quality changes. Let's fix that! 1. Once again, we use the `StateReceiverMixin` to gain access to the backing THEOplayer instance. 2. When we receive the player, we add an event listener for the `addtrack` event of `player.videoTracks`, so we'll be notified when the video track becomes available. 3. We register an `activequalitychanged` event listener on that video track, so we can respond when the active video quality changes. 4. When our `activequalitychanged` listener fires, we update the reactive property `this._activeVideoQuality`, which will trigger a re-render when changed. 5. When we re-render the component, we return an HTML template containing the active quality's height. [my-quality-label.js (step 2)](https://github.com/THEOplayer/web-ui/blob/main/docs/static/open-video-ui/v2/guides/web/custom-component/my-quality-label-step2.js) ```js import { StateReceiverMixin } from '@theoplayer/web-ui'; import { css, html, LitElement } from 'lit'; const myQualityLabelStyle = css` :host { color: var(--theoplayer-text-color, #fff); background: var(--theoplayer-control-background, transparent); padding: var(--theoplayer-control-padding, 10px); } `; export class MyQualityLabel extends StateReceiverMixin(LitElement, ['player']) { static styles = [myQualityLabelStyle]; static properties = { _activeVideoQuality: { state: true } }; constructor() { super(); this._player = undefined; this._activeVideoTrack = undefined; this._activeVideoQuality = undefined; } get player() { return this._player; } set player(player) { if (this._player) { // Clean up this._player.videoTracks.removeEventListener('addtrack', this.handleAddTrack); this.updateActiveTrack(undefined); } this._player = player; if (this._player) { // Listen for the 'addtrack' event this._player.videoTracks.addEventListener('addtrack', this.handleAddTrack); // If the player already has an active video track, // start using it right away! if (this._player.videoTracks.length > 0) { this.updateActiveTrack(this._player.videoTracks[0]); } } } handleAddTrack = (event) => { this.updateActiveTrack(event.track); }; updateActiveTrack(track) { if (this._activeVideoTrack) { // Clean up this._activeVideoTrack.removeEventListener('activequalitychanged', this.handleActiveQualityChanged); } this._activeVideoTrack = track; if (this._activeVideoTrack) { // Listen for the 'activequalitychanged' event this._activeVideoTrack.addEventListener('activequalitychanged', this.handleActiveQualityChanged); // If the track already has an active quality, // start using it right away! if (this._activeVideoTrack.activeQuality) { this.updateActiveQuality(this._activeVideoTrack.activeQuality); } } } handleActiveQualityChanged = (event) => { this.updateActiveQuality(event.quality); }; updateActiveQuality(quality) { // Update our (reactive) internal state, which will trigger a re-render. this._activeVideoQuality = quality; } render() { if (this._activeVideoQuality) { // Show the quality's height in our return html`${this._activeVideoQuality.height}p`; } else { // No active quality yet... return html``; } } } customElements.define('my-quality-label', MyQualityLabel); ``` It should look something like this: Try changing the active quality by clicking the ⚙️ (Settings) button, and changing "Automatic" to a specific quality. You should see your custom label update to show the height of the new quality. That's it! You now have your very own active quality label! 🥳 ## Further reading * [Intro to Lit](https://lit.dev/tutorials/intro-to-lit/) * [Lit documentation](https://lit.dev/docs/) * [Web components guide on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) --- # Localization The Open Video UI for Web can be localized to different languages, enabling you to reach audiences from different regions of the world. Localization works by [registering one or more locales](#register-a-locale) and then [selecting one of the registered locales using the `lang` attribute](#select-a-language). ## Register a locale A locale is a JavaScript object mapping translation IDs to translated messages. You can register a locale with the `addLocale` function: French locale ```javascript import { addLocale } from '@theoplayer/web-ui'; addLocale('fr', { playAria: 'lire', pauseAria: 'pauser', replayAria: 'revoir' // ... }); ``` Some messages may need to be formatted with one or more values, based on the configuration or active state of the player. For those messages, the translation ID maps to a JavaScript function that takes those values as arguments and returns the formatted translation. French locale (continued) ```javascript addLocale('fr', { // ... seekForwardAria: (offset) => `avancer de ${offset}`, seekBackwardAria: (offset) => `reculer de ${offset}` // ... }); ``` Refer to the [`Locale` interface definition](https://theoplayer.github.io/web-ui/api/interfaces/Locale.html) in the API references for the complete list of translatable messages. ## Select a language The UI automatically selects the locale based on the `lang` attribute of the `` (or ``) element, or from the closest parent element with such an attribute. The value of the `lang` attribute must exactly match the locale name as it was passed to `addLocale`. Setting the language on the UI ```html ``` You can also put the `lang` attribute on any parent element. For example, if the entire page is in French, you could put the attribute on the `` element: Setting the language on the HTML document ```html Ma page ``` ## Remarks ### Update translations when upgrading Open Video UI Newer versions of the Open Video UI for Web may add new messages that need to be translated. We follow [semantic versioning](https://semver.org/), so new messages can only be added in *major* or *minor* versions. When using custom translations in your app, we recommend pinning the `@theoplayer/web-ui` dependency in your app's `package.json` to a specific minor version using a tilde constraint (`~`). Avoid using a caret constraint (`^`), since this may cause upgrading past your currently selected minor version. package.json ```json { "dependencies": { "@theoplayer/web-ui": "~2.2.0" } } ``` When you decide to upgrade Open Video UI to the latest version, make sure to also update your translations. Check the history for [`i18n/Locale.ts`](https://github.com/THEOplayer/web-ui/commits/main/src/i18n/Locale.ts) to see whether any messages were added or changed since the previous version. --- # Migrating to Open Video UI for Web 2.x This article will guide you through updating to Open Video UI for Web version 2 (from version 1), and the changes needed in your code. ## Update dependencies Run the following command to install Open Video UI for Web version 2: ```sh npm install @theoplayer/web-ui@2 ``` Open Video UI for Web now depends on [Lit version 3](https://www.npmjs.com/package/lit). If your project already uses Lit, make sure you use a compatible version. ## Update polyfill for legacy browsers In version 1, we recommended loading the [Web Components Polyfills](https://github.com/webcomponents/polyfills) on legacy browsers that do not natively support the Custom Elements API. As of version 2, Open Video UI for Web ships its own polyfill bundle, which includes: * [Web Components Polyfills](https://github.com/webcomponents/polyfills) * [Lit's `polyfill-support` module](https://lit.dev/docs/v2/tools/requirements/#polyfills) We recommend switching to our polyfill bundle, so you'll always have all the necessary polyfills needed to support legacy browsers. Alternatively, [you can load these polyfills manually](https://github.com/THEOplayer/web-ui/blob/%40theoplayer/web-ui%402.0.0/src/polyfills.ts). ```diff - import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js'; - import '@webcomponents/webcomponentsjs/webcomponents-bundle.js'; + import '@theoplayer/web-ui/polyfills'; ``` ## Migrate to Lit for custom components If you've written your own custom components that extend from built-in components provided by Open Video UI (such as the `Button` component), you may notice that these components now extend from [`LitElement`](https://lit.dev/docs/api/LitElement/). This allows you to use Lit's [reactive properties](https://lit.dev/docs/components/properties/) and [template-based rendering](https://lit.dev/docs/components/rendering/) to facilitate developing your custom components. Previously, your custom component would need to pass a `