How to use hls.js with react

Leonardo Drici picture Leonardo Drici · Oct 20, 2018 · Viewed 7.8k times · Source

I need some help trying to figure how to use hls.js in react. Let me explain the situation I have to fetch the m3u8 from an api I'm able to make it work from a basic html with the <script> tag but when i try to implement it on react it doesn't work any help is appreciated. This is what I've got so far:

import React, { Component } from "react";

import PropTypes from "prop-types";
import { withStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper";
import Typography from "@material-ui/core/Typography";
import Grid from "@material-ui/core/Grid";
import ButtonBase from "@material-ui/core/ButtonBase";
import CircularProgress from "@material-ui/core/CircularProgress";

import Hls from "hls.js";

const styles = theme => ({
  root: {
    flexGrow: 1,
    paddingTop: theme.spacing.unit * 2,
    paddingBottom: theme.spacing.unit * 2,
    marginBottom: 24,
    marginLeft: 24,
    marginRight: 60
  },
  image: {
    marginLeft: 24,
    width: 200,
    height: 200
  },
  img: {
    display: "block",
    width: 200,
    height: 200,
    maxWidth: "100%",
    maxHeight: "100%"
  },
  detail: {
    marginLeft: 16
  },
  progress: {
    display: "flex",
    justifyContent: "center",
    alignItems: "center"
  }
});

class Video extends Component {
  constructor(props) {
    super(props);
  }

  componentWillReceiveProps(props) {
    if (props.episode && this.player) {
      var hlsUrl = props.episode.assets.hls;
      var video = this.player;
      if (video.canPlayType("application/vnd.apple.mpegurl")) {
        // If HLS is natively supported, let the browser do the work!
        video.src = "hlsUrl";
        video.addEventListener("loadedmetadata", function() {
          video.play();
        });
      } else if (Hls.isSupported()) {
        // If the browser supports MSE, use hls.js to play the video
        var hls = new Hls({
          // This configuration is required to insure that only the
          // viewer can access the content by sending a session cookie
          // to api.video service
          xhrSetup: function(xhr, url) {
            xhr.withCredentials = true;
          }
        });
        hls.loadSource(hlsUrl);
        hls.attachMedia(video);
        hls.on(Hls.Events.MANIFEST_PARSED, function() {
          video.play();
        });
      } else {
        alert("Please use a modern browser to play the video");
      }
    }
  }

  handleSerieClick = () => {
    this.props.history.push("/" + this.props.serie.apiName);
  };

  _onTouchInsidePlayer() {
    if (this.player.paused) {
      this.player.play();
    } else {
      this.player.pause();
    }
  }

  render() {
    const { classes, theme } = this.props;
    if (this.props.episode) {
      const { assets, title, description, videoId } = this.props.episode;
      return (
        <Grid className={classes.root} item xs={12}>
          <video
            controls
            onClick={this._onTouchInsidePlayer}
            ref={player => (this.player = player)}
            autoPlay={true}
          />
        </Grid>
      );
    } else {
      return (
        <Grid className={classes.progress} item xs={12}>
          <CircularProgress size={100} />
        </Grid>
      );
    }
  }
}

export default withStyles(styles, { withTheme: true })(Video);

This is the code that works with <script> tag

<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>

<video id="video"></video>

<script>
  var hlsUrl = 'https://cdn.libcast.net/stream/3de8ff01-18f7-4262-a1f2-abeeb9bb962b/hls/manifest.hls';
  var video = document.getElementById('video');
  if (video.canPlayType('application/vnd.apple.mpegurl')) {
    // If HLS is natively supported, let the browser do the work!
    video.src = 'hlsUrl';
    video.addEventListener('loadedmetadata',function() {
      video.play();
    });

  } else if (Hls.isSupported()) {
    // If the browser supports MSE, use hls.js to play the video
    var hls = new Hls({
      // This configuration is required to insure that only the 
      // viewer can access the content by sending a session cookie
      // to api.video service
      xhrSetup: function(xhr, url) { xhr.withCredentials = true; }
    });
    hls.loadSource(hlsUrl);
    hls.attachMedia(video);
    hls.on(Hls.Events.MANIFEST_PARSED,function() {
      video.play();
    });

  } else {
    alert('Please use a modern browser to play the video');
  }
</script>

I pass the hls source from a parent component in the props and in componentWillRecieveProps i try to use the source to run the player

EDIT

THe problem seems to be that <video> tag is undefined when I try to apply the source.

Answer

keul picture keul · Oct 20, 2018

Initing hls in componentWillReceiveProps is probably "too early". Refs are created during the render execution so your this.video is probably null at that time.

Try moving your logic into componentDidMount (if you pass proper props from the beginnig) or at least componentDidUpdate.