opentok-rvc

    1.0.9 • Public • Published

    opentok-rvc

    React components for OpenTok.js

    Features included

    • React js
    • semantic react ui
    • opentok session, stream connection
    • opentok peer to peer video chat
    • How to send and recieve signals in opentok
    • Defined the properties used for opentok publisher and subscriber
    • Show username in video chat container
    • Get userdevices used in opentok session(video devices and audio devices)
    • Check screen sharing capability
    • Take screenshot

    Contents

    Install

    Add opentok-rvc as a dependency of your application:

    npm install --save opentok-rvc

    Then include opentok.js before your application:

    Usage

    
    Include opentok.min.js in index.html
    
    <script src="https://static.opentok.com/v2/js/opentok.min.js"></script>
    
    
    config.js
    
    Define you opentok configuration in config.js
    To check single room video connection you need TOKEN and PROVIDER_TOKEN also you can avoid SUBSCRIBER_TOKEN_A and SUBSCRIBER_TOKEN_B.
    
    const SAMPLE_SERVER_BASE_URL = 'YOUR_SERVER_BASE_URL';
    
    const API_KEY = 'API_KEY';
    const SESSION_ID = 'SESSION_ID';
    const TOKEN = 'TOKEN';
    const PROVIDER_TOKEN = 'PROVIDER_TOKEN';
    const SUBSCRIBER_TOKEN_A = 'SUBSCRIBER_TOKEN_A';
    const SUBSCRIBER_TOKEN_B = 'SUBSCRIBER_TOKEN_B';
    
    export {
      SAMPLE_SERVER_BASE_URL,
      API_KEY,
      SESSION_ID,
      TOKEN,
      PROVIDER_TOKEN,
      SUBSCRIBER_TOKEN_A,
      SUBSCRIBER_TOKEN_B
    };
    
    
    
    App.js
    
    In App.js please import the opentok-rvc components and semantic-ui-react components
    
    import React from 'react';
    import { OTSession, OTPublisher, OTStreams, OTSubscriber } from 'opentok-rvc';
    import { Container, Segment, Header, Checkbox, Form, Icon, Popup } from 'semantic-ui-react';
    import 'semantic-ui-css/semantic.min.css';
    
    class App extends React.Component {
      constructor(props) {
        super(props);
    
        this.state = {
          error: null,
          connection: 'Connecting',
          publishAudio: true,
          publishVideo: true,
          publishVideoSource: 'camera',
          subscribeToAudio: true,
          subscribeToVideo: true,
          fitMode: 'cover',
          dimension: 100,
          resolution: '640x480',
          type: 'DEFAULT',
          token: this.props.credentials.token,
          providerToken: this.props.credentials.providerToken,
          subscriberTokenA: this.props.credentials.subscriberTokenA,
          subscriberTokenB: this.props.credentials.subscriberTokenB,
          signal: {
            type: '',
            data: ''
          },
          text: '',
          callActionType: '',
          audioDevices: [],
          videoDevices: [],
          publisherName: 'John'
        };
    
        this.otSession = React.createRef();
    
        this.setUserToken = this.setUserToken.bind(this);
        this.handleChange = this.handleChange.bind(this);
        this.onSignalSend = this.onSignalSend.bind(this);
        this.onCallAction = this.onCallAction.bind(this);
        this.changeVideoDimension = this.changeVideoDimension.bind(this);
        this.changeVideoResolution = this.changeVideoResolution.bind(this);
        this.changeFitmode = this.changeFitmode.bind(this);
    
        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 => {
        console.log(error);
      };
    
      onPublish = () => {
        console.log('Publish Success');
      };
    
      onPublishError = error => {
        this.setState({ publishVideoSource: 'camera' });
        let errMsg = '';
        if (typeof error === 'object') {
          errMsg = error.originalMessage;
        } else {
          errMsg = error
        }
        this.setState({ error: errMsg });
        console.log(error);
      };
    
      onSubscribe = () => {
        console.log('Subscribe Success');
      };
    
      onSubscribeError = error => {
        this.setState({ error });
      };
    
      // set media devices used 
      onMediaDevices = (devices) => {
        const audioDevices = [];
        const videoDevices = [];
        devices.forEach(device => {
          if(device.kind === 'audioInput') {
            audioDevices.push(device);
          } else {
            videoDevices.push(device);
          }
          this.setState({ audioDevices: audioDevices })
          this.setState({ videoDevices: videoDevices })
        });
      }
    
      // check screen sharing is possible or not
      checkScreenSharing = (response) => {
        if (!response.supported || response.extensionRegistered === false) {
          // This browser does not support screen sharing
          console.log('This browser does not support screen sharing');
          alert('This browser does not support screen sharing');
        } else if (response.extensionInstalled === false) {
          // Prompt to install the extension
          console.log('Prompt to install the extension');
          prompt("Please install the extension for screenshare", "Opentok react video chat");
        } else {
          // Screen sharing is available.
          console.log('Screen sharing is available');
        }
      }
    
      togglePublishAudio = () => {
        this.setState(state => ({
          publishAudio: !state.publishAudio
        }));
      };
    
      togglePublishVideo = () => {
        this.setState(state => ({
          publishVideo: !state.publishVideo
        }));
      };
    
      toggleSubscribeToAudio = () => {
        this.setState(state => ({
          subscribeToAudio: !state.subscribeToAudio
        }));
      };
    
      toggleSubscribeToVideo = () => {
        this.setState(state => ({
          subscribeToVideo: !state.subscribeToVideo
        }));
      };
    
      changeVideoSource = (publishVideoSource) => {
        (this.state.publishVideoSource !== 'camera') ? this.setState({ publishVideoSource: 'camera' }) : this.setState({ publishVideoSource: 'screen' })
      }
    
      changeVideoDimension = (event) => {
        this.setState({ dimension: event.target.value })
      }
    
      changeVideoResolution = (event) => {
        this.setState({ resolution: event.target.value })
      }
    
      changeFitmode = (event) => {
        this.setState({ fitMode: event.target.value })
      };
    
      toggleToken = () => {
        let publisherName = this.state.type === 'DEFAULT' ? 'John' : 'Manu';
        let userType = this.state.type === 'DEFAULT' ? 'PROVIDER' : 'DEFAULT';
        let token = this.state.type === 'DEFAULT' ? this.props.credentials.providerToken : this.props.credentials.token;
        this.setState({ type: userType });
        this.setState({ token: token });
        this.setState({ publisherName: publisherName });
      }
    
      setUserToken = (event) => {
        let token = this.props.credentials.providerToken;
        let type = event.target.value;
        if (type === 'select') {
          alert('Please select user type');
          return;
        } else if (type === 'provider') {
          token = this.state.providerToken;
        } else if (type === 'subscriberA') {
          token = this.state.subscriberTokenA;
        } else {
          token = this.state.subscriberTokenB;
        }
        this.setState({ type: type })
        this.setState({ token: token })
      }
    
      handleChange(event) {
        this.setState({ text: event.target.value });
      }
    
      // send opentok signal
      onSignalSend = () => {
        let value = this.state.text;
        this.otSession.current.sessionHelper.session.signal({
          type: 'msg',
          data: value
        }, function signalCallback(error) {
          if (error) {
            console.error('Error sending signal:', error.name, error.message);
          } else {
            console.log(value);
          }
        });
      }
    
      // recieve opentok signal
      onSignalRecieve = (signal) => {
        console.log('onSignalRecieve');
        console.log(signal);
        this.setState({ callActionType: signal.data })
      }
    
      // change video call action
      onCallAction = (type) => {
        this.otSession.current.sessionHelper.session.signal({
          type: 'msg',
          data: type
        }, function signalCallback(error) {
          if (error) {
            console.error('Error sending signal:', error.name, error.message);
          } else {
            console.log(type);
          }
        });
      }
    
      _renderBtns = () => {
        if (this.state.type === 'DEFAULT') {
          if (this.state.callActionType === "" ||
            this.state.callActionType === "STOP_CALL" ||
            this.state.callActionType === "DECLINE_CALL") {
            return (
              <Popup
                trigger={<Icon circular name='phone' size="large" color="blue" onClick={() => this.onCallAction('CALL_INITIATED')} />}
                content='Make Call'
                position='top center'
              />
            )
          } else if (this.state.callActionType === "CALL_INITIATED") {
            return (
              <Popup
                trigger={<Icon circular name='phone' size="large" color="red" onClick={() => this.onCallAction('STOP_CALL')} />}
                content='Cancel Call'
                position='top center'
              />
            )
          } else if (this.state.callActionType === "ACCEPT_CALL") {
            return (
              <Popup
                trigger={<Icon circular name='phone' size="large" color="red" onClick={() => this.onCallAction('STOP_CALL')} />}
                content='Stop Call'
                position='top center'
              />
            )
          }
        } else {
          if (this.state.callActionType === "CALL_INITIATED") {
            return (
              <>
                <Popup
                  trigger={<Icon circular name='phone' size="large" color="teal" onClick={() => this.onCallAction('ACCEPT_CALL')} />}
                  content='Accept Call'
                  position='top center'
                />
                <Popup
                  trigger={<Icon circular name='phone' size="large" color="red" onClick={() => this.onCallAction('DECLINE_CALL')} />}
                  content='Decline Call'
                  position='top center'
                />
              </>
            )
          }
        }
      }
    
      render() {
        const { apiKey, sessionId } = this.props.credentials;
        const { error, connection, token, publishAudio, publishVideo, subscribeToAudio, subscribeToVideo, publishVideoSource, fitMode, callActionType, type, dimension, resolution, publisherName } = this.state;
        return (
          <Container className="app-container">
            <div>
              <Segment color='red'>
                <div onClick={() => this.toggleToken()}>
                  <Header as='h3'>Opentok react video chat</Header>
                  <div>Toggle Token</div>
                  <Checkbox toggle />
                  <div>Current Type: {type}</div>
                  <div>Current Token: {token}</div>
                </div>
    
                {/* <div>
                    <label>
                    Select user chat role:
                    <select value={this.state.type} onChange={this.setUserToken}>
                      <option value="select">select</option>
                      <option value="provdier">Provider</option>
                      <option value="subscriberA">Subscriber A</option>
                      <option value="subscriberB">Subscriber B</option>
                    </select>
                  </label>
                </div> */}
              </Segment>
    
              <Segment color='teal'>
                Resolution: {this.state.resolution}
                <div id="sessionStatus">Session Status: {connection}</div>
                {error ? (
                  <div className="error">
                    <strong>Error:</strong> {error}
                  </div>
                ) : null}
              </Segment>
    
              <Segment color='yellow'>
                <input type="text" placeholder="Enter signal message" value={this.state.text} onChange={this.handleChange} /><br />
                <button onClick={this.onSignalSend}>SEND</button>
              </Segment>
    
              <Segment color="blue" className="video-segment">
                <OTSession
                  ref={this.otSession}
                  apiKey={apiKey}
                  sessionId={sessionId}
                  token={token}
                  onError={this.onSessionError}
                  eventHandlers={this.sessionEventHandlers}
                  onSignalRecieve={this.onSignalRecieve}
                  onMediaDevices={this.onMediaDevices}
                  checkScreenSharing={this.checkScreenSharing}
                >
                  <OTPublisher
                      publisherName={publisherName}
                    properties={{
                      insertMode: "append",
                      publishAudio: publishAudio,
                      publishVideo: publishVideo,
                      videoSource: publishVideoSource === 'screen' ? 'screen' : undefined,
                      audioFallbackEnabled: true,
                      showControls: true,
                      width: dimension,
                      height: dimension,
                      maxResolution: { width: 1920, height: 1080 },
                      facingMode: 'right', // user. environment, left, right
                      fitMode: fitMode, // cover, contain,
                      frameRate: 30,
                      insertDefaultUI: true,
                      // resolution: '640x480'
                      resolution: resolution
                    }}
                    onPublish={this.props.onPublish}
                    onError={this.onPublishError}
                    eventHandlers={this.publisherEventHandlers}
                  />
                  {
                    callActionType === 'ACCEPT_CALL' &&
                    <OTStreams>
                      <OTSubscriber
                        properties={{
                          insertMode: "append",
                          subscribeToAudio: subscribeToAudio,
                          subscribeToVideo: subscribeToVideo,
                          // preferredFrameRate: 15,
                          showControls: true,
                          fitMode: fitMode,
                          width: 100,
                          height: 100,
                          resolution: resolution
                        }}
                        onSubscribe={this.onSubscribe}
                        onError={this.onSubscribeError}
                        eventHandlers={this.subscriberEventHandlers}
                      />
                    </OTStreams>
                  }
                </OTSession>
    
                <div className="btn-container">
                  {this._renderBtns()}
                  <Popup
                    trigger={publishAudio ? <Icon circular name='unmute' size="large" onClick={this.togglePublishAudio} /> : <Icon circular name='mute' size="large" onClick={this.togglePublishAudio} />}
                    content='Toggle Audio'
                    position='top center'
                  />
                  <Popup
                    trigger={publishVideo ? <Icon circular name='video' size="large" onClick={this.togglePublishVideo} /> : <Icon circular name='video' size="large" onClick={this.togglePublishVideo} />}
                    content='Toggle Video'
                    position='top center'
                  />
                  <Popup
                    trigger={publishVideoSource ? <Icon circular name='tv' size="large" onClick={this.changeVideoSource} /> : <Icon circular name='tv' size="large" onClick={this.changeVideoSource} />}
                    content='Share your screen'
                    position='top center'
                  />
                  {
                    callActionType === "ACCEPT_CALL" &&
                    <>
    
                      <Popup
                        trigger={subscribeToAudio ? <Icon circular name='unmute' size="large" onClick={this.toggleSubscribeToAudio} /> : <Icon circular name='mute' size="large" onClick={this.toggleSubscribeToAudio} />}
                        content='Toggle Subscribe to Audio'
                        position='top center'
                      />
                      <Popup
                        trigger={subscribeToVideo ? <Icon circular name='video' size="large" onClick={this.toggleSubscribeToVideo} /> : <Icon circular name='video' size="large" onClick={this.toggleSubscribeToVideo} />}
                        content='Toggle Subscribe to Video'
                        position='top center'
                      />
                    </>
                  }
                  <Popup
                    trigger={<Icon circular name='setting' size="large" />}
                    content={<Form onSubmit={this.handleSubmit}>
                      <Header size='small'>Video</Header>
                      <Form.Field>
                          <label>
                          Video dimension
                          <select value={dimension} onChange={this.changeVideoDimension}>
                            <option value="50">50</option>
                            <option value="100">100</option>
                            <option value="200">200</option>
                          </select>
                        </label>
                        <label>
                          Video quality
                          <select value={resolution} onChange={this.changeVideoResolution}>
                            <option value="320x240">Default</option>
                            <option value="640x480">Low Definition</option>
                            <option value="1280x720">High Definition</option>
                          </select>
                        </label>
                      </Form.Field>
                      <Form.Field>
                        <label>
                          Webcam
                           <select>
                            {
                              this.state.videoDevices.map(device => 
                                <option key={device.deviceId} value={device.deviceId}>{device.label}</option>
                              )
                            }
                          </select>
                        </label>
                      </Form.Field>
                      <Form.Field>
                        <label>
                          Video mode
                          <select value={fitMode} onChange={this.changeFitmode}>
                            <option value="cover">Cover</option>
                            <option value="contain">Contain</option>
                          </select>
                        </label>
                      </Form.Field>
                      <Header size='small'>Audio</Header>
                      <Form.Field>
                        <label>
                          Microphone
                          <select>
                            {
                              this.state.audioDevices.map(device => 
                                <option key={device.deviceId} value={device.deviceId}>{device.label}</option>
                              )
                            }
                          </select>
                        </label>
                      </Form.Field>
                    </Form>}
                    position='top center'
                    hoverable={true}
                  />
                </div>
              </Segment>
            </div>
          </Container>
        );
      }
    }
    
    export default App;
    

    Source code

    * git clone https://github.com/vishnu8429/opentok-rvc.git
    * cd opentok-react/
    * switch branch to 'opentok-rvc-semantic-react-ui'
    * Modify code in src/
    * npm install
    * npm start
    

    Install

    npm i opentok-rvc

    DownloadsWeekly Downloads

    3

    Version

    1.0.9

    License

    ISC

    Unpacked Size

    65 kB

    Total Files

    10

    Last publish

    Collaborators

    • avatar