# Dolby Vision

**Checking support…**

Tip: open your browser's developer console and use the `player` and `THEOplayer` variables to interact with this demo.

**Source**

DASHHLS

This example plays a Dolby Vision profile 8.4 stream (an HLG-compatible base layer with a Dolby Vision enhancement layer, signaled via supplemental codecs: `dvh1.08.xx` in DASH via `scte214:supplementalCodecs`, and `dvhe.08.xx` in HLS via `SUPPLEMENTAL-CODECS`). It probes the browser with [`MediaCapabilities.decodingInfo`](https://developer.mozilla.org/en-US/docs/Web/API/Media_Capabilities_API) for the Dolby Vision codec (`dvh1.08.09` or `dvhe.08.09`) and the HEVC Main10 HLG base layer (`hvc1.2.4.L153.90` or `hev1.2.4.L153.90`), and checks for an HDR display with `matchMedia('(dynamic-range: high)')`, re-checking when the display capability changes.

```js
await navigator.mediaCapabilities.decodingInfo({
  type: 'media-source',
  video: {
    contentType: 'video/mp4; codecs="dvh1.08.09"',
    bitrate: 18000000,
    width: 3840,
    height: 2160,
    framerate: 60,
    transferFunction: 'hlg',
  },
});

const hdrDisplay = matchMedia('(dynamic-range: high)').matches;
```

The status box is green when Dolby Vision decoding and an HDR display are both present, yellow when the browser can decode only the HEVC HLG base layer and not Dolby Vision, or when the display does not support HDR, and red when the browser cannot decode the HEVC base layer at all.

Code

demo.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Dolby Vision</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet" />
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
      }

      theoplayer-default-ui:not(:defined) {
        display: inline-block;
        box-sizing: border-box;
      }

      theoplayer-default-ui {
        width: 100%;
        aspect-ratio: 16 / 9;
        font-family: 'Noto Sans', sans-serif;
        background: #000;
      }
    </style>
    <script src="https://cdn.theoplayer.com/dash/theoplayer/THEOplayer.js"></script>
    <script nomodule src="https://unpkg.com/@theoplayer/web-ui@2/polyfills"></script>
    <script async src="https://unpkg.com/@theoplayer/web-ui@2"></script>
  </head>
  <body>
    <!--
      libraryLocation: For demonstration purposes, we use the THEOplayer CDN.
      For production use, host the library yourself and update this URL and the script above.

      licenseUrl: Point this to your license file, or replace it with a license property.
    -->
    <theoplayer-default-ui
      configuration='{"libraryLocation":"https://cdn.theoplayer.com/dash/theoplayer/","licenseUrl":"../../../../theoplayer-license.txt","mutedAutoplay":"all"}'
      source='{"sources":{"src":"https://ott.dolby.com/OTTEP/prod/v1_1/dovi/11/ta/clear/p8.4/60fps/hvc1/hvc1_supp.mpd","type":"application/dash+xml"},"metadata":{"title":"Dolby Vision (profile 8.4)"}}'
    ></theoplayer-default-ui>
    <script src="../utils.js"></script>
    <script>
      // Detect Dolby Vision support: a Dolby Vision codec (dvh1/dvhe, profile 8) plus an HDR display.
      // The DASH manifest signals dvh1.08.xx, the HLS manifest dvhe.08.xx; base layer is hvc1/hev1 HLG.
      async function probeVideo(contentType, transferFunction) {
        const video = { contentType, bitrate: 18000000, width: 3840, height: 2160, framerate: 60, transferFunction };
        if (navigator.mediaCapabilities && navigator.mediaCapabilities.decodingInfo) {
          try {
            const result = await navigator.mediaCapabilities.decodingInfo({ type: 'media-source', video });
            return { status: result.supported ? 'supported' : 'unsupported' };
          } catch (e) {
            return { status: 'unsupported' };
          }
        }
        if (window.MediaSource) {
          // MediaSource.isTypeSupported cannot probe the transfer function.
          return { status: MediaSource.isTypeSupported(contentType) ? 'supported' : 'unsupported', fallback: true };
        }
        return { status: 'unavailable' };
      }

      function hdrDisplay() {
        if (!window.matchMedia) return { status: 'unavailable' };
        return { status: window.matchMedia('(dynamic-range: high)').matches ? 'supported' : 'unsupported' };
      }

      function reportActiveCodecs(player) {
        const videoTrack = Array.from(player.videoTracks).find((track) => track.enabled);
        const audioTrack = Array.from(player.audioTracks).find((track) => track.enabled);
        const codecs = [
          videoTrack && videoTrack.activeQuality && videoTrack.activeQuality.codecs,
          audioTrack && audioTrack.activeQuality && audioTrack.activeQuality.codecs,
        ]
          .filter(Boolean)
          .join(', ');
        if (codecs) sendToParent('playing-codecs', { codecs });
      }

      onPlayerReady(async (player) => {
        async function report() {
          const [dvh1, dvhe, hvc1, hev1] = await Promise.all([
            probeVideo('video/mp4; codecs="dvh1.08.09"', 'hlg'),
            probeVideo('video/mp4; codecs="dvhe.08.09"', 'hlg'),
            probeVideo('video/mp4; codecs="hvc1.2.4.L153.90"', 'hlg'),
            probeVideo('video/mp4; codecs="hev1.2.4.L153.90"', 'hlg'),
          ]);
          // Either codec string is enough: dvh1 (DASH) or dvhe (HLS) for Dolby Vision,
          // hvc1 (DASH) or hev1 (HLS) for the HEVC HLG base layer.
          const dv =
            dvh1.status === 'supported' || dvhe.status === 'supported'
              ? { status: 'supported', fallback: dvh1.fallback && dvhe.fallback }
              : {
                  status: dvh1.status === 'unavailable' && dvhe.status === 'unavailable' ? 'unavailable' : 'unsupported',
                  fallback: dvh1.fallback || dvhe.fallback,
                };
          const hevc =
            hvc1.status === 'supported' || hev1.status === 'supported'
              ? { status: 'supported', fallback: hvc1.fallback && hev1.fallback }
              : {
                  status: hvc1.status === 'unavailable' && hev1.status === 'unavailable' ? 'unavailable' : 'unsupported',
                  fallback: hvc1.fallback || hev1.fallback,
                };
          const hdr = hdrDisplay();
          const checks = [
            {
              label: 'Dolby Vision codec (dvh1.08.09 or dvhe.08.09, HLG)' + (dv.fallback ? ' — via MediaSource.isTypeSupported' : ''),
              status: dv.status,
            },
            {
              label:
                'HEVC Main10 HLG base layer (hvc1.2.4.L153.90 or hev1.2.4.L153.90)' + (hevc.fallback ? ' — via MediaSource.isTypeSupported' : ''),
              status: hevc.status,
            },
            { label: 'HDR display (dynamic-range: high)', status: hdr.status },
          ];
          const level = hevc.status !== 'supported' ? 'danger' : dv.status !== 'supported' || hdr.status !== 'supported' ? 'warning' : 'success';
          const summary =
            level === 'success'
              ? 'Dolby Vision is supported on this device and display.'
              : level === 'danger'
                ? 'This browser cannot decode HEVC video.'
                : dv.status !== 'supported'
                  ? 'This browser cannot decode Dolby Vision, only the HEVC HLG base layer.'
                  : 'This browser can decode Dolby Vision, but this display does not support HDR.';
          sendToParent('detection-status', { level, summary, checks });
        }

        await report();
        if (window.matchMedia) {
          window.matchMedia('(dynamic-range: high)').addEventListener('change', report);
        }

        player.videoTracks.addEventListener('addtrack', (event) =>
          event.track.addEventListener('activequalitychanged', () => reportActiveCodecs(player))
        );
        player.audioTracks.addEventListener('addtrack', (event) =>
          event.track.addEventListener('activequalitychanged', () => reportActiveCodecs(player))
        );

        onParentMessage('load-source', (message) => {
          player.autoplay = true;
          player.source = message.source;
        });
      });
    </script>
  </body>
</html>
```
