npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@telnyx/video-react-native

v0.0.0-alpha.3

Published

Telnyx Video React Native SDK

Downloads

214

Readme

Telnyx Video React Native

Getting Started

In this guide, you’ll learn how to get run and deploy Telnyx Meet

Just follow these steps:

  1. Sign up for a Telnyx Account
  2. Create an API KEY
  3. Create a Room ID
  4. Installation

Step 1: Sign Up for a Telnyx Mission Control Portal Account

Head to telnyx.com/sign-up to sign up for your free Telnyx account.

Once signed up you will have access to Telnyx Portal, where you can set up and manage your API KEY, and more.

Step 2: Create an API KEY

Go to API Keys section and click on Create API Key button. It will generate a key for you. Copy and save this key in a safe place and don't share it with anyone it is a sensitive value.

You need this API Key to consume the API https://api.telnyx.com/v2/rooms to manage your room ids.

create api key

Step 3: Create a Room ID

You should read this documentation video/Rooms to learn how to create a new video room id. When you get your roomId you can join in a video meet conference.

Step 4. Installation

Install the package with:

npm install @telnyx/video-react-native --save

In android/settings.gradle file add following code

   include ':react-native-webrtc'  
   project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')

In android/build.gradle file make minSdkVersion=24

In android/app/src/main/java/(package name)/MainApplication.java add following:

In import section add

    import com.oney.WebRTCModule.WebRTCModulePackage;

In React package section add

    packages.add(new WebRTCModulePackage());

In android/app/build.gradle file add

In dependency section add:

    implementation project(':react-native-webrtc') 

As long as you can import npm packages with a bundler like Webpack, you're ready to import Room and any other class required and begin.

You will also need some classes from react-native-webrtc such as RTCView which can be used to display a video stream:

import { Room, initialize, State } from '@telnyx/video-react-native';
import { RTCView, mediaDevices, MediaStream } from 'react-native-webrtc';

You will also need https://babeljs.io/docs/en/babel-plugin-proposal-async-generator-functions#installation to be able to use some of the async calls in the SDK.

Permissions

In order to use native mobile features such as internet or record audio, you need to enable permissions for both platforms. You can follow these guides: Android iOS

Usage

General usage is the same as the vanilla web JS video implementation which is described here

And then use a permission library such as react-native-permissions to verify that the permissions are enabled at runtime.

React Native Specific Usage

The key difference between the Web and React Native SDK is how we attach streams to a view. With React Native we are no longer using DOM elements, but rather an RTCView:

  const [localStream, setStream] = useState<string>('');

      ...
      
 const onPressPublish = async () => {
    try {
      let callerStream = await mediaDevices.getUserMedia({
        audio: true,
        video: true,
      });
      console.log('got local stream using getUserMedia...');

      // Get audio and video tracks from the MediaStream's
      // since the sdk works with MediaStreamTrack
      let callerAudioTrack = callerStream.getAudioTracks()[0];
      let callerVideoTrack = callerStream.getVideoTracks()[0];

      await room.addStream('self', {
        audio: callerAudioTrack,
        video: callerVideoTrack,
      });

      setStream(callerStream.toURL());
    } catch (e) {
      console.error("Unable to publish stream...");
    }
  };
  
  ....
  
  <SafeAreaView style={styles.localStreamBox}>
        {
          localStream ?
            <RTCView
              mirror={true}
              objectFit={'contain'}
              streamURL={localStream!}
              style={{ width: '100%', height: '100%' }}
              zOrder={1}
            /> : null
        }
      </SafeAreaView>

Full example:

Below is an adapted version of the following web example adapted for React Native with this library

 import * as React from 'react';

import { StyleSheet, View, Alert, Platform, StatusBar, SafeAreaView, Button } from 'react-native';
import { RTCView, mediaDevices, MediaStream } from 'react-native-webrtc';
import { useEffect, useState } from 'react';
import { Room, initialize, State } from 'telnyx-video-react-native';
import { FlatGrid } from 'react-native-super-grid';
import { requestMultiple, PERMISSIONS } from 'react-native-permissions';
import Config from "react-native-config";

//create .env file in root of example and store Room ID and API Key
let ROOM_ID = Config.ROOM_ID
let API_KEY = Config.API_KEY
export default function App() {
  const [localStream, setStream] = useState<string>('');
  const [room, setRoom] = useState<typeof Room>();
  const [participantStreamMaps, setParticipantStreamMaps] = useState<Map<String, MediaStream>>(new Map<string, MediaStream>())
  const updateStreamMap = (k: string, v: MediaStream) => {
    setParticipantStreamMaps(new Map(participantStreamMaps.set(k, v)));
  }
  const deleteFromStreamMap = (v: string) => {
    participantStreamMaps.delete(v)
    let map = new Map(participantStreamMaps)
    map.delete(v)
    setParticipantStreamMaps(new Map(map));
  }
  const [data, setData] = useState();
  const [isLoading, setLoading] = useState(true);

  useEffect(() => {
    if (API_KEY == undefined || ROOM_ID == undefined) {
      throw Error('Please create a .env file and add your ROOM_ID and API_KEY');
    }
    verifyPermissions();
    getToken();
  }, []);

  const getToken = async () => {
    const requestTokenUrl = `https://api.telnyx.com/v2/rooms/${ROOM_ID}/actions/generate_join_client_token`;
    var tokenReceived;
    try {
      const response = await fetch(requestTokenUrl, {
        body: '{"refresh_token_ttl_secs":3600,"token_ttl_secs":600}',
        headers: {
          Accept: 'application/json',
          Authorization: `Bearer ${API_KEY}`,
          'Cache-Control': 'no-cache',
          'Content-Type': 'application/json',
        },
        method: 'POST',
      });
      const json = await response.json();
      setData(json);
      tokenReceived = json;
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
      makeCall(tokenReceived.data.token);
    }
  };

  async function makeCall(token: string) {
    let room;
    console.log(`Token being provided: ${token}`);

    // Replace this with the client token you generated previously.
    const context = '{"id":99999,"username":"React Native User"}';

    room = await initialize({
      roomId: ROOM_ID,
      clientToken: token,
      context: context,
      logLevel: 'DEBUG',
    });
    setRoom(room);

    // you are connected to the room
    room.on("connected", (state) => {
      let receivedState = state as State 
      console.log(`connected to the room... ` + JSON.stringify(receivedState));
      
      // Once we are connected we can access the list of participants that are already connected to the room.
      const remoteParticipants = state.participants;
      remoteParticipants.forEach((item) => {
        const remoteParticipant = state.participants.get(item.participantId);
        console.log(`participant connected ` + item);
      });

      // We can also access the list of streams available and subscribe to them if need it.
      state.streams.forEach((stream) => {
        if (stream.participantId === room.getLocalParticipant().id) {
          return;
        }

        room.addSubscription(stream.participantId, stream.key, {
          audio: true,
          video: true,
        });
      });
    });

    // a remote participant has joined the room
    room.on("participant_joined", (participantId, state) => {
      const remoteParticipant = state.participants.get(participantId);
      console.log(`participant ${remoteParticipant.context} joined...`);
    });

    // a stream has been published to the room
    room.on("stream_published", async (participantId, streamKey, state) => {
      // ignore streams that are published by the local participant
      // we only care about remote stream from other remote participant
      let participant = state.participants.get(participantId);
      if (participant.origin === "local") {
        return;
      }

      // the remote stream is identified using the participantId and streamKey
      // you need to subscribe to a remote stream in order to access it's `MediaStreamTrack`s
      await room.addSubscription(participantId, streamKey, {
        audio: true,
        video: true
      });

      console.log(
        `subscription to the: ${participantId} ${streamKey} has been added...`
      );
    });

    // a subscription to a remote stream has started
    room.on("subscription_started", (participantId, streamKey, state) => {
      console.log(
        `subscription to the: ${participantId} ${streamKey} stream started...`
      );

      let receivedState = state as State 
      console.log(`Subscription started :: State .. ` + JSON.stringify(receivedState));

      // use a helper method to easily access a remote participants' stream
      let remoteStream = room.getParticipantStream(participantId, streamKey);

      let remoteMediaStream = new MediaStream([
        remoteStream.audioTrack,
        remoteStream.videoTrack
      ]);

      // Add participant stream from participant stream array 
      updateStreamMap(participantId, remoteMediaStream)
    });


    // a subscription to a remote stream has ended
    room.on("subscription_ended", (participantId, streamKey, state) => {
      console.log(
        `subscription to the: ${participantId} ${streamKey} stream ended...`
      );

      deleteFromStreamMap(participantId)
    });

    await room.connect();
  }

  const verifyPermissions = async () => {
    if (Platform.OS == 'android') {
      let perm = [
        PERMISSIONS.ANDROID.CAMERA,
        PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,
        PERMISSIONS.ANDROID.RECORD_AUDIO,
        PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,
      ];
      let permissionStatuses = await requestMultiple(perm);
      console.log('obj', permissionStatuses);
      const result = permissionStatuses[perm[0]];
      if (result !== 'granted') {
        Alert.alert(
          'Insufficient permissions!',
          'You need to grant camera and library access permissions to use this app.',
          [{ text: 'Okay' }],
        );
        return false;
      }
      return true;
    } else {
      let perm = [PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.MICROPHONE];
      let permissionStatuses = await requestMultiple(perm);
      console.log('obj', permissionStatuses);
      const result = permissionStatuses[perm[0]];
      if (result !== 'granted') {
        Alert.alert(
          'Insufficient permissions!',
          'You need to grant camera and library access permissions to use this app.',
          [{ text: 'Okay' }],
        );
        return false;
      }
      return true;
    }
  };

  const onPressPublish = async () => {
    try {
      let callerStream = await mediaDevices.getUserMedia({
        audio: true,
        video: true,
      });
      console.log('got local stream using getUserMedia...');

      // Get audio and video tracks from the MediaStream's
      // since the sdk works with MediaStreamTrack
      let callerAudioTrack = callerStream.getAudioTracks()[0];
      let callerVideoTrack = callerStream.getVideoTracks()[0];

      await room.addStream('self', {
        audio: callerAudioTrack,
        video: callerVideoTrack,
      });

      setStream(callerStream.toURL());
    } catch (e) {
      console.error("Unable to publish stream...");
    }
  };

  return (
    <>
      <StatusBar barStyle="dark-content" />
      <View style={styles.box}>
        <Button
          title="Start"
          onPress={onPressPublish} />
      </View>
      {
        <FlatGrid
          data={Array.from(participantStreamMaps.values())}
          style={styles.gridView}
          itemDimension={130}
          spacing={10}
          renderItem={({ item }) =>
            <SafeAreaView style={styles.remoteStreamBox}>
              <RTCView
                mirror={false}
                objectFit={'contain'}
                streamURL={item.toURL()}
                style={{ width: '100%', height: '100%' }}
                zOrder={1}
              />
            </SafeAreaView>}
          keyExtractor={item => item.toURL()}
        />
      }
      <SafeAreaView style={styles.localStreamBox}>
        {
          localStream ?
            <RTCView
              mirror={true}
              objectFit={'contain'}
              streamURL={localStream!}
              style={{ width: '100%', height: '100%' }}
              zOrder={1}
            /> : null
        }
      </SafeAreaView>
    </>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  gridView: {
    marginTop: 10,
    flex: 1,
  },
  box: {
    width: 60,
    height: 60,
    marginVertical: 20,
  },
  localStreamBox: {
    width: 400,
    height: 300,
    marginVertical: 20,
  },
  remoteStreamBox: {
    width: 200,
    height: 125,
  },
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
  },
  sectionDescription: {
    marginTop: 8,
    fontSize: 18,
    fontWeight: '400',
  },
  highlight: {
    fontWeight: '700',
  },
});