You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用nbmWebRTCPeer.generateOffer导致Android应用崩溃,求排查

Kurento WebRTC Peer Android开发:调用generateOffer崩溃无法生成sdpOffer

我在遵循Kurento WebRTC Peer for Android文档开发时,调用nbmWebRTCPeer.generateOffer("local",true)方法生成sdpOffer时应用直接崩溃,怀疑是nbmWebRTCPeer初始化流程存在问题。

相关代码

private fun receiveVideo(userId: Any?, userName: String) {
    runOnUiThread {
        val localView :SurfaceViewRenderer = binding.localview
        localView.init(EglBase.create().eglBaseContext,null)

        val mediaConfiguration = NBMMediaConfiguration()
        nbmWebRTCPeer  = NBMWebRTCPeer(mediaConfiguration,this, localView, object:NBMWebRTCPeerObserver(){
            override fun onInitialize() {
                super.onInitialize()
                Log.d(TAG,"onInitialize")
                try{
                    nbmWebRTCPeer.generateOffer("local",true)
                }catch (e:Exception){
                    e.printStackTrace()
                }

            }

            override fun onLocalSdpOfferGenerated(
                localSdpOffer: SessionDescription?,
                connection: NBMPeerConnection?
            ) {
                super.onLocalSdpOfferGenerated(localSdpOffer, connection)
                Log.d(TAG,"${localSdpOffer?.description}")
                if(!isMyVideoPublished){
                    val message = Message(
                        event = "receiveVideoFrom",
                        userid = userId,
                        userName = userName,
                        sdpOffer = localSdpOffer
                    )
                    socketManager.emitMessage(message)
                    isMyVideoPublished = true
                }
            }

        })

        lifecycleScope.launch {
            nbmWebRTCPeer.initialize()
        }
    }
}

LogCat崩溃信息截图

LogCat崩溃信息


问题排查与解决方案

  • 线程调度错误:你在UI线程触发初始化,又在初始化回调的UI线程直接调用generateOffer,但Kurento WebRTC Peer的核心操作(初始化、SDP生成)必须在非UI线程执行,线程冲突会直接引发崩溃。
  • EglBase实例管理不当:在方法内临时创建EglBase.create()会导致渲染上下文不稳定,建议将EglBase作为全局成员提前初始化。
  • 初始化未就绪就调用方法:onInitialize回调触发时,PeerConnection内部可能还未完成初始化流程,直接调用generateOffer会触发未就绪状态的异常。

修复后代码示例

// 类成员变量,全局管理资源
private lateinit var eglBase: EglBase
private lateinit var nbmWebRTCPeer: NBMWebRTCPeer

private fun receiveVideo(userId: Any?, userName: String) {
    // 提前初始化EglBase(建议在Activity onCreate中完成)
    if (!::eglBase.isInitialized) {
        eglBase = EglBase.create()
    }
    
    val localView: SurfaceViewRenderer = binding.localview
    localView.init(eglBase.eglBaseContext, null)

    val mediaConfiguration = NBMMediaConfiguration()
    nbmWebRTCPeer = NBMWebRTCPeer(mediaConfiguration, this, localView, object : NBMWebRTCPeerObserver() {
        override fun onInitialize() {
            super.onInitialize()
            Log.d(TAG, "onInitialize")
            // 切换到IO线程执行SDP生成
            lifecycleScope.launch(Dispatchers.IO) {
                try {
                    nbmWebRTCPeer.generateOffer("local", true)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }

        override fun onLocalSdpOfferGenerated(
            localSdpOffer: SessionDescription?,
            connection: NBMPeerConnection?
        ) {
            super.onLocalSdpOfferGenerated(localSdpOffer, connection)
            Log.d(TAG, "${localSdpOffer?.description}")
            if (!isMyVideoPublished) {
                val message = Message(
                    event = "receiveVideoFrom",
                    userid = userId,
                    userName = userName,
                    sdpOffer = localSdpOffer
                )
                // Socket发送也放在IO线程执行,避免阻塞UI
                lifecycleScope.launch(Dispatchers.IO) {
                    socketManager.emitMessage(message)
                }
                isMyVideoPublished = true
            }
        }
    })

    // 在IO线程执行初始化,避免UI线程阻塞
    lifecycleScope.launch(Dispatchers.IO) {
        nbmWebRTCPeer.initialize()
    }
}

额外注意事项

  1. 必须提前申请相机、麦克风权限,未授权情况下调用音视频相关方法会直接崩溃。
  2. 检查NBMMediaConfiguration是否正确配置了音视频轨道参数,确保开启了需要的媒体流。
  3. 页面销毁时务必释放资源,避免内存泄漏:
override fun onDestroy() {
    super.onDestroy()
    if (::nbmWebRTCPeer.isInitialized) {
        nbmWebRTCPeer.dispose()
    }
    if (::eglBase.isInitialized) {
        eglBase.release()
    }
}

内容的提问来源于stack exchange,提问作者Himanshu Arya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 02:06:30