# Dolby OptiView Player > Dolby OptiView Player (formerly THEOplayer) enables you to deploy cutting-edge video playback experiences, efficiently and on any device, including on web, mobile, smart TVs, set-top-boxes and gaming consoles. # 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-860/theoplayer/web.md) [For desktop and mobile web browsers, and smart TVs like Tizen and WebOS](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/web.md) ## [OptiView Player Android SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/android.md) [For smartphones, tablets and Android TVs](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/android.md) ## [OptiView Player iOS & tvOS SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/ios.md) [For iPhone, iPad and Apple TV](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/ios.md) ## [OptiView Player React Native SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/react-native.md) [For cross-platform apps targeting web, Android and iOS](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/react-native.md) ## [OptiView Player Flutter SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/flutter.md) [For cross-platform apps targeting web, Android and iOS](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/flutter.md) ## [OptiView Player Chromecast SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/chromecast.md) [For custom Chromecast receiver apps](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/chromecast.md) ## [OptiView Player Roku SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/roku.md) [For Roku smart TVs](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/getting-started/sdks/web/migrating-to-theoplayer-11.md), [Android](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/android/migrating-to-theoplayer-11.md), [iOS](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/ios/migrating-to-theoplayer-11.md) and [React Native](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/connectors/web.md) ## [Connectors for Android SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/android.md) ## [Connectors for iOS & tvOS SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/ios.md) ## [react-nativeConnectors for React Native SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/react-native.md) ## [Connectors for Roku](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/getting-started/sdks/web/getting-started.md) ## [OptiView Player Android SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/android/getting-started.md) ## [OptiView Player iOS & tvOS SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/ios/getting-started.md) ## [OptiView Player React Native SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/frameworks/react-native/getting-started.md) ## [OptiView Player Flutter SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/frameworks/flutter/getting-started.md) ## [OptiView Player Chromecast SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/chromecast/getting-started.md) ## [OptiView Player Roku SDK](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/theoplayer/getting-started/sdks/webos/getting-started.md) targets LG webOS apps. * [Tizen](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/tizen/getting-started.md) targets Samsung Tizen apps. * [Chromecast](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/getting-started/sdks/ios/getting-started.md) targets iOS, iPadOS and tvOS apps. * Roku SDK * [Roku](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/roku/getting-started.md) targets Roku apps. * [React Native](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/theoplayer/v4/getting-started/sdks/android-tv/getting-started) targets Android TV apps. > * [Fire TV](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v4/getting-started/sdks/ios/getting-started) targets iOS and iPadOS apps. > * [tvOS](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/getting-started/sdks/web/getting-started.md) ## [📖How-to guides](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides.md) [Learn how to implement our rich set of features and integrations.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides.md) ## [🛝Examples](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/examples.md) [See the player in action!](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/examples.md) ## [🔌Connectors](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web.md) [Integrate third-party solutions with THEOplayer using our pre-built connectors.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web.md) ## [📰Changelog](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/changelog.md) [Find out what's new in the OptiView Player.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/changelog.md) ## [\*️⃣API references](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web) [Discover all properties and functions of THEOplayer.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web) ## [🧠Knowledge base](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/knowledge-base.md) [Learn about topics in the video industry from our experts.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/knowledge-base.md) ## [❔FAQ](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq.md) [Answers to common questions we've received from our customers over the years.](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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. > **Open Video UI** > > The Open Video UI is our new Web UI. It provides component libraries for building a world-class video player experience powered by the THEOplayer SDK. [See our Getting Started guide to start using the Open Video UI.](https://docs-preview.optiview.dolby.com/pr-860/open-video-ui/web/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/how-to-guides.md) to learn about [UI customization](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/introduction.md), [advertising](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ads/introduction.md), [DRM](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/drm/introduction.md), [casting](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/cast/chromecast/introduction.md), [analytics](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/analytics/introduction.md) and other topics. * Check out our [API reference](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/getting-started/sdks/web/getting-started.md) or our [API pages](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/open-video-ui/web.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-860/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-860/open-video-ui/web.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-860/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-860/theoplayer/knowledge-base/playback/what-are-chapter-markers.md) * [Reference for Thumbnails](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/theoplayer/getting-started/sdks/how-to-update-a-sdk.md) * [License](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/open-video-ui/android.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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/how-to-guides.md) to learn about [UI customization](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/introduction.md), [advertising](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ads/introduction.md), [DRM](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/drm/introduction.md), [casting](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/cast/chromecast/introduction.md), [analytics](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/analytics/introduction.md) and other topics. * Check out our [API reference](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/open-video-ui/web.md) or one of [our connectors](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/theoplayer/getting-started/sdks/tizen/installing-tizen-studio.md) * [Generating a certificate](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/assets/images/import-1-8aaecab1b94a589fec742e47cd9bd394.jpg) Select the directory, then press Finish. ![](/pr-860/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-860/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-860/assets/images/tizen-extensions-2-e020322a3a7e70ab03067a6c823f80fd.jpg) Install the latest tv extensions under Extension SDK. ![](/pr-860/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-860/assets/images/tizen-certificate-1-5dc8da6607b5532e09b09217eef7d97d.jpg) In the Certificate Manager, click the Add (plus) button to generate a new certificate. ![](/pr-860/assets/images/tizen-certificate-2-4e3672ee2e2fa8cbdcc9d518f3b8ce6a.jpg) Generate a Tizen Certificate. ![](/pr-860/assets/images/tizen-certificate-3-f5c7f5477ef3c33c0438c1f3be6aa5df.jpg) Give your certificate a profile name (name is free to pick). ![](/pr-860/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-860/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-860/assets/images/tizen-certificate-6-c6150763460504d017b394943dbaf60d.jpg) Pick “Use the default Tizen distributor certificate”. ![](/pr-860/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-860/assets/images/tizen-certificate-8-dc745be45feb631cdeb6a54185c9194c.jpg) Your certificate should now be available for selection. ![](/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/PiPConfiguration) * [PlayerConfiguration API](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerConfiguration) * [PresentationMode API](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/faq/what-are-the-benefits-of-preloading.md) * [PreloadType API](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/assets/images/error-code-ErrorCode-198ffee2db62e071b0f3be03b5e2d95d.png "ErrorCode") ## API references * [`ErrorCategory`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/enums/ErrorCategory) * [`ErrorCategory.fromCode()`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/functions/ErrorCategory.fromCode) * [`ErrorCode`](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#manifesterror) and [`segmenterror`](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/theoplayer/getting-started/sdks/web/getting-started.md). ## Adding and removing request interceptors A [`RequestInterceptor`](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/InterceptableRequest#redirect) or [`respondWith`](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/InterceptableResponse#respondWith) method, which accepts a [`ResponseInit`](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/theoplayer/how-to-guides/web/ui/build-chromeless-ui.md), or to [implement a custom analytics integration](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap) | [canplay](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#destroy) | Fired when the the player is destroyed. | | Player | [dimensionchange](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#durationchange) | Fired when ChromelessPlayer.duration changes. | | Player | [emptied](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#error) | Fired when an error occurs. | | Player | [loadeddata](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#manifestupdate) | Fired when the manifest is updated. | | Player | [pause](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#pause) | Fired when ChromelessPlayer.paused changes to true. | | Player | [play](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#play) | Fired when ChromelessPlayer.paused changes to false. | | Player | [playing](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#progress) | Fired when the player loaded media data. | | Player | [ratechange](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#ratechange) | Fired when ChromelessPlayer.playbackRate changes. | | Player | [readystatechange](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#readystatechange) | Fired when ChromelessPlayer.readyState changes. | | Player | [representationchange](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#representationchange) | Fired when the current representation changes. | | Player | [resize](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#sourcechange) | Fired when ChromelessPlayer.source changes. | | Player | [timeupdate](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#volumechange) | Fired when ChromelessPlayer.volume changes. | | Player | [waiting](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#muted) | Whether audio is muted. | | Player | [...](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer) | ... | | [Ads (Client-Side)](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/AdsEventMap) | [adbegin](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adbegin) | Fired when an ad begins. | | Ads (Client-Side) | [adbreakbegin](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#adend) | Fired when an ad ends. | | Ads (Client-Side) | [aderror](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#aderror) | Fired when an ad errors. | | Ads (Client-Side) | [adfirstquartile](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/AdsEventMap#updateadbreak) | Fired when an AdBreak is updated. | | [Vendor Cast](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/VendorCastEventMap) | [statechange](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/VendorCastEventMap#statechange) | Fired when the state changes. | | [Chromecast](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/ChromecastEventMap) | [error](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap) | [addtrack](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap#addtrack) | Fired when a track is added. | | Track List | [change](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap#removetrack) | Fired when a track is removed. | | [Track](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TrackEventMap) | [change](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/TrackEventMap#update) | Fired when the track updates. | | [Media Track](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/MediaTrackEventMap) | [activequalitychanged](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap) | [addcue](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap) | [enter](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap#update) | Fired when the cue is updated. | | [Presentation](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PresentationEventMap) | [error](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PresentationEventMap#error) | Fired when an error occurs. | | Presentation | [presentationmodechange](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PresentationEventMap#presentationmodechange) | Fired when the presentation mode changes. | | [VR](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/VREventMap) | [directionchange](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/VREventMap#directionchange) | Fired when the VR.direction changes. | | VR | [error](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/VREventMap#error) | Fired when an error occurs. | | VR | [statechange](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/VREventMap#statechange) | Fired when the VR.state changes. | | VR | [stereochange](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/VREventMap#stereochange) | Fired when the VR.stereo changes. | | [Uplynk](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/UplynkEventMap) | [assetinforesponse](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/UplynkEventMap#pingresponse) | Fired when a Ping response is received. | | Uplynk | [preplayresponse](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/UplynkEventMap#preplayresponse) | Fired when a Preplay response is received. | | Uplynk | [ads](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/Uplynk#ads) | The Uplynk SSAI API. | | Uplynk | [assets](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/Uplynk#assets) | List of Uplynk assets. | | [Uplynk Ad Break List](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakListEventMap) | [addadbreak](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakEventMap) | [adbreakbegin](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/UplynkAdBreakEventMap#updateadbreak) | Fired when the ad break is updated. | | [Uplynk Ad](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap) | [adbegin](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap#adbegin) | Fired when an ad begins. | | Uplynk Ad | [adcomplete](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap#adcomplete) | Fired when the ad is completed. | | Uplynk Ad | [adend](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/UplynkAdEventMap#adend) | Fired when the ad ends. | | Uplynk Ad | [adfirstquartile](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/how-to-guides/web/player/track-first-playing-event.md) * [How to track player states](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/how-to-guides/web/text-tracks/change-visible-captions.md) * [How to programmatically detect text tracks](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/theoplayer/how-to-guides/web/text-tracks/detect-active-text-track-cues.md) * [How to insert subtitles](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/text-tracks/insert-subtitles.md) * [How to implement preview thumbnails](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/text-tracks/preview-thumbnails.md) * [How to track ID3 cues/tags](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) * [How to insert subtitles](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap#addtrack) event on [`player.textTracks`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#texttracks) to detect every [`TextTrack`](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/TextTrack#mode) of a [`TextTrack`](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/how-to-guides/web/text-tracks/change-visible-captions.md) * [How to programmatically detect text tracks](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/text-tracks/detect-text-tracks.md) * [How to build a chromeless UI](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/TextTrackStyle) property. ## Related articles * [How to dynamically change the visible captions](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) * [How to implement preview thumbnails](https://docs-preview.optiview.dolby.com/pr-860/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-860/open-video-ui/web.md) or [THEOplayer's default UI](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#cuechange) event of a [`TextTrack`](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#texttracks). This `textTracks` property is a [`TextTrackList`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TextTracksList) that inherits from the [`TrackList`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TrackList). This `TrackList` dispatches the events from the [`TrackListEventMap`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap). This `TrackListEventMap` contains the [`change`](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) * [How to insert subtitles](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/text-tracks/insert-subtitles.md) Refer to [how to track ID3 cues](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/how-to-guides/web/ui/build-chromeless-ui.md) * [How to detect video track quality changes](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#texttracks). This `textTracks` property is a [`TextTrackList`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TextTracksList) that inherits from the [`TrackList`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TrackList). This `TrackList` dispatches the events from the [`TrackListEventMap`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TrackListEventMap). This `TrackListEventMap` contains the [`addtrack`](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/TextTrackEventMap#addcue) event through the [`TextTrack`](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap#enter) and [`exit`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TextTrackCueEventMap#exit) events through the [`TextTrackCue`](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) * [How to track ID3 cues](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/text-tracks/track-id3-cues.md) --- # UI on Web THEOplayer lets you build the video player UI that fits your application. You can use a ready-made UI and customize it, or build a brand-new chromeless UI from scratch. Building a chromeless UI means building a new UI from scratch using the THEOplayer API and custom application code. In other words: you are responsible for inserting your own controls (e.g. a play button), and connecting them with the THEOplayer API (e.g. `player.play()`). A chromeless UI gives you total control over your UI and UX, but it requires an understanding of video playback and of the THEOplayer API. ## Default UI versus chromeless UI The Web SDK ships a default UI. The default UI is responsive and optimized for browsers, mobile devices and tablets, and it has all the basic controls you'd expect from a video player, ranging from a scrub bar to a fullscreen button. ![THEOplayer default UI](/pr-860/assets/images/ui-introduction-default-ui-60eb17b239311a93c4099657dcaf8651.png "THEOplayer default UI") As a developer, you may use this UI as is, customize it, or create a brand-new chromeless UI, as depicted in the screenshot below. ![THEOplayer UI overview](/pr-860/assets/images/ui-introduction-overview-03ba6a2cf9c5dafe7480f59155541b41.png "THEOplayer UI overview") It's possible that the envisioned design of your video player looks different from THEOplayer's default UI. When that happens, your team has to decide whether to customize THEOplayer's default UI, or to implement a chromeless UI. The default UI has all the basic functionality you expect from a video player, but it might be missing some more exotic UI (or UX) features. Changing the colors of the default UI is relatively straightforward, as is hiding a default control item (e.g. the fullscreen button), as is inserting a new button in the default control bar. However, truly transforming the default UI, such as repositioning elements, using all new icons, and more complex operations, can be quite challenging. Implementing a chromeless UI gives you total control of your UI and UX. However, this requires an understanding of video and the THEOplayer API, and you might feel like you're reinventing the wheel. So what to pick: modify the default UI or implement a chromeless UI? If the envisioned design looks "quite different" from the default UI, we encourage people to implement a chromeless UI. Alternatively, if that isn't an option, we advise people to compromise, and to adapt their envisioned design to look "quite similar" to the default UI. Our default UI is not optimized for big screen devices like Samsung Tizen, LG webOS and Android TV. If you're targeting those (and similar devices), you should consider implementing a chromeless UI, as demonstrated on our [Tizen](https://github.com/THEOplayer/samples-tizen) and [webOS](https://github.com/THEOplayer/samples-webos) GitHub repositories. Note that some features are dependent on our default UI, because they are built on top of our default UI. For example, the [Up Next](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/miscellaneous/up-next.md) feature (and API) wouldn't work when you build a chromeless UI. Alternatively, you can also use [Open Video UI for Web](https://docs-preview.optiview.dolby.com/pr-860/open-video-ui/web.md), a set of UI components which you can compose and style to build your own UI. ## Modify the default UI You can modify the default UI through JavaScript and CSS. Changing the colors is quite easy. You can use the tool at to interactively change the colors and generate the matching CSS. More information on changing the colors is available at our article on [UI skinning](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/ui-skinning.md). The default UI is a fork of Video.js 5, so you may also use the Video.js 5 API to modify the UI, as demonstrated in some of our articles. This may - for example - simplify the process of [adding a new button](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/insert-a-button.md) to the default control bar. Removing a button is a matter of identifying the appropriate CSS selector, and configuring `display: none!important`. Overlaying text and images is a matter of overlaying a custom `
` on top of the THEOplayer container. ### Related articles Below are some articles related to modifying our default UI: * [How to remove an element from the UI](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/remove-ui-element.md) * [How to align the timeline in the middle](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/align-timeline.md) * [How to change the big play button](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/change-big-play-button.md) * [How to keep the control bar always visible](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/keep-control-bar-visible.md) * [How to detect the visibility of the control bar](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/detect-control-bar-visibility.md) * [How to hide or disable the Google IMA UI](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/hide-google-ima-ui.md) * [How to insert a button](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/insert-a-button.md) * [How to change the UI language](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/change-ui-language.md) * [How to customize the UI during ad playback](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/customize-ui-during-ad-playback.md) * [How to change how a video should fit inside a container](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/change-video-fit.md) * [How to make an audio-only player](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/audio-only-player.md) * [How to customize the volume slider](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/vertical-volume-slider.md) * [How to disable click to pause](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/disable-click-to-pause.md) * [UI skinning](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/ui-skinning.md) ## Implement a chromeless UI Building a chromeless UI may be daunting at first, but it's rewarding. A chromeless UI gives you full control over your UI and UX, but you must understand the flow of a video, its API and its events. Refer to our article on [how to build a chromeless UI](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/build-chromeless-ui.md) for more information. --- # How to build a chromeless UI on Web Building a chromeless UI means building a brand-new video player UI from scratch. You achieve this by implementing your own custom UI components, and by associating those components with the appropriate THEOplayer API. The screenshot below visualizes such a chromeless UI -- albeit not the most pretty one. ![Chromeless UI Layout](/pr-860/assets/images/chromeless-ui-7-4025542f4b233096015b9e6ecce5bcd5.png "Chromeless UI Layout") A chromeless UI gives you complete control over your full UI and UX, but it also means that you are responsible for implementing your full UI and UX. Achieving this feat requires you to have an adequate grasp on the video player architecture and its underlying API. The goal of this guide is to advance your understanding on how to connect the dots between custom components and the THEOplayer API. For example, this guide will explain to which THEOplayer APIs you could map your custom play button. ## Creating a chromeless player instance Create a [`ChromelessPlayer`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#constructor) instead of a `Player`. Unlike `Player`, a `ChromelessPlayer` doesn't come with a default UI, so you can build your own UI on top of it. ```javascript const element = document.querySelector('.theoplayer-container'); const player = new THEOplayer.ChromelessPlayer(element, { libraryLocation: '/path/to/your-theoplayer-folder/', license: 'your_license_string', }); ``` The rest of this article refers to this `player` variable. ## Tracking transitions between states Refer to the article on ["How to track player states"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/player/track-player-states.md) to advance your understanding on video player states. Understanding the video player lifecycle is a vital part of building a chromeless UI. ![Video Player States](/pr-860/assets/images/player-video-player-states-4f4f37f92d4b03a2c750ce7fe03d0a5a.png "Video Player States") This article explains how to track the start of a video and its end, but also how to detect buffering, errors and much more. You'll need to implement (some of) these transitions in your chromeless UI in order to render the appropriate components. ## Mapping components A video player UI can be dissected into different components (or controls). A component offers context, and it may also offer an action. The "play button" is an example of an action component, but the "current time text" (X in the screenshot below) is a context component. As a developer, you should understand which controls are out there, and which video player APIs are relevant. This section addresses the following components: 1. Play button 2. Pause button 3. Volume mute button 4. Volume change button 5. Current time text 6. Duration text 7. Scrub bar 8. Buffered blocks 9. Live button 10. Audio change component 11. Subtitle change component 12. Video quality change component 13. Fullscreen and inline button 14. Picture-in-picture button 15. Chromecast button 16. AirPlay button Additionally, we'll discuss the following overlays: 1. Subtitle cues 2. Advertisement metadata ![Chromeless UI Layout](/pr-860/assets/images/chromeless-ui-layout-3642d810cba7de946ee7cb69b7ab238b.png "Chromeless UI Layout") Instead of providing inline code on this article, we'll refer to other articles as much as possible, because linking the THEOplayer API to your custom components is an application of many of the existing how-to guides for a specific use-case. If you know how to navigate our API references, you don't even need this section. The graphic below (originally referenced in ["custom analytics integration"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/analytics/custom-analytics-integration.md)) gives a basic overview of many of the relevant interfaces and events. ![THEOplayer API Interfaces and Events](/pr-860/assets/images/analytics-events-185d6ab52b556af08d8300b545ae5876.png "THEOplayer API Interfaces and Events") ### Play button You should show your play button when you are in a paused state, as described in ["how to track player states"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/player/track-player-states.md). If a viewer clicks your play button, you should call the `play()` on your `player` instance as documented in our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#play). ### Pause button You should show your pause button when you are in a playing state, as described in ["how to track player states"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/player/track-player-states.md). If a viewer clicks your pause button, you should call the `pause()` on your `player` instance as documented in our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#pause). ### Volume mute button You can check whether your volume is muted through the `muted` property (or method) on your `player` instance as documented in our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#muted). You should consider showing a different button depending on whether `muted` returns `true` or `false`. If a viewer clicks your mute button, you should set `muted` to `!muted`. ### Volume change button You can get and set your volume level through the `volume` property (or method) on your `player` instance as documented in our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#volume). You should consider showing a different button depending on the volume level. ### Current time text You can get the current time through the `currentTime` property (or method) on your `player` instance as documented in our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#currentTime). Note that `currentTime` returns a relative value in seconds. If you are dealing with live streams, you might want to use `currentProgramDateTime` instead, as this returns an absolute value like `"2022-04-01T13:37:42.666Z"`. This property (or method) is especially useful when implementing an [EPG](https://en.wikipedia.org/wiki/Electronic_program_guide) experience. ### Duration text You can get the duration of a stream through the `duration` property (or method) on your `player` instance as documented in our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#duration). The `duration` will return the duration in seconds for VOD streams, and `Infinity` for live streams. You can calculate the remaining duration by subtracting the `currentTime` from the `duration`. ### Scrub bar Related to the subsection on "Current time text", you can seek to a different playhead position through the `currentTime` property (or `setCurrentTime()` method) on your `player` instance. Alternatively, for live streams, you may also use `currentProgramDateTime` to seek to absolute playhead positions. You can only seek to a playhead position that is within any of the time ranges of your `seekable` property (or method) of your `player` instance as documented in our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#seekable). For example, on the Web SDK, this means that you'll stay between `player.seekable.start(0)` and `player.seekable.end(player.seekable.length-1)`. You subscribe to the `timeupdate` event to periodically update your scrub bar bullet. This event is dispatched every \~200ms during playback. Refer to our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#timeupdate) for more info on this event. A `seek` event is dispatched when you set a new value for `currentTime` or `currentProgramDateTime`. A `seeked` event is dispatched when the seek was successful. Refer to our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#seeking) for more info on these events. You should consider displaying a "stalling icon" between these two events. #### Buffered blocks You may also want to annotate parts of the scrub bar that have already been buffered. When the viewer seeks to a buffered block playback immediately starts. You can track information on what's being buffered through the `progress` event. Refer to our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#progress) for more info on this event. In the callback of the `progress` event, you will want to query the `buffered` property (or method) to iterate through the available buffered time ranges. The `buffered` property (or method) is described in our [API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#buffered). ### Live button A stream is a live stream when your `duration` property (or method) returns `Infinity`. If you want to implement a button that takes you to the "most live point" when clicked, then you set `currentTime` to the maximum `seekable` `end` time. For example, on the Web SDK, you would call `player.currentTime = player.seekable.end(player.seekable.length-1)`. ### Audio change component Refer to the article on ["how to detect audio tracks"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/media-tracks/detect-audio-tracks.md) to know how to detect the available audio tracks. You'll need this article to know which audio tracks are part of your stream. Refer to the article on ["how to enable and disable audio tracks"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/media-tracks/enable-disable-audio-tracks.md) to know how to enable or disable another audio track. You'll need this article to enable another audio track. Refer to the article on ["how to detect audio track changes"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/media-tracks/detect-audio-track-changes.md) to know how to detect when an audio track has been enabled or disabled. You'll need this article to correctly annotate your UI. ### Subtitle change component Refer to the article on ["how to detect text tracks"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/text-tracks/detect-text-tracks.md) to know how to detect the available text tracks. You'll need this article to know which subtitles and closed captions are part of your stream. Refer to the article on ["how to enable and disable text tracks"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/text-tracks/enable-disable-text-tracks.md) to know how to enable or disable another text track. Refer to the article on ["how to detect text track changes"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/text-tracks/detect-text-track-changes.md) to know how to detect when a text track has been enabled or disabled. You'll need this article to correctly annotate your UI. ### Video quality change component Refer to the article on ["how to detect video track qualities"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/media-tracks/detect-video-track-qualities.md) to know how to detect the available video track qualities. You'll need this article to know which video qualities are part of your stream. Refer to the article on ["how to select video track quality"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/media-tracks/select-video-track-quality.md) to know how to enable another video track quality. Refer to the article on ["how to detect video track quality changes"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/media-tracks/detect-video-track-quality-changes.md) to know how to detect when a specific video track quality has become active. You'll need this article to correctly annotate your UI. ### Fullscreen and inline button Getting your video player in and out fullscreen requires some getting used to, and differs a bit across SDKs. You cannot use our [`Presentation API`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/Presentation) to switch between fullscreen, inline and picture-in-picture. Instead, you need to implement your own fullscreen handling. One approach to achieve this is by using the `Fullscreen API` as described at . Additionally, to deal with iOS browsers, you can leverage [`webkitEnterFullscreen()`](https://developer.apple.com/documentation/webkitjs/htmlvideoelement/1633500-webkitenterfullscreen), or resize your video player container to a 100% width and height. ### Picture-in-picture button You should implement your Picture-in-Picture UI and UX independently of THEOplayer, regardless of whether you're considering "in-app" picture-in-picture or "out-of-app" picture-in-picture. The [THEOplayer Picture-in-Picture API](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/miscellaneous/picture-in-picture.md) is not available for chromeless players. To get the active video element on the THEOplayer Web SDK you may query `player.element.querySelectorAll('video[src]')[0]`. Related resources: * * * * ### Chromecast button Refer to our [introduction on Chromecast](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/cast/chromecast/introduction.md) to know how to track the availability of Chromecast, and how to start and stop a Chromecast session. ### AirPlay button Refer to our [introduction on AirPlay](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/cast/airplay/introduction.md) to know how to track the availability of AirPlay, and how to start and stop an AirPlay session. ### Subtitle cues You may still leverage THEOplayer's default rendering of subtitles (and closed captions) in your chromeless UI. This may require some extra styling though, depending on your SDK. For example, on the Web SDK, you may have to set a default `font-size` through CSS, as demonstrated in the snippet below. ```css .theoplayer-texttracks * { font-size: 1em !important; } ``` So what's the alternative? Instead of using THEOplayer's default rendering, you can programmatically detect when a subtitle (and closed captions) cue should appear and disappear, as explained in ["how to detect active text track cues"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/text-tracks/detect-active-text-track-cues.md). You could insert the cue when it should appear, and remove it when it should disappear. This alternative makes you fully responsible for the rendering and styling, and gives you total control over it. ### Advertisement metadata When playing back advertisements, you might want to overlay a countdown, show a skip button after some seconds, insert ad markers in the scrub bar, and achieve other use cases. If you use [Google IMA](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ads/google-ima.md) for client-side ad-insertion, then this integration might already take care of some default UI customization. If you are using THEOplayer's [default ad integration](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ads/set-up-vast-and-vmap.md) for client-side ad-insertion, then you need to [subscribe to the appropriate ad events](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ads/subscribe-to-ad-events.md), and apply your UI and UX in the callbacks of these events. Similarly, if you're doing server-side ad-insertion, you also need to apply your UI and UX in the callbacks of your ad events. ## Sample code The sample code at may help you get bootstrapped on our Web SDK. (Note that this sample code doesn't necessarily demonstrate best practices.) ## UX enhancements You can make your user-experience more appealing through various enhancements. For example, when the player is out of video data and is waiting for additional content you could show a 'loading' indication. Below are some common UX enhancements to consider: 1. Loading spinner: provide an indication when no video data is available. 2. Poster: show a poster (thumbnail) before initial play-request, and when the video is complete. 3. Auto next: when approaching the end, render a clickable overlay that allows the viewer to navigate to the next stream. Automatically play this stream when the current stream ends. 4. Skip intro: render a clickable button to skip the (ongoing) intro. 5. Ad countdown: overlay the remaining time of the ongoing ad break. 6. Ad markers: indicate the position of ad breaks in the scrub bar. ## Error handling A UI should also be capable of handling errors and informing the viewer. Refer to our [introduction on errors](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/miscellaneous/error/introduction.md) to further explore this topic. --- # UI skinning on Web THEOplayer allows you to style the player to your own needs. This guide will give an in-depth explanation of which colors can be changed and which parts of the player are affected. First it will explain how to change the skin of your player using 6 custom CSS classes. Afterwards, an overview follows explaining which parts of the players are affected by each class. If you don't already have an up-and-running version of THEOplayer, take a look at our [getting started guide](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/web/getting-started.md). ## CSS properties to override THEOplayer provides multiple colors you can change to create your desired look-and-feel. These colors can be changed easily by including a custom CSS file which overrides some classes. An example of such a file is given below. There is also a demo page available where you can interactively change these colors and immediately see the effect [here](https://demo.theoplayer.com/ui-skinning). **NOTE:** The vjs-selected is needed to change the color of selected items in the menus. ```html ``` ## Affected Components This section discusses which parts of the components are affected by each CSS class. When the player is added to the page, the only visible control is the **big-play-button**. The color of this component is equal to the *theoplayer-primary-color.* When the big-play-button is clicked, the player starts playing the video. Two components are visible now: the **control-bar** and the **sharing button**. The background color of the\*\* control bar\*\* can be styled using *theoplayer-tertiary-color*. The **control bar** exists of: **control-bar-icons**, the **progress bar** and **menu**s. * The color of the control bar icons can be set using `theoplayer-secondary-color`. * The color of the progress bar can be set using `theoplayer-primary-color`. * The color and background color of the header of the menu can be set using `theoplayer-primary-background`. * The color and background color of the content of the menu can be set using `theoplayer-tertiary-background`. * The color of the selected text of the menu can be set using `theoplayer-primary-color`. ## Summary After this tutorial, you should be able to style your THEOplayer to your own needs. All CSS properties and the components they affect were discussed. A brief summary of these properties is given below: | Color | Affected components | | -------------------- | ----------------------------------------------- | | Primary color | Big play button, progress bar, menu text | | Primary background | Menu header | | Secondary color | Control bar icons (including time and tooltips) | | Secondary background | Social sharing close button | | Tertiary color | Control bar background | | Tertiary background | Menu content background | --- # How to remove an element from the UI on Web You may want to limit the options and controls that are shown in the player's UI. There are two general approaches: 1. Build a chromeless UI, and add only the controls you need. This is useful when you don't want to remove just one element, but want a radically different UI. 2. Hide or remove the individual element from the existing UI. This guide focuses on the second approach. ## Code examples You can hide an element through CSS, or remove it through JavaScript. For this example we will consider the Speed menu option (control bar > Settings > Speed) as the element to remove from the page. The following methods may of course be used for other elements. **Hide the element with CSS** We identify the element and hide it. Since In this specific case there is no id or class to identify this element specifically, we make use of its aria attribute. Of course, should it change, this line of code should be adapted accordingly. ```css .theoplayer-skin li[aria-label='Open the video speed settings menu'] { display: none !important; } ``` **Remove the element through JavaScript** In alternative to CSS, it is also possible to achieve the same result with JavaScript, in the following way. ```js function firstplay(event) { player.removeEventListener('playing', firstplay); var speed = document.querySelector('.theoplayer-skin li[aria-label="Open the video speed settings menu"]'); speed.parentNode.removeChild(speed); } player.addEventListener('sourcechange', function () { player.removeEventListener('playing', firstplay); player.addEventListener('playing', firstplay); }); ``` --- # How to insert a button on Web This article provides information on how developers can add custom buttons to THEOplayer's **default UI**. THEOplayer's default UI is based on Video.js, so you insert a button with HTML, CSS and JavaScript. ## Code example This code example adds 2 time-jump buttons to the control bar. A button to go back 10 seconds in time should be positioned to the left of the play-button, and a button to go forward 10 seconds in time should be positioned to the right of the play-button. The buttons use the [Font Awesome](http://fontawesome.io/) icon set. This means that we first need to include the Font Awesome CSS stylesheet. ```html ``` The code below inserts two buttons in the control bar, lets viewers jump in time when it is clicked, and adds a tooltip to the button. ```js // setting up the rewind button by setting up a video-js component var Button = THEOplayer.videojs.getComponent('Button'); var RewindButton = THEOplayer.videojs.extend(Button, { constructor: function () { Button.apply(this, arguments); /* initialize your button */ // this.el() = created DOM-element // add tooltip var tooltipSpan = document.createElement('span'); tooltipSpan.className = 'theo-button-tooltip vjs-hidden'; tooltipSpan.innerText = 'Go back 10 seconds'; function toggleTooltip() { tooltipSpan.classList.toggle('vjs-hidden'); } this.el().addEventListener('mouseover', toggleTooltip); this.el().addEventListener('mouseout', toggleTooltip); this.el().appendChild(tooltipSpan); }, handleClick: () => { player.currentTime -= 10; }, buildCSSClass: function () { return 'fa fa-step-backward vjs-button'; // insert all class names here }, }); THEOplayer.videojs.registerComponent('RewindButton', RewindButton); player.ui.getChild('controlBar').addChild('RewindButton', {}); // setting up the forward button by setting up a video-js component var Button = THEOplayer.videojs.getComponent('Button'); var ForwardButton = THEOplayer.videojs.extend(Button, { constructor: function () { Button.apply(this, arguments); /* initialize your button */ // this.el() = created DOM-element // add tooltip var tooltipSpan = document.createElement('span'); tooltipSpan.className = 'theo-button-tooltip vjs-hidden'; tooltipSpan.innerText = 'Go forward 10 seconds'; function toggleTooltip() { tooltipSpan.classList.toggle('vjs-hidden'); } this.el().addEventListener('mouseover', toggleTooltip); this.el().addEventListener('mouseout', toggleTooltip); this.el().appendChild(tooltipSpan); }, handleClick: () => { player.currentTime += 10; }, buildCSSClass: function () { return 'fa fa-step-forward vjs-button'; // insert all class names here }, }); THEOplayer.videojs.registerComponent('ForwardButton', ForwardButton); player.ui.getChild('controlBar').addChild('ForwardButton', {}); ``` You could add `vjs-control` to the return-string of `buildCSSClass` to make the button more similar to the default buttons. Finally, you can adjust the position of the custom buttons with the order attribute, and force the cursor to use a pointer. ```css .fa-step-backward { order: -101; /* to the left of the play/pause-button */ cursor: pointer; } .fa-step-forward { order: -100; /* to the right of the play/pause-button */ cursor: pointer; } ``` ## Sample resources The pages or projects below demonstrate implementations of similar use cases. * Web SDK - Demo Page: * Web SDK - Sample App: --- # How to change the big play button on Web The question above is typically asked by developers who want to increase/decrease the size, set a different image instead, or introduce on-hover behavior. ## Code example The snippet below demonstrates how you could set another image instead of the default big play button. Additionally, the button becomes bigger when you hover it. ```css /* Increase button size when hovering */ .theoplayer-skin .vjs-big-play-button:hover { background: none; -webkit-transition-duration: 0.2s; -moz-transition-duration: 0.2s; transition-duration: 0.2s; transform: translate(-50%, -50%) scale(1.2); transition: transform 0.2s; } /* Set other image */ .theoplayer-skin .vjs-big-play-button:before { content: ''; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjcycHgiIGhlaWdodD0iNzJweCIgdmlld0JveD0iMCAwIDcyIDcyIiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCA0My4yICgzOTA2OSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+dmlkZW8gaWNvbiBjb3B5IDQ8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz48L2RlZnM+CiAgICA8ZyBpZD0iSG9tZXBhZ2UiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJIb21lcGFnZS1fZGVza3RvcCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQ3MC4wMDAwMDAsIC0zMDEyLjAwMDAwMCkiPgogICAgICAgICAgICA8ZyBpZD0iZnd0diIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTIwLjAwMDAwMCwgMjc4OC4wMDAwMDApIj4KICAgICAgICAgICAgICAgIDxnIGlkPSJiaWctdmlkZW8iIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMDAwMDAwLCA1MC4wMDAwMDApIj4KICAgICAgICAgICAgICAgICAgICA8ZyBpZD0idmlkZW8tMnB4IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNTEuMDAwMDAwLCAxNzUuMDAwMDAwKSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxnIGlkPSJ2aWRlby1jb3B5IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNS4wMDAwMDAsIDM1LjAwMDAwMCkgcm90YXRlKC0zNjAuMDAwMDAwKSB0cmFuc2xhdGUoLTM1LjAwMDAwMCwgLTM1LjAwMDAwMCkgIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwtMiIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjIiIGZpbGwtb3BhY2l0eT0iMC4xNSIgZmlsbD0iIzAwMDAwMCIgY3g9IjM1IiBjeT0iMzUiIHI9IjM1Ij48L2NpcmNsZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxwb2x5Z29uIGlkPSJUcmlhbmdsZSIgZmlsbD0iI0ZGRkZGRiIgcG9pbnRzPSIyNi45ODI4NTY5IDQ5Ljk1Mzc1MiAyNi45ODI4NTY5IDE5LjM0MjEwNTMgNDguNTQzNDU4OSAzMy45OTY5NDI5Ij48L3BvbHlnb24+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==') no-repeat 0 0; /* background: url('http://i.stack.imgur.com/LT3WE.png') no-repeat 0 0; */ /* Use image instead */ background-size: 100%; display: block; } /* Remove default THEOplayer image */ .theo-big-play-button-svg-container svg { display: none; } /* Remove default spotlight around button on focus */ .theoplayer-skin:hover .vjs-big-play-button:after, .theoplayer-skin .vjs-big-play-button:focus:after { opacity: 0; } ``` The snippet above also removes some default behavior, such as the shadow-effect when hovering the big play button. ![Big Play Button](/pr-860/assets/images/big-play-button-7ba8ac3c1d83a855d6fb4f1035f4c466.png "Big Play Button") --- # How to keep the control bar always visible on Web The question above is typically asked by developers who want to be able to see the control bar at all times without having to continuously move the cursor over it (or tap the screen). ## Implementation The following CSS line is aimed at overwriting the opacity transition and have the control bar always visible. ```css .theo-secondary-color.vjs-control-bar { opacity: 1 !important; } ``` --- # How to detect the visibility of the control bar on Web This question is typically asked by developers that want to insert a custom element that should 1. disappear when the default control bar disappears, 2. appear when the default control bar appears. This article discusses three approaches that allow you to detect whether the control bar is visible. 1. [Using a chromeless UI](#using-a-chromeless-ui) 2. [Using a MutationObserver](#using-a-mutationobserver) 3. [Using CSS](#using-css) ## Using a chromeless UI Instead of making the default THEOplayer UI fit your use cases, it might make sense to opt for a [chromeless UI](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/ui/build-chromeless-ui.md). When you use a Chromeless UI, you have full control over the UI and UX, and you are not restricted to the complexities of our default UI. ## Using a MutationObserver You can use a [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) to detect the visibility of the control bar. Generally speaking, the default control bar is visible when the following scenario is true for your THEOplayer container's [`classList`](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList): 1. The `vjs-user-active` class is listed in the `classList`; 2. Or if the `vjs-paused` class is listed in the `classList`; 3. And the `vjs-has-started` class is listed in the `classList`. The control bar is visible in the screenshot below because `vjs-paused` and `vjs-has-started` are classes of the THEOplayer container, as highlighted with the yellow rectangles. ![](/pr-860/assets/images/ui-control-bar-visibile-8e0fc2133328b496d115b9798f356b5c.png) If we map that to JavaScript code: ```javascript const theoplayerContainer = document.querySelector('.video-js'); const controlBarVisible = (theoplayerContainer.classList.contains('vjs-user-active') || theoplayerContainer.classList.contains('vjs-paused')) && theoplayerContainer.classList.contains('vjs-has-started'); ``` It should be noted that this approach does not take the opacity of the control bar into account. The default control bar UI has a "fade in" and "fade out" effect. If you want to take this into account, you can capture its opacity through JavaScript when you have a reference to your control bar DOM element. The snippet below illustrates how you could poll the opacity every 100ms. ```javascript setInterval(function () { const controlBar = document.querySelectorAll('.vjs-control-bar')[1]; const controlBarOpacity = getComputedStyle(controlBar).opacity; console.log('control bar opacity', controlBarOpacity); }, 100); ``` The snippet below demonstrates how use the previously specified `controlBarVisible` variable in combination with a `MutationObserver`. ```javascript const theoplayerContainer = document.querySelector('.video-js'); var controlBarObserver = new MutationObserver(function (event) { for (let i = 0; i < event.length; i++) { const el = event[i].target; const controlBarVisible = (el.classList.contains('vjs-user-active') || el.classList.contains('vjs-paused')) && el.classList.contains('vjs-has-started'); const message = { controlBarVisible: controlBarVisible, }; console.log(message); } }); controlBarObserver.observe(theoplayerContainer, { attributes: true, attributeFilter: ['class'], childList: false, characterData: false, }); ``` You can try out this snippet at , and verify the `{"controlBarVisible": true}` (or `{"controlBarVisible": false}`) logs in the developer console. ## Using CSS If you insert your custom elements through JavaScript, you can use CSS queries to align the visibility of your custom controls with the default control bar. The snippet at has a text ("Hello World!") that's only visible when the control bar is visible. We also leverage the `vjs-has-started`, `vjs-paused` and `vjs-user-active` classes mentioned in the previous approach. --- # How to align the timeline in the middle on Web This question is asked when developers want to change the appearance of the current time digits and duration digits in the seekbar. For example, instead of having it aligned on the left of the control bar, they might want to have it in the middle. Please note that this article isn't final. ![Align timeline middle](/pr-860/assets/images/align-timeline-middle-66bdd5275fa323e16204484e31301562.png "Align timeline middle") ## Implementation One approach is to create a new overlay. The code snippet below implements this strategy. CSS ```css .custom-timeline { display: none; bottom: 0; position: absolute; width: 100%; text-align: center; line-height: 36px; pointer-events: none; } .vjs-has-started .custom-timeline.show { display: block; } .vjs-time-control { display: none !important; } .theo-mobile .custom-timeline { display: none; } .vjs-fullscreen .custom-timeline { margin-bottom: 8px; } ``` HTML and JS ```html
00:00:00 /
``` --- # How to customize the volume slider on Web This article provides information regarding the implementation of a vertical volume slider instead of the default horizontal one. **Please note that the following page is meant as a general example and need in all cases to be further personalized. The aim is to show you the process of implementing a vertical volume slider.** **In other words, this is a first step in a work in progress as it does not yet account for color and aspect customization, as well as for a series of other small details.** There is currently no ready-made option in THEOplayer for changing the orientation of the volume slider with a simple parameter. However, good results can be achieved with a [custom button](https://www.theoplayer.com/theoplayer-demo-adding-buttons-to-the-ui-with-custom-logic). Notes about the current example: * it has been successfully tested on major browsers (Chrome, Firefox, Safari) but no complete investigation has been performed. In Edge the slider still does not appear correctly and needs some more CSS tinkering. * it has been successfully tested in several conditions (fullscreen and inline presentation mode and on different resolutions). However, it still needs testing in your specific implementation and use case. * The aspect of the current volume slider differs depending on the browser: we encourage you to replace it with your favorite one and customize it * The volume icon changes when the player is muted, but otherwise do not reflect volume increases/decreases. * When the player is muted, the slider and the aria values do not go to 0 yet --- # How to change the UI language on Web This article describes how to change the default UI language to other. The [`UIConfiguration`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/UIConfiguration) object has a `language` and a `languages` property to change the default UI language, i.e. English, to any other language. Both properties are optional, but when `language` is defined, its language code must have a matching language dictionary in the `languages` configuration. When this configuration is set, the labels and tooltips in the UI will be replaced by their translated version from the language. If no translation is found for a label or tooltip, the original English version will be used instead. ## Prerequisites * A valid THEOplayer license for the Web SDK. * The language dictionary that you want to add to your project. **Language JSON Links** Arabic : Bashkir: Bulgarian: Catalan: Czech: Danish: German: Greek: Spanish: Persian: Finnish: French: Croatian: Hungarian: Italian: Japanese: Korean: Dutch: Polish: Portuguese: Russian: Serbian: Swedish: Turkish: Ukrainian: Vietnamese: Chinese: | ## Implementation ```js player = new THEOplayer.Player(playerElement, { libraryLocation: 'path/to/theoplayer/', license: 'your-license-here', ui: { language: 'esp', languages: { esp: { Play: 'Reproducir', Pause: 'Pausa', 'Current Time': 'Tiempo actual', 'Duration Time': 'Duración', 'Remaining Time': 'Tiempo restante', }, }, }, }); ``` --- # How to customize the UI during ad playback on Web This how-to guide describes how to customize the look and feel of the player when an advertisement is playing. We will be leveraging mainly some event listeners and CSS. In the section *Resources*, at the bottom of the page, you will also find links to the reference API and to a related how-to guide that explains when and how the texts displayed during ad playback (if any) may be changed. ## How to customize the ad playback UI You may want to alter the UI during ad playback, for example in order to hide some elements, to show some new elements or to change the aspect of existing ones. In the following, we will see some example related to this. ### Detecting ad playback There are two general approaches to detect advertisement playback: 1. A responsive approach using a **CSS rule**. 2. A programmatic approach using **events**. Whenever you create a THEOplayer instance, you need to provide a DOM-element which will be the container of the player (for more information regarding this, see Resources). This container gains the `.theo-ad-playing` class whenever an advertisement is playing. Through CSS, you could use this class to enforce certain behavior. For example, you could hide the fullscreen button if an ad is playing. ```css .theo-ad-playing .vjs-fullscreen-control { display: none !important; } ``` ### Events The [Ads API](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/Ads) exposes, among others, the following events: * `adbegin`: dispatched when an ad starts * `adend`: dispatched when an ad ends * `adbreakbegin`: dispatched when an ad break (or ad slot) starts * `adbreakend`: dispatched when an ad break ends The snippet below demonstrates how you could detect the start of an adbreak through a JavaScript event listener. ```js player.ads.addEventListener('adbreakbegin', console.log); ``` These events can be used to trigger a certain behavior programmatically. ### Displaying an advertisement label The snippet below demonstrates how to add a label to the control bar during ad playback using CSS and JavaScript. CSS ```css .theo-ad-playing .theo-advert-label { display: block !important; } .theo-ad-playing .vjs-fullscreen-control { display: none !important; } ``` JavaScript ```js var button = THEOplayer.videojs.getComponent('Button'); var myButton = THEOplayer.videojs.extend(button, { constructor: function () { button.apply(this, arguments); this.el().innerHTML = 'Ad'; }, handleClick: function () { elementContainer.classList.toggle('hidden'); }, buildCSSClass: function () { return 'theo-advert-label'; }, }); THEOplayer.videojs.registerComponent('AdvertLabelButton', myButton); player.ui.getChild('controlBar').addChild('AdvertLabelButton', {}); ``` ### Adding a custom countdown The snippet below demonstrates how to add a countdown timer using primarily JavaScript. CSS ```css .theo-ad-countdown { position: absolute; top: 50%; left: 50%; font-size: 5em; } ``` JavaScript ```js function updateTime() { var countdown = document.querySelector('.theo-ad-countdown'); if (countdown) { countdown.innerHTML = Math.round(player.duration - player.currentTime); } } player.ads.addEventListener('adbegin', function (e) { var countdown = document.createElement('div'); countdown.className = 'theo-ad-countdown'; countdown.innerHTML = player.duration; element.appendChild(countdown); player.addEventListener('timeupdate', updateTime); }); player.ads.addEventListener('adend', function (e) { var countdown = document.querySelector('.theo-ad-countdown'); if (countdown) { player.removeEventListener('timeupdate', updateTime); element.removeChild(countdown); } }); ``` --- # How to hide the Google IMA UI on Web You may want to know which controls are visible to the viewer during ads, so you can use your own UI elements instead. Unfortunately Google IMA provides very limited control over the UI. The only thing that you can change or hide from THEOplayer is the countdown: to do so, set `showCountdown` to `false` in your [`AdsConfiguration`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/AdsConfiguration#showCountdown). Using other methods, like CSS or JavaScript, to modify the Google IMA UI elements styles will also not work due to: * the usual higher specificity of the *iframe* CSS * the security restrictions on XSS. --- # How to change how a video should fit inside a container on Web You may want to alter how a video is sized inside the player's frame. By default, a video player respects the aspect ratio of the video. This means that black bars can appear on the sides of the player, or above and below the video, when its aspect ratio differs from the player's aspect ratio. Instead, you may want the video to cover the entire player. Note that this either * cuts off the content that falls outside of the player, or * deforms the content, so it no longer matches its original aspect ratio. ## Implementation The CSS [`object-fit`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) property serves this use-case. ```css .theoplayer-skin video { object-fit: contain; /* default */ /* object-fit: cover; // content outside of the container, hence some content might be missing from the container */ /* object-fit: fill; // all content inside of the container, but the content might be deformed to be fitted inside of it */ } /* // if you are doing object-fit: cover, and you don't want to show the content outside of the container, then do: .theoplayer-skin { overflow-y: hidden; } // additionally, the object-position CSS property can help position the content, e.g. .theoplayer-skin video { object-position: top; } */ ``` Alternatively, if you cannot use CSS for some reason, you could try to achieve the same through JavaScript. ```javascript const videos = document.querySelectorAll('.theoplayer-skin video'); for (let i = 0; i < videos.length; i++) { videos[i].style.objectFit = 'cover'; } ``` --- # How to make an audio-only player on Web This article tells you how to manipulate THEOplayer to make an audio-only interface, using only CSS and JavaScript. Going from this ![Audio Only](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAf0AAAEhCAYAAAB1MHncAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAABKvSURBVHhe7d1vsF1VeQdgtWAEiWBAI/9CgoGQSIgGQsQQIhhgpqWdYFstMIrTCmNrFREwQztatKWFgtggcVI6dkBQalOlKBQLUwZoh8GRts60tS0znZYPOvClpbRjP+7y3nafu3Py3su95+x9/qw8Z+bJZH5nnb3WXufDunuftdd6VeXl5eXl5eV1QLxe9fIr/gEAypeGAEB50hAAKE8aAgDlSUMAoDxpCACUJw0BgPKkIQBQnjQEAMqThgBAedIQAChPGgIA5UlDAKA8aQgAlCcNAYDypCEAUJ40BADKk4YAQHnSEAAoTxoCAOVJQwCgPGkIAJQnDQGA8qQhAFCeNAQAypOGAEB50hAAKE8aAgDlSUMAoDxpCACUJw0BgPKkIQBQnjQEAMqThgBAedIQAChPGgIA5UlDAKA8aQgAlCcNAYDypCEAUJ40BADKk4YAQHnSEAAoTxoCAOVJQwCgPGkIAJQnDQGA8qQhAFCeNAQAypOGAEB50hAAKE8aAgDlSUMAoDxpCACUJw0BgPKkIQBQnjQEAMqThgBAedIQAChPGgIA5UlDAKA8aQgAlCcNAYDypCEAUJ40BADKk4YAQHnSEAAoTxoCAOVJQwCgPGkIAJQnDQGA8qQhAFCeNAQAypOGAEB50hAAKE8aAgDlSUMAoDxpCACUJw0BgPKkIQBQnjQEAMqThgBAedIQAChPGgIA5UlDAKA8aQgAlCcNAYDypCEAUJ40BADKk4YAQHnSEAAoTxoCAOVJQ2AKrVh+UHXxtmXV9ZefUN35a2urh3ZtrL53zzurf3ngnOqFR8+rXnry/OrHT10wI/4fWbwXZaJsfCY+G8eIY2V1AFMtDYEpcObaQ2cG6Qc+//bquQe3Vf/z1IWtimPGsaOOqCtrAzBV0hCYUBdtOWLmijyu0LOBut/zf35e9YO9Z1dP37W5emzPGdUju0+fEf+PLN6LMtln+0WdUXe0IWsbMPHSEJgg61YuqW69avW8A/2z92+t9t68obrhylXV+95zVLX+xCXVkte+Jj1eJsrGZ+KzcYw4VhwzqytEW6JN0bbseMBESkNgAsQVddxezwbduDq/78b11RU7jq5WveXg9PNtiGNHHVHXXHcEoo2u/mEqpCEwRjGRLm6/9w+uLz6xvfrKZ0+tdpyzLP3cKETd0YZoS3/7os3R9uxzwERIQ2AMtpx22Mws+v7B9Jl7z6o+cclxi7pd37VoS7Qp2tbf3jiHOJfsc8BYpSEwQjGA7t55yn6DZ1w5x2/s2WcmSbQxuzMR5zRJf6gAeQiMyGUXvnm/CXoxq34aBvt+0eZoe/Nc4tziHLPywMilITAC8fhbc4CMiXJxyzwrO03iHPon/cW5ZmWBkUpDoEOx0E2sgtccFGNy3JFLy7kVHucS59Q8xzhni/zAWKUh0JEPXbR8ZgnceiCMK+LIsrIliHNrXvXHuZd8vjDh0hDowGc+vLI3+IVYGa/LZ+wnRZxjnGvz3KMvsrJAp9IQaNmua07eZ9C747o1abmSxTk3+yD6JCsHdCYNgRb1T9i77gMr0nIHgjj3Zl+Y4AcjlYZAS/oHfL9n/9/v/M0+MfDDyKQh0IL+W/rT+Ox9V6Ivmn3jVj+MRBoCQ+qftGfA31//wG9yH3QuDYEh9N++dkt/bvoKRioNgQHF4jPN5/AP5El7C9Wc3Bd9ZwEf6EwaAgNqrrR3ID6WN6jm43zRh1kZYGhpCAygOVM/FqPJygzq3I1L07wkzQV8zOiHTqQhsEixk1w9YMWys22utPfag19d/e1Xz6q+/7V3VXuuX1td+nJdhx9a3pa10WfNJXvtzgetS0NgEWLP+Ob2uG1PRtu64bDqv/9ydp5A+NF3zq3uv2XDzI52xx51UPq5adSc2Bd9aj9+aFUaAouwe+cpvYEqdpbLygzjql84rnf8zA8ffnd129UnVetWLkk/P22au/NF32ZlgIGkIbBAW047rDdAxa3pLrbHvfPX913Vby7//vj26vevX1ttXjfds9+jD5u3+aOPs3LAoqUhsEAP7drYG5ziVntWZlhP/sGmXh0Ldc/nTq22b3pDerxpEH1Zn0v0cVYGWLQ0BBbg4m3LegPT03dtTsu04flHzu3Vs1jf/N0N1Y5zlqXHnXTRp/V5RF9nZYBFSUNgAR7bc0ZvUOpqmd1TVy3p1TGMvTedVp31ttendUyq5jK90ddZGWBR0hB4BRdtOWIkA9L7t++7Pv0wXnpye3XLx1dXS6focb/mH1bR51kZYMHSEHgFD3z+7b3BqMvNdHZ+cN/959vw91/fUn3kvcek9U2a5tV+9HlWBliwNATmEY/G1QPRM/eelZZpy+5PzS5P27YHf+8dUzHZL/q4bnMpjyXCmKQhMI9br1rdG4S6mrFf+9Zts3cUunLHy39YTPICP82Z/NH3WRlgQdIQmEe9+t6LT2zvfMW4v/nq7FVul+KcPnnZ8Wkbxi36OPq6bmdWBliQNATm0JzA18Xqe/1eeHR2kZpRiDsLZ6w5JG3LODVX6TOhDwaWhsAcmjvpdf38e8yyr+sapfhDIyYQZm0al+jrun124IOBpSEwh/rWfiwTm73fprjirge6cZi0q/56aV63+GFgaQgkzlx7aG9AvO/G9WmZNrX5jP6gJumqP/q8bld8F1kZYF5pCCSuv/yE3qBzxY6j0zJt6uIZ/UFNwlV/9HndnvgusjLAvNIQSDQX5Fn1loPTMm26+WOzjwZOgnFf9Uef122xUA8MJA2BxHMPbpsZcJ69f2v6ftu+cPVJvUFuksSAe8rxr03b3LXo+2hDfBfZ+8C80hDos2L5Qb1Bb+/NG9IybYtFc+o6J01MpvvAT745bXeXou/rNsR3kpUB5pSGQJ/mNro3XLkqLdO25uOBk+q2q09K296V6Pu6btvtwqKlIdCnOYmvyw12mu76jbf16pxkj+4+vdo0otn0zQ14TOaDRUtDoE/zqnv9iaPZ9OWrvzm7Ct2k+9F3zh3Jzn3R93WdFumBRUtDoM9Duzb2Bpuu19uv7b3ptF6d02LP9Wur13XYP9H3dV3xnWRlgDmlIdDne/e8c2agGcVKfLU/vbX7Hfa68FdfPrPauuGw9JzaUK/MF99J9j4wpzQE+tTL7/5g79np+114aNc79hlMp8nzj5xb/eLPvCU9r2HFdxB1WI4XFi0NgT71bndP37U5fb8LMUGuOZBOo9/+6FvTcxtGfAdx7PhOsveBOaUh0OelJ8+fGWge23NG+n4XHr9z0z4D6LT62m+1u09BfAdx3PhOsveBOaUh0OfHT10wM9A88vLVd/Z+Fx790vRf6dfid/5jjvyJ9DwXK76DOGZ8J9n7wJzSEOgzjkH/z26ffWJg2v3Fy1fnRy1tZ1a/QR8GloZAn3Hc3v9WY4OfaXb7tSen5zcot/dhYGkI9BnHRL4/aawzP60+/v5j03Mbhol8MLA0BPqM45G9P7px/T4D6DT5x2+cXf302W9Mz2tYHtmDgaUh0Gcci/N85bPTswxv0903vG1m7/vsnNpgcR4YWBoCfcaxDO8ffnpdr85p8Hdf31Jd/lPL03Npi2V4YShpCPQZx4Y7sY59Xeek+9LOU6ojW5qdPx8b7sBQ0hDoM46tdb943ZpenZPqu3dvrn7uvCPT9nfB1rowlDQE+ly8bVlvsLnhylVpmbbddvVJvTon0S1Xra6WHPzqtO1dib6v64/vJCsDzCkNgT4rlh/UG2z23rwhLdO2mz62ulfnJImVAi/cfETa5q5F39ftiO8kKwPMKQ2BxHMPbpsZbJ69f2v6fts+8+GVvQFuEsQjcp+89Pi0raMSfR9tie8iex+YVxoCiQcaK+R1+Uha7aM/f2yvvnHbvfOU6tijxntlHX1etye+i6wMMK80BBLNyXxX7Dg6LdOmSy54U6++cXn49o3VBZsPT9s3atHndbtM4oOBpCGQOHPtob1B574b290uNnPBmYf36hu1f/rG1upj72t/Cd1hRJ/X7YvvIisDzCsNgTnUy/GOYmW+jScf0hvkRmnXNSdXb3pDO9vgtqleic/yuzCwNATm0FykZ8c53T4yFr+h13WNwre/8I7qvI1L07aMW/R13U6L8sDA0hCYw0VbjugNPrE2flamLc0lZ7v0xJ2bqssufHPahknR3IcgvoOsDPCK0hCYR32L/8Untne+Dv9/PL69N9i17a/vPav65Z89Jq13kkQfR19Hm93ah6GkITCPW6+aXTTnE5ccl5Zpy7/9/9oAbfrnb26tdn5wRXXQa/I6J030cd326PusDLAgaQjMY93K2U1fnnn5ajkr05bvf+1dvbqG9cOH313d+CsnVm98/Wh2CWxL9HF9DtH3WRlgQdIQeAXNhXq63IDn8Ts39eoZ1H8+sb26/dqTq7ce3f2CQm1rbrBjQR4YWhoCr6A5oe+xPWekZdoQK+HV9SzWfz15fvXlT6+rTl9zSHrsaRB9W5+PCXwwtDQEFqA5IHV1tf+R9x7Tq2Ohnn/kvOqL166pNqx+XXrMadG8yu/yDys4gKQhsADN7XafvmtzWmZY79n0hl4dr+Rfv72t+p1ffWt14gj2BRiF6NP63GyjC61IQ2CBHtq1sTcwdTGT/4TGlr5z+Yc/3lJ9+pdWVkcuna4JevNpztiPPs7KAIuWhsACbTntsN7gFMvEdjHwfv++fAb/d+/eXF196fHVkoNfnX5uWkUf1kvuhujjrBywaGkILEJzsl0Xq/R967bZJwViP/k7rlszsxlPVrYEzdX3om+zMsBA0hBYhFgxrl6lL3zoouVpuUHd8ak11b2fO7V6//buHg2cFNF3dT9Gn3a94iEcYNIQWKRYu74erOLW9KoWJ9MtPfTAGPiiz5q39Sd9PwCYQmkIDKC5A98ju09PyzC36LO6/+ykB51IQ2BA37vnnb2BK357z8qwv+irut+iD7MywNDSEBjQmWsPrV568vzeAHbdB1ak5ZgVfVT3V/Rd9GFWDhhaGgJDaE5GC21P7CuJvoKRSkNgSJ/58Mp9BrMuN+WZVs1ldkP0WVYOaE0aAi3Ydc3J+wxqBv5Z/QN+9FVWDmhVGgItac7oD25f739L30x9GJk0BFrUP/AfyJP7mpP2ggEfRioNgZb13+o/EB/naz6WF9zSh5FLQ6AD/ZP7YjGaNlfum1Rxjs2Fd4JJezAWaQh0JH7Pbj7HH8vOlvw7f5xbc2ndOHfzGmBs0hDoUCw+01y5L8TOciXthx/n0twtL8Q5W3gHxioNgRHon+AXV8SfuOS4tOw0iXNoXt0HE/ZgIqQhMCKxk1xzW97w9F2bp/KZ/mhztL15LnFudsuDiZGGwAjFnvG7d56yz2AZHttzxlQM/tHGaGt/++Oc7IcPEyUNgTHYctph1UO7Nu43eD5z71kzt8wnaQCNtkSbom397Y1ziHPJPgeMVRoCY3TxtmXplfOLT2yfmRy345xl6edGIeqONkRb+tsXbY62Z58DJkIaAhPgoi1HVA98/u37Da4hJsrdd+P66oodR3f6rH8cO+qIuvon59WijdHW7PPARElDYIKsW7mkuvWq1ftN+Gt69v6t1d6bN1Q3XLlq5jf29ScuWdTPAVE2PhOfjWPEseKYWV0h2hJtirZlxwMmUhoCEyquqOPxt/n+AGiKq/Mf7D17ZlZ93H6PlfFC/D+yeG+uK/h+UWfU7aoeplYaAlMgFrq5/vITZm6vP/fgtnSgHkYcM44ddVhUB4qQhsAUWrH8oJmJdDFIxxV5zKKPVfDiCv2FR8+bWQL3x09dMCP+H1m8F2WibHwmPhvHiGNldQBTLQ0BgPKkIQBQnjQEAMqThgBAedIQAChPGgIA5UlDAKA8aQgAlCcNAYDypCEAUJ40BADKk4YAQHnSEAAoTxoCAOVJQwCgPGkIAJQnDQGA8qQhAFCeNAQAypOGAEB50hAAKE8aAgDlSUMAoDxpCACUJw0BgPKkIQBQnjQEAMqThgBAedIQAChPGgIA5UlDAKA8aQgAlCcNAYDypCEAUJ40BADKk4YAQHnSEAAoTxoCAOVJQwCgPGkIAJQnDQGA8qQhAFCeNAQAypOGAEB50hAAKE8aAgDlSUMAoDxpCACUJw0BgPKkIQBQnjQEAMqThgBAedIQAChPGgIA5UlDAKA8aQgAlCcNAYDypCEAUJ40BADKk4YAQHnSEAAoTxoCAOVJQwCgPGkIAJQnDQGA8qQhAFCeNAQAypOGAEB50hAAKE8aAgDlSUMAoDxpCACUJw0BgPKkIQBQnjQEAMqThgBAedIQAChPGgIA5UlDAKA8aQgAlCcNAYDypCEAUJ40BADKk4YAQHnSEAAoTxoCAOVJQwCgPGkIAJQnDQGA8qQhAFCeNAQAypOGAEB50hAAKE3l5eXl5eXldQC8qup/AZqSq1BORoY4AAAAAElFTkSuQmCC "Audio Only") to this ![Audio Only](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAf8AAAA0CAYAAACEqTF9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAN2SURBVHhe7d0xSxx5HMdhCWIhiI2FBGLjO0hpZy0ptMlbSJvG0sLORoiCvQgWqQwWwglJFQvroIJgDsRCBEW0UNH8c785R1Z3PDDZXXPzf77woOvODCHNZzeOpiv9s66uLgAgA0X3xR8A8iH+AJAZ8QeAzIg/AGRG/AEgM+IPAJkRfwDIjPgDQGZ+Of4jIyMAwB+iqtWPEX8AqIGqVj9G/AGgBqpa/RjxB4AaqGr1Y345/gDA/5P4A0BmxB8AMiP+AJAZ8QeAzIg/AGRG/AEgM+IPAJkRfwDIjPgDQGayjP/w8HDx8fvf39OnT5/SwMBA0zEAUFdtif+rV6+e/HuGO2V2draIfnx+dnZW/AX8+PEjra6uNh0LAHUUa3n8JyYmiguvrKyk7u7uymOeQ4Q/tr+/XzyemppKl5eXxddiHz9+bDoHAOom1rb4x87Pz9Pbt28rj+ukMvyxMv6hp6cn/bW+fvtMSu/fv793HgDUTayt8S+3sbGR+vr6Ko9vt8bwx8r4Ly0tFfGPz/f29ornbm5uUn9//73zAaBOYh2Jf+zq6ipNTk5WntNKnz9/TicnJ3cerox/fM//6Ogo9fb2Fjf9Rfhjc3NzTdcEgLqIdSz+5XZ3d+/uuG+HiPt/rTH+senp6eJx+e5/Z2fn3vUAoE5iHY9/LN5lz8/PV57/u54a/2/fvhWP44a/WPxrQOP1AKBOYs8S/9iXL18qz/9dT41/+U5/cXGxeHx8fHzvegBQJ7GOx//09DS9efOm8txWeGr8P3z4UDyOFwGx+LZE4/UAoE5iHYt//DKd5eXl9OLFi8rzWmV7e7v4tkLp4Rrjf3FxkQYHB4u7/uOGxFj8FMDDawJAXcQ6Ev+Dg4P0+vXryuPbbW1t7fZP8e/K+K+vr6ehoaHi868bX2+fTXdfA4A6irU1/tfX12lmZqbyuE5qfAFQxr/U+HsAFhYW7j0HAHUTa1v84y76ly9fVh7zHMoXAGX8x8fH0+HhYfG12ObmZtM5AFA3sZbHP4L/7t27yueeW7wAeHjDXyxu9vuT/h8CAGiXWMvj/6cbGxsrPsaP9G1tbaXR0dGmYwCgrrKMf8k7fQBylHX8ASBH4g8AmRF/AMiM+ANAZsQfADIj/gCQGfEHgMyIPwBkRvwBIDN38TczM7N8Jv5mZmaZTfzNzMyyWko/AakVHpXdu7n/AAAAAElFTkSuQmCC "Audio Only") ## Making the audio player from the video player This section explains how to alter the default video player UI into the UI shown in the picture below, thus transforming a default THEOplayer instance in an audio player. This can, of course, be further customized to your liking to adapt to the rest of your implementation. ![Audio Only](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAArMAAAAvCAYAAADw3EIKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAggSURBVHhe7d1vqE3bGsfxkySRlCgpSiQlJSnaipRckRdESrwREfGGdpGQ8sILvBBSnEjyJ+3rb+meQuocUXLLn0S4CeeG/Cnb9X/c8RvHWGfsuZ+19tx7O2ePre9TH9YzxzPHWnOXeow15tw/OYIgCIIgCILopPGTD/0BAAAAdApp+NwuAgAAAHKUhs/tIgAAACBHafjcLgIAAABylIbP7SIAAAAgR2n43C4CAAAAcpSGz+0iAAAAIEdp+NwuAgAAAHKUhs/tIgAAACBHafjcLir632//ALJw8+bNNqmrqwMAABmyes9a0vC5XVRkNRVAR7Aa1TKsfzwAAKDjWb1nLWn43C4qspoKoCNYjWoZ1j8eAADQ8azes5Y0fG4XFVlNBdARrEa1DOsfDwAA6HhW71lLGj63iwAAAIAcpeFzuwgAAADIURo+t4sAAACAHKXhc7sIAAAAyFEaPreLAAAAgByl4XO7CAAAAMhRGj63iwAAAIAcpeFzuwgAAADIURo+t4sAAACAHKXhc7sIAAAAyFEaPreLfiR9+/Z1PXr0cCtXrnRPnz51CxcuNOsAAACQvzR8bheVNXXqVLdjx46KiRMnVsZGjRrVZGzBggVNzv079OvXz71588bNnDnTrV279ttlO/fixYvw2a1zAAAAkK80fG4XlXXu3LlvU/0R+/btq4ylzaPi+r+vNzn3rxYbWYWa2UGDBrkbN26EXPHx48dwzDq3tbp06WIel1pjUXvPTw0ZMiSsRltjRd26dTOPt/Y9i9p7PgAAQDVp+NwuKivXZjZtZBVqZuPYlClT3IcPH8Lx58+fu65duzY5tzU2bNjg3r59G+Z69uyZGz58eDiuOX/97dfQMH/58sVdv37dDR06tNn5avquXLniPn/+HOouX75c+TyLFi1yjx49CnO/fPnSzZkzp9n5llevXrn6+npzLLVq1aowd+/evSvHql1P0bVr18JnjvSeOl7regAAAL6HNHxuF5WVYzNbbGQVambnzp3rli1bFmpmz579bcS506dPN5ujjLq6Ovf161e3Z88eN3bsWPf7f393T548CWP6OaiRnTx5cthuoWZPjV1xjsOHD7tPnz65MWPGhC0amm/37t1u2LBh4bUaw8GDB7uGfzaE5tBqiFNakdV5PXv2NMdFzevFixe/Xf2fzWyt6ylqfNfozp49GxpsmTZtWjhe7XqK5wMAALRVGj63i8rKoZlVE6ZmMVITWYx0z+z69evDeSdOnAi5VmmLc5Zx5MgR9/79+0q+ZMmSMJ++ut+8eXN4vzimVVqteOr1vXv33KVLl8Jrffbp06eH17169QoNq+Zds2ZNmGvgwIFhTHMqtm/fHvJqtLKq1WZrLFq9erVrbGwM+5gVsZmtdT3z5s0LP1et1Hbv3j0c138IVJOu3la7njgOAADQXmn43C4qK4dmVo1qS5E2s2rkdJ5WTWNoJbE4b0vUoOqr+JhrNVOhldi0rn///uFrd9Urb2hocLt27WpSo9XLd+/eBdrzOnr06DDXjBkzwnic+/jx403OK9J2hpMnT5pjkZpRbQfQDXCK2MzWup5Jkya5W7duuQEDBoTGVqFV1xhbt26tnCfF60nHAAAA2iMNn9tFZXXGZlYxcuTIcG5syBYvXtxs3pZcvXrVPX78uJKraVOoSY7HtDqpJydo9VfbH+LxIn09r0ZSn2fjxo3h2OvXr8PX+UePHq1smzh27Fizc1N6H62YWmNFxWa2zPWItmpo9VdbC7RyfPfu3dC0pjXW9QAAAHwPafjcLiqrszaz48aNC+dqxVSxfPnyZvO2RNeefqU/YcKEMFefPn1Crr/jtgettMa6Wu7cuROaWL3W1/VqCNUo/uuXX0Jju2nTpmbnRFpJ1df61pil2My2dD3V6HMq4ipyKr0eAACA7yENn9tFZXXGZjbukY1foyvGjx/fbN6W6Kt03ewUc82vlUi91g1YamT1XrUa2TNnzjT5mZ06dSo0pFrR3bJlS9gSoOO6sUsxa9asSm3Rvp9/dvfv3zfHLMVmttb1pNatW1fZ8yt6frBCN61Vu56YAwAAtFcaPreLysqhmdXX3WqYorh1II20md2/f384b+/evSHX6mxbnouqZ9QqtEdVK71qXrUSqbHz58+HMd2QNX/+/ECfQWM6tmLFivD64MGD4f1145QeGaYbsB4+fBgeZ6XGUlsMdAOWtgDoBrJaj7l6+J+HrXpyQLGZrXU9I0aMcIcOHQq18T8P+nnq6Qp6fFhcfa12PfE9AQAA2isNn9tFZeXQzBbpZq7iEw3UgKmh3LlzZ6VGja8i3pjVFgcOHAhzKLSvVauTOh7nTkOfSWPaQ3v79u3wWiuwDx48CA246LgaR41t27atsg1CWwysr/EjNeN6z+LNZ7Wo2VToM8Rj1a5n6dKl4VicX816vEZdV3wGbq3rAQAA+B7S8LldVFaOzawUG9q4Kiq6Iz/eUKX9qC3tCW2JtgK09PzXlugzpE1lpCa1zNyq0xMHrLHWKns9es9qjWq16wEAAGivNHxuF5WVazMraUOrZlZNmh6LFbch6O+27JUFAABAx0nD53ZRWXqk1YULFyr0W7bimJrJdCz+soK/U2xo1cymzbX2o7blCQYAAADoWGn43C76kehpAvrFBfX19eGGJP3CAt1UZdUCAAAgb2n43C76EWmPp1hjAAAA6BzS8LldBAAAAOQoDZ/bRQAAAECO0vC5XQQAAADkKA2f20UAAABAjtLwuV0EAAAA5CgNn9tFAAAAQI7+DOf+D9Obw/667MRKAAAAAElFTkSuQmCC "Audio Only") The alteration involves 8 steps: 1. Add a new class to 2 HTML elements in the player (JavaScript) 2. Hide unnecessary buttons from the control bar (CSS) 3. Hide content that might appear above the control bar (CSS) 4. Decrease the player's height (CSS) 5. Ensure the control bar is always visible (CSS) 6. Rescale the loading spinner (CSS) 7. Change appearance for when the stream is not yet loaded (CSS) 8. (Optional) Add the audioOnly flag (JavaScript) In the following, you can find a description and a code example for each step. ## Code examples ### 1 - Add a new class to 2 HTML elements in the player To comfortably provide the required CSS during the next steps, we add CSS classes to two HTML elements, respectively: 1. 'audioplayer' to the player element (select it using the 'vjs-fluid' class) 2. 'audiocontrol' to the controls element (select it using the 'vjs-control-bar' class) ```js function transformPlayer(forAudio) { var player = document.querySelector('.vjs-fluid'); var controlbar = document.querySelector('.vjs-control-bar'); if (forAudio) { player.classList.add('audioplayer'); controlbar.classList.add('audiocontrol'); } else { player.classList.remove('audioplayer'); controlbar.classList.remove('audiocontrol'); } } ``` ### 2 - Hide unnecessary buttons from the control bar There are a bunch of buttons in the control bar that might not be relevant for an audio-only stream. Hence, we hide the following buttons using CSS: * Subtitles button * Quality label * Audio menu * Settings menu * Fullscreen control * Cast button ```css .audiocontrol .vjs-icon-subtitles, .audiocontrol .theo-quality-label, .audiocontrol .vjs-icon-audio, .audiocontrol .vjs-icon-cog, .audiocontrol .vjs-fullscreen-control, .audiocontrol .theo-cast-button, .audioplayer video { display: none !important; } ``` ### 3 - Hide content that might appear above the control bar Now we need to make sure no content appears above our controls. Again, we use CSS to hide elements: * Poster image * Video * Metadata (like song info) ```css .audiocontrol .theoplayer-poster, .audiocontrol .song-info, .audioplayer video { display: none !important; } ``` ### 4- Decrease the player's height Next up, decreasing the player height to 50px. ```css .audioplayer { padding: 0 !important; height: 50px !important; } ``` ### 5 Ensure the control bar is always visible By default, the control bar fades away to ensure better user experience for the video viewer. The following snippet ensures that the controls stay always visible. ```css .audiocontrol.vjs-control-bar { visibility: visible !important; opacity: 1 !important; display: flex !important; } ``` ### 6 - Rescale the loading spinner An optional step is to scale the loading icon that spins when the player stalls. ```css .audioplayer .vjs-loading-spinner { transform: scale(0.3) !important; } ``` ### 7 - Change appearance for when the stream is not yet loaded We're almost there. We just need to remove the giant play-button that shows when the video hasn't started yet. ```css :not(.vjs-has-started).audioplayer .vjs-big-play-button { display: none !important; } ``` Should we stop here, the audio player would show 0:00 / 0:00 as time information, since the stream is not yet loaded. This looks a bit clumsy, so we can hide it until the stream has started. ```css :not(.vjs-has-started).audioplayer .vjs-current-time, :not(.vjs-has-started).audioplayer .vjs-time-divider, :not(.vjs-has-started).audioplayer .vjs-duration { display: none !important; } .vjs-has-started .vjs-current-time, .vjs-has-started .vjs-time-divider, .vjs-has-started .vjs-duration { display: flex !important; } ``` Note: if you preload the source, hiding the time information before the player starts playing may not be needed, as the duration will be already available. ### 8 - (Optional) Add the audioOnly flag Finally, we recommend you to set the audioOnly flag to true in the player configuration. In that case, the player will use an audio element instead of a video element for media playback. This is only supported for HLS streams for now. Otherwise, the player uses a video element instead. This plays without any issue on most platforms, however we have noticed that Firefox doesn’t handle video elements with height and width 0 well. ```js const player = new THEOplayer.Player(element, { audioOnly: true, }); ``` ### Result You should end up with a player that looks like this: ![Audio Only](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAq4AAAAxCAYAAAAbUCu5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAefSURBVHhe7d1tiE1rH8fxO0kiKVFSlEhKSlI0ipQckRdESrwREfGGFAkpL7zACyHFiSQPaW6PpaOQckRpTnlIxNwJ54Q8lHE8X+f8/rdr32vW/u+918zuPnONvv/6ZO//tdbae01N/VzrWmv+9XcFAAAAoBNwmwAAAEBq3CYAAACQGrcJAAAApMZtAgAAAKlxmwAAAEBq3CYAAACQGrdZ5s9ffwKScPv27XZpaGgAAAAJ8rJnBW6zjBcggI7ghdIivF8UAADQ8bzsWYHbLOMFCKAjeKG0CO8XBQAAdDwve1bgNst4AQLoCF4oLcL7RQEAAB3Py54VuE0AAAAgNW4TAAAASI3bBAAAAFLjNgEAAIDUuE0AAAAgNW4TAAAASI3bBAAAAFLjNgEAAIDUuE0AAAAgNW4TAAAASI3b/KH07ds39OjRI6xcuTI8e/YsLFy40N0OAAAASXObhU2dOjXs3LmzZOLEiaWxUaNGtRpbsGBBq33/Cf369Qtv374NM2fODOvWrQuxXr58ad/d2wcAAABJcpuFnT9//nsU/G/t37+/NJYNiqqm35pa7fv/FkOrSsF10KBB4datW/Ze9enTJ+t5+7ZVly5d3L5UG4vq3T9ryJAhNsvsjeV169bN7bf1M/Pq3R8AAMDhNgtLNbhmQ6tKwTWOTZkyJXz8+NH6L168CF27dm21b1ts3LgxvHv3zo71/PnzMHz4cOvrmFd/vWrh+OvXr6GpqSkMHTq0bH8FvOvXr4cvX77YdteuXSt9n0WLFoXHjx/bsV+9ehXmzJlTtr/n9evXYc2aNe5Y1qpVq+zYvXv3LvUqnU/ezZs37TtH+kz1q50PAABAndxmYSkG13xoVSm4zp07Nyxbtsy2mT179veREM6cOVN2jCIaGhrCt2/fwt69e8PYsWPD73/8Hp4+fWpj+jkotE6ePNmWTCjYKcTlj3HkyJHw+fPnMGbMGFtmoePt2bMnDBs2zF4rBA4ePDg0/rvRgqAXfrM006r9evbs6Y6Lgurly5e/n/3/gmu188lred8Szp07Z2Fapk2bZv1K55PfHwAAoB3cZmEpBFcFLgXDSIExX9k1rhs2bLD9Tp48ae81+5o/ZhFHjx4NHz58KL1fsmSJHU+X37ds2WKfF8c0+6qZTL1+8OBBuHLlir3Wd58+fbq97tWrl4VTHXft2rV2rIEDB9qYjqnasWOHva9EM6aaRfbGotWrV4eWlhZbd6yKwbXa+cybN89+rpqB7d69u/UV/rVNdla20vnEcQAAgDq4zcJSCK4KpbUqG1wV2rSfZkNjaYYwf9xaFEZ1OT2+1yylSjOs2e369+9vl861vd43NjaG3bt3t9pGs5Lv3783WqM6evRoO9aMGTNsPB77xIkTrfbL05KEU6dOuWORgqcu6evmNFUMrtXOZ9KkSeHOnTthwIABFmJVmk2NtW3bttJ+kj+f7BgAAEA7uc3COmNwVY0cOdL2jeFr8eLFZcet5caNG+HJkyel9wpoKgXi2NOso55goFldLWGI/TxdYldo1PfZtGmT9d68eWOX5I8dO1Za+nD8+PGyfbP0OZoJ9cby8sG1yPmIlltoVlfLAzQjfP/+fQuo2W288wEAAKiT2yysswbXcePG2b6aCVUtX7687Li16Nyzl+UnTJhgx+rTp4+9179x6YJmUON21dy7d88Cq17rkrvCn0LhLxcuWIjdvHlz2T6RZkh1ad4b8+SDa63zqUTfUxVnh7Oy5wMAAFAnt1lYZwyucU1rvBSuGj9+fNlxa9HlcN2IFN/r+Jph1GvdHKXQqs+qFlrPnj3b6md2+vRpC5+aqd26datd1ldfN12pZs2aVdo2b//PP4eHDx+6Y558cK12Plnr168vrdEVPZ9XpRvKKp1PfA8AAFAHt1lYCsFVl6wVjqJ4+T9b2eB64MAB22/fvn32XrOu7XnuqJ4Bq9KaUs3gKqhqhlFjFy9etDHdLDV//nyj76Ax9VasWGGvDx06ZJ+vm5r0mC7dHNXc3GyPkFKI1DIB3Ryly/i6uavao6Wa/9Pcpjv488G12vmMGDEiHD582LaN/1HQz1NPOdAju+KsaqXziZ8JAABQB7dZWArBNU83WuWfLKCwpfC4a9eu0jYKuap401R7HDx40I6h0jpUzTqqH4+dLX0njWnN6927d+21ZlYfPXpkYVvUV0jU2Pbt20tLGbRMwLsUHyl46zPzN4ZVo2Cp0neIvUrns3TpUuvF4yuYx3PUecVnzFY7HwAAgDq5zcJSDK6SD69xtlN0Z3y82UnrR2ut4axFl/NrPV+1Fn2HbICMFEiLHFvb6c5/b6ytip6PPrNSKK10PgAAAHVwm4WlGlwlG14VXBXI9CiquJRA/7ZnbSsAAAA6hNssTI+RunTpUon+OlUcU3DMjsUH//+TYnhVcM0Gaa0fbc+TBAAAANBh3OYPRXf1648A6O/362YhPfxfNzx52wIAACBZbvOHpDWZ4o0BAAAgeW4TAAAASI3bBAAAAFLjNgEAAIDUuE0AAAAgNW4TAAAASI3bBAAAAFLjNgEAAIDUuE0AAAAgNW4TAAAASMv3P91PURRFURRFUUkXwZWiKIqiKIrqBBXCX2Lvh9WhXJHWAAAAAElFTkSuQmCC "Audio Only") You can further modify the audio player that you have at this point to better fit in your implementation. ## Sample application The demo below illustrates the transformation from video player to audio player. * Demo: --- # How to disable click to pause on Web When you use the default UI of THEOplayer you can pause (and resume) the video by clicking anywhere on the video (except in the control bar). You can confirm this behavior at . You might be interested in disabling this behavior because your functional requirements don't allow you to pause the video. You can disable this default UX behavior through CSS. We'll also discuss some related APIs. The following CSS snippet disables pause to click by no longer catching the pointer event. ```css .video-js .vjs-tech { pointer-events: none; } ``` The following JavaScript snippet automatically resumes a video when someone tries to pause it by leveraging the [`pause`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerEventMap#pause) event and the [`play()`](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer#play) method. ```javascript player.addEventListener('pause', () => { player.play(); }); ``` The following CSS snippet hides the play and pause button. ```css .video-js .vjs-play-control { display: none; } ``` Interested in autoplay behavior? You might find ["How to combat autoplay policies"](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-combat-autoplay-policies.md) an interesting read. --- # 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-860/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-860/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-860/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-860/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-860/theoplayer/how-to-guides/web/uplynk/preplay.md) ## [📄️Uplynk Ads](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/uplynk/ads.md) [Uplynk offers support for server-side ad-insertion (SSAI).](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/uplynk/ads.md) ## [📄️Ping](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/how-to-guides/web/uplynk/ping.md) ## [📄️Uplynk configuration](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/how-to-guides/web/uplynk/configuration.md) ## [📄️Uplynk interface](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/how-to-guides/web/uplynk/interface.md) ## [📄️What are the Uplynk challenges](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/theoplayer/how-to-guides/web/uplynk/ads.md) * [Uplynk - Ping](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/how-to-guides/web/uplynk/preplay.md) * [Uplynk - Ping](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/how-to-guides/web/uplynk/preplay.md) * [Uplynk - Ads](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/UplynkConfiguration) for more information. # Related articles * [Uplynk - Ads](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/classes/Player#play) and [pause](https://docs-preview.optiview.dolby.com/pr-860/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-860/theoplayer/v11/api-reference/web/interfaces/Presentation#requestMode) method available on the [`presentation`](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/interfaces/MediaTrack#targetQuality) property which is accessible on the [`videoTracks`](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/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-860/theoplayer/knowledge-base/cors/introduction.md) enabled. Make sure to set [the `crossOrigin` property](https://docs-preview.optiview.dolby.com/pr-860/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-860/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-860/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-860/theoplayer/v11/api-reference/web/classes/ChromelessPlayer). For OptiView Live specific API, check out [player.theolive](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TheoLiveApi). ## More information * [API references](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/TheoLiveSource) --- # Examples See the player in action! ## [▶️Basic player](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/examples/basic.md) [Code](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/examples/basic.md) --- # Basic player Code ```html
``` --- # Connectors for THEOplayer Web SDK ## [Adscript](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/adscript) [Integrate with Adscript analytics.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/adscript) ## [☁️CMCD](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/cmcd) [Integrate with Common Media Client Data (CMCD) for server-side analytics.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/cmcd) ## [Comscore](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/comscore) [Integrate with Comscore for player analytics.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/comscore) ## [![](/pr-860/img/conviva.svg)![](/pr-860/img/conviva_dark.svg)Conviva](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/conviva) [Integrate with Conviva for player analytics.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/conviva) ## [![](/pr-860/img/gemius.svg)![](/pr-860/img/gemius_dark.svg)Gemius](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/gemius) [Integrate with Gemius analytics.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/gemius) ## [Nielsen](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/nielsen) [Integrate with Nielsen for player analytics.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/nielsen) ## [![](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAy0SURBVHgB7d1PbhRJFsfxiLIxs+iW2DbuaZVvwNzASGB6iU+AOQGuHd2YwSVhYId9AsMJYNmYkewb2DdwafjjWXoBEvUvYzKqSMqmjV1ZlZnxIuL7kVpqoZaM6M5f53svMp5SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAORwt3q8fpn8pAKXQSij78Ju5S7tKmXr6m1z/0vm0tbC3eawAFEZkABwurl6Zm/tpN/3NXRv9qm4lpt/89e2TFwpAIcQFwNkP/ykH7U5veWHvWUsBmIqoABjj4f/GaP2i0+42CQJgcmICIM/DP6JbSidbV99sbCoAuYkIgMke/pPoDwCTcB4A0z/8I5QFQD5OA6DIh/+k4diw95IgAM7nLADKevhHKAuAizgJgPIf/pN0q93pXudtAPi7ygOg2od/hP4A8HeVBoCrh/+E4/Rnb/6y87ipAFQXAAIe/hPoDwBWJQEg6+EfoSxA7EoPAKkP/ylab7bb3S2CALEpNQC8ePi/oSxAfEoNgPc31zZrWt1TXhkEwXIaBAcKCFxNlUhrc0V5x9Rrurb/4dbDbW4jQuhKDQCfaWNWLs/NHh4trT1SQKAIgAsYpdY/Lj08fH/zzxUFBIYAGMugLNj+uLT2irIAISEA8rltywL6AwgFATCBYX/g0i5lAXxHAEwsKwseHr67sbaoAA8RAFMz9Zma2qUsgI8IgIIwNoSPCICCMTaETwiAUnwbG+5TFkAyAqBc1xgbQjICoALZ2PDjrQerChCEAKiMqSujn9v+wIelP24rQAACoHJ23fnMK8oCSEAAOHJybGgvTlGAAwSAY3ZseHnu533GhnCBABBhdKyYsgBVIgBEMXXGhqgSASBQNjbkWDHKRgCIZeocK0bZCADxhv0BygKUgQDwRDY2/Hjr4XOCAEUhAHxjzCq3EaEoBICXRmPDNAg82LoEqQgAr7HEBNMhAALwtT+wz9gQeREA4bjC2BB5EQDBYYkJxkcAhIslJrgQARA4lpjgPARAFFhigrMRAFFhiQlOIwAixBITZAiAiDE2BAEQPZaYxIwAQIYlJhEiAHAKY8O4EAA4w2hsyBKTsBEAOAdLTEJHAOBCLDEJFwGAsbHEJDwEAHJiiUlICABMiCUmISAAMBWWmPiNAEABWGLiKwIABWKJiW8IABTu9NiQIJCMAEBphmNDjhVLRgCgZIwNJSMAUBHGhhIRAKgUS0xkIQDgAktMhCAA4BBLTFwjACABS0wcIQAgBrcRVY8AgDCjsWEaBNcUSkUAQKhBEOxTFpSLAIBoLDEpFwEALzA2LAcBAI8M+wNHN9d2KQuKQQDAO0arRcaGxSAA4C3GhtMjAOA5lphMgwBAIFhiMgkCAEFhiUk+pQaASdSBAhxgicl4tCrZ0e/36yaZ2U1/VF0Bbhy0O73lhb1nLYVTSg+AjE3imtaPCAK4YrR+0Wl3mwTBSGUBYB0url+Zm+utpj+UY51wRLeUTrauvtnYVKg2ADK2LEiSmXWt9B0FOKFbiek3f3375IWKmJMAyLy7sbY4UzPblAVwJfaywGkAZOgPwLX0QVj/0um9jC0IRASAZcuCfn92tabVPQU4EV9ZICYAMvQH4J5utTvd6zG8DYgLgAxlAVyLoT8gNgAyBAEcO04fks1fdh43VYDEB4BFWQD3wuwPeBEAmeGx4tnn6d/y2Sdced3u9BqhlAVeBUCGsgDOab3Zbne3fA8CLwMg82FpLS0LzB2CAG74XxZ4HQAW/QG4NwiC5TQIvPv83fsAyHztD7xK/5ZtMnDCx7FhMAGQoT8A1+yxYl/GhsEFQGbYHxgcK+ZaKDjgR38g2ACw6A/ANW3U3pdu767UsiDoAMhwLRlck9ofiCIAMvQH4Ja8siCqALC4lgzu6ZZRvcb8ztPXyrHoAiBDfwCuSSgLog2ATFoWXEvLgleUBXDF5dgw+gDI0B+AW276AwTACcOyYHaF/gAcqnSJCQFwBvoDcK2q/gDLQc/wy1/PWvM7Gyumr5bTfxUtBVRssOT08kzp917wBjAG+gOolFF77Zne3YW/yi8DCIAxURagfKbVT/Tdf/7n8Z6qCAGQE0GAEhwbpbY6nU+bC3ubx6pCBMCEKAtQBKNN2uz73Kj6wc8QAFN6f+PBaq1mPzvWdQWMK63z+0Y1q3zdPwsBUADKAuRwnLb4m1LWkxMABRp8dtyf3U7/VBcV8J20zm+6qPPPQwCUgP4ATqlwrJcXAVAiriWLXfVjvbwIgJLRH4jSYKw3v/N4XQkXVAC8u7G2OKvVo6SmW9KuX+La8ji4HuvlFUQAHC3er6vLlx4ZY1ZO/rrE65npDwRKyFgvL68D4HBx9co/5n66l75uraof1tkyr2f+2h/gs2P/HScmafi6HszbAPjv0h+3Z5XdFGzq4/zzEq9npj/gN4ljvby8C4CszjcTztolXs/MtWSeETzWy8ubALCv+5fnfk5fmc2qmppuKZ1sSTmNlaE/IJ38sV5eXgTA0dLao/Pr/EnJ6w9wbblI3oz18hIdAPZ1f6amt8et8yclsSygPyBDYtRWt/tp3ec6/zwiA2Aw1rs0u22qPlOv9Wa73d2SFATDEDTblAUV83Ssl5eoACi2zp+UzLEh/YHKeD3Wy0tMAPzv5r/vJTpZV2LOzeu04WNENXxsWdDvz67W9OD7AhTL2a08LjkPAPuKW6up51roEVn6AxEIaKyXl7MAcFbnT8geK/7S+bQl6f8OlAXTCm+sl1flATDe8V2p6A8EItixXl6VBsDwP9Tac+X99/G61e50r1MW+Cf0sV5elQTAtMd3pZLaHzCJ/UZClb5VxiuRjPXyKjUAfvSZbmCO0z/ETT47lsqkZZsRV7ZJUWoAfFh68CKeV1Kh/YF4ry2PcqyXFwFQvNftTq9Bf8ChiMd6eREAJRHbHwj62nLGenmxHrwkg/XOc5d2bS2uhLBrz6++fXw9McndwNae22Ubjas7Gws8/PnwBlAJ3TKq15jfefpaCRLCteWM9aZDAFSIY8UFYqxXCALAgeGx4t5LeecHZnblTwtMq6eSxm/C3qZ8RQ/AgXQ8tS6yP5DW0IL7A3as12x3Pv+Lh784vAE4Nzg/sPzr2ycHShBR15Yz1isNbwDOmXpN1/Y/3Hq4fWhPTgphP5TRtd6CUealciT92Qf9RF23kwse/nLwBiCM0G1GVV9bbsd6TWm3NoeINwBhbH/g49LDQ0n9AVueVNUfsGO9dufTAg9/NXgDkO2g3ektyxsbzq4U3h9grOcEAeCBsM8PMNZziRLAA9mx4o+3Hji8Lfk0Ozac39lYMX21PGFZwFhPAN4AvBPEtWSv27Veg86+ewSAp6SWBeddW27HekmiG9T5chAAvhO4zeiM/gBjPaEIgCDILQu0rtW5lUcuAiAo8rYZQTamAEEx9Zma2pV2rBhyEQABGo4NZw+PltYe2UUsCvgBAiBgw8+Of96XdKwYshAAwRt8bbhtvy+gLMD3CIBomLotC+gP4CQCIDJf+wP7tj+gED0CIE5XJH52jOoRAFEb9geObq7tUhbEiQCAslub6Q/EiQDAN9lnx4e/EwKxIAAwYtSevaGYz3TjMasAlmpGiwCIm72VZ6vT+czXepEiAOLFrTwgAKLD7bs4gQCIx/BWnh1u5cEIARABu2yj2/20Tp2P7xEAIWOpJi5AAASJsR7GQwCEhbEeciEAAmG0edFpf27w4CMPAsB338Z6G3sKyIkA8NdxYpKGtF0A8AsB4CG7VJNlGygCAeATxnooGAHgBcZ6KAcBINtgrDe/s7GugBIQAEJxfBdVIACk4Ws9VIgAkIOxHipHALj39fguYz1UjwBwibEeHCMAnGCsBxkIgGoNb+V5w608kIEAqAhjPUhEAJSNsR4EIwBKY1o9lTR+e/v0tQKEIgCKx6088AYBUCyWbcArBEABjDIHSaIb1PnwDQEwncFYb56xHjxFAEyIsR5CQADklY71EjX4aOdAAZ4jAMbG8V2EhwC4GGM9BIsAOMdg2YbuNxnrIVQEwFlYtoFIlBoAxpg9rfUd5Y/h13o7jPUQB61K9v7mnys1XdtWwrFsAzEqPQAs0SHArTyIWCUBYMkLAcZ6QGUBYAkJAS7hBL6qNAAslyEwGOu1Pzd48IGhygPAqjwEuJUHOJOTALAqCgGWbQDncBYAVpkhwFgPuJjTALAKDwHGesDYnAeAVUwIMNYD8hIRANYUITAY683vPF5XAHIREwBW3hDgVh5gOqICwBorBBjrAYUQFwDWj0PAtBJjmoz1gGKIDADruxDg+C5QArEBYA1CQNXuMNYDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABALP4PxPhKst9YjwYAAAAASUVORK5CYII=)Yospace](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/yospace) [Integrate with Yospace for server-side ad insertion (SSAI).](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/web/yospace) --- # adscript-connector-web The AdScript connector provides an AdScript integration for THEOplayer. ## Installation Install using your favorite package manager for Node (such as `npm` or `yarn`): ### npm ```bash npm install @theoplayer/adscript-connector-web ``` ### yarn ```bash yarn add @theoplayer/adscript-connector-web ``` ## Usage First you need to add the AdScript connector to your app : * Add as a regular script ```html ``` * Add as an ES2015 module ```html ``` ## Updating metadata If the metadata has changed during playback, you can update it with: ```javascript adScriptConnector.updateMetadata(newMetadata); ``` ## Updating userInfo If the user info has changed during playback, you can update it with: ```javascript adScriptConnector.updateUser(i12n); ``` --- # @theoplayer/adscript-connector-web ## 0.5.0 ### ✨ Features * Added support for THEOplayer v11. ## 0.4.0 ### ✨ Features * Added support for THEOplayer version 10. ## 0.3.0 ### ✨ Features * Added support for THEOplayer `9.0`. ## 0.2.0 ### ✨ Features * Added support for THEOplayer `8.0`. ## 0.1.0 ### ✨ Features * Initial release. --- # cmcd-connector-web A connector between a THEOplayer instance and a Common Media Client Data (CMCD) server for the THEOplayer HTML5/Tizen/webOS SDK. This implementation supports CMCD data as defined in CTA-5004, published in September 2020. ## Prerequisites In order to use this connector, a [THEOplayer](https://www.npmjs.com/package/theoplayer) build with a valid license is required. You can use your existing THEOplayer HTML5 SDK license or request yours via [THEOportal](https://portal.theoplayer.com/). ## Installation Install using your favorite package manager for Node (such as `npm` or `yarn`): ### Install via npm ```bash npm install @theoplayer/cmcd-connector-web ``` ### Install via yarn ```bash yarn add @theoplayer/cmcd-connector-web ``` ## Usage First you need to add the CMCD connector to your app : * Add as a regular script ```html ``` * Add as an ES2015 module ```html ``` By default, the data is sent via query arguments, but you can configure the transmission mode before creating the CMCD connector. For example, to transmit via HTTP headers: * regular script ```html ``` * ES2015 module ```html ``` The connector will be automatically destroyed upon destruction of the provided player. When changing the player source and a content ID is being passed in, this is to be reset through `reconfigure()` as it will not be cleared automatically. ## Remarks Note that when native playback is being used, either through THEOplayer's configuration, or due to absence of MSE/EME APIs, the JSON Object transmission mode should be used. Currently, all standardized reserved keys are reported, except: * Object duration (`d`) * Next object request (`nor`) * Next range request (`nrr`) --- # @theoplayer/cmcd-connector-web ## 1.5.0 ### ✨ Features * Added support for THEOplayer v11. ## 1.4.0 ### ✨ Features * Added support for THEOplayer version 10. ## 1.3.0 ### ✨ Features * Added support for THEOplayer `9.0`. ## 1.2.0 ### ✨ Features * Disable CMCD for HESP requests ## 1.1.0 ### ✨ Features * Added support for THEOplayer `8.0`. ## 1.0.2 ### 🐛 Issues * Fix missing API types in TypeScript type definitions. * Fixed an issue where multiple CMCD query arguments were being added to the request URL. ## 1.0.1 ### 🐛 Issues * Added support for THEOplayer 7.0. ## 1.0.0 ### ✨ Features * Initial release --- # comscore-connector-web The Comscore connector provides a Comscore integration for THEOplayer. ## Installation ```sh npm install @theoplayer/comscore-connector-web ``` This connector depends on the JavaScript ComScore SDK, which should be loaded and available in the global scope before the connector. Get it from your ComScore dashboard or talk to a ComScore representative. This connector was developed and tested with versions 7.2.0.200214 and 7.6.1.210506 ```html ``` * Add as an ES2015 module: ```html ``` The Conviva connector is now ready to start a session once THEOplayer starts playing a source. Note that the `convivaMetadata` provided to the `ConvivaConnector` constructor is primarily used to pass application specific information to Conviva. Source specific metadata can be provided through the connector's `setContentInfo` method. This data does not carry over to the following sources and needs to be set after the `sourcechange` event has been received. ```js player.source = exampleSource; const onSourceChange = () => { convivaIntegration.setContentInfo(exampleSourceMetadata); } player.addEventListener('sourcechange', onSourceChange); ``` ## Usage with Yospace connector If you have a Yospace SSAI stream and want to also report ad related events to Conviva, you can use this connector in combination with the Yospace connector: [@theoplayer/yospace-connector-web](https://www.npmjs.com/package/@theoplayer/yospace-connector-web) After configuring the Yospace connector, can link it to the Conviva connector: ```javascript async function setupYospaceConnector(player) { const source = { sources: [ { src: "https://csm-e-sdk-validation.bln1.yospace.com/csm/extlive/yospace02,hlssample42.m3u8?yo.br=true&yo.av=4", ssai: { integration: "yospace" } } ] }; // Create the connectors. const yospace = new THEOplayerYospaceConnector.YospaceConnector(player); const conviva = new THEOplayerConvivaConnector.ConvivaConnector(player, convivaMetadata, convivaConfig); // Link ConvivaConnector with the YospaceConnector. conviva.connect(yospace); // Set the source. await yospace.setupYospaceSession(source); } ``` --- # @theoplayer/conviva-connector-web ## 3.3.2 ### 🐛 Issues * Report THEOads interstitial errors as failed ads. ## 3.3.1 ### 🐛 Issues * Added support for `@theoplayer/yospace-connector-web` version 3. ## 3.3.0 ### ✨ Features * Added support for THEOplayer v11. ## 3.2.0 ### ✨ Features * Changed the behaviour of `stopAndStartNewSession`, not starting a new session if the player is paused until play-out is resumed. * Added reporting of THEOads `streamActivityMonitorId`, which is passed to the GAM Pod stream request. ## 3.1.2 ### 🐛 Issues * Fixed an issue where ads were no longer reported correctly, caused by a deprecation in the adBreakBegin event. ## 3.1.1 ### 🐛 Issues * Fixed an issue that prevented the ESM build from being loaded using an [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/importmap). * The ESM build now imports the Conviva SDK using a default import (`import Conviva from '@convivainc/conviva-js-coresdk'`) rather than a named import (`import { Analytics, Constants } from '@convivainc/conviva-js-coresdk'`). ## 3.1.0 ### ✨ Features * Moved the ad start tracking to when the first frame has loaded. * Added reporting of `encoding_type` (either "DASH", "HLS" or "HESP"), as well as `Constant.defaultResource` and `intentToFallback` reason for THEOlive sources. ## 3.0.0 ### 💥 Breaking Changes * Dropped support for THEOplayer version 8.11.0 and lower. ### ✨ Features * Added support for THEOplayer version 10. ## 2.9.0 ### ✨ Features * Added play-out configuration values such as `liveOffset`, `targetBuffer`, `bufferLookbackWindow`, `abrStrategy` and `abrMetadata` as custom metadata fields. * Fixed an issue where the `streamType` value set through the connector API could be overriden with a different value by the connector. ## 2.8.0 ### ✨ Features * Added full call stack info to an error report by splitting it in multiple entries. ## 2.7.0 ### ✨ Features * Added the ability to include the player name in the metadata object passed when creating the ConvivaConnector. * Added support for THEOplayer `9.0`. ## 2.6.0 ### ✨ Features * Added a check to differentiate between Samsung, LG, Xbox and Vizio when defining the default device category in the metadata. * Added a detailed report, containing the current player buffer and last fetched segments, in case of a playback failure. ### 🐛 Issues * Added 'Server Guided' as ad type passed when reporting a THEOads ad break has started. ## 2.5.1 ### 🐛 Issues * Fixed an issue where the stream type, either `VOD` or `Live`, would sometimes be set with a wrong value for live streams. ## 2.5.0 ### ✨ Features * Added `deviceMetadata` property to `ConvivaConfiguration`. ### 🐛 Issues * Fixed an issue where an asset name, provided via a `ConvivaMetadata` object in the `ConvivaConnector` initialization, stops getting reported after a `sourcechange` event. * Fixed an issue where the content type of an ad would sometimes be reported as "Live". ## 2.4.0 ### ✨ Features * Added 'Server Guided' ad technology. ## 2.3.0 ### ✨ Features * Added reporting of slate for THEOads. * Added additional error details on playback failure. ## 2.2.0 ### ✨ Features * Added support for THEOplayer `8.0`. ## 2.1.4 ### 🐛 Issues * Fixed an issue where we could throw an error for non Google-IMA ads. ## 2.1.3 ### 🐛 Issues * Fixed an issue where the metadata is not passed correctly after a replay. ## 2.1.2 ### 🐛 Issues * Fixed an issue where the `segmentnotfound` event was reported as a fatal error. ## 2.1.1 ### 🐛 Issues * Report average bitrate to Conviva. ## 2.1.0 ### ✨ Features * Changed Conviva SDK to a peer dependency, enabling users to update it independently from the Conviva connector. ### 🐛 Issues * Fixed an issue where TypeScript could throw a TS2307 type error on the generated type definitions when the optional `@theoplayer/yospace-connector-web` peer dependency is not installed. ### 📦 Dependency Updates * @theoplayer/yospace-connector-web\@2.1.2 ## 2.0.2 ### 🐛 Issues * Fixed an issue where the THEOplayer library and the Yospace connector were accidentally bundled together with the Conviva connector. ### 📦 Dependency Updates * @theoplayer/yospace-connector-web\@2.1.1 ## 2.0.1 ### 🐛 Issues * Added functionality to listen for external ad events using the `convivaAdEventsExtension` property. ## 2.0.0 ### 📦 Dependency Updates * @theoplayer/yospace-connector-web\@2.1.0 ## 1.3.0 ### ✨ Features * Updated to be compatible with THEOplayer `6.X`. ## 1.2.0 ### ✨ Features * Added error event with addition error information on playback failed. ## 1.1.7 ### 🐛 Issues * Removed reporting a buffering state on getting an `emptied` event. ## 1.1.6 ### ✨ Features * Added ad metadata for CSAI. ### 🐛 Issues * Fixed an issue where the ad break position would be incorrectly reported. ## 1.1.5 ### 🐛 Issues * Updated yospace connector peer dependency. ## 1.1.4 ### 🐛 Issues * Fixed an issue where a session could be created without a source. ## 1.1.3 ### Changed * Made THEOplayer an external dependency. ## 1.1.2 ### 🐛 Issues * Fixed passing content length for a live stream or on early error. ## 1.1.1 ### Changed * Updated THEOplayer version to 5.X. ## 1.1.0 ### ✨ Features * Added `setContentInfo` to pass video metadata during playback. * Added `setAdInfo` to pass ad metadata during playback. * Added `reportPlaybackFailed` to notify Conviva of non-video errors. * Added `stopAndStartNewSession` to enable explicitly stopping the current session and starting a new one. * Added visibility change reporting. * Updated THEOplayer version to 4.X. * Improved error handling. * Improved default metadata. ### 🐛 Issues * Fixed handling a replay of the same source. ## 1.0.0 ### ✨ Features * Initial release --- # gemius-connector-web The Gemius connector provides a Gemius integration for THEOplayer. ## Installation ```sh npm install @theoplayer/gemius-connector-web ``` Load the gplayer.js library from Gemius. There are two options to to this: either you do it synchronously: ```html ``` ... or asynchronously ```html ``` * Add as an ES2015 module ```html ``` ## Updating program parameters If the program parameters changed during playback, you can update it with: ```javascript const newProgramParameters = { ... }; gemiusConnector.update(newProgramParameters); ``` --- # @theoplayer/gemius-connector-web ## 0.5.0 ### ✨ Features * Added support for THEOplayer v11. ## 0.4.0 ### ✨ Features * Added support for THEOplayer version 10. ## 0.3.0 ### ✨ Features * Added support for THEOplayer `9.0`. ## 0.2.0 ### ✨ Features * Added support for THEOplayer `8.0`. ## 0.1.0 ### ✨ Features * Initial release. --- # Nielsen Web Connector A connector implementing Nielsen with THEOplayer. ## Installation ```sh npm install @theoplayer/nielsen-connector-web ``` ## Usage ### Configuring the connector Create the connector by providing the following mandatory parameters: * The `THEOplayer` instance * the Nielsen App ID * the channelName for the asset and optionally the following parameters: * a `NielsenOptions` object * a `NielsenConfiguration` object (if none is provided, the default configuration disables DCR, enables DTVR and sets the country to US) ```js import { NielsenConnector } from '../../dist/THEOplayerNielsenConnector'; const appId = ''; const channelName = ''; // Non-mandatory options const options: NielsenOptions = { containerId: 'THEOplayer', optout: false }; // Non-mandatory configuration (e.g. for DCR tracking with the Czech Republic SDK) const configuration: NielsenConfiguration = { country: NielsenCountry.CZ, enableDTVR: false, enableDCR: true }; const nielsenConnector = new NielsenConnector(player, appId, channelName, options); ``` The `NielsenOptions` can have the following fields: | Key | Value | | -------------- | --------------------------------------------------------------- | | `containerId` | HTML DOM element id of the player container. | | `nol_sdkDebug` | Enables Debug Mode which allows output to be viewed in console. | | `optout` | Whether to opt-out of Nielsen Measurement. | ### Passing metadata dynamically (DTVR) The connector allows updating the current asset's metadata at any time. Note that Nielsen's [documentation](https://engineeringportal.nielsen.com/wiki/updateMetadata_\(Browser\)) prohibits updating of the values for `type`, `vidtype` or `assetid` parameters ```js const metadata = { ['channelName']: 'newChannelName', ['customTag1']: 'customValue1', ['customTag2']: 'customValue2' }; nielsenConnector.updateMetadata(metadata); ``` ### Passing metadata when setting a source to the player (DCR) This can be achieved through the `updateDCRContentMetadata` method, e.g.: ```js const metadata: NielsenDCRContentMetadataCZ = { assetid: 'cz-500358-98731568435405', program: 'Animated Test Content', title: 'Big Buck Bunny', length: '596', airdate: '20230620 20:00:00', isfullepisode: true, crossId1: '915 954 39504', c2: '651678089925925', segB: '011', adloadtype: AdLoadType.linear, hasAds: HasAds.supports_ads }; nielsenConnector.updateDCRContentMetadata(metadata); ``` Note that types are included in the package: `NielsenDCRContentMetadataUS`, `NielsenDCRContentMetadataCZ`. Please contact your THEO Technologies representative if you need support for another International DCR SDK. --- # @theoplayer/nielsen-connector-web ## 1.7.0 ### ✨ Features * Added support for THEOplayer v11. ## 1.6.0 ### ✨ Features * Added support for THEOplayer version 10. ## 1.5.0 ### ✨ Features * Added support for THEOplayer `9.0`. ## 1.4.0 ### ✨ Features * Added an optional c4 parameter to `NielsenDCRContentMetadataCZ`. ## 1.3.0 ### ✨ Features * Added support for THEOplayer `8.0`. ## 1.2.0 ### ✨ Features * Add DCR support (CZ and US). ## 1.1.2 ### 🐛 Issues * Fixed an issue where the package did not properly export the Nielsen types. ## 1.1.1 ### 🐛 Issues * Added support for DASH emsg cue payloads. ## 1.1.0 ### ✨ Features * Updated to be compatible with THEOplayer `6.X`. ## 1.0.0 ### ✨ Features * Initial release --- # yospace-connector-web The Yospace connector provides a Yospace integration for THEOplayer. ## Prerequisites In order to use this connector, a [THEOplayer](https://www.npmjs.com/package/theoplayer) build with a valid license is required. You can use your existing THEOplayer HTML5 SDK license or request yours via [THEOportal](https://portal.theoplayer.com/). For setting up a valid Yospace session, the Yospace Ad Management SDK is required. For more information on how to install the Ad Management SDK, please refer to the documentation of [Yospace](https://docs.yospace.com/library/technical/sdks/en/ad-management-sdks-v3/javascript.html). **Remark:** This version of the Yospace Connector is compatible with Yospace Ad Management SDK version 3.10.0 or higher. If you still want to use an older Ad Management SDK, please use the connector version 2.8.0 or earlier. ## Installation Install using your favorite package manager for Node (such as `npm` or `yarn`): ### Install via npm ```bash npm install @theoplayer/yospace-connector-web ``` ### Install via yarn ```bash yarn add @theoplayer/yospace-connector-web ``` ## Usage First you need to add the Yospace connector to your app : * Add as a regular script ```html ``` * Add as an ES2015 module ```html ``` To make use of the Yospace integration, you need to set up a session for your Yospace source : ```javascript const source = { sources: [ { src: "YOUR_YOSPACE_SRC", ssai: { integration: "yospace" // If necessary, you can define your streamType. // streamType: 'vod' | 'live' | 'livepause' } } ] } await yospaceConnector.setupYospaceSession(source); ``` If you want to customize your session, you can also pass your customized `SessionProperties` from the Yospace Ad Management SDK : ```javascript // create a new SessionProperties object using the Ad Management SDK. const sessionProperties = new YospaceAdManagement.SessionProperties(); const source = { sources: [ { src: "YOUR_YOSPACE_SRC", ssai: { integration: "yospace" // If necessary, you can define your streamType. // streamType: 'vod' | 'live' | 'livepause' } } ] } await yospaceConnector.setupYospaceSession(source, sessionProperties); ``` Once the setup of the Yospace session is done, you can continue to use the player and the connector will handle everything related to Yospace. --- # @theoplayer/yospace-connector-web ## 3.0.0 ### 💥 Breaking Changes * Removed `isPlaceholder()` and `isEncoded()` type definitions since they were removed in Yospace Ad Management SDK 3.11.0. * Replaced `PlaybackMode` with `SessionMode` for compatibility with Yospace Ad Management SDK 3.10.0+. This change brings the minimum supported Yospace Ad Management SDK version to 3.10.0. ### 🐛 Issues * Fixed an exception when an ad break starts without data during live playback. ## 2.8.0 ### ✨ Features * Added support for THEOplayer v11. ## 2.7.0 ### ✨ Features * Added support for THEOplayer version 10. ## 2.6.0 ### ✨ Features * Added support for THEOplayer `9.0`. ## 2.5.0 ### ✨ Features * Create ad break from advert start, if missing ### 🐛 Issues * Require THEOplayer 8.1.0 or higher for correct TypeScript type definitions. ## 2.4.0 ### ✨ Features * Added support for THEOplayer `8.0`. ## 2.3.0 ### ✨ Features * Added `YospaceServerSideAdInsertionConfiguration` type definition to the connector, superseding the type defined by the THEOplayer Web SDK. ## 2.2.0 ### ✨ Features * The connector now integrates with the [custom server-side ad integration API](https://www.theoplayer.com/docs/theoplayer/v7/api-reference/web/interfaces/Ads.html#registerServerSideIntegration.registerServerSideIntegration-1) introduced in THEOplayer 7.4.0. This allows Yospace adverts to show up through the `player.ads` API of THEOplayer. ## 2.1.3 ### 🐛 Issues * Fixed playback position reporting for live DVR streams (with `streamType` set to `"livepause"`). ## 2.1.2 ### 🐛 Issues * Fix missing API types in TypeScript type definitions. ## 2.1.1 ### 🐛 Issues * Added support for THEOplayer 7.0. ## 2.1.0 ### ✨ Features * Exposed SessionErrorCode. ## 2.0.0 ### ✨ Features * Upgrade to latest Yospace Ad Management SDK ## 1.4.0 ### ✨ Features * Allow THEOplayer 6.0.0 as peer dependency ## 1.3.0 ### ✨ Features * Update THEOplayer peer dependency ## 1.2.0 ### ✨ Features * Expose typings ### 🐛 Issues * Handle empty `activeCues` list ## 1.1.0 ### ✨ Features * Add support for custom Analytics Event Observers ## 1.0.0 ### ✨ Features * Initial release --- # OptiView Player Android 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-860/theoplayer/getting-started/sdks/android/getting-started.md) [Set up your first THEOplayer in just a few minutes!](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/android/getting-started.md) ## [📖How-to guides](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides.md) [Learn how to implement our rich set of features and integrations.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides.md) ## [🔌Connectors](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/android.md) [Integrate third-party solutions with THEOplayer using our pre-built connectors.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/android.md) ## [📰Changelog](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/changelog.md) [Find out what's new in the OptiView Player.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/changelog.md) ## [\*️⃣API references](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/android) [Discover all properties and functions of THEOplayer.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/android) ## [🧠Knowledge base](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/knowledge-base.md) [Learn about topics in the video industry from our experts.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/knowledge-base.md) ## [❔FAQ](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq.md) [Answers to common questions we've received from our customers over the years.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq.md) --- # Getting started on Android > **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. ## Add THEOplayer and Kotlin libraries to your application We have to modify the `build.gradle` files to be able to fetch the Android SDK and the Kotlin plugin. 1. In your **top-level** (project) `settings.gradle.kts` file 2. Add the THEOplayer Maven repository. **Groovy** ```groovy dependencyResolutionManagement { repositories { google() mavenCentral() maven { url "https://maven.theoplayer.com/releases" } } } ``` **Kotlin** ```kotlin dependencyResolutionManagement { repositories { google() mavenCentral() maven { url = uri("https://maven.theoplayer.com/releases") } } } ``` 1. Next, we need to edit the `build.gradle` on the **module-level** (**app/build.gradle**) **Groovy** ```groovy implementation "com.theoplayer.theoplayer-sdk-android:core:+" implementation "org.jetbrains.kotlin:kotlin-stdlib:1.6.21" implementation "androidx.appcompat:appcompat:1.7.0" ``` **Kotlin** ```kotlin implementation("com.theoplayer.theoplayer-sdk-android:core:+") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.21") implementation("androidx.appcompat:appcompat:1.7.0") ``` *Note*: It's possible to replace the `+` with a particular version of THEOplayer that should be used, for example: `implementation 'com.theoplayer.theoplayer-sdk-android:core:8.0.0'` ## Setting up the license You need to configure your THEOplayer license, if you don't enter this license string, you might encounter licensing issues (e.g.: when trying to play non-whitelisted video streams). First, obtain a THEOplayer Android SDK license through the [THEOplayer Developer Portal](https://portal.theoplayer.com/login). ![](https://cdn.theoplayer.com/images/git/theoplayer-android-sdk-license-string.png) Then, identify your license string from the screenshot above and add the ` ``` Additionally, to play online videos with THEOplayer, you first need to add [INTERNET](https://developer.android.com/reference/android/Manifest.permission.html#INTERNET) permission into the `AndroidManifest.xml` file too. ```xml ``` More information regarding the license can be found [here](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/android/license/introduction.md). ## Add THEOplayer view to your application THEOplayer can be added to an Android application either: 1. **Through a layout (XML)**: By adding the `THEOplayerView` to your layout XML file (e.g., `activity_main.xml`), or 2. **Programmatically using the Constructor API**: This approach also allows you to configure the license string through a programmatic configuration instead of the `AndroidManifest.xml`. ### Adding THEOplayerView via XML layout You can add the `THEOplayerView` to the layout of the `MainActivity` with the snippet below. ![](/pr-860/assets/images/android-getting-started-13_2-2581c1dcb239371b38441eff6e36e67d.png) At least the `id`, `width` and `height` attributes need to be specified on the new view. The `id` is important as it is used to identify the view object in your `MainActivity` class. ```xml ``` ![](/pr-860/assets/images/android-getting-started-14_2-d3679a00ce0fd4aa120793ddf8a96df3.png) ### Adding THEOplayerView programmatically (Constructor API) In the Activity/Fragment, you can use the [constructor API](https://docs.theoplayer.com/api-reference/android/index.html?com/theoplayer/android/api/THEOplayerView.html) to create a THEOplayer instance by passing along a context and a [`THEOplayerConfig`](https://docs.theoplayer.com/api-reference/android/index.html?com/theoplayer/android/api/THEOplayerConfig.html). In this `THEOplayerConfig`, you can configure your license string, as demonstrated in the snippet below. **Kotlin** ```kotlin val playerConfig = THEOplayerConfig.Builder() .license("your_license_here") .build() val theoPlayerView = THEOplayerView(this, playerConfig) ``` **Java** ```java THEOplayerConfig playerConfig = new THEOplayerConfig.Builder() .license("your_license_here") .build(); THEOplayerView theoPlayerView = new THEOplayerView(this, playerConfig); ``` This approach is ideal when you need to adjust the configuration dynamically, such as during runtime or based on specific app conditions. It removes the need for XML-based configuration and is useful for advanced customization scenarios. ## Use THEOplayer API in your application Now in the `MainActivity` we can retrieve the `THEOplayerView` by the `id` we set earlier using the `findViewById` method. ### 1. Get the THEOplayer instance **Kotlin** ```kotlin ... import com.theoplayer.android.api.THEOplayerView class MainActivity : ComponentActivity() { lateinit var theoPlayerView: THEOplayerView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) theoPlayerView = findViewById(R.id.theoplayer) } } ``` **Java** ```java import com.theoplayer.android.api.THEOplayerView; public class MainActivity extends ComponentActivity { private THEOplayerView theoPlayerView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); theoPlayerView = findViewById(R.id.theoplayer); } } ``` ### 2. Configure source To play a video in THEOplayer, we need to configure a source for the player in our `MainActivity`: **Kotlin** ```kotlin val typedSource = TypedSource .Builder("https://cdn.theoplayer.com/video/dash/big_buck_bunny/BigBuckBunny_10s_simple_2014_05_09.mpd") .type(SourceType.DASH) .build() val sourceDescription = SourceDescription .Builder(typedSource) .build() theoPlayerView.player.source = sourceDescription ``` **Java** ```java TypedSource typedSource = new TypedSource .Builder("https://cdn.theoplayer.com/video/dash/big_buck_bunny/BigBuckBunny_10s_simple_2014_05_09.mpd") .type(SourceType.DASH) .build(); SourceDescription sourceDescription = new SourceDescription .Builder(typedSource) .build(); theoPlayerView.getPlayer().setSource(sourceDescription); ``` ### 3. Control THEOplayer through the API We will now introduce a button to start/stop the video in the player. First add the button to our `activity_main.xml` layout file. ```xml ``` Next step would be adding callback on buttons press. To do so, we will use [roku native observe mechanism](https://developer.roku.com/en-gb/docs/developer-program/core-concepts/handling-application-events.md#handling-node-field-value-changes). ```brightscript sub Init() m.player = m.top.findNode("TestPlayer") m.player.configuration = { "license": "" } m.buttonPlay = m.top.findNode("ButtonPlay") m.buttonPause = m.top.findNode("ButtonPause") m.buttonStop = m.top.findNode("ButtonStop") setupPlayerPosition() setupControlsPosition() setSource() m.buttonPlay.setFocus(true) m.buttonPlay.observeField("buttonSelected", "OnEventPlay") m.buttonPause.observeField("buttonSelected", "OnEventPause") m.buttonStop.observeField("buttonSelected", "OnEventStop") end sub sub OnEventPlay() end sub sub OnEventPause() end sub sub OnEventStop() end sub ``` The next step would be calling the THEOplayer api methods inside our callback. To do so, we will modify the callbacks: ```brightscript sub OnEventPlay() if m.player.source = Invalid setSource() end if m.player.callFunc("play") end sub sub OnEventPause() m.player.callFunc("pause") end sub sub OnEventStop() m.player.source = Invalid end sub ``` Last thing left to do is to make the timeline work. We will add an event listener to an event "timeUpdate". This listener will allow us to react to every time update, so we can draw the current progress in playing video. ```xml ``` ## Control bar Switch audio or text tracks Now, let’s add an audio or text track management menu. In this portion, we will use a fresh SceneGraph component (RadioButtonList). We will use other callbacks, API methods and attributes. The main steps which we have to take to accomplish the desired outcome are: Create an empty RadioButtonList component: ```xml ``` Add the content to our list. To do so, we will observe audio tracks and assign the audio tracks list to a previously created radio button list. ```brightscript sub onEventAudioTracksChanged() list = createObject("roSGNode","ContentNode") option = list.createChild("ContentNode") option.title = "default" option.description = "default" option.id = "" checkedItem = 0 index = 1 for each track in m.player.audioTracks option = list.createChild("ContentNode") option.title = track.label option.description = track.language option.id = track.id if track.enabled then checkedItem = index end if index +=1 end for optionsCount = 0 if Type(m.buttonGroupCategorySecond.content) <> "roInvalid" then optionsCount = m.buttonGroupCategorySecond.content.count() end if if list.count() <> optionsCount then m.buttonGroupCategorySecond.content = list m.buttonGroupCategorySecond.checkedItem = checkedItem setAudioMenuPosition() end if end sub ``` The next step would be managing a focus of the remote. ```brightscript function OnKeyEvent(key, press) as Boolean handled = false showOptions() if press if key = "options" else if m.player.visible = true and key = "back" ' Settings Menu opened if m.settingsOptions.visible = true hideSettings() handled = true ' category 1 Menu opened else if m.categoryFirstOptions.visible = true then hideCategoryFirst() handled = true ' category 2 Menu opened else if m.categorySecondOptions.visible = true then hideCategorySecond() handled = true else m.player.source = Invalid end if else if key = "up" if m.settingsOptions.visible = true and m.buttonCategoryFirst.hasFocus() = true hideSettings() handled = true ' category 1 Menu opened ' else if m.categoryFirstOptions.visible = true and m.buttonGroupCategoryFirst.hasFocus() = true then ' hideCategoryFirst() ' handled = true ' category 2 Menu opened else if m.categorySecondOptions.visible = true and m.categorySecondButtonFirst.hasFocus() = true then hideCategorySecond() handled = true end if end if end if return handled end function ``` In order to allow the user to change audio tracks, we have to prepare a function which will change audio track. ```brightscript sub setAudioTrack(label as String) audioTracks = m.player.audioTracks for i = audioTracks.count() - 1 to 0 step -1 if audioTracks[i].label = label then audioTracks[i].enabled = true else audioTracks[i].enabled = false end if end for 'required because roku deep-copied roAssociativeArray through fields (pass-by-value) 'read more : m.player.audioTracks = audioTracks end sub sub OnEventCategorySecondSelectedItem() if m.player.instance <> Invalid itemIndex = m.buttonGroupCategorySecond.checkedItem item = m.buttonGroupCategorySecond.content.getChild(itemIndex) setAudioTrack(item.title) end if end sub ``` To complete this functionality, we will add an observer to the radio button list and call the setAudioTrack function. ```brightscript sub OnEventCategoryFirstSelectedItem() if m.player.instance <> Invalid itemIndex = m.buttonGroupCategoryFirst.checkedItem item = m.buttonGroupCategoryFirst.content.getChild(itemIndex) setCaptionsLanguage(item.description) end if end sub ``` You can find the whole example below. This example contains two menus which allow users to manipulate audio and text tracks. ```xml ``` ## Sample code The GitHub project at provides a basic implementation of a chromeless UI on the Roku SDK. ## UX enhancements You can make your user-experience more appealing through various enhancements. For example, when the player is out of video data and is waiting for additional content you could show a 'loading' indication. Below are some common UX enhancements to consider: 1. Loading spinner: provide an indication when no video data is available. 2. Poster: show a poster (thumbnail) before initial play-request, and when the video is complete. 3. Auto next: when approaching the end, render a clickable overlay that allows the viewer to navigate to the next stream. Automatically play this stream when the current stream ends. 4. Skip intro: render a clickable button to skip the (ongoing) intro. 5. Ad countdown: overlay the remaining time of the ongoing ad break. 6. Ad markers: indicate the position of ad breaks in the scrub bar. ## Error handling A UI should also be capable of handling errors and informing the viewer. Refer to our [introduction on errors](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/miscellaneous/error/introduction.md) to further explore this topic. --- # How to remove an element from the UI on Roku You may want to limit the options and controls that are shown in the player's UI. There are two general approaches: 1. Build a chromeless UI, and add only the controls you need. This is useful when you don't want to remove just one element, but want a radically different UI. 2. Hide or remove the individual element from the existing UI. This guide focuses on the second approach. ## Implementation By default, THEOplayer on Roku uses the Roku native player and its controls. To remove an element from these controls, you can remove that element manually. For example, the default controls show a clock UI element while paused. To remove it, follow the code below: ```xml ``` ## Removing all default controls Instead of removing individual elements, you can turn off the default UI altogether by setting the player's `controls` field to `false`: ```xml ``` You can also do this from BrightScript: ```brightscript sub Init() m.player = m.top.findNode("TestPlayer") m.player.controls = false end sub ``` From there on, you are responsible for the entire UI. See [How to build a chromeless UI](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/roku/ui/build-chromeless-ui.md) for a walkthrough. --- # License on Roku 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 Pass the license to the `configure` function of the player, either as a license string or as a URL from which the license can be loaded: ```brightscript ' passing the license as a string m.THEOplayer.callFunc("configure", { license: "your_license_here" }) ``` ```brightscript ' passing the license as a URL m.THEOplayer.callFunc("configure", { licenseUrl: "your_licenseUrl_here" }) ``` 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-860/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-860/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. --- # Getting started ## Usage 1. Follow [our Getting Started guide](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/roku/getting-started.md) to set up THEOplayer in your Roku app. 2. Add an OptiView Live source to your player's source. ### Add an OptiView Live source After setting up your THEOplayer on your App, set its source to a `SourceDescription` containing a `THEOliveSource`. You'll need an OptiView Live channel ID: ```brightscript m.THEOsdk.source = { "live": true, "sources": { "src": "your-channel-id", "type": "theolive" } } ``` Please note that the playlist feature cannot be used with OptiView Live. On Roku the OptiView Live source falls back to the HLS stream. ### Configure OptiView Live On the player configuration object, you may add extra configuration using the `theoLive` property on the player configuration. For instance, if you would like to provide an external ID to track the session, you may add it to the `theoLive` configuration: ```brightscript playerConfiguration = { license: "", theoLive: { externalSessionId: "my-unique-id" } } m.player.callFunc("configure", playerConfiguration) ``` ## More information * [API references](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/api-reference/roku.md) * [Migrating to OptiView Live v2](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/roku/theolive/migrating-to-v2.md) --- # Token based security OptiView Live offers the option to enable JWT token security on channel distribution 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-860/theolive/distribution/security/token-based-security.md). This page will demonstrate how to configure the Roku Player SDK for playback of channels with token based security enabled. ## Setting up the Roku THEOplayer SDK for OptiView Live Refer to the [getting started guide](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/roku/theolive/getting-started.md) for the prerequisite steps in getting the Roku SDK up and running for OptiView Live playback. ## Configuring THEOplayer to pass the token The OptiView Live API provides a simple property to configure your token: ```brightscript 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 your token in the authorization header on all subsequent requests it performs for playback of your OptiView Live channel. ## 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 simply be done by updating the header on the player in the same way. 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: Add a Timer to your SceneGraph component to check the token at an interval: ```brightscript ... ... ``` And in your BrightScript code: ```brightscript sub init() ... m.token = "" m.player = getPlayer() maybeUpdateToken() m.tokenTimer = m.top.findNode("tokenTimer") m.tokenTimer.observeField("fire", "maybeUpdateToken") m.tokenTimer.control = "start" end sub sub maybeUpdateToken() if m.token = "" or tokenWillExpireSoon(m.token) then m.token = getToken() ' Generate or request your token, for more information check the token based security guide linked above. m.player.theolive.authToken = m.token end if end sub function tokenWillExpireSoon(token as String) as Boolean parts = token.split(".") if parts.count() < 2 then return true payloadBase64 = parts[1] ba = CreateObject("roByteArray") ba.FromBase64String(payloadBase64) decodedJson = ParseJson(ba.ToAsciiString()) if decodedJson = invalid then return true exp = decodedJson.exp if exp = invalid then return true now = Int(CreateObject("roDateTime").AsSeconds()) return exp - now <= 60 end function ``` ## Clearing the token If the token isn't needed anymore, e.g. when switching to an unprotected channel or a non-OptiView Live source altogether, the header can be simply removed as follows: ```brightscript player.theolive.authToken = "" ``` --- # Migrating to OptiView Live v2 If you are using OptiView Live v2 for your live streams, you will need to do some extra configuration to successfully play back your OptiView Live streams on Roku. ## Initial Setup 1. Follow [our Getting Started guide](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/getting-started/sdks/roku/getting-started.md) to set up THEOplayer in your Roku app. 2. Add an OptiView Live source to your player's source. ### Configure OptiView Live for v2 OptiView Live v2 sources do not use the default discovery URL (). In order to use v2, you will need to specify one or more discovery URLs. On the player configuration object, you may add the discovery URLs using the `theoLive` property on the player configuration. ```brightscript playerConfiguration = { license: "", theoLive: { discoveryUrls: ["https://discovery.theo.live/v2/distributions/"] } } m.player.callFunc("configure", playerConfiguration) ``` --- # Connectors for THEOplayer Roku SDK ## [![](/pr-860/img/conviva.svg)![](/pr-860/img/conviva_dark.svg)Conviva](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/roku/conviva) [Integrate with Conviva analytics.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/roku/conviva) ## [Comscore](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/roku/comscore) [Integrate with Comscore analytics.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/roku/comscore) ## [Adobe Edge](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/roku/adobe-edge) [Integrate with Adobe Edge analytics.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/roku/adobe-edge) ## [![](/pr-860/assets/images/mux-1d56b7f17d515324f1547519db52c2ba.png)Mux](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/roku/mux) [Integrate with Mux analytics.](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/connectors/roku/mux) --- # Getting started with the Conviva Connector for the Roku SDK Here's how to get started integrating the Conviva Connector with the THEOplayer Roku SDK. ## Prerequisites In order to set up the Conviva Connector in your Roku application, you'll need the following: * Your Conviva customer key (available in your Conviva Pulse dashboard) * An app with the THEOplayer SDK for Roku already integrated, see our [Getting Started guide](https://www.theoplayer.com/docs/theoplayer/getting-started/sdks/roku/getting-started/). * Optionally for debug and testing, your Conviva Touchstone gateway URL, which should be in the format of `"https://MY_TOUCHSTONE_DOMAIN.ts-testonly.conviva.com/"` ## Integration 1. First you must download the THEO Conviva Connector as a component library. Add a ComponentLibrary node to your MainScene.brs file, giving it an id of `THEOConvivaConnector` and providing the URI for the THEOConvivaConnector.pkg. Replace the `{SDK_VERSION}` tag with the version of the THEO Roku SDK you're using. The earliest version of the connector is 1.5.0: ```xml ``` 2. Then in the BrightScript file for your MainScene, listen for the loading of the ComponentLibrary to complete by observing the `loadStatus` field. ```brightscript sub Init() THEOConvivaNode = m.top.findNode("THEOConvivaConnector") THEOConvivaNode.observeField("loadStatus", "onLibraryLoadStatusChanged") end sub sub onLibraryLoadStatusChanged(event as object) THEOConvivaNode = event.getROSGNode() if THEOConvivaNode = invalid return end if if THEOConvivaNode.loadStatus = "ready" ' Success else if THEOConvivaNode.loadStatus = "failed" ? "Failed to load component library, please check URL. "; THEOConvivaNode.uri end if end sub ``` 3. Add the THEOConvivaConnector component to the SceneGraph file where your THEOplayer is defined ```xml ``` 4. Then in the BrightScript file, configure the connector by calling the configure method, passing the player instance and your Conviva customer key. ```brightscript m.player = m.top.findNode("THEOPlayer") m.convivaConnector = m.top.findNode("THEOConvivaConnector") m.convivaConnector.callFunc("configure", m.player, "MY_CUSTOMER_KEY") ``` NOTE: that if you want to debug first and use Conviva's Touchstone service to validate your integration, you can include the gateway URL and a debug parameter in the configure call: `m.convivaConnector.callFunc("configure", m.player, "MY_CUSTOMER_KEY", "MY_TOUCHSTONE_GATEWAY_URL", true)` 5. Next, when you start playing the asset, call the `startSession` method and pass it the content metadata for the asset you're playing: ```brightscript m.player.source = sourceDescription contentMetadata = { defaultReportingResource: "MyCDN", playerName: "My Player", assetName: "My Asset Name", encodedFramerate: 24 } m.convivaConnector.callFunc("startSession", contentMetadata) ``` See the [API documentation](https://www.theoplayer.com/docs/theoplayer/connectors/roku/conviva/API-reference/) for more on how to structure the content metadata for Conviva. 6. If you desire to monitor for CDN changes, you can optionally add a configuration for that after starting the session. `m.convivaConnector.callFunc("monitorCdnChanges", { mycdn: ["my-cdn.net"], theo: ["cdn.theoplayer.com"] })` 7. If the content metadata needs to change, you can update it by calling `setContentInfo`. This method accepts partial content metadata: `m.convivaConnector.callFunc( "setContentInfo", {assetName: "New Program"})` 8. When the video has stopped playing because it ended or the user exited, end the Conviva session `m.convivaConnector.callFunc("endSession")` 9. If you are exiting the player screen altogether, and destroying the player, make sure to destroy the connector at the same time, but before calling destroy on the SDK: ```brightscript m.convivaConnector.callFunc("destroy") m.convivaConnector = invalid m.player.callFunc("destroy") m.player = invalid ``` --- # Conviva Connector API reference The attributes, methods and events for the THEOConvivaConnector. ## Attributes | Name | Type | Default | Access Permission | Description | | ---- | ------ | ------- | ----------------- | ------------------- | | id | string | | read, write | The ID of this node | ## Methods | Method | Params | Description | | ---------------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | configure | player: THEOplayer, customerKey: string, gatewayUrl: string (Optional), debug: boolean (Optional) | Configure the Conviva SDK for use. The `gatewayUrl` param is the URL for the Touchstone service for use in validating your Conviva integration. Omit this for production builds. | | destroy | none | Destroy the connector. It also ends the current session, if any. | | endSession | none | End the current Conviva session, but do not destroy the connector. | | getContentInfo | none | Returns the content metadata for the current session. | | monitorCdnChanges | mappings: CDNMonitoringConfig, defaultCdn: string (optional) | Updates the CDN monitoring settings. If passed a CDN monitoring config (see below), it will begin monitoring the downloaded segments for changes in CDN. This will update the defaultReportingResource for the current session if a CDN change is detected If passed invalid as the first parameter, it will stop the monitoring and use any string passed as the second param as the defaultReportingResource. | | reportPlaybackEvent | eventType: string, eventDetail: AssociativeArray | Reports a playback event with the associated data to Conviva. | | reportPlaybackFailed | errorMessage: string | Reports a playback failure to Conviva. | | setAdInfo | adInfo: AssociativeArray | Sets the supplied ad info to Conviva. adInfo should be in the form of ConvivaContentInfo | | setContentInfo | contentMetadata: AssociativeArray | Sets or updates the content metadata for the current session. Partials are supported and will be merged with the existing content metadata. See below for the schema of content metadata. | | startSession | contentMetadata: AssociativeArray | Starts a Conviva session with the supplied content metadata. See below for the schema of content metadata. | | stopAndStartNewSession | contentMetadata: AssociativeArray | Stops the existing Conviva session and starts a new one with the supplied content metadata. See below for the schema of content metadata. | ### DPI Methods The THEO Conviva Connector now exposes methods for Conviva DPI (Digital Product Insights) integration that the application can use to report application level analytics. If you are using Conviva for both DPI and Video, call `configureDPI` first for application analytics, then call `configure` when you have access to the player. In depth documentation for the parameters passed to these methods is provided in the (Conviva DPI Integration guide)\[]: | Method | Params | Description | | ------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | configureDPI | appId: string, customerKey: string, gatewayUrl: string (Optional), debug: boolean (Optional) | Configure the Conviva SDK for use for Conviva DPI. The `gatewayUrl` param is the URL for the Touchstone service for use in validating your Conviva integration. Omit this for production builds. | | setUserId | userId: string | Set the user ID for this DPI session. | | setChannelInfo | channelInfo: assocarray | Set the channel load times for this DPI session. | | trackCustomEvent | eventName: string, eventData: assocarray | Report a custom DPI event to Conviva. | | trackClick | clickDetails: assocarray | Report a click event. | | trackAppError | error: assocarray | Report an application error. | | trackNetworkRequest | requestDetails: assocarray | Report the stats for a network request. | | setCustomTags | customTags: assocarray | Add custom tags to this DPI session. | | unsetCustomTags | customTags: array of strings | Remove custom tags from this DPI session. | ### CDN Monitoring Config The configuration for CDN monitoring should be an AssociativeArray with keys that are the name of the CDN and values that are arrays of strings to search for in segment URLs. ```brightscript cdnMappings = { akamai: ["akamaized.net"], theo: ["dev.theoads.live", "cdn.theoplayer.com"] level3: ["llnw.com"] } ``` Passing a config as the only param will start CDN change monitoring with the provided configuration, or update the config if it is already started. `m.theoConvivaConnector.callFunc("monitorCDNChanges", cdnMappings)` However, if you want to stop CDN change monitoring, pass `invalid` as the first parameter, and the string you want to use for the defaultReportingResource as the second parameter. `m.theoConvivaConnector.callFunc("monitorCDNChanges", invalid, "theo")` ### Content Metadata Content metadata should be an AssociativeArray following [Conviva's schema](https://pulse.conviva.com/learning-center/content/sensor_developer_center/sensor_integration/roku/roku_stream_sensor.htm#PredefinedVideoandContentMetadata). However, the THEOConviaConnector does provide a shortcut for c3 data. You may specify the c3 data by its full name: ```brightscript contentMetadata = { customMetadata: { myCustomTag: "Value", "c3.cm.contentType": "DVR", "c3.cm.name": "CMS" } } ``` However, we also support using an object notation: ```brightscript contentMetadata = { customMetadata: { myCustomTag: "Value", c3: { cm: { contentType: "DVR", name: "CMS" } } } } ``` --- # Getting started with the Comscore Connector for the Roku SDK Here's how to get started integrating the Comscore Connector with the THEOplayer Roku SDK. ## Prerequisites In order to set up the Comscore Connector in your Roku application, you'll need the following: * Your Comscore publisher ID and publisher secret (available in your Comscore Direct dashboard under Mobile Apps > Get Tag). * An app with the THEOplayer SDK for Roku already integrated, see our [Getting Started guide](https://www.theoplayer.com/docs/theoplayer/getting-started/sdks/roku/getting-started/). ## Integration 1. First you must download the THEO Comscore Connector as a component library. Add a ComponentLibrary node to your MainScene.brs file, giving it an id of `THEOComscoreConnector` and providing the URI for the THEOComscoreConnector.pkg. Replace the `{SDK_VERSION}` tag with the version of the THEO Roku SDK you're using. The earliest version of the connector is 1.5.0: ```xml ``` 2. Then in the BrightScript file for your MainScene, listen for the loading of the ComponentLibrary to complete by observing the `loadStatus` field. ```brightscript sub Init() THEOComscoreNode = m.top.findNode("THEOComscoreConnector") THEOComscoreNode.observeField("loadStatus", "onLibraryLoadStatusChanged") end sub sub onLibraryLoadStatusChanged(event as object) THEOComscoreNode = event.getROSGNode() if THEOComscoreNode = invalid return end if if THEOComscoreNode.loadStatus = "ready" ' Success else if THEOComscoreNode.loadStatus = "failed" ? "Failed to load component library, please check URL. "; THEOComscoreNode.uri end if end sub ``` 3. Add the THEOComscoreConnector component to the SceneGraph file where your THEOplayer is defined ```xml ``` 4. Then configure the connector by calling the configure method, passing the player instance and your Comscore customer key. ```brightscript m.player = m.top.findNode("THEOplayer") m.comscoreConnector = m.top.findNode("THEOComscoreConnector") m.comscoreConnector.callFunc("configure", m.player, { publisherId: "MY_PUBLISHER_ID", publisherSecret: "MY_PUBLISHER_SECRET", applicationName: "MY_APPLICATION_NAME"}) ``` 5. Next, when you start playing the asset, call the `startSession` method and pass it the content metadata for the asset you're playing: ```brightscript m.player.source = sourceDescription contentMetadata = { adLoadFlag: false, assetId: "myAssetId", c3: "Arbitrary C3 value", c4: "*null", c6: "*null", clipLength: 600, completeEpisodeFlag: true, contentGenre: "Sports", digitalAirDate: "2025-03-04", episodeNumber: "59", episodeSeasonNumber: "5", episodeTitle: "The Game Last Night", programTitle: "The Sports Show", publisherBrandName: "Sports Publisher Network", stationTitle: "KXYZ", tvAirDate: "2025-03-04" } m.comscoreConnector.callFunc("startSession", contentMetadata) ``` See the [API documentation](https://www.theoplayer.com/docs/theoplayer/connectors/roku/comscore/API-reference/) for more on how to structure the content metadata for Comscore. 6. Comscore requests a call to their `tick` method when the main event loop executes. If you update a global field called `tccTick`, the THEOComscoreConnector will automatically observe that field and call `tick` for you. Otherwise, you can manually call the `sendTick` method on the THEOComscoreConnector. ```brightscript m.global.addField("tccTick", "integer", false) m.global.tccTick = 0 while true m.global.tccTick = m.global.tccTick + 1 msg = wait(1000, m.port) msgType = type(msg) if msgType = "roSGScreenEvent" if msg.isScreenClosed() then return end if end while ``` NOTE: the `wait` method is running for 1000ms here, making the `tccTick` field get updated every second. Setting it to 0ms will cause the `tccTick` field to not get updated regularly. 7. If the content metadata needs to change, you can update it by calling `update`. This method accepts partial content metadata: `m.comscoreConnector.callFunc( "update", contentMetadata)` 8. When the video has stopped playing because it ended or the user exited, end the Comscore session `m.comscoreConnector.callFunc("endSession")` 9. If you are exiting the player screen altogether, and destroying the player, make sure to destroy the connector at the same time, but before calling destroy on the SDK: ```brightscript m.comscoreConnector.callFunc("destroy") m.comscoreConnector = invalid m.player.callFunc("destroy") m.player = invalid ``` --- # Comscore Connector API Reference The attributes, methods and events for the THEOComscoreConnector. ## Attributes | Name | Type | Default | Access Permission | Description | | ------------ | ---------------- | -------------------- | ----------------- | -------------------------------------------- | | id | string | | read,write | The id of the node. | | MEDIA\_TYPES | associativeArray | Comscore Media Types | read | Constant with the Comscore media type enums. | | AD\_TYPES | associativeArray | Comscore Ad Types | read | Constant with the Comscore ad type enums. | ## Methods | Method | Params | Description | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | configure | player: THEOplayer, configuration: THEOConnectorConfiguration, metadata: (optional) THEOComscoreReadableMetadata or THEOComscoreTagMetadata | Add a player to monitor, configure the Comscore SDK, and also pass in optional metadata for your content. | | destroy | none | Destroy the connector. It also ends the current session, if any. | | endSession | none | End the current Comscore session, but do not destroy the connector. | | update | metadata: THEOComscoreReadableMetadata or THEOComscoreTagMetadata | Updates the content metadata for the current session. | | sendTick | none | Sends a tick to Comscore. This is only needed if you do not implement the `tccTick` field in your main event loop. | | startSession | mediaType: string, metadata: (optional) THEOComscoreReadableMetadata or THEOComscoreTagMetadata | Starts a Comscore streaming session with the specified media type, and optionally applies the content metadata. If not passing content metadata, you will need to add it separately with the `update` method before calling `startSession` | | reportPlaybackEvent | eventType: string, eventDetail: AssociativeArray | Reports a playback event with the associated data to Comscore. | | reportPlaybackFailed | errorMessage: string | Reports a playback failure to Comscore. | | setAdInfo | adInfo: AssociativeArray | Sets the supplied ad info to Comscore. adInfo should be in the form of ComscoreContentInfo | | setContentInfo | contentMetadata: AssociativeArray | Sets or updates the content metadata for the current session. Partials are supported and will be merged with the existing content metadata. See below for the schema of content metadata. | | startSession | contentMetadata: AssociativeArray | Starts a Comscore session with the supplied content metadata. See below for the schema of content metadata. | ### Comscore Config The configuration the Comscore connector is the THEOConnectorConfiguration interface. ```brightscript configuration = { publisherId: "MY_PUBLISHER_ID", publisherSecret: "MY_PUBLISHER_SECRET", applicationName: "MY_APPLICATION_NAME" } ``` The `applicationName` property is optional. If omitted, Comscore will automatically collect it from the manifest file. To get your publisher ID and secret, log into your Comscore account. Go to the Direct dashboard, and switch to the Mobile App tab. Then click the Get Tag button, and a dialog should show with the ID and secret in it. ### Content Metadata Content metadata should be an AssociativeArray following Comscore's schema for streaming tags. However, the THEOComscoreConnector also will accept a more human readable format. These are in the THEOComscoreReadableMetadata and THEOComscoreTagMetadata interfaces. NOTE: if you omit a string value, replace it with `"*null"`, which is the value requested by Comscore for empty fields. #### THEOComscoreReadableMetadata ```brightscript comscoreMetadata = { adLoadFlag: false, assetId: "myAssetId", c3: "Arbitrary C3 value", c4: "*null", c6: "*null", clipLength: 600, completeEpisodeFlag: true, contentGenre: "Sports", digitalAirDate: "2025-03-04", episodeNumber: "59", episodeSeasonNumber: "5", episodeTitle: "The Game Last Night", programTitle: "The Sports Show", publisherBrandName: "Sports Publisher Network", stationTitle: "KXYZ", tvAirDate: "2025-03-04" } ``` #### THEOComscoreTagMetadata NOTE: all values must be strings for this object, with Booleans being represented as `"0"` and `"1"`. ```brightscript comscoreMetadata = { ns_st_ia: "0", ns_st_ci: c3.cm.id, c3: "Arbitrary C3 value", c4: "*null", c6: "*null", ns_st_cl: "600", ns_st_ce: "1", ns_st_cg: "Sports", ns_st_ddt: "2025-03-04", ns_st_en: "59", ns_st_sn: "5", ns_st_ep: "The Game Last Night", ns_st_pr: "The Sports Show", ns_st_pu: "Sports Publisher Network", ns_st_st: "KXYZ", ns_st_tdt: "2025-03-04" } ``` ### Sending Ticks Comscore wants a tick to be sent to it on every run of the main event loop. You can do this manually by calling the `sendTick` method on the THEOComscoreConnector: `m.comscoreConnector.callFunc("sendTick")` However, if you want to have the connector automatically send the tick for you, in your `main.brs` file, add an integer field to the global node called `tccTick`. Set it equal to 0 and then increment it on each run of your event loop. Note that the `wait` line will make it run every 1000 milliseconds. ```brightscript m.global.addField("tccTick", "integer", false) m.global.tccTick = 0 while true m.global.tccTick = m.global.tccTick + 1 msg = wait(1000, m.port) msgType = type(msg) if msgType = "roSGScreenEvent" if msg.isScreenClosed() then return end if end while ``` --- # Getting started with the Adobe Edge Connector for the Roku SDK Here's how to get started integrating the Adobe Edge Connector with the THEOplayer Roku SDK. ## Prerequisites In order to set up the Adobe Edge Connector in your Roku application, you'll need the following: * Your Adobe Edge domain and config ID (also known as data stream ID) * An app with the THEOplayer SDK for Roku already integrated, see our [Getting Started guide](https://www.theoplayer.com/docs/theoplayer/getting-started/sdks/roku/getting-started/). ## Integration 1. First you must download the THEO Adobe Edge Connector as a component library. Add a ComponentLibrary node to your MainScene.brs file, giving it an id of `THEOAEPConnector` and providing the URI for the THEOAEPConnector.pkg. Replace the `{SDK_VERSION}` tag with the version of the THEO Roku SDK you're using. The earliest version of the connector is 9.2.0: ```xml ``` 2. Then in the BrightScript file for your MainScene, listen for the loading of the ComponentLibrary to complete by observing the `loadStatus` field. ```brightscript sub Init() THEOAEPNode = m.top.findNode("THEOAEPConnector") THEOAEPNode.observeField("loadStatus", "onLibraryLoadStatusChanged") end sub sub onLibraryLoadStatusChanged(event as object) THEOAEPNode = event.getROSGNode() if THEOAEPNode = invalid return end if if THEOAEPNode.loadStatus = "ready" ' Success else if THEOAEPNode.loadStatus = "failed" ? "Failed to load component library, please check URL. "; THEOAEPNode.uri end if end sub ``` 3. Add the THEOAEPConnector component to the SceneGraph file where your THEOplayer is defined ```xml ``` 4. Then in the BrightScript file, configure the connector by calling the configure method, passing the player instance and your Adobe Edge configuration. ```brightscript m.player = m.top.findNode("THEOPlayer") m.aepConnector = m.top.findNode("THEOAEPConnector") aepConfig = { configId: "", domainName: "", mediaChannel: "My Channel", mediaPlayerName: "My Player", mediaAppVersion: "1.0", logLevel: 3 } m.aepConnector.callFunc("configure", m.player, aepConfig) ``` 5. Next, when you start playing the asset, call the `startSession` method and pass it the session details for the asset you're playing. You may also include an optional session configuration: ```brightscript m.player.source = sourceDescription aepSessionConfig = {} aepSessionConfig["config.channel"] = "My Channel" aepSessionConfig["config.adpinginterval"] = 5 aepSessionConfig["config.mainpinginterval"] = 30 aepSessionDetails = { name: "my-asset-id", friendlyName: sourceDescription.title, contentType: "video", streamType: "vod", length: 300, customMetadata: { key1: "value1", key2: "value2", } } m.aepConnector.callFunc("startSession", aepSessionDetails, aepSessionConfig) ``` See the [API documentation](https://github.com/adobe/aepsdk-roku/blob/main/Documentation/api-reference.md) for more on how to structure the data for Adobe Edge. 6. When the video has stopped playing because it ended or the user exited, end the Adobe Edge session. `m.aepConnector.callFunc("endSession")` 7. If you are exiting the player screen altogether, and destroying the player, make sure to destroy the connector at the same time, but before calling destroy on the SDK: ```brightscript m.aepConnector.callFunc("destroy") m.aepConnector = invalid m.player.callFunc("destroy") m.player = invalid ``` --- # Adobe Edge Platform Connector API The attributes, methods and events for the THEOAEPConnector. ## Attributes | Name | Type | Default | Access Permission | Description | | ------------- | ---------------- | ---------------- | ----------------- | ---------------------------------------- | | id | string | | read,write | The id of the node. | | ecid | string | invalid | read | User's Experience Cloud ID. | | MEDIA\_EVENTS | associativeArray | AEP Media Events | read | Constant with the AEP media event enums. | ## Methods | Method | Params | Description | | ---------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | configure | player: THEOplayer, configuration: THEOAEPConfiguration | Adds the THEOplayer instance to monitor, and applies a configuration to the AEP SDK. | | destroy | none | Destroy the connector. It also ends the current session, if any. | | endSession | none | End the current AEP session, but do not destroy the connector. | | getExperienceCloudId | none | Retrieves the ECID for the user. The ECID will be added to the `ecid` field on the connector once it has been retrieved. | | resetIdentities | none | Resets the identities used in the SDK for the user. | | sendMediaEvent | eventXDM: AssociativeArray | Send a media event to AEP. | | setContentInfo | contentMetadata: AssociativeArray | Sets or updates the content metadata for the current session. Partials are supported and will be merged with the existing content metadata. See below for the schema of content metadata. | | setExperienceCloudId | ecid: string | Sets a pre-existing ECID for the user on the SDK. | | startSession | sessionDetails: THEOAEPMediaSessionDetails, sessionConfig: (optional) object | Starts a AEP session with the supplied session details and optional config. See below for the schema of details and config. | | stopAndStartNewSession | sessionDetails: THEOAEPMediaSessionDetails, sessionConfig: (optional) object | Stops the existing session if any. Starts a AEP session with the supplied session details and optional config. See below for the schema of details and config. | ### AEP Connector Config The configuration the AEP connector is the THEOConnectorConfiguration interface. ```brightscript configuration = { configId: "", domainName: "", mediaChannel: "My Channel", mediaPlayerName: "My Player", mediaAppVersion: "1.0.0" logLevel: 3 } ``` The `logLevel` property is optional. The accepted values are (VERBOSE: `0`, DEBUG: `1`, INFO: `2`, WARNING: `3`, ERROR: `4`). ### Session Details and Config The session details and session config control the metadata for the session you're starting. See the [AEP SDK documentation](https://github.com/adobe/aepsdk-roku/blob/main/Documentation/api-reference.md#createMediaSession) for expected values. The `customMetadata` property is optional. Values on that object must be strings. #### THEOAEPMediaSessionDetails ```brightscript aepSessionDetails = { streamType: "vod" friendlyName: "My Asset", name: "my-media-id", length: 596, contentType: "video", customMetadata: { key1: "value1", key2: "value2" } } ``` #### Session Config The optional session config should follow the schema described in the [AEP SDK documentation](https://github.com/adobe/aepsdk-roku/blob/main/Documentation/api-reference.md#createMediaSession). ```brightscript aepSessionConfig = {} aepSessionConfig["config.channel"] = "My Channel" aepSessionConfig["config.adpinginterval"] = 5 aepSessionConfig["config.mainpinginterval"] = 30 ``` ### Getting the Experience Cloud ID To get an ECID for a user, you'll need to observe the `ecid` field on the connector, and then call `getExperienceCloudId` on the connector. The field will update with the ECID when it is available and trigger your observer. ```brightscript m.aepConnector.observeField("ecid", "onEcidChange") sub onEcidChange( event as object ) m.myEcid = event.getData() end sub m.aepConnector.callFunc("getExperienceCloudId") ``` ### Reusing an existing AEP SDK library The AEP SDK allows you to reuse instances of the SDK in case you're already using the AEP SDK in your application. This is done by adding a Task node to the scene. The THEO AEP Connector can reuse this Task node. In the MainScene.brs: ```brightscript m.aepSDK = AdobeAEPSDKInit() m.top.appendChild(m.aepSDK.getTaskNode()) ``` As long as the task node is on the Scene before the connector is configured, the THEO AEP connector can pick up the AEP Task node and reuse it. ### Supported media events The THEO AEP Connector will automatically detect most media events and send them to AEP for you, including player commands such as play or pause and events like bitrate change. It will not send media events related to chapters in this version: * `media.chapterStart` * `media.chapterComplete` * `media.chapterSkip` For these you may use the `sendMediaEvent` method and construct your own XDM payload: ```brightscript chapterStartXDM = { "xdm": { "eventType": m.aepConnector.MEDIA_EVENTS.CHAPTER_START "mediaCollection": { "playhead": m.theoPlayer.currentTime, "chapterDetails": { "friendlyName": "My Chapter", "index": 1, "length": 300, "offset": 0 } } } } m.aepConnector.callFunc("sendMediaEvent", chapterStartXDM) ``` --- # Getting started with the Mux Connector for the Roku SDK Here's how to get started integrating the Mux Connector with the THEOplayer Roku SDK. ## Prerequisites In order to set up the Mux Connector in your Roku application, you'll need the following: * Your Mux environment key * An app with the THEOplayer SDK for Roku already integrated, see our [Getting Started guide](https://www.theoplayer.com/docs/theoplayer/getting-started/sdks/roku/getting-started/). ## Integration 1. First you must download the THEO Mux Connector as a component library. Add a ComponentLibrary node to your MainScene.brs file, giving it an id of `THEOMuxConnector` and providing the URI for the THEOMuxConnector.pkg. Replace the `{SDK_VERSION}` tag with the version of the THEO Roku SDK you're using. The earliest version of the connector is 10.9.0: ```xml ``` 2. Then in the BrightScript file for your MainScene, listen for the loading of the ComponentLibrary to complete by observing the `loadStatus` field. ```brightscript sub Init() THEOMuxNode = m.top.findNode("THEOMuxConnector") THEOMuxNode.observeField("loadStatus", "onLibraryLoadStatusChanged") end sub sub onLibraryLoadStatusChanged(event as object) THEOMuxNode = event.getROSGNode() if THEOMuxNode = invalid return end if if THEOMuxNode.loadStatus = "ready" ' Success else if THEOMuxNode.loadStatus = "failed" ? "Failed to load component library, please check URL. "; THEOMuxNode.uri end if end sub ``` 3. Add the THEOMuxConnector component to the SceneGraph file where your THEOplayer is defined. ```xml ``` 4. Then in the BrightScript file, configure the connector by calling the configure method, passing the player instance and your Mux configuration. ```brightscript m.player = m.top.findNode("THEOPlayer") m.muxConnector = m.top.findNode("THEOMuxConnector") muxConfig = { env_key: "", } m.muxConnector.callFunc("configure", m.player, muxConfig) ``` 5. Next, when you start playing the asset, call the `startSession` method and pass it the metadata for the asset you're playing: ```brightscript m.player.source = sourceDescription muxContentMetadata = { video_id: "", video_title: "" } m.muxConnector.callFunc("startSession", muxContentMetadata) ``` There are more properties available to add to the metadata, but `video_id` and `video_title` are required. See [Mux's schema for streaming metadata](https://www.mux.com/docs/guides/make-your-data-actionable-with-metadata#optional-configurable-metadata) for more properties that are available for Mux. 6. If you are using the THEOplayer in a size other than fullscreen, before you start the session, you can set the presentation mode to reflect the different size. This is also referred to as the playback mode. ```brightscript m.muxConnector.callFunc("setPresentation", m.muxConnector.PRESENTATION_MODES.INLINE) m.muxConnector.callFunc("startSession", muxContentMetadata) ``` The `setPresentation` method can also be called mid-session if the presentation mode changes during a session. The available presentation modes are `FULLSCREEN`, `INLINE`, `MINI`, and `PIP`. 7. If you desire to monitor for CDN changes, you can optionally add a configuration for that to start monitoring. `m.muxConnector.callFunc("monitorCdnChanges", { mycdn: ["my-cdn.net"], theo: ["cdn.theoplayer.com"] })` 8. When the video has stopped playing because it ended or the user exited, end the Mux session. `m.muxConnector.callFunc("endSession")` 9. If you are exiting the player screen altogether, and destroying the player, make sure to destroy the connector at the same time, but before calling destroy on the player: ```brightscript m.muxConnector.callFunc("destroy") m.muxConnector = invalid m.player.callFunc("destroy") m.player = invalid ``` --- # Mux Connector API The attributes, methods and events for the THEOMuxConnector. ## Attributes | Name | Type | Default | Access Permission | Description | | ------------------- | ---------------- | ---------------------- | ----------------- | ---------------------------------------------- | | id | string | | read,write | The id of the node. | | CONTENT\_TYPES | AssociativeArray | Mux Content Types | read | Constant with the Mux content types enums. | | PRESENTATION\_MODES | AssociativeArray | Mux Presentation Modes | read | Constant with the Mux presentation mode enums. | ## Methods | Method | Params | Description | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | configure | player: THEOplayer, configuration: THEOMuxConfiguration, metadata: (optional) AssociativeArray | Add a player to monitor, configure the Mux SDK, and also pass in optional metadata for your content. | | destroy | none | Destroy the connector. It also ends the current session, if any. | | endSession | none | End the current Mux session, but do not destroy the connector. | | monitorCdnChanges | mappings: CDNMonitoringConfig, defaultCdn: string (optional) | Updates the CDN monitoring settings. If passed a CDN monitoring config (see below), it will begin monitoring the downloaded segments for changes in CDN. This will update the defaultReportingResource for the current session if a CDN change is detected If passed invalid as the first parameter, it will stop the monitoring and use any string passed as the second param as the defaultReportingResource. | | notifyError | code: integer, message: string, context: string (optional), severity: string (optional), isBusinessException: boolean (optional) | Report an error to Mux. Note that most errors should be automatically reported. Severity defaults to "fatal". | | setPresentation | presentationMode: MuxPresentationModes, data: (optional) AssociativeArray | Change the reported presentation mode (also called playback mode) for the player. Can also take optional arbitrary data about the mode. | | startSession | metadata: (optional) THEOMuxMetadata | Starts a Mux streaming session with the specified media type, and optionally applies the content metadata. See below for the schema of content. If not passing content metadata, you will need to add it separately with the `update` method before calling `startSession` | ### Mux Config The configuration the Mux connector is the THEOMuxConfiguration interface. All properties except for `env_key` are optional. ```brightscript configuration = { env_key: "", viewer_user_id: "", mux_base_url: "" } ``` In order to get debug output from Mux in your console, you need to add the settings to your application's manifest file. ```text mux_debug_events=partial mux_debug_beacons=partial ``` The accepted values for the debug properties are `full`, `partial`, and `none`, defaulting to `none`. ### CDN Monitoring Config The configuration for CDN monitoring should be an AssociativeArray with keys that are the name of the CDN and values that are arrays of strings to search for in segment URLs. ```brightscript cdnMappings = { akamai: ["akamaized.net"], theo: ["dev.theoads.live", "cdn.theoplayer.com"] level3: ["llnw.com"] } ``` Passing a config as the only param will start CDN change monitoring with the provided configuration, or update the config if it is already started. `m.muxConnector.callFunc("monitorCDNChanges", cdnMappings)` However, if you want to stop CDN change monitoring, pass `invalid` as the first parameter, and the string you want to use for the defaultReportingResource as the second parameter. `m.muxConnector.callFunc("monitorCDNChanges", invalid, "theo")` ### Content Metadata Content metadata should be an AssociativeArray following [Mux's schema for streaming tags](https://www.mux.com/docs/guides/make-your-data-actionable-with-metadata#optional-configurable-metadata). All properties on it are optional, except for `video_id` and `video_title`. For content type metadata values, there is an enum property on the THEOMuxConnector named `CONTENT_TYPES`. Its values are SHORT, MOVIE, CLIP, EPISODE, TRAILER, and EVENT. For example: `metadata.video_content_type = m.muxConnector.CONTENT_TYPES.MOVIE`. ### Presentation Modes The THEOMuxConnector supports reporting changes in the presentation mode (also called playback mode) your application uses the player in, such as fullscreen or inline. The `PRESENTATION_MODES` enum property on the THEOMuxConnector holds the possible values (FULLSCREEN, INLINE, MINI, and PIP). For example, if your application changes the THEO player so it is in a list row component, you could call: ```brightscript m.muxConnector.callFunc("setPresentation", m.muxConnector.PRESENTATION_MODES.INLINE) ``` There is also an optional `data` parameter for that method that can be an arbitrary associative array, as long as it can be converted to JSON. ```brightscript presentationModeData = { reason: "Viewed Asset", timestamp: 1767735434 } m.muxConnector.callFunc("setPresentation", m.muxConnector.PRESENTATION_MODES.FULLSCREEN, presentationModeData) ``` --- # Frequently asked questions (FAQ) In this section we will list all the frequently asked questions we received over the years. For our customers with a support plan package, if you have additional questions or can't find your answer here please contact our [Service Desk](https://opentelly.atlassian.net/servicedesk/customer/portal/1) for further support. ## Questions * [How to combat autoplay policies](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-combat-autoplay-policies.md) * [Why does fullscreen not behave as expected on iOS](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-does-fullscreen-not-behave-as-expected-on-ios.md) * [Why does the Network API not work on iOS devices](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-does-network-api-not-work-on-ios-devices.md) * [Why does Chromecast not work on iOS Chrome?](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-does-chromecast-not-work-on-ios-chrome.md) * [Why can't I select another video quality on iOS/tvOS?](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-cant-we-select-other-video-quality-on-ios-tvos.md) * [Is YouTube supported](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/is-youtube-supported.md) * [Why does the player load only one audio track (even though there are several in the manifest)](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-does-player-load-only-one-audio-track.md) * [Is it possible to see 360 degrees photo with THEOplayer](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/is-it-possible-to-see-360-picture.md) * [Why the Visibility API does not work through an iframe on Safari and IE11](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-visibilty-api-doesnt-work-in-iframe-on-safari-and-ie11.md) * [What is an impression](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-is-an-impression.md) * [How to know whether a live stream is playing](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-know-when-livestream-is-playing.md) * [Why did my subtitles stop working](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-did-subtitles-stop-working.md) * [How does Media Engagement Index (MEI) affect Autoplay on Chrome](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-does-mei-affect-autoplay-on-chrome.md) * [Why are not all response headers exposed](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-are-not-all-response-headers-exposed.md) * [Why does the currentTime seem off in my livestream & what can I do about it](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-does-currentime-seem-to-be-off.md) * [How to remove CORS restrictions from a reproduction stream](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-remove-cors-restrictions.md) * [Which network calls (or requests) does THEOplayer do](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/which-network-calls-does-THEOplayer-do.md) * [Why does the playback not work when using the Chrome iPhone/iPad simulator](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-does-playback-not-work-using-chrome-iphone-simulator.md) * [How to remove unwanted CC track in iOS or Safari](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-remove-unwanted-cc-track-ios-safari.md) * [MediaTek limitations](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/mediatek-limitations.md) * [How to use ProGuard with THEOplayer Android SDK](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-use-proguard-with-android-sdk.md) * [Self-hosting and versioning of THEOplayer](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/self-hosting-theoplayer.md) * [Does THEOplayer support EXT-X-DATERANGE](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/does-theoplayer-support-ext-x-daterange.md) * [Can clipping be used on a playlist](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/can-clipping-be-used-in-a-playlist.md) * [Can timeline thumbnails be made available before playback start](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/can-timeline-thumbnails-be-made-available-before-playback-start.md) * [What are the benefits of preloading](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-are-the-benefits-of-preloading.md) * [What are the player seeking and seeked events and when are they fired](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-are-the-player-seeking-and-seeked-events.md) * [Can we use HLS or DASH ads](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/can-we-use-hls-or-dash-ads.md) * [How to change text in THEOplayer](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-change-text-in-theoplayer.md) * [Change text when AirPlaying](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-change-text-when-airplaying.md) * [ITP2.1 problems using THEOplayer](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/itp21-problems-using-theoplayer.md) * [Removing context menu 'Powered by THEOplayer...'](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/removing-context-menu-powered-by.md) * [What aspects of THEOplayer do we need to take into account to deploy a proper Content Security Policy (CSP)](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/whats-aspects-to-take-into-account-when-developing-csp.md) * [How can we avoid that the player keeps looking for chunks/segments if they are not found](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-avoid-loading-chuncks-when-not-found.md) * [Can we show a custom message on 403 on mp4](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/can-we-show-custom-message-on-403-on-mp4.md) * [Can we prevent UpNext feature from redirecting](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/prevent-up-next-from-redirecting.md) * [Is it possible to preload VOD content while the pre-roll is playing](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/preload-vod-content-when-pre-roll-is-playing.md) * [Why is my video not playing automatically](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-is-video-not-playing-automatically.md) * [Is it possible to have multiple player instances play at the same time](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/possible-to-have-multiple-player-instances-at-same-time.md) * [Is it a problem if the viewer pauses a live stream for longer than the DVR window](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/is-it-a-problem-if-viewer-pauses-live-stream-longer-than-dvr-window.md) * [THEOplayer Features & Modules](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/theoplayer-features-modules.md) * [Chromecast on my webplayer does not work any longer despite no change in my implementation](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/chromecast-on-weblayer-does-not-longer-work.md) * [How to track network errors](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-track-network-errors.md) * [How to use the CDN fallback/backup stream feature](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/cdn-fallback-backup-stream-feature.md) * [How to apply accurate buffering strategy](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-apply-accurate-buffering-strategy.md) * [How can I distribute 4K content?](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-can-i-distribute-4k-content.md) * [What is the collaboration between Azure Media Services and THEOplayer](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-is-the-collaboration-between-azure-media-services-and-THEOplayer.md) * [Is Portrait mode supported](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/is-portrait-mode-supported.md) * [How to prevent screen recording](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-prevent-screen-recording.md) * [The provided video source is incompatible with the license for this player](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/the-provided-video-source-is-incompatible-with-the-license-for-this-player.md) * [Page and Source domains](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/page-and-source-domains.md) * [Why is my PlayReady stream not working in Chromium Edge?](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/Why-is-my-Playready-stream-not-working-in-Chromium-Edge.md) * [Which subtitle and CC formats are supported on native Safari](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/which-subtitle-and-cc-formats-are-supported-on-native-safari.md) * [How to navigate through the documentation and resources](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-navigate-through-the-documentation-and-resources.md) * [What are the limitations of AirPlay](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-are-the-limitations-of-AirPlay.md) * [What are the Uplynk challenges](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-are-the-uplynk-challenges.md) * [How to use the Media Session API](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-use-media-session-api.md) * [How to use THEOplayer iOS SDK on an M1 mac](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-use-theoplayer-ios-sdk-on-m1.md) * [Widevine CDM deprecation notice for old browser versions](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/old-widevine-cdm-deprecation-notice.md) * [Which source is the one being played](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/which-source-is-the-one-being-played.md) * [What PiP options to use to make the video stay visible while browsing or on other applications](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-make-pip-video-stay-visible-while-browsing.md) * [Why are the FairPlay license and certificate being requested again on iOS after ads?](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/why-is-DRM-license-retrieved-again-after-ads-on-iOS.md) * [How to detect if the user changes the video quality](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-detect-if-user-changes-video-quality.md) * [List of supported platforms](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/supported-platforms.md) ## Error handling * [Which error related events does the player expose](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/which-error-related-events-does-player-expose.md) * [What does the error message 'Unknown CDM error' mean](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-does-error-unknown-cdm-mean.md) * [What does the error message 'Something went wrong with Native playback' mean](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-does-error-something-went-wrong-during-playback-mean.md) * [What does the error message 'can only be initiated by a user gesture' imply? Can I still force the desired action](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-does-error-can-only-be-initiated-by-user-gesture-mean.md) * [What does the error message “Something went wrong determining the initial period of the provided MPEG-DASH stream” mean](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/error-message-initial-period-provided-MPEG-DASH.md) * [What is the "WebAssembly.compileStreaming failed" warning and what does it mean?](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-is-the-webAssembly-compileStreaming-failed-warning.md) ## Support * [What is the support for Wowza](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-is-the-support-for-wowza.md) * [How to create a (great) ticket](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-create-a-ticket.md) * [How to investigate a ticket](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-investigate-a-ticket.md) * [How to use AI coding assistants with THEOplayer](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-use-ai-coding-assistants.md) --- # How to combat autoplay policies This short article explains what autoplay is, where autoplay is restricted, and what THEOplayer does to facilitate working with autoplay. When you enable [autoplay](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#autoplay) for a HTML5 video, you instruct the viewer's browser to automatically play the content as soon as possible without action from the viewer. ## Restrictions Browsers are restricting their autoplay capabilities. These policies are the result of the continuous misuse of autoplay which causes bad user experiences. There are two types of autoplay: * Unmuted Autoplay: autoplay with audio enabled. * Muted Autoplay: autoplay with audio disabled. The first type is considered more abrupt, while the second type is considered more user-friendly. Some browsers which normally disallow unmuted autoplay allow it when the viewer explicitly indicates so\*. ## API ##### Web SDK An HTML5 video player can't circumvent these technical limitations, but it can offer an API which enables you in a smart way to switch between muted and unmuted autoplay. THEOplayer has the `mutedAutoplay` property in the `PlayerConfiguration`. When you set this property to all, THEOplayer will run in unmuted autoplay on browsers where this is allowed, and in muted autoplay where autoplay with sound is not allowed. You can find a valid example below: ```js // ... var player = new THEOplayer.Player(element, { // ... mutedAutoplay: 'all', // ... }); // ... player.autoplay = true; // ... ``` There's a possibility that new restrictions will be introduced in the future, so it's advised to keep autoplay restrictions in mind when developing your product. --- # Why does fullscreen not behave as expected on iOS One of the most frequently encountered issues on iOS devices is unexpected behavior concerning fullscreen playback. This is in fact an iOS limitation, since iOS + Safari does not allow showing non-video elements in fullscreen, as mentioned on this [support matrix](https://caniuse.com/#feat=fullscreen). THEOplayer's workaround involves playback in full-window, which sets THEOplayer's container to a `width` and `height` of 100%. Other elements can be placed on top of this view by giving it the CSS property `position: absolute;` (Combined with a top/bottom/right/left value) If this is not desired, reduce the `z-index` of these other elements. Note that this solution keeps the THEOplayer UI intact, but doesn't get rid of the address bar as desired in some cases. Alternatively, developers can use the [allowNativeFullscreen](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/PlayerConfiguration#allowNativeFullscreen) property mentioned here, which pushes the video element to fullscreen as seen below. ```js ``` The downside of this approach is that the iOS native look & feel will replace the custom UI, although the address bar will be gone. When you go full-window in an iframe, you won't see the container of the video grow because the iframe can't get any larger. --- # Why does the Network API not work on iOS devices Most Network API capabilities are not available on iOS devices. Generally speaking, video playback on iOS behaves like a black box. This black box handles the requests (and responses) related to video manifests and segments, which makes them impossible to intercept. This limits the possible uses of the Network API on such devices. The Network API can still be used to intercept and alter requests and responses unrelated to video playback manifests and segments (e.g.: DRM-related requests and responses). --- # Why does Chromecast not work on iOS Chrome? iOS Chrome used to support Chromecast. However, Google silently removed support for it at some point. It is also documented [on their Web Sender guide here](https://developers.google.com/cast/docs/web_sender) that: > **Info** > > Casting (Chromecast) is not supported on the iOS Chrome browser. You can still implement Chromecast inside a native iOS app following our guide [here](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/ios/cast/chromecast/enable-chromecast-on-the-sender.md). --- # Why can't I select another video quality on iOS/tvOS? **In short**: Apple technically restricts video players from doing this, and there is no workaround. #### The case for iOS Safari, macOS Safari, iOS SDK and tvOS SDK Any HTML5 video player, like THEOplayer, needs to use native technologies to do video playback, decoding and decryption. On web browsers, video players typically leverage the MSE and EME API by default. If this API is not available, then video players have to rely on native playback to handle video playback, decoding, decryption and ABR selection. On iOS browsers, THEOplayer has to use the native playback pipeline, instead of the [MSE](https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API) and [EME](https://developer.mozilla.org/en-US/docs/Web/API/Encrypted_Media_Extensions_API) API. The native playback pipeline on iOS also exposes an API, but this API doesn't expose any information on the available qualities in a HLS stream, nor does it indicate which quality is currently active. When a FairPlay DRM protected stream is used, we are limited by the same scenario on macOS Safari since for DRM playback we rely on the native pipeline. The underlying AVFoundation stack on iOS/tvOS SDKs, which THEOplayer has to use, brings along the technical limitation that you cannot select a specific video quality. Instead, you can set a maximum resolution or bitrate as explained [here](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/ios/media-tracks/select-video-track-quality.md#usage-for-hls-streams). In result, THEOplayer cannot offer functionality to developers and viewers to change qualities on these scenarios. ## Resources The following resources provide more information: * * [How to programmatically select a video track quality](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/ios/media-tracks/select-video-track-quality.md#usage-for-hls-streams) --- # Is YouTube supported This question above is typically asked by customers who have experience with the YouTube embedded player. It's also related to the following questions: * Does THEOplayer support the playback of YouTube videos? * How do you embed YouTube inside THEOplayer? It is not legal to embed YouTube videos inside any video player, and overlay any custom UI elements. You can not use the YouTube embedded player inside THEOplayer. Source: : > You must not display overlays, frames, or other visual elements in front of any part of a YouTube embedded player, including player controls. Similarly, you must not use overlays, frames or other visual elements to obscure any part of an embedded player, including player controls. Links: * * * --- # Why does the player load only one audio track (even though there are several in the manifest) You may be asking this question if you notice a discrepancy in the number of audio tracks present in the manifest and the audio tracks that you can select from the player UI. Similar questions may be: * How to make all audio tracks in of my stream appear in THEOplayer * Why the player does not return the correct audio tracks? * Why does the player detect only one of the audio tracks of my manifest? First of all, a reassuring word: if your manifest is well-formed and contains multiple audio tracks, you do not need to do anything to import them all, as the player does this by default. However, if your audio tracks are missing, it usually means that something in the manifest is wrong. **If the manifest is indeed correct according to specs, this kind of behavior may occur if the video track includes also audio** (even in the presence of separated audio tracks). In this case, the player considers that audio track as primary and ignores all the others. In other words, when you want to be able to switch between different audio tracks, take care that all of them are included in the manifest separately from the video and that the video track does not include any audio track. This will also result in a slightly faster download time. --- # Is it possible to see 360 degrees photo with THEOplayer This question is asked by those users and developers that are wondering if the player 360 degree functionality can be used to view also images. No, this cannot be done. The player is intended for video sources, so an image as input is not officially supported, which is also true for 3D images. --- # Why the Visibility API does not work through an iframe on Safari and IE11 This question may be asked when you observe a different behavior of the Visibility API on different browsers. The [Visibility API](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/Visibility) provides developers a way to observe how much of the player is visible. When the player is embedded in an iframe, this API does not work on Safari and IE11. This depends on the fact that the player uses the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Browser_compatibility) if it's available in the browser. Unfortunately, IE and Safari do not have support for this, so the player necessarily falls back to scroll events for these environments. This is the reason why on those browsers the Visibility API works correctly when the player is on the page but it is not functional through an iframe. February 2019: A piece of good news: the Intersection Observer API is now in beta in Safari: --- # What is an impression This article explains what an impression is and how THEOplayer calculates impressions. THEOplayer will count an impression when the video or audio source is being played for the first time by our SDK. This can be triggered by a user clicking the Play button, or by playing the video automatically (autoplay). When you pause, play or seek within the same video or audio source, it still will be counted as only one impression. If a user refreshes the page or reloads the application, a second impression will be counted. If the content contains ads, these will not be counted towards the impression count. Except when the content only contains ads, then these will be counted towards the impression count. As an example, if you start a video, pause the video and then resume again it is only considered one impression. If on the other hand you start a video and then refresh your browser, or reload your application, it would be considered 2 impressions. --- # How to know whether a live stream is playing You can check if a stream being played is live or VOD by checking its duration. If the duration returns `Infinity`, the stream would be live. Examples: ##### Web SDK ```js var isLive = player.duration === Infinity; // true or false ``` ##### Android SDK ```java boolean isLive = Double.isNaN(tpv.getPlayer().getDuration()); // true or false ``` --- # Which error related events does the player expose THEOplayer exposes different types of errors. Refer to "[How to do error handling](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/miscellaneous/error/error-handling.md)" for information on how to programmatically detect an `error` event through an event listener. Note that only the `error` events in the "Player API" are fatal. Error events dispatched in different APIs (e.g. Ads API) are considered non-fatal, because some level of playback might still be possible, or the video player might recover from it. | API | Event | Use | Example/how to trigger | | ---------------------- | -------------------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | | Player | error | Triggered for media issues, NOT network issues | A poorly encoded segment | | | segmentnotfound (not on iOS + only for DASH) | A segment was not found | A 404 returned on a segment | | Ads API | aderror | Something went wrong during ad handling | Empty ad tag / Adblocker | | Network API (web only) | offline | Thrown to indicate that the stream is offline | DASH: take whole stream offline

HLS: take 1 segment offline is enough | | Chromecast API | error | Thrown to indicate that there was an error while casting / trying to cast | Unplugged Chromecast power cable during casting | | Android Player API | NoSupportedRepresentationFoundEvent | Indicates that none of the provided representations is supported by the player | Trying to play a 4k stream encoded in AV1 | --- # Why did my subtitles stop working This question is occasionally asked by developers who've configured the latest release of THEOplayer. ### Before version 2.80 The most common reason on an older build why closed captions are no longer working is because the **`texttrackrendering` feature isn't enabled**. To verify whether this feature is enabled you can execute the query below. ```js var subsEnabled = THEOplayer.features.indexOf('texttrackrendering') > -1; ``` ### After version 2.80 Starting in THEOplayer version 2.80 the `texttrackrendering` feature is no longer an optional feature. It is instead part of the default build, and is always enabled. ### Other frequent reasons: * A new stream is configured which does not list any subtitles in its manifest. * The subtitle file can't be retrieved. (e.g. 404) * There are CORS issues with the subtitle file. * The subtitle file is encoded in a non-compatible format. --- # How does Media Engagement Index (MEI) affect Autoplay on Chrome The question above is related to the following questions: * Why is autoplay not working on Chrome? * Is THEOplayer limited by the Media Engagement Index? In summary, **it's possible that on the desktop-version of Chrome (unmuted) autoplay is no longer working on your web page due to the Media Engagement Index**. The following two strategies can help you resolve this issue: 1. Increase your MEI. 2. Enable muted autoplay. ([How to combat autoplay policies](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/how-to-combat-autoplay-policies.md)) You can read more about Chrome's autoplay policy at . A demo illustrating this policy can be viewed at . Remarks * The following scenario is possible due to the Chrome autoplay policy: when you first land on a web page, unmuted autoplay is not permitted. When you click through to another page, or refresh the page, unmuted autoplay is permitted, due to the additional user interactions which affect the MEI. * You can check your MEI at chrome://media-engagement/. --- # What does the error message 'Unknown CDM error' mean If you got to see this page, you probably know that CDM stands for Content Decryption Module, which is a component in the DRM pipeline, a part of the content protection process. There are 2 scenarios where this error can be triggered: * If a user is watching a DRM-protected stream, and then tries to Airplay **their whole screen** (In the device status bar as opposed to AirPlaying by clicking the icon in THEOplayer when watching in Safari). Because it cannot be guaranteed that the content is not being hijacked in this scenario, the player throws the "Unknown CDM error". This scenario is Mac/Safari specific. * A second scenario where you would see this error is when, on iOS, you record your screen with built-in or third-party software while playing DRM-protected content. This scenario is iOS specific. Safari uses media error code 5, according to VideoJS documentation () Using this knowledge, developers could handle this error by capturing the 2 cases under one, more generic message towards the user e.g. 'This content is protected and can't be viewed this way.' --- # What does the error message 'Something went wrong with Native playback' mean This error, "Something went wrong with Native playback", occurs whenever the player struggles on the native rendering pipeline of the browser in use. The error code itself can be different, and it reflects the HTML5 MediaError interface. "Something went wrong with Native playback" is a possible error message which can be exposed by THEOplayer. Although it sounds generic, it is related to native rendering, as explained above. ## Native Rendering Native rendering is used when THEOplayer leverages the native media playback pipeline. Usually, THEOplayer leverages this when this pipeline is the only possible pipeline, for example when you can't use MSE. Examples: * When doing playback on iOS Safari, THEOplayer will do native playback, because native rendering is the only option on iOS Safari. * When a plain MP4 asset is configured, instead of an HLS or MPEG-DASH stream, THEOplayer will do native rendering. Native rendering can log an error due to multiple causes, but they relate to MediaError code. There are several MediaError codes, but for this case the ones relevant are: * `MEDIA_ERR_DECODE: 3` – This is shown when a media resource was deemed as usable, but when trying to decode the media resource in question it appears to have some problems in it (can be segments missing,...). * `MEDIA_ERR_SRC_NOT_SUPPORTED: 4` – The source added to the player is not suitable for this playback. A good example would be trying to play a DRM protected DASH stream on Safari (Safari only supports HLS-FairPlay) The THEOplayer API can be used to access the MediaError code: `player.error.code` This line will return the MediaError together with the specific code. This makes it easier to find out what the error exactly means, and allows developers to plan for further actions. ## Related articles * --- # Why are not all response headers exposed You may ask this question if, for example, you are trying to intercept a certain XHR response to use data contained in custom headers but you don't find these among the exposed headers (see documentation below regarding network interceptors). This happens because by default, only some headers are exposed by default. You can however decide to specify which additional headers you want exposed. This is done through the Access-Control-Expose-Headers response header. It "indicates which headers can be exposed as part of the response by listing their names" (see [documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers)). --- # Why does the currentTime seem off in my livestream & what can I do about it THEOplayer has a currentTime property, which returns you the current playback position in the video (in seconds). For VOD this works as you would expect. If you have a video, let it play for 30 seconds and query the currentTime, the player will report 30 seconds. However, if you start watching a livestream and query the currentTime after 30 seconds, you won't get 30 seconds reported. Nor will you get the time that would have elapsed if you watched the segments in the most recent manifest file until the point where you queried the currentTime. This might seem odd, but there's a very good reason for this, which we'll explain in a bit, along with some workarounds if you do want to use one of the aforementioned definitions of currentTime in a live streaming context. To calculate the currentTime, THEOplayer follows the HTML5 specification. However, for livestreams, this is not specified. Hence, if you try to use currentTime for synchronization between players, you'd get different behavior among different players. It's even possible to see different behavior among different streaming protocols in conjunction with the same player. The internal implementation THEOplayer uses, is constructed to never have a negative currentTime. A few things are being checked to ensure this: * Check if it's a VOD. In this case the timeline starts at 0 * In case it's a live stream, there are two options **It's a live-EVENT stream** * Manifest contains `EXT-X-PLAYLIST-TYPE: EVENT` * All renditions started at the same time, so the first segment would have timestamp 0 **It's a livestream with a DVR window** * For these some players choose to start the 0-offset at the start of the first segment * However, segments are not necessarily aligned across multiple renditions: * Switching renditions can cause new content to become available before the start timestamps of the first rendition * Such a segments would halve a negative start timestamp * To avoid negative timestamps, THEOplayer keeps an offset of 3 target durations from the start as its zero-reference How can you deal with this if you want to synchronize players or get a zero-based timestamp? You have **3 options**: 1. Use `EXT-X-PROGRAM-DATE-TIME`, which gives you an absolute time reference. 2. Make your stream an event stream by including `EXT-X-PLAYLIST-TYPE: EVENT` in the manifest 3. Use the player's seekable property (which is based on the manifest) and query seekable.start(0) to find the starting time of your stream 3.1 Attach an event listener for the 'durationchange' event (triggered the first time a media playlist is loaded) 3.2 Upon this event, store player.seekable.start(0) in a variable 'timeOffset' and remove the eventlistener 3.3 Subtract the value of 'timeOffset' from the player's currentTime to get a zero-based timestamp Here's a code snippet for option 3 : ```js function AbsoluteTimeline(player) { var timeOffset = 0; function durationchangeHandler() { timeOffset = player.seekable.start(0); player.removeEventListener('durationchange', durationchangeHandler); } player.addEventListener('durationchange', durationchangeHandler); return { getOffset: function () { return timeOffset; }, getCurrentTime: function () { return player.currentTime - timeOffset; }, seek: function (timestamp) { player.currentTime = timestamp + timeOffset; }, }; } var timeline = AbsoluteTimeline(player); ``` --- # How to remove CORS restrictions from a reproduction stream A "CORS-issue" is a common cause of a stream not starting. In a nutshell, a CORS issue implies that the origin is not allowed to fetch the resource. This issue has to be resolved from the server-side. Additionally, sometimes we ask customers to alter their CORS-policy in order to playback streams from a development environment. A CORS-issue blocks us from deploying the stream in our development environment. --- # Which network calls (or requests) does THEOplayer do The question above is related to the following questions: * What is the call to license.theoplayer.com? * Why is THEOplayer sending a request to licensing.theoplayer.com? * What is the call to licenseapi.theoplayer.com, and why is it failing? THEOplayer can do three different network requests to `*.theoplayer.com` which are not related to your stream. 1. `license.theoplayer.com`: this request logs an impression to THEOplayer's analytics server. THEOplayer uses impressions to know how many views you've received, because it's taken into account for billing purposes. The analytics server does not track personal information on your users. 2. `licensing.theoplayer.com/t?`: this request serves as a fallback request when the call to license.theoplayer.com fails. 3. `licenseapi.theoplayer.com/api/v1/check`: this request happens when your current license is invalid, for example on a domain (or IP-address) which hasn't been whitelisted. This request will not be successful because there's no service configured for this request. (This might change in the future.) --- # Why does the playback not work when using the Chrome iPhone/iPad simulator You might need an answer to this question if you are testing your page using the Chrome iPhone/iPad simulator and notice that, reloading the page, the video player stops working (even though the ads, if any, are displayed correctly) and some errors are displayed in the console. Similar questions are: * Why does the playback not work when using Mozilla in Responsive Design Mode? * Why does the playback not work when using the Edge emulator to test my page on other platforms? * Why does the playback not work when testing device-specific viewports with the browser dev tools? The simple answer is that this occurs because the video player gets confused between the user agent (set as iOS during the simulation) and the browser actually in use. This does not imply that there is any problem with the video player. In fact, when the tested page will be accessed by an actual iOS device, the video player will work fine, in absence of other (unrelated) issues. The same behavior is to be expected in similar tests, that is to say when a platform is emulated on another and they do not share the same playback logic of the native video element. Some more details: If you use Chrome + iOS simulator, your user-agent will change to iOS/Safari. THEOplayer detects this new user-agent, and because it's iOS it'll think that the MSE API is not available, and that they need to use the playback logic of the native video element. However, you're still on Chrome, so this alternate native iOS playback logic is not available. These circumstances lead to playback failure. It works for ads because they typically do not use the HLS protocol, so we do not have to leverage MSE. It is not advisable to use (or rely on) the Chrome + iOS simulator mode to test anything else than the view mode. The native APIs which iOS offers are not available in this mode, so it's not really a useful testing mode. --- # What does the error message 'can only be initiated by a user gesture' imply? Can I still force the desired action You may get this error if you are trying to have the player execute an action without it being initiated by the user. A typical example is going fullscreen or automatically starting playback (often on mobile). Related questions are: * Why can’t I set the player to fullscreen mode on screenrotation? (Related error message: “Failed to execute 'requestFullScreen' on 'Element': API can only be initiated by a user gesture.”) * Why is an error generated if I want to automatically start playback when certain conditions are met (Related error message: DOMException: play() can only be initiated by a user gesture) The short answer is no, you cannot (and should not) force an action that gets this kind of error message: this is a block implemented by the browser to protect the user. The details: In some cases, the browser deems that a certain behavior cannot be initiated automatically and needs instead to be started by the user (or user action). This happens because these behaviors may be perceived as bad or harmful by a user, if automated. In other words, this limitation is implemented by the browser to protect the user from (for example) invasive pop ups, advertisement, etc., as well as from more serious threats to a good and safe web surfing. To our knowledge, there is no way to force the browser to ignore this error, and you probably shouldn't want to do so, for the above-mentioned reasons. Therefore, if you incur in this situation you may want to find an alternative way to achieve the desired result (or take the hint from the browser and reconsider whether what you want to achieve is appropriate for your use case and good for the user). Update 7/12/2018 This behavior is common on devices and on Safari. Chrome 71, released a few days ago, also introduces the need for user interaction to go fullscreen. --- # How to remove unwanted CC track in iOS or Safari This article is intended to show how to remove any unwanted CC tracks that might be showing up in iOS or Safari. This can be done by editing the HLS master playlist. **Unwanted CC in Safari native** ![Unwanted CC in Safari](/pr-860/assets/images/unwanted-cc-safari-afa200bfd56332b7a019c6a8b4d2ce62.png) **Unwanted CC in THEOplayer iOS** ![Unwanted CC in iOS](/pr-860/assets/images/unwanted-cc-ios-9ca2c0569660319c77f96ecf29561a5a.png) ## How to remove unwanted cc tracks? To remove any unwanted cc tracks, the “CLOSED-CAPTION” attribute has to be set to “NONE” inside the HLS master playlist. This is to be compatible with the HLS specification: If it is a multi-variant stream, this has to be done for all variants. ```text CLOSED-CAPTION=NONE ``` --- # MediaTek limitations This page overviews a list of known issues caused by the MediaTek chipset. ## 1. Switching to fullscreen halts playback for approximately 10 seconds The MediaTek decoder seems lackluster at handling a transition to fullscreen when **experimental rendering** is enabled because THEOplayer uses a separate Android Activity for fullscreen. Using the default rendering pipeline does not trigger this issue. A workaround is doing fullscreen without detaching the SurfaceView from the window. ### Why does THEOplayerView use a separate activity for fullscreen? THEOplayerView uses a separate Activity for fullscreen so that the fullscreen view is completely isolated from the Activity the THEOplayerView is in. The Activity has its own orientation, window settings, and back button behavior. By using a separate Activity, we can leave those settings and the view hierarchy intact in the Activity in which the THEOplayerView is embedded. A downside of this in case of experimental rendering is that we need to move the SurfaceView that holds the Surface on which video is rendered. This, however, means that the Surface is destroyed and a new one is created. On newer devices, this is normally no problem because the decoder is decoupled from the Surface. When the Surface is destroyed, a new one can be attached to the same decoder. On devices with an API lower than 23 (and some other devices which bug) however, the decoder can not change the Surface on which it draws without re-initialization. This means that on older and some buggy devices, switching to fullscreen will make the video hang until the next keyframe is reached in the stream. ### Solution: Fullscreen without detaching the SurfaceView from the window If an implementer does not want the Surface to be destroyed when going fullscreen, the implementer needs to implement this themselves to fit within the Activity. A few things are needed for this: 1. FullscreenCoupling needs to be disabled (with `getTPV().getSettings().setFullScreenOrientationCoupled(false)`). 2. If the UI is enabled, the fullscreen button needs to be hidden or overwritten. This needs to be done with [custom CSS and/or custom JavaScript](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v4/faq/how-to-add-css-or-javascript-files-to-android-ios). 3. Orientation needs to be handled in the Activity. 3.1 To change the orientation of the Activity, [`setRequestedOrientation`](https://developer.android.com/reference/android/app/Activity.html#setRequestedOrientation\(int\)) has to be called on the Activity or the Activity needs to be allowed to rotate. Allowing the activity to rotate means that the android:screenOrientation property in the [manifest](https://developer.android.com/guide/topics/manifest/manifest-intro) needs to be set to multiple values or not set. 3.2 To make sure that the Activity is not recreated on an orientationchange,android:configChanges field in the manifest need to include "orientation|screenSize". 3.3 To detect the change, the onConfigurationChanged method needs to be overridden in the Activity. In the implementation the orientation can be checked and fullscreen can be initiated or stopped depending on it. 4. To make the THEOplayerView fill the whole Activity without moving the view, the THEOplayerView and all of its parents view's width and height layout parameters need to be set to "match\_parent" and they all need to be the first child-view (with a non-zero surface area). 5. To hide the navigation buttons and status bar, the Activity should change the window and visibility flags. this can be done with: 5.1 getWindow().getDecorView().[setSystemUiVisibility](https://developer.android.com/reference/android/view/View#setSystemUiVisibility\(int\))(SYSTEM\_UI\_FLAG\_LOW\_PROFILE|SYSTEM\_UI\_FLAG\_FULLSCREEN|SYSTEM\_UI\_FLAG\_LAYOUT\_STABLE| SYSTEM\_UI\_FLAG\_IMMERSIVE\_STICKY|SYSTEM\_UI\_FLAG\_LAYOUT\_HIDE\_NAVIGATION|SYSTEM\_UI\_FLAG\_HIDE\_NAVIGATION); 5.2 getWindow().[setFlags](https://developer.android.com/reference/android/view/Window.html#setFlags\(int,%20int\))(FLAG\_FULLSCREEN, FLAG\_FULLSCREEN); 6. To let the player exit fullscreen on a back button press instead of stopping the Activity, the onBackPressed method of the Activity should be overridden with the desired behavior. 7. When going back from fullscreen, the original settings for the window and back button behavior need to be set back to desired values. --- # How to use ProGuard with THEOplayer Android SDK ProGuard is a popular tool to obfuscate and optimize Java code in Android projects. Depending on the settings, this tool can clash with the THEOplayer Android SDK. Below, you can find a list of rules tested against THEOplayer Android SDK on 28 Nov 2018. ```text -keep class com.theoplayer.android.** {*;} -dontwarn com.theoplayer.android.** -keep public class com.google.android.gms.* { public *; } -dontwarn com.google.android.gms.** -keep class android.support.v7.app.MediaRouteActionProvider {*;} -dontwarn android.support.v7.app.MediaRouteActionProvider # In case of using yospace integration -keep class com.yospace.util.** {*;} -dontwarn com.yospace.util.** # For native Google IMA integration -keep class com.google.obf.** { *; } -keep interface com.google.obf.** { *; } -keep class com.google.ads.interactivemedia.** { *; } -keep interface com.google.ads.interactivemedia.** { *; } # And in case of using a CastOptionsProvider and/or custom MediaRouteActionProvider for chromecast, you have to keep those classes too. # eg: # -keep class com.yourcomp.yourchromecastpackage.** {*;} # -dontwarn com.yourcomp.yourchromecastpackage.** ``` --- # Self-hosting and versioning of THEOplayer Your licensed THEOplayer can also be hosted on your own servers or CDN. This can be useful when you do not want to have a dependency on a third-party hosting infrastructure, for using THEOplayer on internal/local (and even offline) networks or for using THEOplayer inside mobile apps. There are two approaches to download the THEOplayer SDK for self-hosting purposes: 1. Use [NPM](https://www.npmjs.com/package/theoplayer). 2. Download the SDK through . > **Important** > > When self-hosting THEOplayer, it is important you regularly update your self-hosted THEOplayer files in order to benefit from the latest improvements. In order to download the THEOplayer package and host it on your server, you will first have to determine which version of the player you want to use. Read up about version changes in the [changelog](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/changelog.md). ## Configuring self-hosting Once you extracted the ZIP package to your server, you must configure THEOplayer and provide the information where the resources can be found. For this, you should configure 1 parameter correctly: * `libraryLocation`: This parameter should point to the location of the library. It is the location where the ZIP package has been extracted and the resources are available on the server. Of course, also keep in mind to correctly link to the correct JavaScript library and CSS file. ```html ``` ## Common mistakes * Configuring the libraryLocation incorrectly. The referred location/folder should contain `theoplayer.d.js`, `theoplayer.e.js`, `theoplayer.p.js`, `THEOplayer.js`, and so on, and they need to all have the same version. * Using the THEOplayer library on a web page which resides on a non-whitelisted domain (e.g. ). Although you can host your library assets anywhere, you can only use the actual video player on white-listed domains. * Linking to an unreachable JavaScript or CSS library. --- # Does THEOplayer support EXT-X-DATERANGE Yes, the HLS tag `#EXT-X-DATERANGE` is supported in THEOplayer as of 2.61.0. To enable it, hlsDateRange needs to be added to either the player configuration or the source description and be set to true. The date range metadata will be made available through the TextTrack API. `#EXT-X-DATERANGE` is a way of providing timed metadata in an HLS manifest. It is used to define date range metadata in a media playlist. A possible use case is defining timed metadata for interstitial regions such as advertisements, but can be used to define any timed metadata needed by your stream. As indicated above, `#EXT-X-DATERANGE` is supported in THEOplayer as of 2.61.0. However, the feature is not enabled by default. To enable it, add hlsDateRange to the player configuration and set it to true. You can also choose (instead) to enable/disable the feature per source by adding the same option to a source in your source description. The TextTrack API is used to retrieve the timed metadata. A MetadataTextTrack will provide cues which contain the timed metadata and will dispatch an event when started and ended. ## Remarks * A manifest with EXT-X-DATERANGEs needs to have an EXT-X-PROGRAM-DATE-TIME to be able to calculate dates from playback times. This is also a specification requirement. * If the tag name for a date range tag in the manifest is incorrect, the tag will be ignored * If the name of any attribute is misspelled in a date range tag in the manifest, the attribute will not be parsed * When using native browser playback features such as FairPlay content protection, the player might use more resources to parse date ranges from the manifest than in other scenarios. ## Resources The following resources provide more information: * [HLS Specification - #EXT-X-DATERANGE](https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.2.7) * [API reference - TextTrackType](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/types/TextTrackType) --- # Can clipping be used on a playlist Yes. This is in principle not different from using it on a single video: a startTime and an endTime must be given for each video and applied when the video is loaded/played. Let’s expand slightly the idea. You can have 2 use cases: * The same start and end time can be used for all contents. * Each content has a different start and end time. ## The same start and end time can be used for all contents In this case, you can apply the Clip API without any further precaution or modification. You can find an example at the page Video clipping in THEOplayer (see below). Please remember to make sure that clipping happens after the source is set (e.g.: use a `durationchange` event). ## Each content has a different start and end time. In this case, some adjustment is needed to be able to provide and read the right custom start and end time for each content. One possible way to do it is to simply pass such data together with the source data and read it directly from there when clipping the video, like in the example below. ```js // an array is provided, containing multiple player.source. Each source contains startTime and endTime. var playlist = [ { source: { sources: { src: 'https://cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny_metadata.m3u8', startTime: 100, endTime: 150, }, title: 'Big Buck Bunny', description: 'Big Buck Bunny', poster: 'https://cdn.theoplayer.com/video/big_buck_bunny/poster.jpg', }, }, { source: { sources: { src: 'https://cdn.theoplayer.com/video/star_wars_episode_vii-the_force_awakens_official_comic-con_2015_reel_(2015)/index.m3u8', startTime: 120, endTime: 180, }, title: 'Star Wars Reel', description: 'Star Wars Reel', poster: 'https://cdn.theoplayer.com/video/star_wars_episode_vii-the_force_awakens_official_comic-con_2015_reel_(2015)/poster.jpg', }, }, { source: { sources: { src: 'https://cdn.theoplayer.com/video/tears_of_steel/index.m3u8', startTime: 50, endTime: 150, }, title: 'Tears of Steel', description: 'Tears of Steel', poster: 'https://cdn.theoplayer.com/video/tears_of_steel/poster.jpg', }, }, ]; // the desired source is set in the player - // this is not reported here as it depends on your implementation, // but an example can be seen in the resources linked below. // For the purpose of this example // player.source = playlist[0]; // the following lines make sure that // when the first playing event is fired for each (new) content // clipping times are read from directly within the player.source function firstplaying() { player.clip.startTime = player.source.sources.startTime; player.clip.endTime = player.source.sources.endTime; THEOplayer.players[0].removeEventListener('durationchange', firstplaying); } THEOplayer.players[0].addEventListener('sourcechange', function () { THEOplayer.players[0].removeEventListener('durationchange', firstplaying); THEOplayer.players[0].addEventListener('durationchange', firstplaying); }); ``` Notes: * In this example, we are only describing how to apply the clipping, not how to play a playlist. For this, please consult the linked resources. * The example is provided only in JavaScript because clipping is currently available only for the JavaScript SDK. It may be used also in other SDKs through JavaScript injection. ## Resources The following resources provide more information: API reference - [Clip API](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/Clip) Clipping - [How-to guide](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/miscellaneous/clipping.md) --- # Can timeline thumbnails be made available before playback start Yes, this is possible using the preload feature, provided your timeline is already visible. Both “metadata” and “auto” will be effective values to this end. If the player on your implementation shows the timeline before playback start (differently than on our demo pages), you may indeed be interested to show the timeline thumbnails, and possibly other data (e.g.: video duration). As explained above, this may be achieved using the preload. Both values “metadata” and “auto” will work for this. The advantage of using “auto” is that also some media files will be buffered, reducing the start-up time. To know more about preload, please visit the linked resources. In case your timeline is not visible before playback start, however, using preload will have no visible impact on the timeline thumbnails. ## Resources The following resources provide more information: * [Preloading - How to](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/miscellaneous/preloading.md) * [What are the benefits of preloading?](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/faq/what-are-the-benefits-of-preloading.md) * [PreloadType API reference](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/types/PreloadType) --- # What are the benefits of preloading Choosing whether to preload parts of the video before it starts playing will help you considerably improve the user experience. Preloading involves loading parts of a video source before the video starts playing. When the video later starts playing, it can do this much faster because it has already downloaded some data. This can improve the user experience dramatically when you expect the user will want to play the video. On the other hand, when the video is less likely to be played, you can configure the player to not preload it and save bandwidth. Interactive video experiences are one example use case for this functionality. In an interactive video, the user must choose between multiple options at the end of a video, with each option leading to a different video. Using preloading will help you ensure a smooth transition between videos (to this purpose you may also want to use other THEOplayer features, such as Cache API and Canvas API - see Resources). Choosing to preload your video content will also make some data regarding the current source available before the user initiates playback. Such data include, for example, the video duration and the timeline thumbnails. ## Resources The following resources provide more information: * : HTML5 specification * [Preloading - How-to](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/how-to-guides/web/miscellaneous/preloading.md) * [API reference: preloading](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/types/PreloadType) * [API reference: Cache API](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/Cache) * [API reference: Canvas API](https://docs-preview.optiview.dolby.com/pr-860/theoplayer/v11/api-reference/web/interfaces/Canvas) --- # What are the player seeking and seeked events and when are they fired They are events related to the playback of media files. `seeking` is fired when the user starts seeking/moving to a new position. `seeked` is fired when the user is finished seeking/moving to a new position and the player has buffered a video portion, being therefore ready to play. The player `seeking` and `seeked` events are not to be confused with the homonym `