DEV Community

Creating a video player web component

Eyevinn has an open-source web player that can play both HLS and MPEG-DASH streams, automatically selecting the appropriate library for either format.

Eyevinn Webplayer page
You can check it out here! It's simple to use: Just enter a manifest URI and press "LOAD" (or use one of the preset buttons).

Eyevinn Webplayer video loaded

Voilà! If we want to share a link to the page with our manifest URI of choice, we can use the "SHARE" button.

But what if we want to embed the player on our own web page? Then we can click the "EMBED" button to display a code snippet. We copy this snippet, paste it into our HTML file, and we should see an eyevinn-video element on our webpage.

Web component snippet

How does this work? It's thanks to the use of web components.

<script type="text/javascript" src="https://unpkg.com/@eyevinn/web-player-component@0.1.1/dist/web-player.component.js"></script>
<eyevinn-video source="https://f53accc45b7aded64ed8085068f31881.egress.mediapackage-vod.eu-north-1.amazonaws.com/out/v1/1c63bf88e2664639a6c293b4d055e6bb/ade303f83e8444d69b7658f988abb054/2a647c0cf9b7409598770b9f11799178/manifest.m3u8" muted autoplay ></eyevinn-video>
Enter fullscreen mode Exit fullscreen mode

The snippet consists of two parts:

  • A script tag containing the web component code

  • An eyevinn-video custom element

Let's have a look at the code:

import WebPlayer from '@eyevinn/web-player-core';
import { renderEyevinnSkin } from '@eyevinn/web-player-eyevinn-skin';
import style from '@eyevinn/web-player-eyevinn-skin/dist/index.css';

export default class PlayerComponent extends HTMLElement {
  static get observedAttributes() {
    return ['source', 'starttime', 'muted', 'autoplay'];
  };
  constructor() {
    //Call constructor of HTMLElement
    super();
    //Attach shadow DOM
    this.attachShadow({ mode: 'open' });
    const { shadowRoot } = this;
    //Create style and attach to shadow DOM
    let styleTag = document.createElement('style');
    styleTag.innerHTML = style;
    shadowRoot.appendChild(styleTag);
    //Create wrapper and attach to shadow DOM
    const wrapper = document.createElement('div');
    shadowRoot.appendChild(wrapper);
    //Create video element and attach to shadow DOM
    this.video = document.createElement('video');
    wrapper.appendChild(this.video);
    //Init player and skin
    this.player = new WebPlayer({ video: this.video });
    renderEyevinnSkin({
      root: wrapper,
      player: this.player,
      castAppId: {}
    });
  }

  attributeChangedCallback(name) {
    if (name === 'source') {
      if (this.hasAttribute('source')) {
        this.player.load(this.getAttribute('source')).then(() => {
          if (this.hasAttribute('starttime')) {
            this.video.currentTime = this.getAttribute('starttime');
          }
          if (this.hasAttribute('autoplay')) {
            this.player.play();
          }
        });
      }
      else {
        console.error("Invalid source was provided to <eyevinn-video> element");
      }
    }
    if (name === 'muted') {
      if (this.hasAttribute("muted")) {
        this.video.muted = true;
      }
      else {
        this.video.muted = false;
      }
    }
  }

  connectedCallback() {
    this.setupEventProxy();
  }

  disconnectedCallback() {
    this.player.reset();
  }

  setupEventProxy() {
    if (!this.player) return;
    this.player.on('*', (event, data) => {
      this.dispatchEvent(new CustomEvent(event, { detail: data }));
    });
  }
}
//Register custom element
customElements.define('eyevinn-video', PlayerComponent);
Enter fullscreen mode Exit fullscreen mode

First off, we need to import the necessary libraries. We then create a custom class for our web component, PlayerComponent, which extends the basic HTMLElement class. The class contains several observedAttributes; as the name implies, these are the attributes of the custom element that we want to observe.

When an observedAttribute is changed, the attributeChangedCallback function is triggered. The function has the properties name, oldValue and newValue, and we use the the name property to determine which code to run. For example, when name === 'source', this means that the source attribute of the element has been changed. In that case we want to load the new manifest URI, and apply other attributes if present, such as starttime.

As for the constructor, it's important to first call the constructor of the super class HTMLElement, to give our custom element its basic functionality. And rather than appending the elements inside our class to the DOM directly, we're using a shadow DOM: This encapsulates code inside our web component, ensuring that it doesn't affect anything outside it.

Besides attributeChangedCallback, we also have access to other callback functions, including connectedCallback and disconnectedCallback, which are run when the component is added to or removed from the DOM. In our case, we want to initiate an event proxy with our setupEventProxy function when the component is added, and reset the player when the component is removed.

Finally, in order for our custom element to be used in the regular DOM, we need to register it with customElements.define. This will allow us to add our PlayerComponent in our HTML under the name eyevinn-video.

That's it! We can now include as many eyevinn-video elements as we want on our webpage. We can also add our own style to the element with CSS. Keep in mind that for the super class HTMLElement, the default value of the display property is inline. In the example below, an HLS stream and an MPEG-DASH stream are playing on the same webpage, both using our web player component. The attributes starttime, muted and autoplay were set for both videos, resulting in automatic, muted (required by browsers for autoplay) playback starting at the specified times.

Web components with CSS

You might be wondering why we should go through the effort of making our own web components, instead of using iFrame: iFrames have been around forever, and are still the most common way of embedding external HTML on webpages. They are also supported by older browsers.

Web components, however, give greater control over what parts to include, and may offer both search engine optimization and performance benefits.

Discussion (0)