You can see the problem in the screenshots. I'm trying to create a channel for 2 people using Agora, but when I try to connect I only get the local stream connection to work. The remote user can't be seen or heard and I get errors in the console.
Below are screenshots and code on AlpineJs where the connection is made after accepting the Agora RTM (Signaling) invitation.
All token generation happens on the Backend side. Tokens are valid.
document.addEventListener('alpine:init', () => {
Alpine.data('video', () => ({
callPlaced: false,
client: null,
localStream: null,
onlineUsers: [],
incomingCall: false,
incomingCaller: "",
remoteInvitation: null,
localInvitation: null,
authuser: @js( auth()->user()->username ?? null ),
authuserid: @js( auth()->id() ?? null ),
agoraRtmClient: null,
agoraOnlineChannel: null,
channelParameters: {
localAudioTrack: null,
localVideoTrack: null,
remoteAudioTrack: null,
remoteVideoTrack: null,
remoteUid: null,
},
agoraOptions: {
appId: @js(config('app.agora_app_id'))
},
init() {
const rtmClient = window.AgoraRTM.createInstance(this.agoraOptions.appId)
this.agoraRtmClient = rtmClient
let channel = rtmClient.createChannel("online")
this.agoraOnlineChannel = channel
this.initAgoraRtmListeners();
let user = this.authuser
window.onload = async function () {
let response = await fetch(@js(route('agora.tokens.rtm')), {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8'
}
});
if (response.ok) {
let token = (await response.json()).token;
await rtmClient.login({
uid: user,
token: token
})
await channel.join().then(() => {
console.log("You have successfully joined channel " + channel.channelId)
})
} else {
console.log(response.status)
}
}
this.client = AgoraRTC.createClient({mode: "rtc", codec: "vp8"});
this.client.on("user-published", async (remoteUser, mediaType) => {
console.log('remote user: ' + remoteUser.uid.toString())
await this.client.subscribe(remoteUser, mediaType);
if (mediaType == "video") {
console.log("subscribe video success");
this.channelParameters.remoteVideoTrack = remoteUser.videoTrack
this.channelParameters.remoteAudioTrack = remoteUser.audioTrack
this.channelParameters.remoteUid = remoteUser.uid.toString()
this.createRemoteVideoBlock()
this.channelParameters.remoteVideoTrack.play("remote-video");
}
if (mediaType == "audio") {
console.log("subscribe audio success");
this.channelParameters.remoteAudioTrack = remoteUser.audioTrack
remoteUser.audioTrack.play();
}
});
this.client.on("user-unpublished", user => {
console.log(user.uid + "has left the channel");
this.endCall()
let callingStatus = document.createElement('p');
callingStatus.id = 'callingState'
callingStatus.innerText = "Call ended";
let videoBlock = document.getElementById('video-block');
videoBlock.append(callingStatus);
setTimeout(() => {
document.getElementById('callingState').remove()
}, 2000)
});
console.log('Init listeners done')
},
initAgoraRtmListeners() {
this.agoraRtmClient.on('MessageFromPeer', function (message, peerId) {
document.getElementById("receivedPeerMessage").appendChild(document.createElement('div')).append(peerId + ": " + message.text)
})
this.agoraRtmClient.on('ConnectionStateChanged', function (state, reason) {
console.log("State changed To: " + state + " Reason: " + reason)
})
this.agoraRtmClient.on('RemoteInvitationReceived', (remoteInvitation) => this.showCall(remoteInvitation))
this.agoraRtmClient.on('RemoteInvitationRefused', function (response) {
console.log('RemoteInvitationRefused')
});
this.agoraOnlineChannel.on('MemberJoined', function (memberId) {
console.log(memberId + " joined the channel")
})
this.agoraOnlineChannel.on('MemberLeft', function (memberId) {
console.log(memberId + " left the channel")
})
},
showCall(remoteInvitation) {
this.incomingCall = true
this.remoteInvitation = remoteInvitation
remoteInvitation.on('RemoteInvitationAccepted', async () => {
console.log("Accepted Remote Invitation")
})
remoteInvitation.on('RemoteInvitationCanceled', function (content) {
this.incomingCall = false
this.incomingCaller = ''
})
this.incomingCaller = remoteInvitation.callerId
},
async joinChannel(channel, channelName) {
await channel.join().then(() => {
document.getElementById("log").appendChild(document.createElement('div')).append("You have successfully joined channel " + channel.channelId)
})
},
async acceptCall() {
this.incomingCall = false;
try {
const channelName = `${this.incomingCaller}_${this.authuser}`;
await this.joinRoom(channelName)
this.remoteInvitation.accept();
} catch (e) {
console.log(e)
}
},
refuseCall() {
this.incomingCall = false;
this.remoteInvitation.refuse();
this.localInvitation = null;
this.incomingCaller = ""
},
cancelCall() {
this.localInvitation.cancel()
},
async sendPeerMessage() {
let peerId = this.incomingCaller
let peerMessage = document.getElementById("peerMessage").value.toString()
if (peerMessage !== '') {
await this.agoraRtmClient.sendMessageToPeer(
{text: peerMessage},
peerId,
).then(sendResult => {
if (sendResult.hasPeerReceived) {
document.getElementById("receivedPeerMessage").appendChild(document.createElement('div')).append(this.authuser + ": " + peerMessage)
// document.getElementById("log").appendChild(document.createElement('div')).append("Message has been received by: " + peerId + " Message: " + peerMessage)
} else {
document.getElementById("log").appendChild(document.createElement('div')).append("Message sent to: " + peerId + " Message: " + peerMessage)
}
})
document.getElementById("peerMessage").value = ''
}
},
async placeCall(id, calleeName) {
try {
if (this.callPlaced) {
await this.endCall();
}
if (this.localInvitation) {
this.localInvitation.cancel()
}
document.getElementById('callingState')?.remove()
let callingStatus = document.createElement('p');
callingStatus.id = 'callingState'
callingStatus.innerText = "Calling";
let videoBlock = document.getElementById('video-block');
videoBlock.append(callingStatus);
const channelName = `${this.authuser}_${calleeName}`;
let invitation = this.agoraRtmClient.createLocalInvitation(calleeName.toString());
invitation.on('LocalInvitationCanceled', function () {
console.log('Local Invitation ended')
})
invitation.on('LocalInvitationAccepted', async (response) => {
document.getElementById('callingState').remove()
console.log("Accepted Local Invitation on channel " + channelName)
await this.joinRoom(channelName)
this.localInvitation = null
})
invitation.on('LocalInvitationRefused', (response) => {
callingStatus.innerText = 'Declined'
setTimeout(() => {
document.getElementById('callingState').remove()
}, 2000)
this.incomingCaller = ''
this.localInvitation = null
})
this.localInvitation = invitation
invitation.send();
this.incomingCaller = calleeName.toString();
} catch (error) {
console.log(error);
}
},
async generateVideoToken(channelName) {
let response = await fetch(@js(route('agora.tokens.rtc')), {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify({
channelName: channelName.toString()
}),
});
console.log(response)
if (response.ok) {
return (await response.json()).token
} else {
console.log(response.status)
}
},
async joinRoom(channel) {
const tokenRes = await this.generateVideoToken(channel);
console.log("join channel " + channel)
await this.client.join(
this.agoraOptions.appId,
channel,
tokenRes,
this.authuserid,
);
await this.createLocalStream();
this.callPlaced = true;
},
async endCall() {
this.callPlaced = false;
this.channelParameters.localAudioTrack.close()
this.channelParameters.localVideoTrack.close()
this.removeVideoBlock('local-video')
this.removeVideoBlock('remote-video')
document.getElementById('receivedPeerMessage').innerHTML = ''
await this.client.leave();
console.log("You left the channel");
},
createLocalVideoBlock() {
let localVideoBlock = document.createElement('div')
localVideoBlock.style.top = '0'
localVideoBlock.style.right = '0'
localVideoBlock.style.width = '240px'
localVideoBlock.style.height = '120px'
localVideoBlock.id = "local-video"
localVideoBlock.classList.add("position-absolute", "z-3")
document.getElementById('video-block').append(localVideoBlock)
},
createRemoteVideoBlock() {
let removeVideoBlock = document.createElement('div')
removeVideoBlock.style.width = '640px'
removeVideoBlock.style.height = '480px'
removeVideoBlock.style.overflow = 'hidden'
removeVideoBlock.style.borderRadius = '0.5rem'
removeVideoBlock.id = "remote-video"
document.getElementById('video-block').append(removeVideoBlock)
},
removeVideoBlock(elementId) {
// console.log("Removing " + elementId + " Div");
let Div = document.getElementById(elementId);
if (Div) {
Div.remove();
}
},
async createLocalStream() {
this.channelParameters.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
this.channelParameters.localVideoTrack = await AgoraRTC.createCameraVideoTrack();
console.log("create local audio/video track success");
this.createLocalVideoBlock()
this.channelParameters.localVideoTrack.play("local-video");
await this.client.publish([
this.channelParameters.localAudioTrack,
this.channelParameters.localVideoTrack
]);
console.log("publish success!");
},
// getUserOnlineStatus(id) {
// return this.onlineUsers.findIndex((data) => data.id === id) < 0 ? "Offline" : "Online";
// },
})
)
});
I tried going through all the steps with the console.log output. But in this listener for example the connection is not played back.
this.client.on("user-published", async (remoteUser, mediaType) => {
console.log('remote user: ' + remoteUser.uid.toString())
await this.client.subscribe(remoteUser, mediaType);
if (mediaType == "video") {
console.log("subscribe video success");
this.channelParameters.remoteVideoTrack = remoteUser.videoTrack
this.channelParameters.remoteAudioTrack = remoteUser.audioTrack
this.channelParameters.remoteUid = remoteUser.uid.toString()
this.createRemoteVideoBlock()
this.channelParameters.remoteVideoTrack.play("remote-video");
}
if (mediaType == "audio") {
console.log("subscribe audio success");
this.channelParameters.remoteAudioTrack = remoteUser.audioTrack
remoteUser.audioTrack.play();
}
});