vue.jsagora.ioagora-web-sdk-ng

AgoraRTCError INVALID_REMOTE_USER: user is not in the channel


I am using the Agora Web SDK NG 4.X, and I am trying to build a video conference application using Vue JS. I have been closely following the Agora documentation, but I keep getting this error "AgoraRTCError INVALID_REMOTE_USER: user is not in the channel" and I have no idea how this is possible Here is my code in the scripe section:

<script>
import AgoraRTC from "agora-rtc-sdk-ng"

export default {
  data() {
    return {
      remoteUsers: {},
      agoraClient: null,
      agoraAppID: null,
      agoraToken: null,
      localAudioTrack: null,
      localVideoTrack: null,
      localScreenTrack: null,
      userID: null,
      roomID: null,
      participants: [],
    }
  },
  mounted() {
    this.agoraAppID = import.meta.env.VITE_AGORA_APP_ID
    this.getUserID()
    this.getRoomID()
    this.initializeRTCClient()
    this.joinRoomInit()
  },
  methods: {

    async joinRoomInit() {
      try {
        await this.agoraClient.join(this.agoraAppID, "demo_channel", this.agoraToken, this.userID)

        this.joinStream()

        this.agoraClient.on('user-published', this.handleUserPublished)
        this.agoraClient.on('user-unpublished', this.handleUserLeft)

      } catch (error) {
        console.log(error);
      }
    },
    initializeRTCClient() {
      this.agoraClient = AgoraRTC.createClient({ mode: 'rtc', codec: 'vp8' })
    },
    getUserID() {
      if (!this.userID) {
        this.userID = String(Math.floor(Math.random() * 10000))
      }
      console.log('user_id', this.userID)
    },
    getRoomID() {
      if (!this.roomID) {
        this.roomID = this.$route.params.roomId
        console.log('room_id', this.roomID)
      }
    },
    async joinStream() {
      this.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack()
      this.localVideoTrack = await AgoraRTC.createCameraVideoTrack()
      // this.localScreenTrack = await AgoraRTC.createScreenVideoTrack()
      this.participants.push({ uid: this.userID })
      console.log(this.participants)

      let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
      await sleep(500);

      this.localVideoTrack.play(`user-${this.userID}`)
      await this.agoraClient.publish([this.localAudioTrack, this.localVideoTrack])
    },
    async handleUserPublished(user, mediaType) {
      this.remoteUsers[user.uid] = user

      // Initiate the Subscription
      await this.agoraClient.subscribe(user, mediaType);

      this.participants.push({ uid: user.uid })
      console.log(this.participants)

      let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
      await sleep(5000);

      if (mediaType === 'audio') {
        let audioTrack = user.audioTrack
        audioTrack.play()
      } else {
        let videoTrack = user.videoTrack
        videoTrack.play(`user-${user.uid}`)
      }

    },
    async handleUserLeft(user) {
      delete this.remoteUsers[user.uid]
      this.participants = this.participants.filter(participant => participant.uid !== user.uid)
    },
    handleError(error) {
      if (error.name === 'Not Allowed Error') {
        console.log('Permissions have not been granted to use your camera and ' +
        'microphone, you need to allow the page access to your devices in ' +
        'order for the demo to work.')
      }
    }
  }
}
</script>

In the template section I have:

<section id="stream-container" class="h-5/6">
   <div id="tracks-container" class="w-full flex flex-nowrap mt-8 mb-12 overflow-x-auto">
      <div v-for="participant in participants" :id="'user-container-' + participant.uid" @click="pinStream" class="flex content-center items-center border border-indigo-600 rounded bg-indigo-600 overflow-hidden" style="width: 360px; height: 270px;">
         <div class="w-full h-full" :id="'user-' + participant.uid">

         </div>
      </div>
   </div>
</section>

Any help is appreacited!


Solution

  • The problem is with vue3's reactivity system kicking in and creating proxies around all objects assigned to data.

    You should use markRaw and make objects returned from agora SDK non-reactive so that Vue doesn't interfere with them.

    initializeRTCClient() {
      this.agoraClient = markRaw(AgoraRTC.createClient({ mode: 'rtc', codec: 'vp8' }))
    },
    

    You might also have to wrap the other objects returned from Agora in markRaw.