Android Webrtc为语音聊天应用创建网格架构

问题描述

我正在尝试为4人一组创建一个android语音聊天应用,4人可以互相交谈。这可以使用android web RTC中的网格架构来完成。我已经创建了所有先决条件,即设置了转弯服务器,信令服务器和android应用程序代码。这里的问题是我不能使它适用于两个以上的人。在s what发生的情况下,最初,用户(A)加入了一个房间。然后另一个用户(B)加入该房间。 SDP提供,应答和ICE候选人成功交换并建立连接。 A B 都可以互相交谈。当第三个用户(C)加入会议室时,问题开始。 C 进入会议室后,SDP报价,答案和ICE候选人将与 A B C ,在 B C 成功连接后,他们都可以互相交谈,而 B A 断开连接>,表示 A 听不到 B B 听不到 A ,尽管它们在开始时已成功连接,然后用户 A 以单向连接到 C ,这意味着 C 可以听 A A 无法听 C

我无法弄清楚。任何帮助都非常有用。

我的 RTcclient 类中有此代码

class RTcclient(context: Application,mSocketId: String) {

    val socketId: String = mSocketId
    private val listofServers = ArrayList<IceServer>()

    init {
        initPeerConnectionFactory(context)
        createServeList()
    }

    private fun createServeList() {

        listofServers.add(IceServer.builder("stun:stun.services.mozilla.com").createIceServer())
        listofServers.add(IceServer.builder("stun:stun.l.google.com:19302").createIceServer())
        listofServers.add(
            IceServer.builder("turn:ip:port").setUsername("usernmae")
                .setPassword("password").createIceServer()
        )
    }

    private val peerConnectionFactory by lazy { buildPeerConnectionFactory() }

    private val localAudioSource by lazy { peerConnectionFactory.createAudioSource(MediaConstraints()) }
    private val peerConnection by lazy { buildPeerConnection(peerConnectionObserver) }


    private fun initPeerConnectionFactory(context: Application) {
        val options = PeerConnectionFactory.Initializationoptions.builder(context)
            .setEnableInternalTracer(true)
            .setFieldTrials("WebRTC-H264HighProfile/Enabled/")
            .createInitializationoptions()
        PeerConnectionFactory.initialize(options)
    }


    private fun buildPeerConnectionFactory(): PeerConnectionFactory {
        return PeerConnectionFactory
            .builder()
            .setoptions(PeerConnectionFactory.Options().apply {
                disableEncryption = true
                disableNetworkMonitor = true
            })
            .createPeerConnectionFactory()
    }

    private fun buildPeerConnection(observer: PeerConnection.Observer) =
        peerConnectionFactory.createPeerConnection(
            listofServers,observer
        )

    fun startLocalAudioSource(): ConnectionFactory {
        val audioTrack = peerConnectionFactory.createAudioTrack("ARdamSa0",localAudioSource)
        val localMediaStream = peerConnectionFactory.createLocalMediaStream("ARdamS")
        localMediaStream.addTrack(audioTrack)
        peerConnection?.addStream(localMediaStream)
        return ConnectionFactory(peerConnectionFactory,peerConnection)
    }

    private fun PeerConnection.call(sdpObserver: SdpObserver) {
        val constraints = MediaConstraints().apply {
            mandatory.add(MediaConstraints.keyvaluePair("OfferToReceiveVideo","false"))
        }

        createOffer(object : SdpObserver by sdpObserver {
            override fun onCreateSuccess(desc: SessionDescription?) {

                setLocalDescription(object : SdpObserver {
                    override fun onSetFailure(p0: String?) {
                    }

                    override fun onSetSuccess() {
                    }

                    override fun onCreateSuccess(p0: SessionDescription?) {
                    }

                    override fun onCreateFailure(p0: String?) {
                    }
                },desc)
                sdpObserver.onCreateSuccess(desc)
            }
        },constraints)
    }

    private fun PeerConnection.answer(sdpObserver: SdpObserver) {
        val constraints = MediaConstraints().apply {
            mandatory.add(MediaConstraints.keyvaluePair("OfferToReceiveVideo","false"))
        }

        createAnswer(object : SdpObserver by sdpObserver {
            override fun onCreateSuccess(p0: SessionDescription?) {
                setLocalDescription(object : SdpObserver {
                    override fun onSetFailure(p0: String?) {
                    }

                    override fun onSetSuccess() {
                    }

                    override fun onCreateSuccess(p0: SessionDescription?) {
                    }

                    override fun onCreateFailure(p0: String?) {
                    }
                },p0)
                sdpObserver.onCreateSuccess(p0)
            }
        },constraints)
    }

    fun call(sdpObserver: SdpObserver) = peerConnection?.call(sdpObserver)

    fun answer(sdpObserver: SdpObserver) = peerConnection?.answer(sdpObserver)

    fun onRemoteSessionReceived(sessionDescription: SessionDescription) {
        peerConnection?.setRemoteDescription(object : SdpObserver {
            override fun onSetFailure(p0: String?) {
            }

            override fun onSetSuccess() {
            }

            override fun onCreateSuccess(p0: SessionDescription?) {
            }

            override fun onCreateFailure(p0: String?) {
            }
        },sessionDescription)
    }

    fun addIceCandidate(iceCandidate: IceCandidate?) {
        peerConnection?.addIceCandidate(iceCandidate)
    }

    val sdpObserver = object : AppSdpObserver() {
        override fun onCreateSuccess(p0: SessionDescription?) {
            super.onCreateSuccess(p0)
            SocketConnection.socket.emit("offer",BaseClass.gSon.toJson(p0),socketId)
        }
    }

    private val peerConnectionObserver = object : PeerConnectionObserver() {
        override fun onIceCandidate(p0: IceCandidate?) {
            super.onIceCandidate(p0)
            SocketConnection.socket.emit("ice",socketId)
        }
    }   
}

这是我的片段代码

class VoiceChatFragment : Fragment(),UsersAdapter.OnItemClickListener,SocketConnection.socketCallbacks,EasyPermissions.PermissionCallbacks {
    private var doubleBackToExitpressedOnce: Boolean = false
    private lateinit var joinResponse: Roomresponse
    private var rtcclientList = ArrayList<RtcclientModel>()
    private val hashMapCallSetUp = HashMap<String,Boolean>()
    private lateinit var loginResponse: Response
    private lateinit var binding: VoiceChatBinding
    private val userAdapter = UsersAdapter(this)
    private lateinit var me: Users
    private var usersList: ArrayList<Users> = ArrayList()

    companion object {
        const val RC_CAMERA_PERM = 100
    }

    //other code.

    override fun onJoin(params: String) {
        Timber.d("On user joined. $params")
        joinResponse = gSon.fromJson(params,Roomresponse::class.java)
        if (joinResponse.result == true) {
            setVisibility(joinResponse.result ?: false)
            initCall()            
        } else {
            loginResponse.message = joinResponse.message
            loginResponse.result = joinResponse.result
            activity?.runOnUiThread { joinResponse.message?.toast() }
        }
    }   

    private fun initCall() {
        val mRoomData = joinResponse.roomData?.users
        val reqTime = mRoomData?.filterIndexed { _,element ->
            element?.userId == me.userId
        }?.firstOrNull()?.reqTime ?: 0
        me.reqTime = reqTime
        val roomData = mRoomData?.filterIndexed { _,element ->
            element?.userId != me.userId
        }
        activity?.runOnUiThread { mRoomData?.size?.toast() }
        roomData?.forEach { roomUser ->
            if (hashMapCallSetUp[roomUser?.userId] == null) {
                Timber.d("creating call setup process for ${roomUser?.userNick}")
                hashMapCallSetUp[roomUser?.userId.orEmpty()] = true
                initRtcclient(roomUser?.userId.orEmpty(),roomUser?.socketId.orEmpty())
                if (me.reqTime > roomUser?.reqTime!!) {
                    Timber.d("creating offer for ${roomUser.userNick}")
                    val client = rtcclientList.filterIndexed { _,item ->
                        item.userId == roomUser.userId
                    }.firstOrNull()?.rtcclient
                    client?.call(client.sdpObserver)
                    Timber.d("created call offer for ${roomUser.userNick}")
                }
            }
        }
    }

    private fun initRtcclient(userId: String,socketId: String) {
        val rtcclient = RTcclient(activity?.application!!,socketId)
        val connectionFactory = rtcclient.startLocalAudioSource()
        val rtcObject = RtcclientModel(userId,socketId,rtcclient,connectionFactory)
        rtcclientList.add(rtcObject)
    }

    

    override fun onOfferReceived(
        description: SessionDescription,user: Roomresponse.RoomData.User
    ) {
        Timber.d("Override onOfferReceived ${user.userId}")
        val client = rtcclientList.filterIndexed { _,rtcclientModel ->
            rtcclientModel.userId == user.userId
        }.firstOrNull()?.rtcclient
        Timber.d("setting remote description for ${user.userId}")
        client?.onRemoteSessionReceived(description)
        Timber.d("creating answer for ${user.userId}")
        client?.answer(client.sdpObserver)

    }

    override fun onAnswerReceived(
        description: SessionDescription,user: Roomresponse.RoomData.User
    ) {
        Timber.d("Override onAnswerReceived ${user.userId}")
        val client = rtcclientList.filterIndexed { _,rtcclientModel ->
            rtcclientModel.userId == user.userId
        }.firstOrNull()?.rtcclient
        Timber.d("setting remote description for ${user.userId}")
        client?.onRemoteSessionReceived(description)
    }

    override fun onIceCandidateReceived(
        iceCandidate: IceCandidate,user: Roomresponse.RoomData.User
    ) {
        Timber.d("Override onICE candidates received for ${user.userId}")
        val client = rtcclientList.filterIndexed { _,rtcclientModel ->
            rtcclientModel.userId == user.userId
        }.firstOrNull()?.rtcclient
        Timber.d("setting ice candidates for ${user.userId}")
        client?.addIceCandidate(iceCandidate)
    }
}

SDp观察器界面

open class AppSdpObserver : SdpObserver {
    override fun onSetFailure(p0: String?) {
        Timber.tag("VoiceChatFragment").d("On set failure. Reason $p0")
    }

    override fun onSetSuccess() {
        Timber.tag("VoiceChatFragment").d("On set success")
    }

    override fun onCreateSuccess(p0: SessionDescription?) {
        Timber.tag("VoiceChatFragment").d("On create success")
    }

    override fun onCreateFailure(p0: String?) {
        Timber.tag("VoiceChatFragment").d("On create failure. Reason $p0")
    }
}

这是对等连接观察者类

open class PeerConnectionObserver : PeerConnection.Observer {
    override fun onIceCandidate(p0: IceCandidate?) {
        Timber.tag("VoiceChatFragment").d("On ice candidates")
    }

    override fun onDataChannel(p0: DataChannel?) {
        Timber.tag("VoiceChatFragment").d("On Data Channel")
    }

    override fun onIceConnectionReceivingChange(p0: Boolean) {
        Timber.tag("VoiceChatFragment").d("On Ice connection receiving change")
    }

    override fun onIceConnectionChange(p0: PeerConnection.IceConnectionState?) {
        Timber.tag("VoiceChatFragment").d("On ice connection change")
    }

    override fun onIceGatheringChange(p0: PeerConnection.IceGatheringState?) {
        Timber.tag("VoiceChatFragment").d("On ice gathering change")
    }

    override fun onAddStream(p0: MediaStream?) {
        Timber.tag("VoiceChatFragment").d("On Add stream")
    }

    override fun onSignalingChange(p0: PeerConnection.SignalingState?) {
        Timber.tag("VoiceChatFragment").d("On Signaling change")
    }

    override fun onIceCandidatesRemoved(p0: Array<out IceCandidate>?) {
        Timber.tag("VoiceChatFragment").d("On ice candidates removed")
    }

    override fun onRemoveStream(p0: MediaStream?) {
        Timber.tag("VoiceChatFragment").d("On remove stream")
    }

    override fun onRenegotiationNeeded() {
        Timber.tag("VoiceChatFragment").d("On renegotiation needed")
    }

    override fun onAddTrack(p0: RtpReceiver?,p1: Array<out MediaStream>?) {
        Timber.tag("VoiceChatFragment").d("On add track")
    }
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)