GithubHelp home page GithubHelp logo

opentok / opentok-react Goto Github PK

View Code? Open in Web Editor NEW
107.0 15.0 105.0 667 KB

React components for OpenTok.js

Home Page: https://www.npmjs.com/package/opentok-react

License: MIT License

JavaScript 91.46% HTML 0.37% TypeScript 8.18%
react react-components opentok tokbox webrtc

opentok-react's Introduction

⚠️ This repository is now deprecated ⚠️



Please consider using OpenTok Web Components instead.



An example React project can be found here








opentok-react

npm version Build Status

React components for OpenTok.js

Contents

Pre-Requisites

  1. NodeJS
  2. Register a TokBox account: https://tokbox.com/account/user/signup

Install

Add opentok-react as a dependency of your application:

yarn add opentok-react

Or if you're still using npm:

npm install --save opentok-react

Then include opentok.js before your application:

<script src="https://static.opentok.com/v2/js/opentok.min.js"></script>

Alternatively, wrap your top-level component using OpenTok with the preloadScript HOC. The HOC will take care of loading opentok.js for you before rendering.

Example App

There is an example application provided in example/ and you can run it with the following steps:

  1. git clone https://github.com/opentok/opentok-react.git
  2. cd opentok-react/example/
  3. cp config.template.js config.js
  4. Edit config.js:
  5. Add your OpenTok API key, Session ID and Token (https://tokbox.com/account/)
  6. Add your Chrome Extension ID (https://tokbox.com/developer/guides/screen-sharing/js/)
  7. cd ..
  8. yarn (or npm install)
  9. npm run example
  10. Visit http://localhost:8000 in your browser

Refer to the App.js, Publisher.js and Subscriber.js files in example/components/ for library usage.

Usage

The following sections explains how to import and use opentok-react in your React application.

Importing opentok-react

Import the opentok-react components into your React application:

import { OTSession, OTPublisher, OTStreams, OTSubscriber } from 'opentok-react';

Or require it if you need to use CommonJS modules:

const { OTSession, OTPublisher, OTStreams, OTSubscriber } = require('opentok-react');

Example with OTSession Component

class MyApp extends React.Component {
  render() {
    return (
      <OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
        <OTPublisher />
        <OTStreams>
          <OTSubscriber />
        </OTStreams>
      </OTSession>
    );
  }
}

Example with createSession Helper

class MyApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = { streams: [] };
  }

  componentWillMount() {
    this.sessionHelper = createSession({
      apiKey: 'your-api-key',
      sessionId: 'your-session-id',
      token: 'your-session-token',
      onStreamsUpdated: streams => { this.setState({ streams }); }
    });
  }

  componentWillUnmount() {
    this.sessionHelper.disconnect();
  }

  render() {
    return (
      <div>
        <OTPublisher session={this.sessionHelper.session} />

        {this.state.streams.map(stream => {
          return (
            <OTSubscriber
              key={stream.id}
              session={this.sessionHelper.session}
              stream={stream}
            />
          );
        })}
      </div>
    );
  }
}

API Reference

The opentok-react library comprises of:

  • OTSession Component
  • OTPublisher Component
  • OTStreams Component
  • OTSubscriber Component
  • createSession Helper
  • preloadScript Higher-Order Component

OTSession Component

Prop Type Required Description
apiKey String Yes TokBox API Key
sessionId String Yes TokBox Session ID
token String Yes TokBox token
options Object No TokBox options options
eventHandlers Object<Function> No Event handlers passed into session.on
onConnect Function() No Invoked when session.connect successfully completes
onError Function(err) No Invoked when session.connect fails

The OTSession component manages the connection to an OpenTok Session. It passes the Session instance as the session prop to its child components. It is recommended that you nest OTPublisher and OTStreams inside OTSession:

<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
  <OTPublisher />
  <OTStreams>
    <OTSubscriber />
  </OTStreams>
</OTSession>

OTPublisher Component

Prop Type Required Description
session Session No OpenTok Session instance. This is auto populated by wrapping OTPublisher with OTSession
properties Object No Properties passed into OT.initPublisher
eventHandlers Object<Function> No Event handlers passed into publisher.on
onInit Function() No Invoked when OT.initPublisher successfully completes
onPublish Function() No Invoked when session.publish successfully completes
onError Function(err) No Invoked when either OT.initPublisher or session.publish fail

The OTPublisher component will initialise a publisher and publish to a specified session upon mounting. It will also ensure the Publisher video element is attached to the DOM inside the component rather than appending to the body (which is the Publisher's default behaviour).

<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
  <OTPublisher />
</OTSession>

If you are not using OTSession then you must specify a Session object using the session prop.

<OTPublisher session={this.sessionHelper.session} />

Use the properties prop to specify a properties object for OT.initPublisher and the eventHandlers prop to specify an object of event handlers for Publisher#on.

class MyApp extends React.Component {
  constructor(props) {
    super(props);

    this.publisherProperties = {
      audioFallbackEnabled: false,
      showControls: false
    };

    this.publisherEventHandlers = {
      streamCreated: event => {
        console.log('Publisher stream created!');
      },
      streamDestroyed: event => {
        console.log('Publisher stream destroyed!');
      }
    };
  }

  render() {
    return (
      <OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
        <OTPublisher
          properties={this.publisherProperties}
          eventHandlers={this.publisherEventHandlers}
        />
      </OTSession>
    );
  }
}

The properties prop is used for initial set up of the Publisher and making changes to it will not update the Publisher, you will instead need to invoke Publisher methods. To facilitate this the Publisher instance is exposed via the getPublisher() component method. You should always call this method to get the latest Publisher instance instead of keeping a reference to it as it's possible for the Publisher instance to change, leaving you with a stale reference.

However, for convenience the OTPublisher does watch for changes on a few keys of the properties object and makes the necessary changes. Currently these are:

Publisher Property Action
videoSource Destroys and recreates the Publisher with the new video source. This is the only way to change the video source of a Publisher. This is used in the example application to allow the user to toggle between publishing their camera and publishing their screen
publishAudio Calls publisher.publishAudio() to toggle audio on and off
publishVideo Calls publisher.publishVideo() to toggle video on and off

There are plans to support more Publisher properties but for now you will have to call getPublisher() to retrieve the Publisher instance and make the necessary changes yourself.

You can also get access to the publisher object by calling the getPublisher method using a Ref. For example:

class MyApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      publisher: null,
    };
    this.otPublisher = React.createRef();
  }

  componentDidMount() {
    this.getPublisher();
  }

  getPublisher() {
    if (this.otPublisher) {
      this.setState({
        publisher: this.otPublisher.current.getPublisher(),
      });
    }
  }

  render() {
    return (
      <OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
        <OTPublisher
          ref={this.otPublisher}
        />
      </OTSession>
    );
  }
}

OTStreams Component

Prop Type Required Description
children ReactElement Yes Must have a single child component that accepts session and stream props, eg. OTSubscriber
session Session Yes OpenTok Session instance. This is auto populated by wrapping OTStreams with OTSession
streams Array<Stream> No Array of OpenTok Stream instances. This is auto populated by wrapping OTStreams with OTSession

OTSubscriber Component

Prop Type Required Description
session Session No OpenTok Session instance. This is auto populated by wrapping OTSubscriber with OTStreams
stream Stream No OpenTok Stream instance. This is auto populated by wrapping OTSubscriber with OTStreams
properties Object No Properties passed into session.subscribe
retry Boolean No Set true to retry the subscribe process in case of failure (Default: false)
maxRetryAttempts Number No Max retry attempts in case of subscribe failure (Default: 5)
retryAttemptTimeout Number No Timeout value between every subscribe retry attempt, expressed in ms (Default: 1000ms)
eventHandlers Object<Function> No Event handlers passed into subscriber.on
onSubscribe Function() No Invoked when session.subscribe successfully completes
onError Function(err) No Invoked when session.subscribe fails

The OTSubscriber component will subscribe to a specified stream from a specified session upon mounting. It will also ensure the Subscriber video element is attached to the DOM inside the component rather than appending to the body (which is the Subscriber's default behaviour).

<OTStreams>
  <OTSubscriber />
</OTStreams>

If you are not using OTStreams then you must provide a Stream object using the stream prop and a Session object using the session prop.

{this.sessionHelper.streams.map(stream => (
  <OTSubscriber
    key={stream.id}
    session={this.sessionHelper.session}
    stream={stream}
  />
))}

Use the properties prop to specify a properties object for session.subscribe and the eventHandlers prop to specify an object of event handlers for Subscriber#on.

class MyApp extends React.Component {
  constructor(props) {
    super(props);

    this.subscriberProperties = {
      preferredFrameRate: 15,
      showControls: false
    };

    this.subscriberEventHandlers = {
      videoDisabled: event => {
        console.log('Subscriber video disabled!');
      },
      videoEnabled: event => {
        console.log('Subscriber video enabled!');
      }
    };
  }

  render() {
    return (
      <OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
        <OTStreams>
          <OTSubscriber
            properties={this.subscriberProperties}
            eventHandlers={this.subscriberEventHandlers}
          />
        </OTStreams>
      </OTSession>
    );
  }
}

The properties prop is used for initial set up of the Subscriber and making changes to it will not update the Subscriber, you will instead need to invoke Subscriber methods. To facilitate this the Subscriber instance is exposed via the getSubscriber() component method. You should always call this method to get the latest Subscriber instance instead of keeping a reference to it as it's possible for the Subscriber instance to change, leaving you with a stale reference.

However, for convenience the OTSubscriber does watch for changes on a few keys of the properties object and makes the necessary changes. Currently these are:

Subscriber Property Action
subscribeToAudio Calls subscriber.subscribeToAudio() to toggle audio on and off
subscribeToVideo Calls subscriber.subscribeToVideo() to toggle video on and off

There are plans to support more Subscriber properties but for now you will have to call getSubscriber() to retrieve the Subscriber instance and make the necessary changes yourself.

You can also get access to the subscriber object by calling the getSubscriber method using a Ref. For example:

class MyApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      subscriber: null,
    };
    this.otSubscriber = React.createRef();
  }

  componentDidMount() {
    this.getSubscriber();
  }

  getSubscriber() {
    if (this.otSubscriber) {
      this.setState({
        subscriber: this.otSubscriber.current.getSubscriber(),
      });
    }
  }

  render() {
    return (
      <OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
        <OTStreams>
          <OTSubscriber
            ref={this.otSubscriber}
          />
        </OTStreams>
      </OTSession>
    );
  }
}

createSession Helper

The createSession helper has been provided to easily create a session and monitor the current list of subscriber streams.

class MyApp extends React.Component {
  componentWillMount() {
    this.sessionHelper = createSession({
      apiKey: 'your-api-key',
      sessionId: 'your-session-id',
      token: 'your-session-token',
      onStreamsUpdated: streams => {
        console.log('Current subscriber streams:', streams);
      }
    });
  }

  componentWillUnmount() {
    this.sessionHelper.disconnect();
  }
}

The createSession helper returns an object with the following properties:

  • session - The Session instance.
  • streams - An up-to-date array of Stream instances.
  • disconnect - A clean up function. Call this when your component unmounts.

Use of this helper is optional and you can instead use the OTSession component or directly call OT.initSession and listen to streamCreated events if you prefer.

preloadScript Higher-Order Component

Prop Type Required Description
opentokClientUrl String No The URL of the OpenTok client script to load. It defaults to https://static.opentok.com/v2/js/opentok.min.js.
loadingDelegate ReactElement No An element that will be displayed while the OpenTok client script is loading. It defaults to an empty <div />.

In larger applications, one might not want to load the opentok.js client with a <script> tag all the time. The preloadScript higher-order component will do this for you at the appropriate time.

For example, imagine you have a React Router application with the following route structure:

<Router>
  <Route path="/">
    <IndexRoute component="..." />
    <Route path="something" component="..." />
    <Route path="video" component={VideoChat} />
    <Route path="something-else" component="..." />
  </Route>
</Router>

What you'd like to do is delay the loading of opentok.js until the VideoChat component is being used. Here's how you can do this:

class VideoChat extends React.Component {
  // All the code of your component
}

export default preloadScript(App);

Custom Build

  1. git clone https://github.com/opentok/opentok-react.git
  2. cd opentok-react/
  3. yarn (or npm install)
  4. Modify code in src/
  5. npm run build
  6. Check that files in dist/ have been updated.

Contributing

If you make changes to the project that you would like to contribute back then please follow the contributing guidelines. All contributions are greatly appreciated!

Tests

Run the unit tests locally with the following command:

npm run unit

By default this will launch the Chrome browser. To run tests in Firefox use:

npm run unit -- --browsers Firefox

Run the linter with:

npm run lint

The unit tests are automatically run on Travis on both Chrome and Firefox and the current build status is shown at the top of this document.

About

Originally authored by Aiham Hammami. Currently maintained by OpenTok Engineers and community members. Please note that this is not officially supported by TokBox.

opentok-react's People

Contributors

abdulajet avatar aiham avatar ashifa454 avatar devwithzachary avatar emguide avatar enricop89 avatar gasi avatar ggoldens avatar janjakubnanista avatar julien-meichelbeck avatar mheap avatar michaeljolley avatar michaellopez avatar msach22 avatar pronebird avatar sreuter avatar veligura avatar ziad-saab avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

opentok-react's Issues

Document getSubscriber

It mentions in the README that in order to manipulate the instance of OT.Publisher you need to use the getPublisher method of the OTPublisher component.

There is currently no documentation on this method, and the only example is in a test where it is being used to access the component instance via Enzyme. This is not how components are used in the real world, there needs to be documentation on usage of the method or an example of using it in an app.

OT is not defined

Hi,

I'm trying out TokBox with Meteor and React and I'm running into a problem.

In my file I've imported the following:
import { OTSession, OTPublisher, OTStreams, OTSubscriber, createSession } from 'opentok-react';

And I have this code:

{
          this.state.token && video.currentSessionId && video.sessionOn &&
          <OTSession
            apiKey={Meteor.settings.public.toxbox.apiKey}
            sessionId={video.currentSessionId}
            token={this.state.token}
          >
            <OTPublisher />
            <OTStreams>
              <OTSubscriber />
            </OTStreams>
          </OTSession>
        }

However, whenever I start a session, I get this error
_Exception from Tracker recompute function:
meteor.js?hash=e3f53db…:930 ReferenceError: OT is not defined
at createSession (modules.js?hash=97a78a9…:64952)
at OTSession.componentWillMount (modules.js?hash=97a78a9…:64850)
at modules.js?hash=97a78a9…:16742
at measureLifeCyclePerf (modules.js?hash=97a78a9…:16469)
at ReactCompositeComponentWrapper.performInitialMount (modules.js?hash=97a78a9…:16741)
at ReactCompositeComponentWrapper.mountComponent (modules.js?hash=97a78a9…:16652)
at Object.mountComponent (modules.js?hash=97a78a9…:9577)
at Object.updateChildren (modules.js?hash=97a78a9…:16215)
at ReactDOMComponent._reconcilerUpdateChildren (modules.js?hash=97a78a9…:15731)
at ReactDOMComponent.updateChildren (modules.js?hash=97a78a9…:15835)

Any idea what could be causing this problem?

OT is not defined React.js

I am trying to use OT.checkSystemRequirements() but when I am using it's giving an error OT is not defined I have installed the library and added script file can someone tell me how to get OT.

Publisher breaks when insertDefaultUI = false is used

OTK library throws error when insertDefaultUI: false:

You cannot specify a target element if insertDefaultUI is false

So I guess the easy fix would be to check properties.insertDefaultUI and provide false instead of container

var publisher = OT.initPublisher(container, this.props.properties);

Initial orientation of session is opposite of actual device orientation

Hi OT team!

Noticed that if a user starts a session on a tablet(iPad) in portrait orientation, they will still appear in landscape orientation when the session begins; they appear sideways. Discovered that this can be resolved by changing the orientation of the tablet as opentok will update the orientation and the session remains in the correct orientation no matter how many times the tablet is rotated.

Is there any way to set the initial orientation of the session to match the initial orientation of the device?

Thanks for any advice you can offer!

The switch between camera and screen is too harsh

    if (shouldUpdate('videoSource', undefined)) {
      this.destroyPublisher();
      this.createPublisher();
      return;
    }

When you switch video source the transition is really harsh. You essentially drop off the call while the new publisher is created and connects to the stream - and in the case of switching to screen sharing, you are off the call for the entire duration of time you select what windows/applications you want to share.

I'd suggest improving this transition so that the existing publisher isn't destroyed until the new publisher is publishing to the stream.

Disable / enable camera & microphone

Hi,

I would like to add some callback functions that will block the camera or microphone. Do you have any implementations currently for doing this?

Kind regards,
Niels.

Only add session/streams props to child elements that accept them

If you add an element such as a <div> inside OTSession or OTStreams you get this warning:

Warning: Unknown props `session`, `streams` on <div> tag. Remove these props from the element. For details, see https://fb.me/react-unknown-prop

These props should only be added if the child element lists them in their propTypes.

Unable to set `publishVideo` to false

When I set on a OTPublisher publishVideo property to false, I get the following error message:

opentok.js:19601 Video element paused, auto-resuming. If you intended to do this, use publishVideo(false) or subscribeToVideo(false) instead.
(anonymous) @ opentok.js:19601
_playVideoOnPause @ opentok.js:24789

And the subscriber continue to receive the video stream.

opentok-react:0.7, @opentok/client :2.14.7

Error from latest @opentok/client library

The latest library complains about passing class names. It receives from opentok-react the class names "OT_root OT_subscriber" and prints an error:
logging.warn('passing classNames to widgetView is deprecated');

image

I am using

"@opentok/client": "2.15.3",
"opentok-react": "0.8.0"

OTPublisher remount issue

Hello,

I am encountering a number of JavaScript console errors when unmounting, then subsequently remounting the OTPublisher component based on browser device permissions.

I am currently using macOS Mojave v.10.14.6 (18G103) and Chrome Version 76.0.3809.132, however I assume any recent version of Chrome will exhibit this behavior.

Reproduction Steps:

  1. Load up the code below in your favorite dev environment (I'm partial to https://codesandbox.io):
import React from "react";
import ReactDOM from "react-dom";
import { OTSession, OTPublisher } from "opentok-react";

class App extends React.Component {
  state = {
    showPublisher: true
  };

  publisherEventHandlers = {
    accessAllowed: event => {
      console.log('accessAllowed called');
      this.setState({ showPublisher: true });
    },
    accessDialogOpened: event => {
      console.log('accessDialogOpened called');
      this.setState({ showPublisher: false });
    }
  };

  componentDidMount() {
    navigator.mediaDevices.getUserMedia({ 
      audio: true,
      video: true 
    }).then(mediaStream => {
      console.log('getUserMedia resolved', mediaStream);
      this.setState({ showPublisher: true });
    }).catch(error => {
      console.log('getUserMedia error', error);
    });
  }

  render() {
    return (
      <div className="App">
        <h1>OTPublisher Test</h1>
        { this.state.showPublisher ? 
          <OTSession
            apiKey="valid-api-key" 
            sessionId="valid-session-id" 
            token="valid-session-token"
          >
            <OTPublisher 
              eventHandlers={ this.publisherEventHandlers }
            />
          </OTSession>
          :
          <span>Please Grant the Browser Access to your Camera and Microphone</span>
        }
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Direct Link to app in CodeSandbox:
Edit OTPublisher Mounting Error

  1. Reset your browser camera and microphone permissions to "Ask", by clicking the lock symbol to the left of the web address in the address bar:
    Screen Shot 2019-10-15 at 4 31 52 PM
  2. Reload your page
  3. "Allow" the page to access your camera and microphone:
    Screen Shot 2019-10-15 at 4 37 55 PM
  4. Check the JavaScript console for the following errors:
  • OpenTok:Publisher:error onStreamAvailableError Error: Did not pass Error as second argument: function(e,t,n){if(-1===a.indexOf(e))return new Error("Attempt to use invalid error name ("+e+"). Original message: "+t.message);if(!(t instanceof Error||/^\[object .*Error\]$/.test(Object.prototype.toString.call(t))))return new Error("Did not pass Error as second argument: "+t);var r=new o(void 0,t.message);if(r.name=e,!t.stack)try{throw t}catch(e){}return r.stack=t.stack,n&&(r.code=n),i.send(r),r}
  • OpenTok:Publisher:error OT.Publisher State Change Failed: 'Destroyed' cannot transition to 'Failed'
  • OpenTok:Publisher:warn Received connectivity event: "Failure" without "Attempt"
  • OpenTok:GlobalExceptionHandler:error OT.exception :: title: Unable to Publish (1500) msg: GetUserMedia

Any insight or advice on how to avoid these errors would be greatly appreciated.

Thank You!

Controls not working on the Opentok Video

I am working on the video in Opentok. I added controls like publishAudio and publishVideo in my React app. I have changed the layout of video of publisher and subscriber a bit to beautify the UI. I have set the publishVideo in the beginning to true and I am toggling it's status with a function but issue is that function works and state of publishVideo also toggles correctly but video does not hide. Also I want to remove the mic control which is shown by default at the publisher and subscriber video. Any thoughts
from your side @msach22 ?

videoDisabled on Subscriber eventHandlers doesn't work any more

videoDisabled work perfectly before but since couple of week it is not trigger when
publishVideo in OTPublisher is toggle from true to false.
Despite the event videoEnabled is trigger when it's toggle to false or true, with the type videoEnabled in both case.

I found out that videoDisabled is trigger each time we toggle publishVideo and with OTPublisher init value publishVideo=false and return event type videoDisabled

And videoEnabled is trigger each time we toggle publishVideo and with OTPublisher init value publishVideo=true and return event type videoEnabled

Do you have any clue? The problem it's certainly on openTok side but i don't know where to ask them.

          <OTSubscriber
          key={stream.id}
          stream={stream}
          session={session}
          eventHandlers={{
            videoElementCreated: this.initSubscriber,
            audioLevelUpdated: speakerDetection,
            videoDisabled: () => console.log('disabled'),
            videoEnabled: () => console.log('enabled'),
          }} 
/>

thank alot

Why Can't add component Inside OTsubscriber

Well as the title say, why we can't do this:

<OTSubscriber>
    <OtherComponent>
</OTSubscriber>

That way we could add components to be rendered as childs of OTSubscriber,
right now I achieve this with document.createElement(), but as far as I understand and know
this isn't a good practice.

video issue in iOS Safari 11

I am getting this exception in Safari 11 iOS in video. This is due to some recent changes in autoplay policy by Safari.
Error is : Unhandled Rejection (NotAllowedError): The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.

How to solve this? For Safari docs suggested video to be H.264, but it also doesn't work either. How to override the default behaviour of Safari 11?

I saw an example in their official docs : https://github.com/opentok/opentok-web-samples/tree/master/React-Basic-Video-Chat. This works finely in Safari 11 when I checked it's live preview. I have used the same code in my React based application but it only works when I enable Autoplay for this website in Safari 11. Otherwise it breaks with an exception which I showed above with said error.

@aiham any thoughts on this from your side?
@aullman
@opentok
@msach22

Signaling

Can you add an example on sending and receiving signals? I'm banging my head against the wall trying to figure out how to do that with opentok-react and not getting anywhere.

OTPublisher rapid videoSource change errors

Hello,

I have stumbled upon a scenario in which the OTPublisher component generates a number of JavaScript console errors as a result of rapidly changing the videoSource property between different devices. The errors I am encountering are detailed below:

  • OpenTok:Publisher:warn The publisher 85712da5-0f62-4955-983d-bf878250ecbd is trying to unpublish from a session valid-session-id it is not attached to (it is attached to no session)
  • OpenTok:Publisher:error onStreamAvailableError Error: Did not pass Error as second argument: function(e,t,n){if(-1===a.indexOf(e))return new Error("Attempt to use invalid error name ("+e+"). Original message: "+t.message);if(!(t instanceof Error||/^\[object .*Error\]$/.test(Object.prototype.toString.call(t))))return new Error("Did not pass Error as second argument: "+t);var r=new o(void 0,t.message);if(r.name=e,!t.stack)try{throw t}catch(e){}return r.stack=t.stack,n&&(r.code=n),i.send(r),r}
  • OpenTok:Publisher:error OT.Publisher State Change Failed: 'Destroyed' cannot transition to 'Failed'
  • OpenTok:Publisher:warn Received connectivity event: "Failure" without "Attempt"
  • OpenTok:GlobalExceptionHandler:error OT.exception :: title: Unable to Publish (1500) msg: GetUserMedia

I am currently using macOS Mojave v.10.14.6 (18G103) and Chrome Version 76.0.3809.132.

I am currently using both my internal MacBook FaceTime camera and a generic USB camera. It appears as though at least 2 different cameras are necessary to replicate this issue, I was not able to replicate it with a single camera.

You can reproduce this error using the code below. Simply click on the "Cycle Camera" button multiple times in quick succession until you start seeing JavaScript errors in your console:

import React from "react";
import ReactDOM from "react-dom";
import { OTSession, OTPublisher } from "opentok-react";

class App extends React.Component {
  state = {
    audioSource: undefined,
    videoSource: undefined
  };

  getDeviceIds = (kind) => {
    const deviceIds = this.devices
      .filter(device => device.kind === kind)
      .map(device => device.deviceId);
    return deviceIds;
  }

  componentDidMount() {
    window.OT.getDevices((error, devices) => {
      if (error) {
        console.log('getDevices error:', error);
        return;
      }
      this.devices = devices;
      this.audioDeviceIds = this.getDeviceIds('audioInput');
      this.videoDeviceIds = this.getDeviceIds('videoInput');
      this.audioIndex = 0;
      this.videoIndex = 0;
    }); 
  }

  cycleCamera = () => {    
    const videoSource = this.videoDeviceIds[this.videoIndex];
    this.setState({ videoSource });   
    this.videoIndex = this.videoIndex === this.videoDeviceIds.length ? 0 : this.videoIndex + 1; 
  }

  cycleMicrophone = () => {
    const audioSource = this.audioDeviceIds[this.audioIndex];
    this.setState({ audioSource });
    this.audioIndex = this.audioIndex === this.audioDeviceIds.length ? 0 : this.audioIndex + 1;
  }

  render() {
    return (
      <div className="App">
        <h1>OTPublisher Device Test</h1>
        <button
          onClick={ this.cycleCamera }
        >
          Cycle Camera
        </button>
        <button
          onClick={ this.cycleMicrophone }
        >
          Cycle Microphone
        </button>
        
         <OTSession
           apiKey="valid-api-key" 
           sessionId="valid-session-id" 
           token="valid-session-token"
         >
           <OTPublisher 
            properties={
              {
                audioSource: this.state.audioSource,
                videoSource: this.state.videoSource
              }
            }
           />
         </OTSession>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

OR, simply modify directly on CodeSandbox:

Edit OTPublisher Device Error

I am very interested in know if there is any way I can prevent these errors from happening.

Thank you!

Could not change audio input/output and video input

Hi 👋
I would like to swap the audio input output (headset) used for the call as well as the video one (multiple camera) but i don't think i can do it actually or maybe i didn't find how ?
So far i just found how to activate/deactivate them or swap between camera and screen sharing.

Best regards.

OTSubscriber doesn’t re-render correctly when `props.stream` changes

I believe this is the case because OTSubscriber captures props.stream in the constructor onto this.state:

stream: props.stream || context.stream || null,
session: props.session || context.session || null,

This never changes again, i.e. no setState to change this.state.stream.

When componentDidUpdate checks for changes, it only looks at this.state and prevState instead of this.props and prevProps:

if (prevState.session !== this.state.session || prevState.stream !== this.state.stream) {
this.destroySubscriber(prevState.session);
this.createSubscriber();
}

@opentok/client not compatible with SSR

Importing @opentok/client in an application that is rendered server side in Node (e.g. using a popular framework like Gatsby, Next etc) results in the following error:

  35497 |       // to operate correctly into non-standard environments
  35498 |       // @see https://github.com/webpack-contrib/style-loader/issues/177
> 35499 |       return window && document && document.all && !window.atob;
        | ^
  35500 | });
  35501 |
  35502 | var getElement = (function (fn) {


  WebpackError: ReferenceError: window is not defined

It's hard to pin-point exactly where this error is coming from because sadly @opentok/client doesn't seem to be open source or viewable on GitHub, but it looks like it's a problem with how the above code is checking for the presence of browser globals. The correct way is:

typeof window !== 'undefined' // assume a browser-like environment

ReferenceError: OT is not defined

For some reason I am getting ReferenceError: OT is not defined when I am managing to log valid API responses.

import React from 'react';
import { OTSession, OTPublisher, OTStreams, OTSubscriber } from 'opentok-react';

import {
  SAMPLE_SERVER_BASE_URL,
} from './config';

export default class CallPane extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      error: null,
      connection: 'Connecting',
      publishVideo: true,
      sessionData: null,
    };

    this.sessionEventHandlers = {
      sessionConnected: () => {
        this.setState({ connection: 'Connected' });
      },
      sessionDisconnected: () => {
        this.setState({ connection: 'Disconnected' });
      },
      sessionReconnected: () => {
        this.setState({ connection: 'Reconnected' });
      },
      sessionReconnecting: () => {
        this.setState({ connection: 'Reconnecting' });
      },
    };

    this.publisherEventHandlers = {
      accessDenied: () => {
        console.log('User denied access to media source');
      },
      streamCreated: () => {
        console.log('Publisher stream created');
      },
      streamDestroyed: ({ reason }) => {
        console.log(`Publisher stream destroyed because: ${reason}`);
      },
    };

    this.subscriberEventHandlers = {
      videoEnabled: () => {
        console.log('Subscriber video enabled');
      },
      videoDisabled: () => {
        console.log('Subscriber video disabled');
      },
    };
  }

  onSessionError = error => {
    this.setState({ error });
  };

  onPublish = () => {
    console.log('Publish Success');
  };

  onPublishError = error => {
    this.setState({ error });
  };

  onSubscribe = () => {
    console.log('Subscribe Success');
  };

  onSubscribeError = error => {
    this.setState({ error });
  };

  toggleVideo = () => {
    this.setState(state => ({
      publishVideo: !state.publishVideo,
    }));
  };

  componentWillMount() {
    fetch(SAMPLE_SERVER_BASE_URL + '/session')
      .then(data => data.json())
      .then(data => {
        this.setState({ sessionData: data })
      })
      .catch((err) => {
        console.error('Failed to get session credentials', err);
        alert('Failed to get opentok sessionId and token. Make sure you have updated the config.js file.');
      });
  }

  render() {

    const { error, connection, publishVideo } = this.state;
    // const { apiKey, sessionId, token } = this.state;
    if (this.state.sessionData === null) {
      return (
        <div>
          Loading...
        </div>
      )
    } else {
      return (
        // console.log(this.state.sessionData.apiKey, this.state.sessionData.sessionId, this.state.sessionData.token);
        <div>
          <div id="sessionStatus">Session Status: {connection}</div>
          {error ? (
            <div className="error">
              <strong>Error:</strong> {error}
            </div>
          ) : null}
          <OTSession
            apiKey={this.state.sessionData.apiKey}
            sessionId={this.state.sessionData.sessionId}
            token={this.state.sessionData.token}
            onError={this.onSessionError}
            eventHandlers={this.sessionEventHandlers}
          >
            <button id="videoButton" onClick={this.toggleVideo}>
              {publishVideo ? 'Disable' : 'Enable'} Video
          </button>
            <OTPublisher
              properties={{ publishVideo, width: 250, height: 250, }}
              onPublish={this.onPublish}
              onError={this.onPublishError}
              eventHandlers={this.publisherEventHandlers}
            />
            <OTStreams>
              <OTSubscriber
                properties={{ width: 300, height: 300 }}
                onSubscribe={this.onSubscribe}
                onError={this.onSubscribeError}
                eventHandlers={this.subscriberEventHandlers}
              />
            </OTStreams>
          </OTSession>
        </div>
      );
    }
  }
}

cycleVideo OT_HARDWARE_UNAVAILABLE

Sometimes, when calling cycleVideo, the OTPublisher unmounts and an error is thrown.

Example:

<>
  <OTPublisher
    eventHandlers={{
      streamCreated: event => {
        this.publisher = event.target
      },
    }}
  />
  <button
    onClick={event => {
      event.preventDefault()
      if (this.publisher) this.publisher.cycleVideo()
    }}
  >
    Switch camera
  </button>
</>

Error :

OpenTok:Publisher:error onStreamAvailableError OT_HARDWARE_UNAVAILABLE: The selected voice or video devices are unavailable. Verify that the chosen devices are not in use by another application. (getUserMedia error: NotReadableError)
OpenTok:Publisher:warn Received connectivity event: "Failure" without "Attempt"
OpenTok:GlobalExceptionHandler:error OT.exception :: title: Unable to Publish (1500) msg: GetUserMedia

This issue can be seen on Android and iOS.

scriptjs dependency not compatible with SSR

Including opentok-react in an application that is rendered server side in Node (e.g. using a popular framework like Gatsby, Next etc) results in the following error:

ReferenceError: document is not defined

From this line: https://github.com/ded/script.js/blob/master/src/script.js#L6

To be clear, you don't need to use the preloadScript utility to trigger this error, all you need to do is include opentok-react in your app. The error happens as scriptjs is imported here:

https://github.com/opentok/opentok-react/blob/master/src/preloadScript.js#L4

This is related to #43. You could either set @opentok/client as a normal dependency and let npm do what it's designed for - resolve dependencies, or use an alternative approach more suited to modern tooling (e.g. dynamic import). I'll make some suggestions on #43.

Screen Sharing

Is there anyway to implement screen sharing feature with opentok-react library.

Why not have @opentok/client as a dependency?

With modern tooling, intelligent code splitting and caching incorporating @opentok/client into your bundle could be a more efficient approach than forcing an extra HTTP request and hoping the OT global is present in apps.

You could also ensure your library is always bundled with the correct version of @opentok/client, and avoid version mismatches.

It would simplify your documentation too, since you wouldn't have to worry about external script loading.

Styling and lifecycle capabilities

To improve the styling it would be a great addition to be able to change the dimensions of the video containers with props.

Additionnaly, the stream created could be provided when calling the onPublish and onSuscribe.

getting errors on end call

import React, { Component } from 'react';
import { OTSession, OTStreams, preloadScript, OTPublisher, OTSubscriber } from 'opentok-react';
import { chrApi } from '../../api/api';

class SCOpenTok extends Component {
  state = {
    error: null,
    connected: false,
    apiKey: '',
    sessionId: '',
    token: '',
  };

  async componentDidMount() {
    const obj = await chrApi('post', 'callpatient', { patientId: this.props.match.params.id });
    this.setState({
      apiKey: obj.data.apiKey,
      sessionId: obj.data.sessionId,
      token: obj.data.token,
    });
  }

  sessionEvents = {
    sessionConnected: () => {
      this.setState({ connected: true });
    },
    sessionDisconnected: () => {
      this.setState({ connected: false });
    },
  };
  onError = err => {
    this.setState({ error: `Failed to connect: ${err.message}` });
  };

  render() {
    return (
      <div className={'opentok'}>
        {this.state.apiKey ? (
          <OTSession
            apiKey={this.state.apiKey}
            sessionId={this.state.sessionId}
            token={this.state.token}
            eventHandlers={this.sessionEvents}
            onError={this.onError}
            onConnect={e => console.log(e, 'Connected Succesfully')}
            ref={instance => {
              this.otSession = instance;
            }}
            className={'opentok'}
          >
            <OTPublisher />
            <OTStreams>
              <OTSubscriber />
            </OTStreams>
          </OTSession>
        ) : (
          ''
        )}
        <button onClick={() => this.otSession.sessionHelper.disconnect(this.state.sessionId)}>
          {' '}
          End call
        </button>
      </div>
    );
  }
}

SCOpenTok.propTypes = {};
SCOpenTok.defaultProps = {};

export default preloadScript(SCOpenTok);

This is the route defined

<Route
          path={'/patient/:id/video'}
          exact
          component={cProps => (
            <SCOpenTok
              loadingDelegate={<div>Loading...</div>}
              opentokClientUrl="https://static.opentok.com/v2/js/opentok.min.js"
              {...cProps}
            />
          )}
        />

Uncaught TypeError: Cannot read property 'off' of null at OTSession.destroySession (OTSession.js?57c4:93) at OTSession.componentWillUnmount (OTSession.js?57c4:60) at callComponentWillUnmountWithTimer (react-dom.development.js?61bb:9703) at HTMLUnknownElement.callCallback (react-dom.development.js?61bb:100) at Object.invokeGuardedCallbackDev (react-dom.development.js?61bb:138) at invokeGuardedCallback (react-dom.development.js?61bb:187) at safelyCallComponentWillUnmount (react-dom.development.js?61bb:9710) at commitUnmount (react-dom.development.js?61bb:9956) at commitNestedUnmounts (react-dom.development.js?61bb:9992) at unmountHostComponents (react-dom.development.js?61bb:10279)

When ever I click on the end call and go back home or to any other route this error is popping up

Error running the example app

Greetings I tried today to run the app, I followed the instructions but I get the next error:

"python" no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] example: cd example && browserify -o bundle.js app.js && python -m SimpleHTTPServer
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] example script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

It says python is not recognized as a command

Difference between onSubscribe / onError and videoEnabled / videoDisabled

In a component using opentok-react, I'm currently doing something like this:

const settings = {
  eventHandlers: {
    videoDisabled: () => this.props.actions.enableVideo(false),
    videoEnabled: () => this.props.actions.enableVideo(true),
  }
  // ...
}

// ...

<OTPublisher {...settings} />

I'm glad to see that 0.4.0 add some error handling support and other event stuff, like onPublish and onError. But I'm not sure to understand the difference between the two and videoDisabled / videoEnabled.

Can you help, please? 🙏

How to call disconnect method

Hello,

<OTSession apiKey={tokboxConfig.apiKey} sessionId={tokboxConfig.sessionID} token={tokboxConfig.token} onError={onError} > <OTStreams> <TokBoxSubscriber /> </OTStreams> </OTSession>

How to call disconnect method, once I leave the component.
Because i'm not creating any OT session object. I'm using your plugin.
How to know stream video is connected or not. For example I want to show some message if subscriber is not connected.

Keep the same accept ratio when device orientation was changed.

Hi,

Is it possible to keep the same accept ratio when device orientation was changed?
To say in other words, is there some way to have the equals screen sizes(resolutions) that would not depend on device rotation.

Please check the screen that I wanted to improve.

screen shot 2018-12-06 at 1 18 55 am

Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.