Issue with recieving remote stream in server

114 views
Skip to first unread message

Tarun Korat

unread,
May 15, 2024, 12:00:22 PMMay 15
to discuss-webrtc
Hello Team,

I have created Video call or Audio call implement using WebRTC and Laravel Reverb.
I have tried my code in local, its working perfect but when i upload it on nginx server and test it then remote video was not showing.

Below I have attached my code.

import { toastify } from "../plugin/toastify";

document.addEventListener("alpine:init", () => {
    const csrfToken = document
        .querySelector('meta[name="csrf-token"]')
        .getAttribute("content");
    const authUserId = document
        .querySelector('meta[name="auth-user-id"]')
        .getAttribute("content");
    // const iceServers = [
    //     {
    //         urls: [
    //             "stun:stun.l.google.com:19302",
    //             "stun:stun1.l.google.com:19302",
    //             "stun:stun2.l.google.com:19302",
    //         ],
    //     },
    // ];

    const iceServers = [
        {
            urls: "stun:stun.relay.metered.ca:80",
        },
        {
            urls: "turn:global.relay.metered.ca:80",
            username: "36e52b53f6c1d0334d48dcbb",
            credential: "tpC9FLpFZxt0VLQj",
        },
        {
            urls: "turn:global.relay.metered.ca:80?transport=tcp",
            username: "36e52b53f6c1d0334d48dcbb",
            credential: "tpC9FLpFZxt0VLQj",
        },
        {
            urls: "turn:global.relay.metered.ca:443",
            username: "36e52b53f6c1d0334d48dcbb",
            credential: "tpC9FLpFZxt0VLQj",
        },
        {
            urls: "turns:global.relay.metered.ca:443?transport=tcp",
            username: "36e52b53f6c1d0334d48dcbb",
            credential: "tpC9FLpFZxt0VLQj",
        },
    ];

    Alpine.store("call", {
        store: Alpine.store("chatApp"),
        groupStore: Alpine.store("group"),
        authUserId: authUserId,
        csrfToken: csrfToken,
        audioring: new Audio("build/notification/callertune.mp3"),
        callerring: new Audio("build/notification/call-ring.mp3"),
        callType: "",
        ringing: false,
        localStream: null,
        remoteStream: null,
        peerConn: null,
        call_from: [],
        is_incoming_call: false,
        offer: null,
        incoming_call_type: "",
        incoming_user: [],
        is_accept_call:
            sessionStorage.getItem("is_accept_call") === "true" ? true : false,
        answer: null,
        candidate: null,
        pendingIceCandidates: [],
        call_status: "",
        video: true,
        audio: true,
        callStartTime: null,
        callDuration: "00:00",
        timerInterval: null,

        initializeChannel() {
            window.Echo.private(`presence-video.${this.authUserId}`).listen(".incoming-call", (e) => {
                    if (e.data.type === "offer") {
                        this.handleIncomingCall(e.data);
                    } else if (e.data.type === "candidate") {
                        this.handleIceCandidate(e.data.candidate);
                    }
                }
            );

            window.Echo.private(`answer-call.${this.authUserId}`).listen(".accept-call", (e) => {
                    if (e.data.type === "answer") {
                        if (e.data.is_accepted === true) {
                            this.handleAcceptCall(e.data);
                        } else {
                            if (e.data.data.type === "end") {
                                this.handleDeclineCall();
                                this.call_status = "end";
                                toastify("Call Ended", "success");
                            } else {
                                this.handleDeclineCall();
                                this.call_status = "decline";
                                this.incoming_user = e.data.answer_from;
                                toastify("Call Declined", "error");
                            }
                        }
                    }
                }
            );
        },
        declineCall(type = null) {
            const declineData = {
                type: type,
                calleeId: this.authUserId, // Include the ID of the callee
            };

            this.handleSendingData(
                "/video/decline-call/" + this.incoming_user.id,
                declineData
            );
            this.handleDeclineCall();
        },
        handleDeclineCall() {
            // Reset call-related data
            this.ringing = false;
            this.is_incoming_call = false;
            this.is_accept_call = false;
            sessionStorage.setItem("is_accept_call", false);
            this.offer = null;
            this.answer = null;
            this.candidate = null;
            this.remoteStream = null;

            // Stop tracks of the local media stream
            if (this.localStream) {
                this.localStream.getTracks().forEach((track) => {
                    track.stop();
                });
            }

            if (this.remoteStream) {
                this.remoteStream.getTracks().forEach((track) => {
                    track.stop();
                });
            }

            this.localStream = null;
            this.remoteStream = null;

            // Stop the call timer
            this.stopCallTimer();
            this.peerConn?.close();
        },
        makeCall(callType) {
            this.callType = callType;
            this.call_status = "";
            this.incoming_user = [];

            if (!this.store.userStatus) {
                toastify("User is not online", "error");
                return;
            }

            // Request permission for audio and/or video
            navigator.mediaDevices
                .getUserMedia({
                    video: this.callType === "video" ? true : false, // Set to true for video call, false for audio-only call
                    audio: true,
                })
                .then((stream) => {
                    this.localStream = stream;
                    this.audioring.play();
                    this.ringing = true;

                    this.createPeerConnection();

                    this.remoteStream = new MediaStream();
                    this.peerConn.createOffer().then((offer) => {
                            return this.peerConn.setLocalDescription(offer);
                        })
                        .then(() => {
                            var offer = {
                                callType: this.callType,
                                type: "offer",
                                offer: this.peerConn.localDescription.sdp,
                            };
                            console.log("offer", offer);
                            this.handleSendingData("/video/call-user/" + this.store.chatId, offer);
                        })
                        .catch((error) => {
                            this.audioring.pause();
                            this.audioring.currentTime = 0;
                            console.error("Error creating offer:", error);
                        });
                })
                .catch((error) => {
                    console.log("Error accepting call:", error);
                    // If permission is denied or device is not found
                    toastify("Device not found or permission denied", "error");
                    return;
                });
        },
        // Function to accept the incoming call
        acceptCall(callType) {
            this.call_status = "";

            this.callerring.pause();
            this.callerring.currentTime = 0;

            // Start the call timer
            this.startCallTimer();

            navigator.mediaDevices
                .getUserMedia({
                    video: callType === "video" ? true : false,
                    audio: true,
                })
                .then((stream) => {
                    this.localStream = stream;
                    this.createPeerConnection();

                    this.remoteStream = new MediaStream();

                    this.peerConn
                        .setRemoteDescription(
                            new RTCSessionDescription(this.offer)
                        )
                        .then(() => {
                            return this.peerConn.createAnswer();
                        })
                        .then((answer) => {
                            return this.peerConn.setLocalDescription(answer);
                        })
                        .then(() => {
                            var answer = {
                                type: "answer",
                                sdp: this.peerConn.localDescription.sdp,
                            };

                            console.log("answer", answer);
                            this.handleSendingData("/video/accept-call/" + this.incoming_user.id, answer);
                        });
                })
                .catch((error) => {
                    console.log("Error accepting call:", error);
                    // If permission is denied or device is not found
                    toastify("Device not found or permission denied", "error");
                    return;
                });

                if (callType === "video") {
                    document.getElementById("videoCallModal").click();
                } else {
                    document.getElementById("audioCallModal").click();
                }
        },
        handleIncomingCall(data) {
            this.is_incoming_call = true;
            this.handleCallModal(data.call_type);
            this.callType = data.call_type;
            this.incoming_user = data.call_from;

            this.offer = {
                type: "offer",
                sdp: (data.offer.offer += "\r\n"),
            };

            this.callerring.play();
        },
        handleAcceptCall(data) {
            this.is_accept_call = true;
            sessionStorage.setItem("is_accept_call", true);
            this.incoming_user = data.answer_from;
            this.answer = {
                type: "answer",
                sdp: (data.answer.sdp += "\r\n"),
            };

            this.handleVideoAnswerMsg();

            this.audioring.pause();
            this.audioring.currentTime = 0;

            // Start the call timer
            this.startCallTimer();

            // open call modal
            if (this.callType === "video") {
                document.getElementById("videoCallModal").click();
            } else {
                document.getElementById("audioCallModal").click();
            }
        },
        handleVideoAnswerMsg() {
            // this.peerConn
            //     .setRemoteDescription(new RTCSessionDescription(this.answer))
            //     .catch((error) => {
            //         console.error("Error setting remote description:", error);
            //     });

            this.peerConn.setRemoteDescription(new RTCSessionDescription(this.answer))
                .then(() => {
                    // Once the remote description is set, add pending ICE candidates
                    this.addPendingIceCandidates();
                })
                .catch((error) => {
                    console.error("Error setting remote description:", error);
                });
        },
        handleCallModal(callType) {
            if (callType === "video") {
                this.incoming_call_type = "video";
                document.getElementById("videoCallOpenButton").click();
            } else {
                this.incoming_call_type = "audio";
                document.getElementById("audioCallOpenButton").click();
            }
        },
        createPeerConnection() {
            let configuration;
            if (this.callType === "video") {
                // If video call, set ICE servers configuration
                document.getElementById("local-video").srcObject = this.localStream;
                configuration = {
                    iceServers: iceServers,
                };
            }

            this.peerConn = new RTCPeerConnection(configuration);
            this.ringing = true;

            this.localStream.getTracks().forEach((track) => {
                this.peerConn.addTrack(track, this.localStream);
            });

            this.peerConn.ontrack = (e) => {
                if (this.callType === "video") {
                    if (e.streams && e.streams[0]) {
                        e.streams[0].getTracks().forEach((track) => {
                            if(track.kind === "video") {
                                this.remoteStream.addTrack(track);
                            }
                        });
                    }

                    // Set the srcObject of the remote video element
                    const remoteVideo = document.getElementById("remote-video");
                    if (remoteVideo) {
                        remoteVideo.srcObject = null;
                        remoteVideo.srcObject = this.remoteStream;
                        console.log("Remote video element set.", remoteVideo.srcObject);
                    } else {
                        console.error("Remote video element not found.");
                    }
                }

                if (this.callType === "audio") {
                    const audio = new Audio();
                    audio.srcObject = e.streams[0];
                    audio.play();
                }

                if (this.onRemoteTrack) {
                    this.onRemoteTrack(e.streams[0]);
                }
            };

            this.peerConn.onicecandidate = (e) => {
                if (e.candidate === null) {
                    return;
                }
                this.sendIceCandidate(e.candidate);
            };
        },
        handleSendingData(url, data) {
            fetch(url, {
                method: "POST",
                headers: {
                    "X-CSRF-TOKEN": csrfToken,
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({ data }),
            })
                .then((response) => response.json())
                .catch((error) => {
                    console.error("Error:", error);
                });
        },
        sendIceCandidate(candidate) {
            this.handleSendingData(
                "/video/send-candidate/" + this.store.chatId,
                candidate
            );
        },
        // handleIceCandidate(candidate) {
        //     if (this.peerConn) {
        //         this.addIceCandidate(candidate);
        //     } else {
        //         this.pendingIceCandidates.push(candidate);
        //     }
        // },
        handleIceCandidate(candidate) {
            if (this.peerConn && this.peerConn.remoteDescription) {
                this.addIceCandidate(candidate);
            } else {
                this.pendingIceCandidates.push(candidate);
            }
        },
        addPendingIceCandidates() {
            this.pendingIceCandidates.forEach((candidate) => {
                this.addIceCandidate(candidate);

            });
            this.pendingIceCandidates = []; // Clear the pending ICE candidates
        },
        addIceCandidate(candidate) {
            if (this.peerConn && candidate) {
                console.log("Adding ICE candidate:", candidate);
                this.peerConn.addIceCandidate(candidate).catch((error) => {
                    console.error("Error adding ICE candidate:", error);
                });
            }
        },
        toggleVideo() {
            let videoTrack = this.localStream.getTracks().find((track) => track.kind === "video");

            if (videoTrack.enabled) {
                this.video = false;
                videoTrack.enabled = false;
            } else {
                this.video = true;
                videoTrack.enabled = true;
            }
        },
        toggleAudio() {
            let audioTrack = this.localStream.getTracks().find((track) => track.kind === "audio");

            if (audioTrack.enabled) {
                this.audio = false;
                audioTrack.enabled = false;
            } else {
                this.audio = true;
                audioTrack.enabled = true;
            }
        },
        cutCallByUser() {
            this.audioring.pause();
            this.audioring.currentTime = 0;

            if (this.ringing) {
                const declineData = {
                    type: "end",
                    calleeId: this.authUserId, // Include the ID of the callee
                };

                this.handleSendingData("/video/decline-call/" + this.store.userDetail.id, declineData);
                this.handleDeclineCall();
            }
        },

        // Function to start the call timer
        startCallTimer() {
            this.callStartTime = Date.now(); // Record the start time of the call
            this.timerInterval = setInterval(() => {
                const elapsedTimeInSeconds = Math.floor(
                    (Date.now() - this.callStartTime) / 1000
                ); // Calculate elapsed time in seconds
                const minutes = Math.floor(elapsedTimeInSeconds / 60); // Calculate minutes
                const seconds = elapsedTimeInSeconds % 60; // Calculate remaining seconds
                // Format minutes and seconds with leading zeros
                const formattedMinutes = String(minutes).padStart(2, "0");
                const formattedSeconds = String(seconds).padStart(2, "0");
                this.callDuration = `${formattedMinutes}:${formattedSeconds}`; // Format duration as mm:ss
            }, 1000); // Update every second
        },

        // Function to stop the call timer
        stopCallTimer() {
            clearInterval(this.timerInterval); // Stop the timer interval
            this.callStartTime = null; // Reset call start time
            this.callDuration = 0; // Reset call duration
        },
    });

    const chatStore = Alpine.store("call");
    chatStore.initializeChannel();

    // window.onload = () => {
    //     if(chatStore.is_accept_call === true) {
    //         chatStore.cutCallByUser();
    //     }
    // }
});

Reply all
Reply to author
Forward
0 new messages