aws-samples / amazon-ivs-player-web-sample

This project contains code samples demonstrating how to build, package, and integrate with the Amazon IVS Player Web SDK.
https://docs.aws.amazon.com/ivs/
MIT No Attribution
81 stars 32 forks source link

amazon-ivs-wasworker.min.wasm dependency not found #4

Closed FKholbutayev closed 3 years ago

FKholbutayev commented 4 years ago

I am trying to integrate amazon-ivs-player into nuxt project. Followed along sample code into nuxt project however getting this issue.

tonyjin commented 4 years ago

Hey @FKholbutayev we haven't tried using the player with Nuxt.js before. Can you share sample code or a repository that replicates the issue you're seeing?

dcharp05 commented 4 years ago

@tonyjin I am seeing the same issue as well. Im using the example code from the repro and am just trying to load this component in the next.js page.

import {
  create,
  isPlayerSupported,
  MediaPlayer,
  PlayerError,
  PlayerEventType,
  PlayerState,
  Quality,
  TextCue,
  TextMetadataCue,
} from 'amazon-ivs-player';
/**
 * These imports are loaded via the file-loader, and return the path to the asset.
 * We use the TypeScript compiler (TSC) to check types; it doesn't know what this WASM module is, so let's ignore the error it throws (TS2307).
 */
// @ts-ignore
import * as wasmBinaryPath from 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm';
import wasmWorkerPath from 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js';
import React from 'react';

class PlayerDemo {
  private player: MediaPlayer;
  private videoElement: HTMLVideoElement = document.querySelector('#video-player');

  constructor(player: MediaPlayer) {
    this.player = player;
    player.attachHTMLVideoElement(this.videoElement);
    this.attachListeners();

    const versionString: HTMLElement = document.querySelector('.version');
    versionString.innerText = `Amazon IVS Player version ${player.getVersion()}`;
  }

  loadAndPlay(stream: string) {
    const { player } = this;
    /**
     * With setAutoplay, we don't need to call play() here to try and start the stream. One of three things will happen:
     * - Autoplay with sound
     * - Autoplay muted
     * - Playback blocked
     * If autoplay is muted or blocked, the viewer will need to manually interact with the video player in order to unmute or start playback.
     * See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes for more info on autoplaying video and best practices.
     * */
    player.setAutoplay(true);
    player.load(stream);
  }

  destroy() {
    // Event listeners are automatically removed on player destruction
    this.player.delete();
  }

  private attachListeners() {
    const { player } = this;
    for (let state of Object.values(PlayerState)) {
      player.addEventListener(state, () => {
        console.log(state);
      });
    }

    player.addEventListener(PlayerEventType.INITIALIZED, () => {
      console.log('INITIALIZED');
    });

    player.addEventListener(PlayerEventType.ERROR, (error: PlayerError) => {
      console.error('ERROR', error);
    });

    player.addEventListener(PlayerEventType.QUALITY_CHANGED, (quality: Quality) => {
      console.log('QUALITY_CHANGED', quality);
    });

    // This event fires when text cues are encountered, such as captions or subtitles
    player.addEventListener(PlayerEventType.TEXT_CUE, (cue: TextCue) => {
      console.log('TEXT_CUE', cue.startTime, cue.text);
    });

    // This event fires when embedded Timed Metadata is encountered
    player.addEventListener(PlayerEventType.TEXT_METADATA_CUE, (cue: TextMetadataCue) => {
      console.log('Timed metadata', cue.text);
    });
  }
}

// This is the "quiz" stream, which contains Timed Metadata. See the README for more sample streams.
const defaultStream = 'https://fcc3ddae59ed.us-west-2.playback.live-video.net/api/video/v1/us-west-2.893648527354.channel.xhP3ExfcX8ON.m3u8';
const inputEl: HTMLInputElement = document.querySelector('.src-input');
let demo: PlayerDemo;

function setup() {
  /**
   * The IVS player can only be used in browsers which support WebAssembly.
   * You should use `isPlayerSupported` before calling `create`.
   * Otherwise, wrap `create` in a `try/catch` block, because an error will be thrown in browsers without WebAssembly support.
   */
  if (isPlayerSupported) {
    setupForm();
    setupPlayer();
  } else {
    console.error('IVS Player is not supported in this browser');
  }
}

function setupPlayer() {
  const createAbsolutePath = (assetPath: string) => new URL(assetPath, document.URL).toString();
  const player = create({
    wasmWorker: createAbsolutePath(wasmWorkerPath),
    wasmBinary: createAbsolutePath(wasmBinaryPath),
  });

  demo = new PlayerDemo(player);
  loadFormStream();

  /**
   * Add the demo and player to the window so that you can play around with them in the console.
   * This is not necessary for production.
   * */
  // @ts-ignore
  window.demo = demo;
  // @ts-ignore
  window.player = demo.player;
}

function setupForm() {
  // Set the stream to load using the `playbackUrl=` query param
  const params = new URLSearchParams(window.location.search);
  const streamParam = params.get('playbackUrl');
  if (streamParam) {
    inputEl.value = streamParam;
  }

  const formEl = document.querySelector('.src-container-direct');
  formEl.addEventListener('submit', (event) => {
    event.preventDefault();
    loadFormStream();
  })
}

function loadFormStream() {
  demo.loadAndPlay(inputEl.value || defaultStream);
}

export class LivePlayer extends React.Component {
  componentDidMount() {
    setup();
  }

  render() {
    return (
      <div className="demo">
        <div className="video-container">
          <form className="src-container-direct">
            <input className="src-input" placeholder="Enter IVS .m3u8" />
            <button className="src-submit" type="submit">Load</button>
          </form>
          <video id="video-player" playsinline controls></video>
          <div className='version'></div>
        </div>
      </div>
    );
  }
}
tonyjin commented 4 years ago

@dwhiz thanks for sharing the snippet, we'll take a look. To double check, you're seeing problems using Next.js right? The original poster mentioned they were using Nuxt.js.

dcharp05 commented 4 years ago

@tonyjin Yes I meant next.js, but I am getting the exact same error when trying to import the wasm module.

dcharp05 commented 4 years ago

@tonyjin I think it as something do with the way the wasm file is compiled. I tried nextjs example project and loaded the add.wasm with no issue. https://github.com/vercel/next.js/tree/canary/examples/with-webassembly

DT1000101 commented 4 years ago

I am having the same issue and am also working in next.js.

I also tired the web assembly next.js example and tried adding the 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js' file in place of the example and it wouldn't load.

In the meantime I have made a workaround using the alternative implementation (Setup With Script Tag) mentioned in https://docs.aws.amazon.com/ivs/latest/userguide/SWPG.html. But as next.js is react based, loading the script tag isn't as direct as including it in your html.. So here is my component in case it helps anyone till this issue is closed. I am having trouble creating multiple streams using this method though so keep that in mind and let me know if you have any idea why.

import React,{Component}          from 'react'

class Stream extends Component{

  constructor(props){
    super(props)
    this.ref = React.createRef()
  }

  componentDidMount(){
    let script = document.createElement('script');
    script.onload = function(){
      if (IVSPlayer.isPlayerSupported) {
        const player = IVSPlayer.create();
        player.attachHTMLVideoElement(document.getElementById('video-player'));
        player.load("https://fcc3ddae59ed.us-west-2.playback.live-video.net/api/video/v1/us-west-2.893648527354.channel.DmumNckWFTqz.m3u8");
        player.play();
      }
    }
    script.src = "https://player.live-video.net/1.0.0/amazon-ivs-player.min.js";
    const node = this.ref.current
    node.appendChild(script);
  }

  render(){

    return (
      <div ref={this.ref}>
          Stream!
          <div className="demo">
          <div className="video-container">
              <form className="src-container-direct">
                  <input className="src-input" placeholder="Enter IVS .m3u8"/>
                  <button className="src-submit" type="submit">Load</button>
              </form>
              <video id="video-player" playsInline controls></video>
              <div className='version'></div>
          </div>
        </div>
      </div>
    )
  }

}
export default Stream
tonyjin commented 4 years ago

Thanks for sharing the workaround! We've reproduced this issue internally and are working on a fix.

FKholbutayev commented 4 years ago

@tonyjin sorry for not replying earlier I was not working these days. I see other developers had similar issue. Nevertheless here the snippet of my code.

import Vue from 'vue'
import videojs from 'video.js';
import {
  VideoJSQualityPlugin,
  VideoJSIVSTech,
  registerIVSQualityPlugin,
  registerIVSTech,
  VideoJSEvents,
} from 'amazon-ivs-player';

import wasmWorkerPath from 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js';

// @ts-ignore
import wasmBinaryPath from 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm';

export default Vue.extend({

  methods: {
    createAbsolutePath: function(assetPath:string) {
      return new URL(assetPath, document.URL).toString();
    }
  },
  created() {
    registerIVSTech(videojs, {
      wasmWorker: this.createAbsolutePath(wasmWorkerPath),
      wasmBinary: this.createAbsolutePath(wasmBinaryPath)
    });
    registerIVSQualityPlugin(videojs);

    const player = videojs('videojs-player', {
      techOrder: ["AmazonIVS"],
      autoplay: true
    }, function() {
      console.warn('Player is ready to use')
    }) as videojs.Player & VideoJSIVSTech & VideoJSQualityPlugin;

    player.enableIVSQualityPlugin();

    const events: VideoJSEvents = player.getIVSEvents();
    const ivsPlayer = player.getIVSPlayer();
    ivsPlayer.addEventListener(events.PlayerState.PLAYING, () => { console.log('IVS Player is playing') });
    const defaultStream = PLAYBACK_URL;
    player.src(defaultStream);
  }
})
cm-wada-yusuke commented 4 years ago

Hi, I'm getting the same issue with React.js (SPA). I found out wasm files are not bundled at build. So, I did as follows:

{
          /**
           * Developers packaging the IVS player into an app are required to resolve and import the following assets via URL:
           *
           * 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm'
           * 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js';
           * 'amazon-ivs-player/dist/assets/amazon-ivs-worker.min.js';
           *
           * These assets must not be re-compiled during packaging.
           * The webpack file-loader (https://webpack.js.org/loaders/file-loader/) accomplishes this.
           * Rollup's plugin-url (https://github.com/rollup/plugins/tree/master/packages/url) also seems to do this, but has not been tested.
           */
          test: /[\/\\]amazon-ivs-player[\/\\].*dist[\/\\]assets[\/\\]/,
          loader: 'file-loader',
          type: 'javascript/auto',
          options: {
            name: '[name].[ext]',
          },
        }
import {
  registerIVSQualityPlugin,
  registerIVSTech,
  VideoJSEvents,
  VideoJSIVSTech,
  VideoJSQualityPlugin,
} from 'amazon-ivs-player';
import React, { useEffect, useRef } from 'react';
import videojs from 'video.js';

// Styles
import 'video.js/dist/video-js.css';

/**
 * These imports are loaded via the file-loader, and return the path to the asset.
 * We use the TypeScript compiler (TSC) to check types; it doesn't know what this WASM module is, so let's ignore the error it throws (TS2307).
 */
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
import wasmBinaryPath from 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm';
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
import wasmWorkerPath from 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js';

export type AmazonIVSOptions = {
  stream: string;
};

function AmazonIVS(options: AmazonIVSOptions) {
  const createAbsolutePath = (assetPath: string) =>
    new URL(assetPath, document.URL).toString();

  const videoEl = useRef<HTMLVideoElement>(null);
  useEffect(() => {
    registerIVSTech(videojs, {
      wasmWorker: createAbsolutePath(wasmWorkerPath),
      wasmBinary: createAbsolutePath(wasmBinaryPath),
    });
    // register the quality plugin
    registerIVSQualityPlugin(videojs);
    // create the player with the appropriate types. We're using @types/video.js VideoJsPlayer, and intersecting our Player and Quality Plugin interface
    const player = videojs(
      'videojs-player',
      {
        techOrder: ['AmazonIVS'],
        autoplay: true,
      },
      function () {
        console.warn('Player is ready to use');
      },
    ) as videojs.Player & VideoJSIVSTech & VideoJSQualityPlugin;

    player.enableIVSQualityPlugin();

    // listen to IVS specific events
    const events: VideoJSEvents = player.getIVSEvents();
    const ivsPlayer = player.getIVSPlayer();
    ivsPlayer.addEventListener(events.PlayerState.PLAYING, () => {
      console.log('IVS Player is playing');
    });
    player.src(
      'https://fcc3ddae59ed.us-west-2.playback.live-video.net/api/video/v1/us-west-2.893648527354.channel.DmumNckWFTqz.m3u8',
    );
  }, []);

  return (
    <video
      id="videojs-player"
      ref={videoEl}
      playsInline
      autoPlay
      height={300}
      controls
    />
  );
}

export default AmazonIVS;

n._emscripten_get_callstack_js is not a function Error occuered

image

Repo for Reproduce

https://github.com/cm-wada-yusuke/amazon-ivs-react-ts-sample

johnBartos commented 4 years ago

Thanks for the reports everyone. I just want to re-emphasize that our assets must not be transpiled by any build tool. We show how to to do that with Webpack's file loader in the sample webpack config.

boushley commented 4 years ago

As an example of what @johnBartos is calling out if you add this next.config.js (or add the relevant pieces to your existing config) you'll get past these errors. However even with this configuration you'll need to be sure that components using the IVS web player are not loaded during server side rendering (it looks like one way to do that would be with dynamic imports).

next.config.js

module.exports = {
  webpack(config) {
    config.module.rules.push({
        /**
        * Developers packaging the IVS player into an app are required to resolve and import the following assets via URL:
        *
        * 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm'
        * 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js';
        * 'amazon-ivs-player/dist/assets/amazon-ivs-worker.min.js';
        *
        * These assets must not be re-compiled during packaging.
        * The webpack file-loader (https://webpack.js.org/loaders/file-loader/) accomplishes this.
        * Rollup's plugin-url (https://github.com/rollup/plugins/tree/master/packages/url) also seems to do this, but has not been tested.
        */
        test: /[\/\\]amazon-ivs-player[\/\\].*dist[\/\\]assets[\/\\]/,
        loader: 'file-loader',
        type: 'javascript/auto',
        options: {
            name: '[name].[ext]',
            outputPath: 'static/ivs-player',
            publicPath: '/_next/static/ivs-player'
        }
    })
    return config
  },
}

I'll note internally that in future versions we should eliminate the hard dependency on browsers during import of the IVS player.

boushley commented 4 years ago

@FKholbutayev it looks like you should be able to use a similar setup just using nuxt.config.js instead of next.config.js. I believe you will also need to make the component that uses IVS load async to avoid Server Side Rendering similar to Next.js.

h0jeZvgoxFepBQ2C commented 4 years ago

Can somone tell me how to make this work with rails / react and webpacker (not plain webpack)? Really frustrating to get it working, when I try to add the webpack rule it looks like it's not working, I only get:

ERROR in ./node_modules/amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm
WebAssembly module is included in initial chunk.
This is not allowed, because WebAssembly download and compilation must happen asynchronous.
Add an async splitpoint (i. e. import()) somewhere between your entrypoint and the WebAssembly module:
johnBartos commented 4 years ago

@h0jeZvgoxFepBQ2C Thanks for the report, sorry to hear it's frustrating. I'm not familiar with webpacker, but what you need to do is load the WASM files "untouched" by webpack. So the same as you would for static assets like images. In webpack you'd use the file loader. Have you tried copying our file-loader example into webpacker? https://github.com/rails/webpacker/blob/master/docs/webpack.md#loaders

h0jeZvgoxFepBQ2C commented 4 years ago

Unfortunately it really doesn't work out. Right now I don't get any errors anymore, but the player doesn't show anything (no UI elements, no video) - but unfortunately I don't get any errors at all :(

I did following:

1) Added .wasm in config/webpacker.yml under extensions

  extensions:
    - .jsx
    - .js
    - .sass
    - .scss
    - .css
    - .module.sass
    - .module.scss
    - .module.css
    - .png
    - .svg
    - .gif
    - .jpeg
    - .jpg
    - .wasm

2) Added the loader in my config/webpack/environment.js

const { environment } = require('@rails/webpacker')

environment.loaders.append('amazon-ivs-player', {
  // Developers packaging the IVS player into an app are required to resolve and import the following assets via URL:
  //
  // 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm'
  // 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js';
  // 'amazon-ivs-player/dist/assets/amazon-ivs-worker.min.js';
  //
  // These assets must not be re-compiled during packaging.
  // The webpack file-loader (https://webpack.js.org/loaders/file-loader/) accomplishes this.
  // Rollup's plugin-url (https://github.com/rollup/plugins/tree/master/packages/url) also seems to do this, but has not been tested.
  test: /[\/\\]amazon-ivs-player[\/\\].*dist[\/\\]assets[\/\\]/,
  loader: 'file-loader',
  type: 'javascript/auto',
  options: {
    name: '[name].[ext]',

    // tried also with following:
    // outputPath: 'static/ivs-player',
    // publicPath: '/_next/static/ivs-player'
  },
})

module.exports = environment

3) Add code to my jsx files:

// THIS IS NOT WORKING, DONT COPY PASTE

import React, { useEffect, useRef, useState } from 'react';
import videojs from 'video.js';
import 'video.js/dist/video-js.css'
import {
    VideoJSQualityPlugin,
    VideoJSIVSTech,
    registerIVSQualityPlugin,
    registerIVSTech,
    VideoJSEvents,
} from 'amazon-ivs-player';

import wasmWorkerPath from 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js'
import wasmBinaryPath from 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm'

const createAbsolutePath = (assetPath) => new URL(assetPath, document.URL).toString();

registerIVSTech(videojs, {
    wasmWorker: createAbsolutePath(wasmWorkerPath),
    wasmBinary: createAbsolutePath(wasmBinaryPath),
});

// register the quality plugin
registerIVSQualityPlugin(videojs)

export default function AmazonIvsPlayer({videoUrl,...props }) {
  let player = null;
  useEffect(() => {
            player = videojs('amazon-ivs-videojs', {
                techOrder: ["AmazonIVS"], 
                options: {
                  wasmWorker: createAbsolutePath(wasmWorkerPath),
                  wasmBinary: createAbsolutePath(wasmBinaryPath),
                },
                src: videoUrl
            }, () => {
               // ready callback is never called also (so this line wont be reached)
               player.enableIVSQualityPlugin(); 
            });

            // if you console.log(player) here, it says player is null?!?
        } catch(e) {
          console.error("handled exception", e)
        }
      }, []}

  return (
              <div className="video-container">
                  <video id="amazon-ivs-videojs" className="video-js vjs-4-3 vjs-big-play-centered" controls autoPlay playsInline></video>
              </div>
  )
}

I tried multiple things (also move the register*Plugin and createAbsolutePath methods into the function), but none of them worked, showed errors.. This configuration looks like the best one - but doesn't work :(

rahul-nath commented 4 years ago

Thanks for sharing the workaround! We've reproduced this issue internally and are working on a fix.

Has there been headway made on this? I am seeing this when I don't include the web assembly files included in the distribution:

Couldn't make the player TypeError: Cannot read property 'asmWorker' of undefined
    at Se (index.js:6)
    at Video.js:87
    at commitHookEffectListMount (react-dom.development.js:19732)
    at commitPassiveHookEffects (react-dom.development.js:19770)
    at HTMLUnknownElement.callCallback (react-dom.development.js:189)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:238)
    at invokeGuardedCallback (react-dom.development.js:293)
    at flushPassiveEffectsImpl (react-dom.development.js:22854)
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11040)
    at flushPassiveEffects (react-dom.development.js:22821)
    at react-dom.development.js:22700
    at workLoop (scheduler.development.js:597)
    at flushWork (scheduler.development.js:552)
    at MessagePort.performWorkUntilDeadline (scheduler.development.js:164)

I am using Gatsby. I tried @boushley 's solution but within my gatsby.config file. Why does create need the paths to amazon-ivs-wasmworker.min.js' and amazon-ivs-wasmworker.min.wasm? It's unclear from the docs that this is necessary although I see it in the web snippet, which, while helpful, became more esoteric as I tried to apply the same method used but in a React environment.

h0jeZvgoxFepBQ2C commented 4 years ago

Yeah it would be much easier if these files come bundled :/

johnBartos commented 4 years ago

@rahul-nath Can you post your code where you instantiate the player? From what I can tell, that error is thrown when you call create with no arguments (which is incorrect)

Why does create need the paths to amazon-ivs-wasmworker.min.js' and amazon-ivs-wasmworker.min.wasm?

Because those files are the player. They are split into separate bundles because browsers require workers to be instantiated via URLs.

@h0jeZvgoxFepBQ2C

Yeah it would be much easier if these files come bundled :/

Unfortunately this is the reality of using WebAssembly. The WASM file needs to be in a separate file because it's instantiated via URL. We are exploring how we can make this easier, but we have no timeline right now.

h0jeZvgoxFepBQ2C commented 4 years ago

Ok, I managed it now make the paths available with the file-loader... But it doesn't work, no errors, just the black video player screen (no UI) :(

rahul-nath commented 4 years ago

@johnBartos This is what I have towards loading them with webpack.

  exports.onCreateWebpackConfig = ({ actions, loaders, getConfig }) => q{
    const config = getConfig()

    config.module.rules = [
      // Omit the default rule where test === '\.jsx?$'
      ...config.module.rules.filter(
        rule => String(rule.test) !== String(/\.jsx?$/)
      ),{
        /**
        * Developers packaging the IVS player into an app are required to resolve and import the following assets via URL:
        *
        * 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm'
        * 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js';
        * 'amazon-ivs-player/dist/assets/amazon-ivs-worker.min.js';
        *
        * These assets must not be re-compiled during packaging.
        * The webpack file-loader (https://webpack.js.org/loaders/file-loader/) accomplishes this.
        * Rollup's plugin-url (https://github.com/rollup/plugins/tree/master/packages/url) also seems to do this, but has not been tested.
        */
        test: /[\/\\]amazon-ivs-player[\/\\].*dist[\/\\]assets[\/\\]/,
        loader: 'file-loader',
        type: 'javascript/auto',
        options: {
            name: '[name].[ext]',
            outputPath: 'static/ivs-player',
            publicPath: '/_next/static/ivs-player'
        }
      }
    ]

    // This will completely replace the webpack config with the modified object.
    actions.replaceWebpackConfig(config)
  }

It is in a file known as gatsby-node.js, which can be read about here if it helps.

As for the player, I have a React component set up like so:

import React, { useState, useEffect } from "react"
import {
    create,
    isPlayerSupported,
    MediaPlayer,
    PlayerError,
    PlayerEventType,
    PlayerState,
    Quality,
    TextCue,
    TextMetadataCue,
} from 'amazon-ivs-player'

// what is going on here

/**
  const videoJsOptions = {
    autoplay: true,
    controls: true,
    sources: [{
      src: '/path/to/video.mp4',
      type: 'video/mp4'
    }]
  }

  return <VideoPlayer { ...videoJsOptions } />
*/

const defaultURL = "https://fcc3ddae59ed.us-west-2.playback.live-video.net/api/video/v1/us-west-2.893648527354.channel.DmumNckWFTqz.m3u8"

const VideoPlayer = ({ src, wasmBinaryPath, wasmWorkerPath }) => {

  const [player, setPlayer] = useState()
  const [url, setURL] = useState()

  const attachListenersAndPlay = () => {

      if(!player) return

      for (let state of Object.values(PlayerState)) {
          player.addEventListener(state, () => {
              console.log(state);
          });
      }

      player.addEventListener(PlayerEventType.INITIALIZED, () => {
          console.log('INITIALIZED');
      });

      player.addEventListener(PlayerEventType.ERROR, (error) => {
          console.error('ERROR', error);
      });

      player.addEventListener(PlayerEventType.QUALITY_CHANGED, (quality) => {
          console.log('QUALITY_CHANGED', quality);
      });

      // This event fires when text cues are encountered, such as captions or subtitles
      player.addEventListener(PlayerEventType.TEXT_CUE, (cue) => {
          console.log('TEXT_CUE', cue.startTime, cue.text);
      });

      // This event fires when embedded Timed Metadata is encountered
      player.addEventListener(PlayerEventType.TEXT_METADATA_CUE, (cue) => {
          console.log('Timed metadata', cue.text);
      });
      console.log("this is the url")
      player.setAutoplay(true)
      player.load(url)
  }

  useEffect(attachListenersAndPlay, [player])

  useEffect(() => {
    /**
     * The IVS player can only be used in browsers which support WebAssembly.
     * You should use `isPlayerSupported` before calling `create`.
     * Otherwise, wrap `create` in a `try/catch` block, because an error will be thrown in browsers without WebAssembly support.

     */
    src ? setURL(src) : setURL(defaultURL)
    let ivsPlayer
    if (isPlayerSupported) {
      try {
        ivsPlayer = create();
        setPlayer(ivsPlayer)
      } catch (e) {
        console.log("Couldn't make the player", e)
      }

      // const createAbsolutePath = (assetPath) => new URL(assetPath, document.URL).toString();
      // {
      //     wasmWorker: createAbsolutePath('amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js'),
      //     wasmBinary: createAbsolutePath('amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm'),
      // }

    } else {
        console.error('IVS Player is not supported in this browser');
    }

    return ivsPlayer && ivsPlayer.delete()
  }, [src])

  return (
    <video id="video-player" playsInline controls></video>
  )

}

export default VideoPlayer

Thanks for the reports everyone. I just want to re-emphasize that our assets must not be transpiled by any build tool. We show how to to do that with Webpack's file loader in the sample webpack config.

I'm fairly new to the details of Webpack. It seems the only way to load the wasm is transpiling it because Gatsby and other SSGs and other SSR-like What to do then?

The errors I see are as so:


 ERROR #98124  WEBPACK

Generating development JavaScript bundle failed

Can't resolve 'a' in '/Users/me/written_things/work/InHouseLive/inhouselive/node_modules/amazon-ivs-player/dist/assets'

Perhaps you need to install the package 'a'?

File: node_modules/amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm

 ERROR #98123  WEBPACK

Generating development JavaScript bundle failed

WebAssembly module is included in initial chunk.
This is not allowed, because WebAssembly download and compilation must happen asynchronous.
Add an async splitpoint (i. e. import()) somewhere between your entrypoint and the WebAssembly module:
* multi ./node_modules/event-source-polyfill/src/eventsource.js (webpack)-hot-middleware/client.js?path=/__webpack_hmr&reload=true&overlay=false ./.cache/app --> ./.cache/app.js --> ./.cache/sync-requires.js --> ./src/pages/app.js --> ./src/templates/Livestream.js --> ./node_modules/amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm

File: node_modules/amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm

failed Building development bundle - 26.002s
rahul-nath commented 4 years ago

Ok, I managed it now make the paths available with the file-loader... But it doesn't work, no errors, just the black video player screen (no UI) :(

Exactly where I'm at now. The player is stuck on "pause" when player.isPaused() is called.

h0jeZvgoxFepBQ2C commented 4 years ago

@johnBartos is there any debug flag which we can activate to output something helpfull?

johnBartos commented 4 years ago

@h0jeZvgoxFepBQ2C @rahul-nath Is there anything logged in the console? Let me check that the test streams are working.

johnBartos commented 4 years ago

The test stream https://fcc3ddae59ed.us-west-2.playback.live-video.net/api/video/v1/us-west-2.893648527354.channel.DmumNckWFTqz.m3u8 works on my end.

It seems the only way to load the wasm is transpiling it because Gatsby and other SSGs and other SSR-like What to do then?

SSR libraries needs to be told to ignore the wasm files. For now you also need to ignore the JS files. We are currently working on a fix so that you shouldn't need to exclude JS files in the future.

h0jeZvgoxFepBQ2C commented 4 years ago

@johnBartos Unfortunately there is not a single log entry... I can access the passed wasm + js path in my browser, so the passed URLs should be correct. Also they are not transpiled on my setup and included with the webpack-file-loader, so it should be fine 🤔

h0jeZvgoxFepBQ2C commented 4 years ago

I couldn't get it running, only with following workaround it works: https://github.com/cm-wada-yusuke/amazon-ivs-react-js-sample/blob/master/src/AmazonIVSWorkaround.js

johnBartos commented 4 years ago

@h0jeZvgoxFepBQ2C That's our script tag build, which automatically sets up the WASM worker. It can do that because we host the wasm files ourselves. When building from NPM, you need to virtually "host" the wasm files via the file-loader.

Can you post a full page screenshot of your page which includes the console? I am not sure what our next steps are given that there are no logs or errors to dig into.

johnBartos commented 4 years ago

@h0jeZvgoxFepBQ2C Also, please call player.setLogLevel('debug') to enable debug logging. Call this after instantiation, but before load.

veromarchand commented 4 years ago

I am having similar issues. Basically I have trouble getting a combination of IVS with VideoJS implementation to work with React.

My app crashes at this line:

import { registerIVSTech } from 'amazon-ivs-player';

I get (truncated error string):

webpack:///./node_modules/amazon-ivs-player/dist/index.js?:6
module.exports=function(e){var t={};function i(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=e,i.c=t,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i(i.s=25)}([function(e,t){e.exports=__w

ReferenceError: navigator is not defined
    at Object.eval (webpack:///./node_modules/amazon-ivs-player/dist/index.js?:6:2473)

I followed the documentation in AWS here but can't even seem to be able to import the library...? What am I missing?

h0jeZvgoxFepBQ2C commented 4 years ago

Man this player is really a pain to implement.. We solved the wasm not found error by configuring webpacker correctly and setting mime types in nginx.. And then it works, but only in browsers != chrome || OS != windows.

If you open the developer console, it works fine. But with closed developer tools the browser just does nothing, but eats up more and more ram, until it freezes completely. This has been reliably reproduced with 4 colleagues. I really don't know what to do anymore, the logs don't show anything related. It also works when connecting over lan to a local machine, but after deployed to a server it only shows the mentioned behaviour. I've never spent so much time to implement a player, wth.. :(

tonyjin commented 4 years ago

Hey folks, thanks for the feedback. We'll work on creating a basic example with React & video.js.

@h0jeZvgoxFepBQ2C and @veromarchand do you mind opening separate issues with sample code (ideally a GitHub repository we can clone) so we can try to reproduce on our end?

Edit - the player should work on Chrome and Windows, but I understand it can be tricky to load the assets properly.

jokester commented 4 years ago

The file-loader way can be tricky for those who are not webpack pro, or with an already complicated webpack configuration.

I ended up hosting these assets as static file. Maybe one can also load them from CDN.

tonyjin commented 4 years ago

Our player assets are also hosted on Cloudfront, you can see where to find them here: https://docs.aws.amazon.com/ivs/latest/userguide/IVSPRN.html#jul1520.

johnBartos commented 4 years ago

https://codepen.io/amazon-ivs/pen/c3b13a2df34b60ada7756f3a2af8d2f0 Here's our codepen showing how to load the player from our CDN.

yeukfei02 commented 3 years ago

use next.js encounter the same problem

Attempted import error: 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm' does not contain a default export (imported as 'wasmBinaryPath').
tonyjin commented 3 years ago

Hey @yeukfei02 do you mind filing a new issue with details about your setup? This issue is becoming a bit overloaded and I'm going to close it out.

For others in this issue, we plan to look into a way to include the player via NPM, but have it use static assets hosted by IVS. We expect that will provide an easier way to integrate the player via NPM while keeping the current method for advanced use cases.

smhatre59 commented 3 years ago

I found an easy and straight forward way to use Amazon IVS with video.js tech script for react projects in this repository:- https://github.com/anoop4bhat/UGCAppRepo/tree/main/web-ui

I am using create-react-app for my project setup and found it easier to append the base IVS and IVS-videojs-tech scripts using javascript rather than trying to use complicated WASM file module loader implementations. The base URLs for both the scripts can be found in index.html file added in the above repo:- https://github.com/anoop4bhat/UGCAppRepo/blob/main/web-ui/public/index.html

This might not be good approach for everyone but hope it helps some of you.

ColtraneNadler commented 1 year ago

I am trying to integrate amazon-ivs-player into nuxt project. Followed along sample code into nuxt project however getting this issue.

  • a in ./node_modules/amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm friendly-errors 16:57:06

issue with detail

Were you ever able to resolve this? I'm currently getting the same issue with a nuxt.js app...

[nitro] [dev] [unhandledRejection] {                                                       18:43:43
  message: 'WebAssembly support is required for AmazonIVS tech',
  code: 2
}
brendanfurtado commented 1 year ago

I know this is an old thread but combining some answers above led me to a solution for a NextJS Application.

First create a script tag in RootLayout with src="https://player.live-video.net/1.22.0/amazon-ivs-player.min.js"

import Script from "next/script";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <Script src="https://player.live-video.net/1.22.0/amazon-ivs-player.min.js" />
      <body className={inter.className}>
            {{Other code....}}
      </body>
   </html>

)

My next.config.js file:

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: true,
    nextScriptWorkers: true,
  },
  webpack(config) {
    config.experiments = {
      asyncWebAssembly: true,
      layers: true,
    };
    config.module.rules.push({
      /**
       * Developers packaging the IVS player into an app are required to resolve and import the following assets via URL:
       *
       * 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm'
       * 'amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js';
       * 'amazon-ivs-player/dist/assets/amazon-ivs-worker.min.js';
       *
       * These assets must not be re-compiled during packaging.
       * The webpack file-loader (https://webpack.js.org/loaders/file-loader/) accomplishes this.
       * Rollup's plugin-url (https://github.com/rollup/plugins/tree/master/packages/url) also seems to do this, but has not been tested.
       */
      test: /[\/\\]amazon-ivs-player[\/\\].*dist[\/\\]assets[\/\\]/,
      loader: "file-loader",
      type: "javascript/auto",
      options: {
        name: "[name].[ext]",
        outputPath: "static/ivs-player",
        publicPath: "/_next/static/ivs-player",
      },
    });
    return config;
  },
};

module.exports = nextConfig;

My VideoFrame code that then takes in the playback url and now I'm able to see the stream that is live if created:

import React, { useRef, useEffect } from "react";
import videojs from "video.js";
import "video.js/dist/video-js.css";
import {
  VideoJSQualityPlugin,
  VideoJSIVSTech,
  registerIVSQualityPlugin,
  registerIVSTech,
  VideoJSEvents,
} from "amazon-ivs-player";

import "video.js/dist/video-js.css";

/**
 * These imports are loaded via the file-loader, and return the path to the asset.
 * We use the TypeScript compiler (TSC) to check types; it doesn't know what this WASM module is, so let's ignore the error it throws (TS2307).
 */
// @ts-ignore
import wasmBinaryPath from "amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.wasm";
// @ts-ignore
import wasmWorkerPath from "amazon-ivs-player/dist/assets/amazon-ivs-wasmworker.min.js";

export type AmazonIVSOptions = {
  stream: string;
};

const VideoFrame: React.FC<any> = () => {
  var playbackURL =
    "{YOUR_PLAYBACK_URL}";

  const createAbsolutePath = (assetPath: string) =>
    new URL(assetPath, document.URL).toString();

  useEffect(() => {
    registerIVSTech(videojs, {
      wasmWorker: createAbsolutePath(wasmWorkerPath),
      wasmBinary: createAbsolutePath(wasmBinaryPath),
    });
    // register the quality plugin
    registerIVSQualityPlugin(videojs);
    const player = videojs(
      "video-js-amazon-IVS",
      {
        techOrder: ["AmazonIVS"],
        autoplay: true,
      },
      () => {
        console.log("player is ready");
        player.src(playbackURL);
        player.play();
      }
    );

    return () => {
      player.dispose();
    };
  }, [playbackURL]);

  return (
    <div data-vjs-player>
      <video id="video-js-amazon-IVS" className="video-js" />
    </div>
  );
};

export default VideoFrame;

Dev Dependencies:

  "devDependencies": {
    "@builder.io/partytown": "^0.8.1",
    "@types/qs": "^6.9.8",
    "file-loader": "^6.2.0",
    "video.js": "^8.6.1"
  }

and obviously "amazon-ivs-player": "^1.22.0" within a dependency.

Let me know if this works for any one in the future!

johnBartos commented 1 year ago

@brendanfurtado Thanks for providing this!

mohamadlotfy commented 7 months ago

@brendanfurtado I'm trying out the implementation you are provided. It gets the player working, However, I'm facing an issue that the IVS player opened threads are not closed when disposing the player. So whenever the player is rendered a new worker thread for the IVS player is opened. Accumulating web worker threads causing a memory leak. Are you facing the same issue ?