Gene Yllanes
Gene Yllanes

Reputation: 117

How do I publish a video on the OpenTok videochat API that is dynamically loaded from Google Firestore?

OpenTok has a demo that publishes a video from a file. I want to publish a video that is dynamically loaded from Firebase based on their basic demo but the publisher is not displaying the video I pull from firestore and I believe it has to do with when I call the publish function and async.

I thought that perhaps I should load the publisher once I get the URL of the firestore video I am looking for, so in the callback of fetching the URL I put the code that the demo used to publish the video. I thought that was the solution, but when the loaded video finally plays, the publisher does not play the video stream.

Here is the OpenTok demo that I reference

Demo: https://opentok.github.io/opentok-web-samples/Publish-Video/ Based on this repo: https://github.com/opentok/opentok-web-samples/tree/master/Publish-Video

Here is the codesandbox of my project: https://codepen.io/gene-yllanes/pen/MdoVYM

Here is the js code which handles the video:

storageRef.child(vid).getDownloadURL().then(function (url) {
  const video =  document.getElementById("video");
  video.src = url;
  // console.log(video)

  //Once I get the video I want, then I seek to publish it.
  //this is the code lifted directly from original demo
  //There has to be an easy way to do this, I hope you guys see so too

  (function closure() {
    const video = document.querySelector('#video');
    if (!video.captureStream) {
      alert('This browser does not support VideoElement.captureStream(). You must use Google Chrome.');
      return;
  }
  const stream = video.captureStream();
  console.log(stream)
  let publisher;
  const publish = () => {
    console.log("in publish")

    const videoTracks = stream.getVideoTracks();
    const audioTracks = stream.getAudioTracks();
    if (!publisher && videoTracks.length > 0 && audioTracks.length > 0) {
      console.log("!publisher && videoTracks.length > 0 && audioTracks.length > 0")
      stream.removeEventListener('addtrack', publish);
      publisher = OT.initPublisher('publisher', {
        videoSource: videoTracks[0],
        audioSource: audioTracks[0],
        fitMode: 'contain',
        width: 320,
        height: 240
      }, (err) => {
        if (err) {
          console.log("error")
          video.pause();
          alert(err.message);
        } else {
          console.log("vid plauy")
          video.play();
        }
      });
      publisher.on('destroyed', () => {
        video.pause();
      });
    }
  }
  stream.addEventListener('addtrack', publish);
  publish()

})()

Right now, publish is firing twice and I'm unsure why. Furthermore, the publisher is not pushing the stream that it explicitly says it has. Hopefully the community can easily sort this out for me.

Thanks in advance! Gene

P.s This demo is limited just to Chrome right now

Upvotes: 2

Views: 463

Answers (1)

Gene Yllanes
Gene Yllanes

Reputation: 117

The answer ended up being super simple when dynamically publishing videos

1)Once you receive the URL from Firestore of the video, set the src of video element to the new URL

const video =  document.getElementById("video");

// Get the download URL and switch vids
storageRef.child(vid).getDownloadURL().then(function (url) {
  video.src = url;
  // console.log("downloaded and updated")
  // console.log("video")
}).catch(function (error) { <stuff>}

2)Place an event listener on the video element so you can then trigger the publish function once the new video is loaded.

video.addEventListener('loadeddata', function() {
  //Create Stream object and change it if in mozilla
  const stream = video.mozCaptureStream ? video.mozCaptureStream() : video.captureStream();
  //console.log(stream)
  let publisher;

//publisher function is called when stream has tracks added to it
  const publish = () => {
    const videoTracks = stream.getVideoTracks();
    const audioTracks = stream.getAudioTracks();
    if (!publisher && videoTracks.length > 0 && audioTracks.length > 0) {
      stream.removeEventListener('addtrack', publish);
      publisher = OT.initPublisher('publisher', {
        videoSource: videoTracks[0],
        audioSource: audioTracks[0],
        fitMode: 'contain',
        width: 320,
        height: 240
      }, (err) => {
        if (err) {
          video.pause();
          alert(err.message);
        } else {
          video.play();
        }
      });
      publisher.on('destroyed', () => {
        video.pause();
      });
    }
  };

  stream.addEventListener('addtrack', publish);
  publish();

}, false);

Boom, you are offically publishing to the OpenTok video chat API with a video dynamically loaded from Firebase.

P.s VERY IMPORTANT When attempting to captureStream of a URL from Google Firestore, you run into issues with CORS. In order to deal with that issue, I first followed Google Firebase's guide on how to set the CORS rule for the specific bucket that I was drawing from and then I set the crossorigin value of my video in the HTML.

CORS Configuration from google documentation To download data directly in the browser, you must configure your Cloud Storage bucket for cross-origin access (CORS). This can be done with the gsutil command line tool, which you can install from here.

If you don't want any domain-based restrictions (the most common scenario), copy this JSON to a file named cors.json:

[
  {
    "origin": ["*"],
    "method": ["GET"],
    "maxAgeSeconds": 3600
  }
]

Run gsutil cors set cors.json gs://your-cloud-storage-bucketto deploy these restrictions.

Then, within the video tag on the HTML file, I added the crossorigin property

<video id="video" 
        src="video/BigBuckBunny_320x180.mp4" 
        crossorigin="Anonymous"
        width="320" height="240" 
        controls 
        autoplay
        loop
        muted
        >

      </video>

Second BOOM! you have now dealt with the CORS restrictions on your Firebase account and can dynamically publish video over video chat with OpenTok and Google Firestore

Upvotes: 3

Related Questions