Building an Audio Room using the Huddle01 SDK

Walkthrough

The following guide explains how you can integrate audio communication into your application seamlessly using Huddle01 SDKs.

Installation

Install @huddle01/react in your react app

To power your React DApp with audio communication using Huddle01 install the following:

  pnpm i @huddle01/react 

Initialization of project

Head over to API Keys Page and connect your wallet to get your project credentials:

  • API Key
  • projectId

Once done, initialize your project by calling the initialize() method and pass projectId in params.

app.tsx
import { useHuddle01 } from '@huddle01/react';
 
const App = () => {
  const { initialize, isInitialized } = useHuddle01();
 
  useEffect(() => {
    // its preferable to use env vars to store projectId
    initialize('YOUR_PROJECT_ID');
  }, []);
 
  return <div>{isInitialized ? 'Hello World!' : 'Please initialize'}</div>;
};

Joining the lobby

Room Id can be generated using the : Create Room API

Add the following button to your jsx and call the joinLobby() method.

app.tsx
  import { useHuddle01 } from '@huddle01/react';
  import { useLobby } from '@huddle01/react/hooks';
 
 
  const App = () => {
    const { initialize, isInitialized } = useHuddle01();
    const { joinLobby } = useLobby();
 
    useEffect(() => {
      // its preferable to use env vars to store projectId
      initialize('YOUR_PROJECT_ID');
    }, []);
  
    return (
      <div>{isInitialized ? 'Hello World!' : 'Please initialize'}
        <button 
          disabled={joinLobby.isCallable} 
          onClick={() => joinLobby('YOUR_ROOM_ID');
        }>
          Join Lobby
        </button>
      </div>
    );
  };

The isCallable is an attribute available on all methods to check whether the function is callable or not.

Room creation using the Create Room API can be done in the following ways:

NextJs API Route to create room (NodeJs)

This is how you can create a room using NextJS API Route (opens in a new tab) and pass the roomId to the joinLobby() method.

pages/api/create-room.ts
import axios from 'axios';
 
import type { NextApiRequest, NextApiResponse } from 'next';
 
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  try {
    const { data } = await axios.post(
      'https://api.huddle01.com/api/v1/create-room',
      {
        title: 'Huddle01-Test',
        roomLock: false,
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': process.env.API_KEY,
        },
      }
    );
 
    res.status(200).json(data);
  } catch (error) {
    res.status(500).json(error);
  }
};
 
export default handler;

NextJS getServerSideProps() room creation example

This is how you can create a room using getServerSideProps() (opens in a new tab) and pass the roomId to the joinLobby() method.

app.tsx
export const getServerSideProps: GetServerSideProps<{
  roomId: string;
}> = async () => {
  const { data } = await axios.post(
    'https://api.huddle01.com/api/v1/create-room',
    {
      title: 'Huddle01-SDK-Test',
      roomLock: false,
    },
    {
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': process.env.API_KEY,
      },
    }
  );
 
  return {
    props: {
      roomId: data.roomId,
    },
  };
};

Enabling and Disabling Media Devices

app.tsx
import { useHuddle01 } from '@huddle01/react';
import { useLobby, useAudio } from '@huddle01/react/hooks';
 
 
const App = () => {
  const { initialize, isInitialized } = useHuddle01();
  const { joinLobby } = useLobby();
  const { fetchAudioStream, stopAudioStream, error: micError } = useAudio();
  // Show error if it exists
 
  useEffect(() => {
    // its preferable to use env vars to store projectId
    initialize('YOUR_PROJECT_ID');
  }, []);
 
  return (
    <div>{isInitialized ? 'Hello World!' : 'Please initialize'}
      <button 
        disabled={joinLobby.isCallable} 
        onClick={() => joinLobby('YOUR_ROOM_ID');
      }>
        Join Lobby
      </button>
 
    {/* Mic */} 
      <button disabled={!fetchAudioStream.isCallable} onClick={fetchAudioStream}>
        FETCH_AUDIO_STREAM
      </button>
 
    </div>
  );
};

The isCallable is an attribute available on all methods to check whether the function is callable or not.

Joining and leaving the room

Add the joinRoom() and leaveRoom() methods for joining and leaving functionalities. The joinRoom() method can be called to enter the room, once the lobby has been joined. To leave that respective room you can call the leaveRoom() method in the ROOM state.

app.tsx
  import { useHuddle01 } from '@huddle01/react';
  import { useLobby, useAudio, useVideo, useRoom } from '@huddle01/react/hooks';
 
  const App = () => {
    const { initialize, isInitialized } = useHuddle01();
    const { joinLobby } = useLobby();
    const { fetchAudioStream, stopAudioStream, error: micError } = useAudio();
    const { fetchVideoStream, stopVideoStream, error: camError } = useVideo(); 
    const { joinRoom, leaveRoom } = useRoom();
 
 
    useEffect(() => {
      // its preferable to use env vars to store projectId
      initialize('YOUR_PROJECT_ID');
    }, []);
  
    return (
      <div>{isInitialized ? 'Hello World!' : 'Please initialize'}
        <button 
          disabled={joinLobby.isCallable} 
          onClick={() => joinLobby('YOUR_ROOM_ID');
        }>
          Join Lobby
        </button>
 
        {/* Mic */} 
        <button disabled={!fetchAudioStream.isCallable} onClick={fetchAudioStream}>
          FETCH_AUDIO_STREAM
        </button>
 
        {/* Webcam */} 
        <button disabled={!fetchVideoStream.isCallable} onClick={fetchVideoStream}>
          FETCH_VIDEO_STREAM
        </button>
 
        <button disabled={!joinRoom.isCallable} onClick={joinRoom}>
          JOIN_ROOM 
        </button>
 
        <button disabled={!leaveRoom.isCallable} onClick={leaveRoom}>
          LEAVE_ROOM 
        </button>
      </div>
    );
  };
 

The isCallable is an attribute available on all methods to check whether the function is callable or not.

Clicking on the JOIN_ROOM button, will make the user join the room and will allow them to send/receive media with other participants.

Sending media across to other participants

Here, we are using a term PRODUCE which means sending your audio/video stream across to the other peer who will CONSUME (or receive) the streams.

app.tsx
  import { useHuddle01 } from '@huddle01/react';
  import { useLobby, useAudio, useVideo, useRoom } from '@huddle01/react/hooks';
 
  const App = () => {
    const { initialize, isInitialized } = useHuddle01();
    const { joinLobby } = useLobby();
    const { 
      fetchAudioStream, stopAudioStream, error: micError, 
      produceAudio, stopProducingAudio 
    } = useAudio();
 
    const { 
      fetchVideoStream, stopVideoStream, error: camError, 
      produceVideo, stopProducingVideo 
    } = useVideo(); 
    const { joinRoom, leaveRoom } = useRoom();
 
 
    useEffect(() => {
      // its preferable to use env vars to store projectId
      initialize('YOUR_PROJECT_ID');
    }, []);
  
    return (
      <div>{isInitialized ? 'Hello World!' : 'Please initialize'}
        <button 
          disabled={joinLobby.isCallable} 
          onClick={() => joinLobby('YOUR_ROOM_ID')} >
          Join Lobby
        </button>
 
        {/* Mic */} 
        <button disabled={!fetchAudioStream.isCallable} onClick={fetchAudioStream}>
          FETCH_AUDIO_STREAM
        </button>
 
        {/* Webcam */} 
        <button disabled={!fetchVideoStream.isCallable} onClick={fetchVideoStream}>
          FETCH_VIDEO_STREAM
        </button>
 
        <button disabled={!joinRoom.isCallable} onClick={joinRoom}>
          JOIN_ROOM 
        </button>
 
        <button disabled={!leaveRoom.isCallable} onClick={leaveRoom}>
          LEAVE_ROOM 
        </button>
 
        <button disabled={!produceVideo.isCallable} onClick={() => produceVideo(camStream)}>
          Produce Cam  
        </button>
 
        <button disabled={!produceAudio.isCallable} onClick={() => produceAudio(micStream)}>
          Produce Mic  
        </button>
 
        <button disabled={!stopProducingVideo.isCallable} onClick={stopProducingVideo}>
          Stop Producing Cam  
        </button>
 
        <button disabled={!stopProducingAudio.isCallable} onClick={stopProducingAudio}>
          Stop Producing Mic  
        </button>
 
      </div>
    );
  };
 

Receiving the audio and video streams

Here, we are using a term PRODUCE which means sending your audio/video stream across to the other peer who will CONSUME (or receive) the streams.

Getting access to data related to peers

JSX:

import { usePeers } from '@huddle01/react/hooks';
 
export default function() {
  const peers = usePeers();
  
  return <div>{JSON.stringify(peers)}</div>
}
 

Output: (Example Output for 1 peer)

[
  {
    peerId: "sdksjdsjd",
    mic: MediaStreamTrack,
    cam: MediaStreamTrack
  }
]
 

Using the audio and video tags

Importing the Audio and Video tags:

// importing
import { Video, Audio } from '@huddle01/react/components'

There are three ways to use the audio and video tags

1. Using peerId :

  <Video peerId="PEER_ID" />
  <Audio peerId="PEER_ID" />

2. Using track or stream :

The stream value for mic and cam are available on the useAudio and useVideo hooks respectively.

The tracks for peers can be found from the usePeers hook.

🚨

To be used only when you want to make changes to streams from your side otherwise use peerId method!

  <Video peerId="PEER_ID" track={camTrack}  />
  <Audio peerId="PEER_ID" track={micTrack} />
  <Video peerId="PEER_ID" stream={camStream}  />
  <Audio peerId="PEER_ID" stream={micStream} />

The video and audio streams for the peers can be accessed using the usePeers hook. The usePeers hook returns an array of objects with the following structure:

[
  {
    peerId: "sdksjdsjd",
    mic: MediaStreamTrack,
    cam: MediaStreamTrack
  }
]
 

The peerId is the unique identifier for the peer. The mic and cam are the MediaStreamTrack objects for the audio and video streams respectively.

The usePeers hook can be used to access the audio and video streams for the peers and can be used in the following way to render the audio and video streams:

app.tsx
  import { useHuddle01 } from '@huddle01/react';
  import { useLobby, useAudio, useVideo, useRoom } from '@huddle01/react/hooks';
 
  const App = () => {
    const { initialize, isInitialized } = useHuddle01();
    const { joinLobby } = useLobby();
    const { 
      fetchAudioStream, stopAudioStream, error: micError, 
      produceAudio, stopProducingAudio 
    } = useAudio();
 
    const { 
      fetchVideoStream, stopVideoStream, error: camError, 
      produceVideo, stopProducingVideo 
    } = useVideo(); 
    const { joinRoom, leaveRoom } = useRoom();
 
    const { peerIds } = usePeers();
 
    useEffect(() => {
      // its preferable to use env vars to store projectId
      initialize('YOUR_PROJECT_ID');
    }, []);
  
    return (
      <div>{isInitialized ? 'Hello World!' : 'Please initialize'}
 
        <div className="grid grid-cols-4">
          {peerIds.map(peerId => (
              <Video key={peer.peerId} peerId={peer.peerId} debug />
          ))}
 
          {peerIds.map(peerId => (
              <Audio key={peer.peerId} peerId={peer.peerId} debug />
          ))}
        </div>
 
        <button 
          disabled={joinLobby.isCallable} 
          onClick={() => joinLobby('YOUR_ROOM_ID');
        }>
          Join Lobby
        </button>
 
        {/* Mic */} 
        <button disabled={!fetchAudioStream.isCallable} onClick={fetchAudioStream}>
          FETCH_AUDIO_STREAM
        </button>
 
        {/* Webcam */} 
        <button disabled={!fetchVideoStream.isCallable} onClick={fetchVideoStream}>
          FETCH_VIDEO_STREAM
        </button>
 
        <button disabled={!joinRoom.isCallable} onClick={joinRoom}>
          JOIN_ROOM 
        </button>
 
        <button disabled={!leaveRoom.isCallable} onClick={leaveRoom}>
          LEAVE_ROOM 
        </button>
 
        <button disabled={!produceVideo.isCallable} onClick={() => produceVideo(camStream)}>
          Produce Cam  
        </button>
 
        <button disabled={!produceAudio.isCallable} onClick={() => produceAudio(micStream)}>
          Produce Mic  
        </button>
 
        <button disabled={!stopProducingVideo.isCallable} onClick={stopProducingVideo}>
          Stop Producing Cam  
        </button>
 
        <button disabled={!stopProducingAudio.isCallable} onClick={stopProducingAudio}>
          Stop Producing Mic  
        </button>
 
      </div>
    );
  };
 

You're all set! Happy Hacking! 🎉

For more information, please refer to the SDK Reference.

Audio/Video Infrastructure designed for the developers to empower them ship simple yet powerful Audio/Video Apps.
support
company
Copyright © 2022 Graphene 01, Inc. All Rights Reserved.