Android WebRTC не работает в другой сети - нет видео - PullRequest
0 голосов
/ 26 февраля 2019

Я пытаюсь транслировать видео с Raspberry Pi на устройство Android через webrtc.Я использую FireBase (FireStore) в качестве сигнализации.Я могу запустить установку при подключении к одному и тому же Wi-Fi, но он не работает, когда используются разные сети.

Устройство - RPI

Клиент 1) Веб-клиент (размещен на firebase) 2) Android-приложение

В одной сети (Wi-Fi) между устройством и клиентами оба клиента могутдля воспроизведения видео и аудио.

Но когда устройство и клиент находятся в другой сети, веб-клиент может показывать видео, а приложение Android не может показывать видео.

Сигнализация работает правильно иНа устройстве запускаются камера и микрофон, и ледовые кандидаты успешно обмениваются.Я также получаю удаленный поток (вызывается onAddStream) на Android.Но видео и аудио не воспроизводятся.

Android PeerConnectionClient

    class PeerConnectionClient(private val activity: MainActivity, private val fSignalling: FSignalling) {

    internal var isVideoRunning = false

    private val rootEglBase by lazy {
        EglBase.create()
    }

    private val peerConnectionFactory: PeerConnectionFactory by lazy {
        val initializationOptions = PeerConnectionFactory.InitializationOptions.builder(activity).createInitializationOptions()
        PeerConnectionFactory.initialize(initializationOptions)

        val options = PeerConnectionFactory.Options()
        val defaultVideoEncoderFactory = DefaultVideoEncoderFactory(rootEglBase.eglBaseContext, true, true)
        val defaultVideoDecoderFactory = DefaultVideoDecoderFactory(rootEglBase.eglBaseContext)
        PeerConnectionFactory.builder()
                .setOptions(options)
                .setVideoEncoderFactory(defaultVideoEncoderFactory)
                .setVideoDecoderFactory(defaultVideoDecoderFactory)
                .createPeerConnectionFactory()
    }

    private val iceServersList = mutableListOf("stun:stun.l.google.com:19302")

    private var sdpConstraints: MediaConstraints? = null
    private var localAudioTrack: AudioTrack? = null

    private var localPeer: PeerConnection? = null

    private var gotUserMedia: Boolean = false
    private var peerIceServers: MutableList<PeerConnection.IceServer> = ArrayList()

    init {
        peerIceServers.add(PeerConnection.IceServer.builder(iceServersList).createIceServer())

        // activity.surface_view.release()
        activity.surface_view.init(rootEglBase.eglBaseContext, null)
        activity.surface_view.setZOrderMediaOverlay(true)

        createPeer()
    }

    private fun createPeer() {
        sdpConstraints = MediaConstraints()


        val audioconstraints = MediaConstraints()
        val audioSource = peerConnectionFactory.createAudioSource(audioconstraints)
        localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource)
        gotUserMedia = true

        activity.runOnUiThread {
            if (localAudioTrack != null) {
                createPeerConnection()
                // doCall()
            }
        }

    }

    /**
     * Creating the local peerconnection instance
     */
    private fun createPeerConnection() {
        val constraints = MediaConstraints()
        constraints.mandatory.add(MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"))
        constraints.mandatory.add(MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"))
        constraints.optional.add(MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"))

        val rtcConfig = PeerConnection.RTCConfiguration(peerIceServers)
        // TCP candidates are only useful when connecting to a server that supports
        // ICE-TCP.
        rtcConfig.enableDtlsSrtp = true
        rtcConfig.enableRtpDataChannel = true
        // rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED
        // rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE
        // rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE
        // rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY
        // Use ECDSA encryption.
        // rtcConfig.keyType = PeerConnection.KeyType.ECDSA
        localPeer = peerConnectionFactory.createPeerConnection(rtcConfig, constraints, object : PeerObserver {
            override fun onIceCandidate(p0: IceCandidate) {
                super.onIceCandidate(p0)
                onIceCandidateReceived(p0)
            }

            override fun onAddStream(p0: MediaStream) {
                activity.showToast("Received Remote stream")
                super.onAddStream(p0)
                gotRemoteStream(p0)
            }

        })

        addStreamToLocalPeer()
    }

    /**
     * Adding the stream to the localpeer
     */
    private fun addStreamToLocalPeer() {
        //creating local mediastream
        val stream = peerConnectionFactory.createLocalMediaStream("102")
        stream.addTrack(localAudioTrack)
        localPeer!!.addStream(stream)
    }

    /**
     * This method is called when the app is initiator - We generate the offer and send it over through socket
     * to remote peer
     */
    /*private fun doCall() {
        localPeer!!.createOffer(object : mySdpObserver {
            override fun onCreateSuccess(p0: SessionDescription) {
                super.onCreateSuccess(p0)
                localPeer!!.setLocalDescription(object: mySdpObserver {}, p0)
                Log.d("onCreateSuccess", "SignallingClient emit ")
            }
        }, sdpConstraints)
    }*/

    private fun onIceCandidateReceived(iceCandidate: IceCandidate) {
        //we have received ice candidate. We can set it to the other peer.
        if (localPeer == null) {
            return
        }

        val message = JSONObject()
        message.put("type", "candidate")
        message.put("label", iceCandidate.sdpMLineIndex)
        message.put("id", iceCandidate.sdpMid)
        message.put("candidate", iceCandidate.serverUrl)

        fSignalling.doSignalingSend(message.toString())
    }

    private fun gotRemoteStream(stream: MediaStream) {
        isVideoRunning = true
        //we have remote video stream. add to the renderer.
        val videoTrack = stream.videoTracks[0]
        videoTrack.setEnabled(true)
        activity.runOnUiThread {
            try {
                // val remoteRenderer = VideoRenderer(surface_view)
                activity.surface_view.visibility = View.VISIBLE
                // videoTrack.addRenderer(remoteRenderer)
                videoTrack.addSink(activity.surface_view)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    fun onReceivePeerMessage(data: JSONObject) {
        if (data.getString("type") == "offer") {
            // val sdpReturned = SdpUtils.forceChosenVideoCodec(data.getString("sdp"), "H264")
            val sdpReturned = data.getString("sdp")
            // data.remove("sdp")
            // data.put("sdp", sdpReturned)

            val sessionDescription = SessionDescription(SessionDescription.Type.OFFER, sdpReturned)

            localPeer?.setRemoteDescription(object: mySdpObserver { }, sessionDescription)

            localPeer?.createAnswer(object : mySdpObserver {
                override fun onCreateSuccess(p0: SessionDescription) {
                    super.onCreateSuccess(p0)
                    localPeer!!.setLocalDescription( object : mySdpObserver {}, p0)

                    val description = JSONObject()
                    description.put("type", p0.type.canonicalForm())
                    description.put("sdp", p0.description)

                    this@PeerConnectionClient.fSignalling.doSignalingSend(description.toString())
                }

                override fun onCreateFailure(p0: String) {
                    super.onCreateFailure(p0)
                    activity.showToast("Failed to create answer")
                }

            }, MediaConstraints())

        } else if (data.getString("type") == "candidate") {
            val iceCandidates = IceCandidate(data.getString("id"), data.getInt("label"), data.getString("candidate"))
            localPeer?.addIceCandidate(iceCandidates)
        }
    }

    internal fun close() {
        isVideoRunning = false
        localPeer?.close()
        localPeer = null
    }
 }

У меня сложилось впечатление, что если веб-клиент может отображать видео в другой сети (мобильная точка доступа), клиент Android включентот же Интернет, который используется веб-клиентом, должен также отображать видео.Это неправильно?Почему андроид не отображает видео (вызывается onAddStream)

Требуется ли использовать сервер Turn?Мое предположение опять же, если веб-клиент работает, то и Android.Служба, которую я использую на RPI, не имеет поддержки сервера поворотов.

Дополнительная информация: устройство находится за провайдером с двойной натурой (я полагаю) (но, поскольку веб-клиент может подключиться, это не будет проблемой для меня).думаю).

1 Ответ

0 голосов
/ 27 февраля 2019

Я нашел решение проблемы

Я использовал

private fun onIceCandidateReceived(iceCandidate: IceCandidate) {
    //we have received ice candidate. We can set it to the other peer.
    if (localPeer == null) {
        return
    }

    val message = JSONObject()
    message.put("type", "candidate")
    message.put("label", iceCandidate.sdpMLineIndex)
    message.put("id", iceCandidate.sdpMid)
    message.put("candidate", iceCandidate.serverUrl)

    fSignalling.doSignalingSend(message.toString())
}

Вместо этого было необходимо использовать

message.put("candidate", iceCandidate.sdp) // iceCandidate.serverUrl)
...