Agora Video call webRTC doesn't display with React and Laravel

1.1k views Asked by At

I am working on react and laravel project and I want to implement the ability of to users to call each other and joint in a video call. I have implemented all of the code as instructed in their documentation and I have used Laravel back-end to generate the token. Apparently everything works and I get no errors. However, my camera opens but no local stream (video) is displayed on the screen. I suspect is something wrong with my react code:

import React, { useEffect, useState, useRef } from "react";
import { connect } from "react-redux";
import Echo from "laravel-echo";
import axios from "axios";
import AgoraRTC from "agora-rtc-sdk";
import styles from "./css/settingspace.module.css";

const AGORA_ID = "my app id copyed from agora website";

const VideoCallPage = (props) => {
  const [agoraConnection, setAgoraConnection] = useState({
    callPlaced: false,
    mutedAudio: false,
    mutedVideo: false,
    userOnlineChannel: null,
    onlineUsers: [],
    incomingCall: false,
    incomingCaller: "",
    agoraChannel: null,
  });

  const client = useRef(null);
  const localStream = useRef(null);

  useEffect(() => {
    const initUserOnlineChannel = () => {
      console.log("Initiate User Online Channel");
      setAgoraConnection((prevAgoraConnection) => ({
        ...prevAgoraConnection,
        userOnlineChannel: window.Echo.join("agora-online-channel"),
      }));
      console.log("Initiate User Online Channel - Finished");
    };

    const initUserOnlineListeners = () => {
      console.log("Initiate User Online Listeners");
      agoraConnection.userOnlineChannel.here((users) => {
        setAgoraConnection((prevAgoraConnection) => ({
          ...prevAgoraConnection,
          onlineUsers: users,
        }));
      });
      agoraConnection.userOnlineChannel.joining((user) => {
        // check user availability
        const joiningUserIndex = agoraConnection.onlineUsers.findIndex(
          (data) => data.id === user.id
        );
        if (joiningUserIndex < 0) {
          setAgoraConnection((prevAgoraConnection) => ({
            ...prevAgoraConnection,
            onlineUsers: [...agoraConnection.onlineUsers, user],
          }));
        }
      });
      agoraConnection.userOnlineChannel.leaving((user) => {
        const leavingUserIndex = agoraConnection.onlineUsers.findIndex(
          (data) => data.id === user.id
        );
        setAgoraConnection((prevAgoraConnection) => ({
          ...prevAgoraConnection,
          onlineUsers: prevAgoraConnection.onlineUsers.splice(
            leavingUserIndex,
            1
          ),
        }));
      });
      // listen to incoming call
      agoraConnection.userOnlineChannel.listen("MakeAgoraCall", ({ data }) => {
        console.log("Incoming call");
        if (parseInt(data.userToCall) === parseInt(props.user.id)) {
          const callerIndex = agoraConnection.onlineUsers.findIndex(
            (user) => user.id === data.from
          );
          // the channel that was sent over to the user being called is what
          // the receiver will use to join the call when accepting the call.
          setAgoraConnection((prevAgoraConnection) => ({
            ...prevAgoraConnection,
            incomingCaller: agoraConnection.onlineUsers[callerIndex]["name"],
            incomingCall: true,
            agoraChannel: data.channelName,
          }));
        }
      });
    };

    initUserOnlineChannel();
    if (agoraConnection.userOnlineChannel) {
      initUserOnlineListeners();
    }
  }, [
    agoraConnection.onlineUsers,
    agoraConnection.userOnlineChannel,
    props.user.id,
  ]);

  const placeCall = async (id, calleeId) => {
    try {
      // channelName = the caller's and the callee's id. you can use anything. tho.
      const channelName = `${props.user.id}_${calleeId}`;
      const tokenRes = await generateToken(channelName);
      // Broadcasts a call event to the callee and also gets back the token
      await axios.post("api/agora/call-user", {
        user_to_call: id,
        channel_name: channelName,
      });
      initializeAgora();
      joinRoom(tokenRes.data, channelName);
    } catch (error) {
      console.log(error);
    }
  };

  const acceptCall = async () => {
    initializeAgora();
    const tokenRes = await generateToken(agoraConnection.agoraChannel);
    joinRoom(tokenRes.data, agoraConnection.agoraChannel);
    setAgoraConnection({
      ...agoraConnection,
      incomingCall: false,
      callPlaced: true,
    });
  };

  const declineCall = () => {
    // You can send a request to the caller to
    // alert them of rejected call
    setAgoraConnection({
      incomingCall: false,
    });
  };

  const generateToken = (channelName) => {
    return axios.post("api/agora/token", {
      channelName,
    });
  };

  const initializeAgora = () => {
    client.current = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });
    client.current.init(
      AGORA_ID,
      () => {
        console.log("AgoraRTC client initialized");
      },
      (err) => {
        console.log("AgoraRTC client init failed", err);
      }
    );
  };

  const joinRoom = async (token, channel) => {
    client.current.join(
      token,
      channel,
      props.user.id,
      (uid) => {
        console.log("User " + uid + " join channel successfully");
        setAgoraConnection({ ...agoraConnection, callPlaced: true });
        createLocalStream();
        initializedAgoraListeners();
      },
      (err) => {
        console.log("Join channel failed", err);
      }
    );
  };

  const initializedAgoraListeners = () => {
    //   Register event listeners
    client.current.on("stream-published", function (evt) {
      console.log("Publish local stream successfully");
      console.log(evt);
    });
    //subscribe remote stream
    client.current.on("stream-added", ({ stream }) => {
      console.log("New stream added: " + stream.getId());
      client.current.subscribe(stream, function (err) {
        console.log("Subscribe stream failed", err);
      });
    });
    client.current.on("stream-subscribed", (evt) => {
      // Attach remote stream to the remote-video div
      evt.stream.play("remote-video");
      client.current.publish(evt.stream);
    });
    client.current.on("stream-removed", ({ stream }) => {
      console.log(String(stream.getId()));
      stream.close();
    });
    client.current.on("peer-online", (evt) => {
      console.log("peer-online", evt.uid);
    });
    client.current.on("peer-leave", (evt) => {
      var uid = evt.uid;
      var reason = evt.reason;
      console.log("remote user left ", uid, "reason: ", reason);
    });
    client.current.on("stream-unpublished", (evt) => {
      console.log(evt);
    });
  };

  const createLocalStream = () => {
    localStream.current = AgoraRTC.createStream({
      audio: true,
      video: true,
    });
    // Initialize the local stream
    localStream.current.init(
      () => {
        // Play the local stream
        localStream.current.play("me");
        console.log("Local stream played");
        // Publish the local stream
        client.current.publish(localStream.current, (err) => {
          console.log("publish local stream", err);
        });
      },
      (err) => {
        console.log(err);
      }
    );
  };

  const endCall = () => {
    localStream.current.close();
    client.current.leave(
      () => {
        console.log("Leave channel successfully");
        setAgoraConnection({
          ...agoraConnection,
          callPlaced: false,
        });
      },
      (err) => {
        console.log("Leave channel failed");
      }
    );
  };

  const handleAudioToggle = () => {
    if (agoraConnection.mutedAudio) {
      localStream.current.unmuteAudio();
      setAgoraConnection({
        ...agoraConnection,
        mutedAudio: false,
      });
    } else {
      localStream.current.muteAudio();
      setAgoraConnection({
        ...agoraConnection,
        mutedAudio: true,
      });
    }
  };

  const handleVideoToggle = () => {
    if (agoraConnection.mutedVideo) {
      localStream.current.unmuteVideo();
      setAgoraConnection({
        ...agoraConnection,
        mutedVideo: false,
      });
    } else {
      localStream.current.muteVideo();
      setAgoraConnection({
        ...agoraConnection,
        mutedVideo: true,
      });
    }
  };

  return (
    <div className="mt-5">
      <div>
        <button
          type="button"
          className="btn btn-primary"
          onClick={() => placeCall(4, 4)}
        >
          Call
        </button>
      </div>
      {agoraConnection.incomingCall && (
        <div className="row my-5">
          <div className="col-12">
            <p>
              Incoming Call From{" "}
              <strong>{agoraConnection.incomingCaller}</strong>
            </p>
            <div className="btn-group" role="group">
              <button
                type="button"
                className="btn btn-danger"
                data-dismiss="modal"
                onClick={() => declineCall()}
              >
                Decline
              </button>
              <button
                type="button"
                className="btn btn-success ml-5"
                onClick={() => acceptCall()}
              >
                Accept
              </button>
            </div>
          </div>
        </div>
      )}

      {agoraConnection.callPlaced && (
        <section id="video-container">
          <div id="me"></div>
          <div id="remote-video"></div>

          <div className="action-btns">
            <button
              type="button"
              className="btn btn-info"
              onClick={() => handleAudioToggle()}
            >
              {agoraConnection.mutedAudio ? "Unmute" : "Mute"}
            </button>
            <button
              type="button"
              className="btn btn-primary mx-4"
              onClick={() => handleVideoToggle()}
            >
              {agoraConnection.mutedVideo ? "ShowVideo" : "HideVideo"}
            </button>
            <button
              type="button"
              className="btn btn-danger"
              onClick={() => endCall()}
            >
              EndCall
            </button>
          </div>
        </section>
      )}
    </div>
  );
};

const mapStateToProps = (state) => {
  return {
    user: state.auth,
  };
};

export default connect(mapStateToProps)(VideoCallPage);

After I place a call like I said my web camera starts the place where my local stream should be displayed is shown but without the actual stream: enter image description here

I really have no other ideas how to fix this.

1

There are 1 answers

0
Ubaldo Suarez On

in the most cases AgoraRTC insert the html video element inside two divs (first div is the trackid, second div is the video element wrapper), you need to style this elements cu'z they have width 0px & heigth 0px, check in the inspector, you can also use the AgoraVideoPlayer component imported from agora-rtc and need style this too