Resolving Agora SDK Token Error (CONNECTION_CHANGED_INVALID_TOKEN) in Live Stream App

79 views Asked by At

I've been experiencing an issue for the past week while trying to create an app that uses the interactive live stream agora SDK. I've noticed that if I use a token generated from the console, everything works perfectly. However, if I generate a token myself and try to use it to access the channel, I get an error message showing "state value 5. The Reason 8 (CONNECTION_CHANGED_INVALID_TOKEN)". I've deployed the agora-token-service using Render, and I've made sure that the appID and App certificate are correct and match each other. Additionally, I've confirmed that the channel name and ID used to generate the token are the exact same ones that were used to join the channel. Despite all this, I'm still confused as to what the problem could be. I'm not sure what else to do or try. here is my viewModel code: ` @HiltViewModel class VideoViewModel @Inject constructor() : ViewModel() {

var mEngine: RtcEngine? by mutableStateOf(null)

private var channelName by mutableStateOf("punchword")

private var clientRole = Constants.CLIENT_ROLE_AUDIENCE

private val tokenExpireTime = 300

private var agoraToken by mutableStateOf("")

var isJoined by mutableStateOf(false)

private var uid by mutableIntStateOf(1)

fun initial(context: Context, channelName: String, userRole: String, uid: Int, token: String, eventHandler: IRtcEngineEventHandler) {
    this.channelName = channelName

// this.uid = uid agoraToken = token clientRole = if (userRole == "Broadcaster") Constants.CLIENT_ROLE_BROADCASTER else Constants.CLIENT_ROLE_AUDIENCE viewModelScope.launch { if (agoraToken == ""){ agoraToken = fetchToken() Log.d(TAG, "Token Fetched: $agoraToken") }

        mEngine = initEngine(
            current = context,
            eventHandler = eventHandler
        )
    }
}

private fun initEngine(
    current: Context,
    eventHandler: IRtcEngineEventHandler,
): RtcEngine =
    RtcEngine.create(current, KHALIL_ID, eventHandler).apply {
        enableVideo()
        setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING)
        setClientRole(clientRole)
        joinChannel(agoraToken, channelName, "", uid)
    }

private suspend fun fetchToken(): String {
    // Prepare the Url
    val urlString = (KHALIL_URL + "/rtc/" + channelName + "/" + clientRole + "/"
            + "uid" + "/" + uid + "/?expiry=" + tokenExpireTime)
    val client = OkHttpClient()

    // Instantiate the RequestQueue.
    val request: Request = Builder()
        .url(urlString)
        .header("Content-Type", "application/json; charset=UTF-8")
        .get()
        .build()

    val call: Call = client.newCall(request)

    return withContext(Dispatchers.IO) {
        call.execute().use { response ->
            if (!response.isSuccessful) throw IOException("Unexpected code $response")
            val responseJsonString = response.body?.string()
            val gson = Gson()
            val jsonObject = gson.fromJson(responseJsonString, JsonObject::class.java)
            jsonObject.get("rtcToken").toString()
        }
    }
}

fun leaveChannel() {
    mEngine?.leaveChannel()
}

} and here is my screen code: @Destination( route = "CallScreen", ) @Composable fun CallScreen( navigator: DestinationsNavigator, channelName: String, userRole: String, uid: Int, token: String, viewModel: VideoViewModel = hiltViewModel() ) { val context = LocalContext.current

var localSurfaceView: TextureView? by remember { mutableStateOf(null) }
var remoteUserMap by remember { mutableStateOf(mapOf<Int, TextureView?>()) }

val eventHandler = object : IRtcEngineEventHandler() {


    override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
        Log.d(TAG, "channel:$channel,uid:$uid,elapsed:$elapsed")
        viewModel.isJoined = true
    }

    override fun onUserJoined(uid: Int, elapsed: Int) {
        Log.d(TAG, "onUserJoined:$uid")
        val desiredUserList = remoteUserMap.toMutableMap()
        desiredUserList[uid] = null
        remoteUserMap = desiredUserList.toMap()
    }

    override fun onUserOffline(uid: Int, reason: Int) {
        Log.d(TAG, "onUserOffline:$uid")
        val desiredUserList = remoteUserMap.toMutableMap()
        desiredUserList.remove(uid)
        remoteUserMap = desiredUserList.toMap()
    }

    override fun onLeaveChannel(stats: RtcStats?) {
        Log.d(TAG, "onLeaveChannel")
        viewModel.isJoined = false
    }

    override fun onConnectionStateChanged(state: Int, reason: Int) {
        Log.d(TAG, "onConnectionStateChanged: state: $state, reason: $reason")
    }
}

DisposableEffect(key1 = true) {
    onDispose {
        viewModel.leaveChannel()
    }
}

LaunchedEffect(key1 = true) {
    localSurfaceView = RtcEngine.CreateTextureView(context)
    viewModel.initial(
        context = context,
        channelName = channelName,
        userRole = userRole,
        uid = uid,
        token = token,
        eventHandler = eventHandler
    )
}
if (userRole == "Broadcaster") {
    viewModel.mEngine?.setupLocalVideo(
        VideoCanvas(
            localSurfaceView,
            Constants.RENDER_MODE_FIT,
            0
        )
    )
}

Box(Modifier.fillMaxSize()) {
    localSurfaceView?.let { local ->
        AndroidView(factory = { local }, Modifier.fillMaxSize())
    }
    viewModel.mEngine?.let { mEngine ->
        RemoteView(remoteListInfo = remoteUserMap, mEngine = mEngine)
        UserControls(mEngine = mEngine) {
            navigator.popBackStack()
        }
    }
}

} ` Just to provide some context, on the previous screen, the user has the option to enter a channel name. If the user doesn't provide a token, the app generates one automatically. If a token is provided, the app will use the one entered by the user. I understand that to join an existing channel, a user needs both the channel name and its token. Therefore, we're providing the option to either create or join a channel. It's important to note that this project is solely for testing and prototyping purposes.

.....................................................

0

There are 0 answers