Last active
July 12, 2022 07:53
-
-
Save digitallysavvy/3d9c0bf73dabee3c51fea856aa372bec to your computer and use it in GitHub Desktop.
An implementation of the Agora RTC and RTM Web SDKs for use within the Agora WebXR tutorial.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // create client | |
| var client = AgoraRTC.createClient({mode: 'live', codec: 'vp8'}); // vp8 to work across mobile devices | |
| const agoraAppId = ''; // insert Agora AppID here | |
| const channelName = 'WebAR'; | |
| var streamCount = 0; | |
| // set log level: | |
| // -- .DEBUG for dev | |
| // -- .NONE for prod | |
| AgoraRTC.Logger.setLogLevel(AgoraRTC.Logger.DEBUG); | |
| client.init(agoraAppId, () => { | |
| console.log('AgoraRTC client initialized'); | |
| joinChannel(); // join channel upon successfull init | |
| }, function (err) { | |
| console.log('[ERROR] : AgoraRTC client init failed', err); | |
| }); | |
| // connect remote streams | |
| client.on('stream-added', (evt) => { | |
| const stream = evt.stream; | |
| const streamId = stream.getId(); | |
| console.log('New stream added: ' + streamId); | |
| console.log('Subscribing to remote stream:' + streamId); | |
| // Subscribe to the stream. | |
| client.subscribe(stream, (err) => { | |
| console.log('[ERROR] : subscribe stream failed', err); | |
| }); | |
| streamCount++; | |
| createBroadcaster(streamId); // create 3d broadcaster | |
| }); | |
| client.on('stream-removed', (evt) => { | |
| const stream = evt.stream; | |
| stream.stop(); // stop the stream | |
| stream.close(); // clean up and close the camera stream | |
| console.log("Remote stream is removed " + stream.getId()); | |
| }); | |
| client.on('stream-subscribed', (evt) => { | |
| const remoteStream = evt.stream; | |
| const remoteId = remoteStream.getId(); | |
| console.log('Successfully subscribed to remote stream: ' + remoteStream.getId()); | |
| // get the designated video element and add the stream as its video source | |
| var video = document.getElementById("faceVideo-" + remoteId); | |
| connectStreamToVideo(remoteStream, video) | |
| }); | |
| // remove the remote-container when a user leaves the channel | |
| client.on('peer-leave', (evt) => { | |
| console.log('Remote stream has left the channel: ' + evt.uid); | |
| evt.stream.stop(); // stop the stream | |
| const remoteId = evt.stream.getId(); | |
| document.getElementById(remoteId).remove(); | |
| document.getElementById("faceVideo-" + remoteId); | |
| streamCount--; | |
| }); | |
| // show mute icon whenever a remote has muted their mic | |
| client.on('mute-audio', (evt) => { | |
| console.log("mute-audio for: " + evt.uid); | |
| }); | |
| client.on('unmute-audio', (evt) => { | |
| console.log("unmute-audio for: " + evt.uid); | |
| }); | |
| // show user icon whenever a remote has disabled their video | |
| client.on('mute-video', (evt) => { | |
| console.log("mute-video for: " + evt.uid); | |
| }); | |
| client.on('unmute-video', (evt) => { | |
| console.log("unmute-video for: " + evt.uid); | |
| }); | |
| // join a channel | |
| function joinChannel() { | |
| const token = generateToken(); | |
| // set the role | |
| client.setClientRole('audience', () => { | |
| console.log('Client role set to audience'); | |
| }, (e) => { | |
| console.log('setClientRole failed', e); | |
| }); | |
| client.join(token, channelName, 0, (uid) => { | |
| console.log('User ' + uid + ' join channel successfully'); | |
| joinRTMChannel(uid); | |
| }, function(err) { | |
| console.log('[ERROR] : join channel failed', err); | |
| }); | |
| } | |
| function leaveChannel() { | |
| client.leave(() => { | |
| console.log('client leaves channel'); | |
| }, (err) => { | |
| console.log('client leave failed ', err); //error handling | |
| }); | |
| } | |
| // Agora RTM | |
| // setup the RTM client and channel | |
| const rtmClient = AgoraRTM.createInstance(agoraAppId); | |
| const rtmChannel = rtmClient.createChannel(channelName); | |
| rtmClient.on('ConnectionStateChange', (newState, reason) => { | |
| console.log('on connection state changed to ' + newState + ' reason: ' + reason); | |
| }); | |
| // event listener for receiving a channel message | |
| rtmChannel.on('ChannelMessage', ({ text }, senderId) => { | |
| // text: text of the received channel message; senderId: user ID of the sender. | |
| console.log('AgoraRTM msg from user ' + senderId + ' recieved: \n' + text); | |
| }); | |
| function joinRTMChannel(uid){ | |
| rtmClient.login({ token: null, uid: String(uid) }).then(() => { | |
| console.log('AgoraRTM client login success'); | |
| // join a channel and send a message | |
| rtmChannel.join().then(() => { | |
| // join-channel success | |
| localStreams.rtmActive = true | |
| console.log('RTM Channel join success'); | |
| }).catch(error => { | |
| // join-channel failure | |
| console.log('failed to join channel for error: ' + error); | |
| }); | |
| }).catch(err => { | |
| console.log('AgoraRTM client login failure', err); | |
| }); | |
| } | |
| function rotateModel(uid, direction) { | |
| var model = document.getElementById(uid) | |
| if (direction === 'counter-clockwise') { | |
| model.object3D.rotation.y += 0.1; | |
| } else if (direction === 'clockwise') { | |
| model.object3D.rotation.y -= 0.1; | |
| } | |
| } | |
| function moveModel(uid, direction) { | |
| var model = document.getElementById(uid) | |
| switch (direction){ | |
| case 'forward': | |
| model.object3D.position.z += 0.1 | |
| break; | |
| case 'backward': | |
| model.object3D.position.z -= 0.1 | |
| break; | |
| case 'left': | |
| model.object3D.position.x -= 0.1 | |
| break; | |
| case 'right': | |
| model.object3D.position.x += 0.1 | |
| break; | |
| default: | |
| console.log('Unable to determin direction: ' + direction); | |
| } | |
| } | |
| // use tokens for added security | |
| function generateToken() { | |
| return null; // TODO: add a token generation | |
| } | |
| function createBroadcaster(streamId) { | |
| // create video element | |
| var video = document.createElement('video'); | |
| video.id = "faceVideo-" + streamId; | |
| video.setAttribute('webkit-playsinline', 'webkit-playsinline'); | |
| video.setAttribute('playsinline', 'playsinline'); | |
| video.setAttribute('poster', '/imgs/no-video.jpg'); | |
| console.log(video); | |
| // add video object to the DOM | |
| document.querySelector("a-assets").appendChild(video); | |
| // configure the new broadcaster | |
| const gltfModel = "#broadcaster"; | |
| const scale = "-0.55 -0.55 -0.55"; // invert UVs (hack) | |
| const offset = (streamCount-1); | |
| const position = offset + " 0 0"; | |
| const rotation = "180 0 0"; | |
| // create the broadcaster element using the given settings | |
| const parent = document.querySelector('a-marker'); | |
| var newBroadcaster = document.createElement('a-gltf-model'); | |
| newBroadcaster.setAttribute('id', streamId); | |
| newBroadcaster.setAttribute('gltf-model', gltfModel); | |
| newBroadcaster.setAttribute('scale', scale); | |
| newBroadcaster.setAttribute('position', position); | |
| newBroadcaster.setAttribute('rotation', rotation); | |
| parent.appendChild(newBroadcaster); | |
| console.log(newBroadcaster); | |
| // add event listener for model loaded: | |
| newBroadcaster.addEventListener('model-loaded', () => { | |
| var mesh = newBroadcaster.getObject3D('mesh'); | |
| mesh.traverse((node) => { | |
| // search the mesh's children for the face-geo | |
| if (node.isMesh && node.name == 'face-geo') { | |
| // create video texture from video element | |
| var texture = new THREE.VideoTexture(video); | |
| texture.minFilter = THREE.LinearFilter; | |
| texture.magFilter = THREE.LinearFilter; | |
| texture.flipY = false; | |
| // set node's material map to video texture | |
| node.material.map = texture | |
| node.material.color = new THREE.Color(); | |
| node.material.metalness = 0; | |
| } | |
| }); | |
| }); | |
| } | |
| function connectStreamToVideo(agoraStream, video) { | |
| video.srcObject = agoraStream.stream;// add video stream to video element as source | |
| video.onloadedmetadata = () => { | |
| // ready to play video | |
| video.play(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment