对于单向本地 webRTC 视频,不会在 android 接收器上生成 Ice 候选人

问题描述 投票:0回答:0

我正在尝试在没有 STUN 或 TURN 服务器的私有网络上创建一个从 android 相机(使用 kotlin)到 web 浏览器(chrome)的单向视频 webRTC。浏览器调用 android,当 android 应答时,它应该将 android 视频流发送到浏览器服务器,在那里它可以通过视频元素显示。浏览器不需要向 android 发送任何视频或音频。 问题是 android 没有生成任何 ICE 候选人。它创建一个答案,并设置本地描述但不创建或发送任何 ICE 候选人。他们交换描述,并在 chrome://webrtc-internals 中表示 SDP 连接稳定。

WebRTC Internals pic

Android answer() 函数:

 fun answer(target: String) {
        Log.d("answer","xxx answer: $target")

        val constraints = MediaConstraints().apply {
            mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", "false")) 
            mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "false"))  
        }
         
        peerConnection?.createAnswer(object : SdpObserver {

            override fun onCreateSuccess(desc: SessionDescription?) {
              Log.d("onCreateSuccess", "xxx onCreateSuccess")
              peerConnection?.setLocalDescription(object : SdpObserver {  
                    override fun onCreateSuccess(p0: SessionDescription?) {
                        Log.d("answer success", "xxx local description created")
                    }


                    override fun onSetSuccess() {
                        Log.d("onSetSuccess", "xxx onSetSuccess")
                        // print the localDescription
                        Log.d("localDescription", "xxx ${peerConnection?.localDescription}")
 



                        val answer = hashMapOf(
                            "sdp" to desc?.description,
                            "type" to desc?.type
                        )
                        socketRepository.sendMessageToSocket(
                            MessageModel(
                                "create_answer", username, target, answer
                            )
                        )but android isn’t generating ice candidates for some reason,

                    }

                    override fun onCreateFailure(p0: String?) {
                        Log.d("xxx onCreateFailure", "$p0")
                    }

                    override fun onSetFailure(p0: String?) {
                        Log.d("xxx onSetFailure", "$p0")
                    }

                }, desc) // this line means that we are passing the desc object to the onCreateSuccess method of the SdpObserver object we created above

            }

            override fun onSetSuccess() {
             }

            override fun onCreateFailure(p0: String?) {
                Log.d("xxx onCreateFailure", "$p0")
            }

            override fun onSetFailure(p0: String?) {
                Log.d("xxx onSetFailure", "$p0")
            }

        }, constraints)
     }

浏览器服务器:


function handleNegotiationNeededEvent() {
    console.log("handleNegotiationNeededEvent")
    peerConnection
        .createOffer(offer_constraints)
        .then((offer) => peerConnection.setLocalDescription(offer))
        .then(async () => {

            await waitUntilIceGatheringStateComplete(peerConnection, options);

            console.log('offer', peerConnection.localDescription)
            client.send(JSON.stringify({
                type: "create_offer",
                name: peer2,
                target: peer1,
                data: peerConnection.localDescription
            }))
        })
        .catch((e) => {
            console.log(e)
        });
}

浏览器服务器消息切换语句:

 client.onmessage = async function (e) {
        const data = JSON.parse(e.data)
        console.log(data);

        switch (data.type) {
            case "call_response":
                if (data.data == "user is ready for call") {

                    // 3.  Create a new RTCPeerConnection for broadcaster
                    createPeerConnection();
 
                    peerConnection.addTransceiver('video', { direction: 'recvonly' })
                    videoTrack = peerConnection.getTransceivers()[0].receiver.track;

                   


                }
                break;

            case "answer_received":
                if (data.name == peer1) {

                    console.log('answer received', data)

                    await peerConnection.setRemoteDescription({
                        type: "answer",
                        sdp: data.data
                    })

                    const videos = document.createElement('div');
                    videos.className = 'grid';
                    document.body.appendChild(videos);

                    const localVideo = document.createElement('video');
                    localVideo.muted = true;
                    localVideo.autoplay = true;
                    localVideo.height = 240;
                    localVideo.width = 320;
                    localVideo.id = 'vid_player'

                    videos.appendChild(localVideo);

                    const mediaStream = new MediaStream(peerConnection.getReceivers().map(receiver => receiver.track))

                    console.log('mediaStream', mediaStream)
                    let vid_player = document.getElementById('vid_player')

                    vid_player.srcObject = mediaStream
 

                }
                break;
            case "ice_candidate":
                if (data.name == peer1) {
                    console.log('ice candidate received', data)
                    // create new ice candidate and add it to our peer connection
                    const candidate = new RTCIceCandidate({
                        sdpMLineIndex: data.data.sdpMLineIndex,
                        sdpMid: data.data.sdpMid,
                        candidate: data.data.sdpCandidate,
                    })
                    await peerConnection.addIceCandidate(candidate).catch(e => console.error(e));

                }
        }

    }
android kotlin webrtc cross-platform rtcpeerconnection
© www.soinside.com 2019 - 2024. All rights reserved.