pedroSG94 / RootEncoder

RootEncoder for Android (rtmp-rtsp-stream-client-java) is a stream encoder to push video/audio to media servers using protocols RTMP, RTSP, SRT and UDP with all code written in Java/Kotlin
Apache License 2.0
2.54k stars 772 forks source link

Unable to connect to RTMP server on Wifi re-connection #1436

Closed mrdesai109 closed 6 months ago

mrdesai109 commented 6 months ago

This happens sometimes. When I change the RTMP server URL, sometimes the app is not able to connect to the server. The error shown in the logcat is "Failed to connect to from ". However, if I uninstall and reinstall the app, it successfully connects. FYI, I am using local Ubuntu RTMP server.

Steps to reproduce the behavior:

  1. Connect and stream to a RTMP server
  2. Change the URL in the Android app to a different RTMP server. (If on localhost, just connect the laptop and Android device to new network, IPV4 of laptop will change)
  3. Try to connect and stream.
  4. Now, sometimes it won't connect. If you uninstall and reinstall the app, it does connect! Not sure if the library is caching previous connection details.

Expected behavior The Android app should connect to different RTMP server on URL change.

Smartphone (please complete the following information): Tested on Galaxy M32 5G, with latest version of the library - 2.4.0

Additional context Here is my connection service class for reference -

`class RtmpService : Service() {

//service section
private var isServiceRunning = false
val streamingState: MutableStateFlow<Boolean> = MutableStateFlow(false)

private lateinit var mCtx: Context

private var handler = Handler (Looper.getMainLooper())

//notification section
private lateinit var notificationBuilder: NotificationCompat.Builder
private lateinit var notificationManager: NotificationManager

//rtp section
private var displayBase: DisplayBase? = null
private val myConnectChecker = object : ConnectionChecker() {
    override fun onConnectionSuccess() {
        CoroutineScope(Dispatchers.IO).launch {
            println("Test A : onConnectionSuccess()")
            streamingState.emit(true)
        }
        showToast("Connection Success!")
    }

    override fun onConnectionStarted(url: String) {
        println("Test A : onConnectionStarted() : ${url}")
        super.onConnectionStarted(url)
    }

    override fun onDisconnect() {
        super.onDisconnect()
        println("Test A : onDisconnect()")
        CoroutineScope(Dispatchers.IO).launch {
            streamingState.emit(false)
        }
        showToast("Disconnected")
    }

    override fun onConnectionFailed(reason: String) {
        println("Test A : onConnectionFailed() : $reason")
        CoroutineScope(Dispatchers.IO).launch {
            streamingState.emit(false)
        }
        showToast("Connection fail - $reason")
    }

    override fun onNewBitrate(bitrate: Long) {
        super.onNewBitrate(bitrate)
        println("Test A : onNewBitrate() : ${bitrate}")
    }

    override fun onAuthError() {
        super.onAuthError()
        println("Test A : onAuthError()")
        showToast("Auth Error")
    }

    override fun onAuthSuccess() {
        super.onAuthSuccess()
        println("Test A : onAuthSuccess()")
    }
}

fun prepareStreamRtp(incomingUrl: String, resultCode: Int, data: Intent) {
    url = incomingUrl
    if (url.startsWith("rtmp")) {
        displayBase = RtmpDisplay(baseContext, true, myConnectChecker)
        displayBase?.setIntentResult(resultCode, data)
    } else {
        displayBase = RtspDisplay(baseContext, true, myConnectChecker)
        displayBase?.setIntentResult(resultCode, data)
    }
    displayBase?.glInterface?.apply {
        start()
        setForceRender(true)
    }
}

fun startStreamRtp() {
    if(!isServiceRunning){
        handleStartService()
    }
    if (displayBase?.isStreaming != true) {
        if (displayBase?.prepareVideo(840,472,1200 * 1024) == true && displayBase?.prepareAudio() == true) {
            try{
                displayBase?.streamClient?.apply {
                    resetDroppedAudioFrames()
                    resetDroppedVideoFrames()
                    resetSentAudioFrames()
                    resetSentVideoFrames()
                    clearCache()
                }
                displayBase?.startStream(url)
                CoroutineScope(Dispatchers.IO).launch {
                    streamingState.emit(true)
                }
            }catch (ex : Exception){
                showToast("Error - $ex")
                handleStopService()
            }
        }
    }
}

override fun onCreate() {
    super.onCreate()
    setupNotification()
}

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    mCtx = applicationContext
    intent.action?.let { serviceAction ->
        when (serviceAction) {
            "Start" -> handleStartService()
            "Stop" -> handleStopService()
        }
    }
    return START_STICKY
}

//Use this method to show toast
fun showToast(msg: String) {
        handler.post{
            Toast.makeText(mCtx, msg, Toast.LENGTH_LONG).show()
        }
}

private fun handleStartService() {
    if (!isServiceRunning) {
        startForeground(1, notificationBuilder.build())
        isServiceRunning = true
    }
}

fun handleStopService() {
    displayBase?.stopStream()
    isServiceRunning = false
    displayBase?.stopStream()
    displayBase?.glInterface?.stop()
    displayBase?.streamClient?.apply {
        resetDroppedAudioFrames()
        resetDroppedVideoFrames()
        resetSentAudioFrames()
        resetSentVideoFrames()
        clearCache()
    }
    stopForeground(STOP_FOREGROUND_REMOVE)
    stopSelf()
    CoroutineScope(Dispatchers.IO).launch {
        streamingState.emit(false)
    }
}

inner class LocalBinder : Binder() {
    fun getService(): RtmpService = this@RtmpService
}

private val binder = LocalBinder()
override fun onBind(intent: Intent): IBinder {
    return binder
}

private fun setupNotification() {
    notificationManager = getSystemService(
        NotificationManager::class.java
    )
    val notificationChannel = NotificationChannel(
        "rtmpChannel", "foreground", NotificationManager.IMPORTANCE_HIGH
    )

    val intent = Intent(this, RtmpBroadcastReceiver::class.java).apply {
        action = "ACTION_EXIT"
    }
    val pendingIntent: PendingIntent =
        PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
    notificationManager.createNotificationChannel(notificationChannel)
    notificationBuilder = NotificationCompat.Builder(
        this, "rtmpChannel"
    ).setSmallIcon(R.mipmap.ic_launcher)
        .addAction(R.drawable.ic_launcher_foreground, "Stop Streaming", pendingIntent).setOngoing(true)
}

companion object {
    //    private var url:String = "rtmp://141.11.184.69/live/${UUID.randomUUID()}"
    var url: String = ""
    fun startService(context: Context) {
        Thread {
            context.startForegroundService(Intent(context, RtmpService::class.java).apply {
                action = "Start"
            })
        }.start()
    }

    fun stopService(context: Context) {
        context.startForegroundService(Intent(context, RtmpService::class.java).apply {
            action = "Stop"
        })
    }

    fun bindService(context: Context, connection: ServiceConnection) {
        context.bindService(
            Intent(context, RtmpService::class.java),
            connection,
            Context.BIND_AUTO_CREATE
        )
    }
}`
pedroSG94 commented 6 months ago

Hello,

I'm not caching anything in the library. This seems a code error in your side. I tested using 2 servers and alternate with both and all is working fine

Are you sure that the url is correct? Did you check the url set to the library each time to know the value when it fails? (check it when you call displayBase?.startStream(url) line)

mrdesai109 commented 6 months ago

Hey @pedroSG94 , I confirm that URL being passed to the library is correct. I am having a localhost RTMP server. The issue is happening specifically when I change my Wifi connection of laptop. On changing the Wifi connection, the IPV4 address of the laptop will change, right?

So, on changing the Wifi connection the RTMP URL will also change. At this point, the library is not able to make connection the RTMP server. But, now if I uninstall and reinstall the app it actually connects and streams successfully. Why this behavior? Do you think it's my code side issue?

Here is the exact log - 192.168.1.8 is the changed IPV4 address of my laptop. URL - rtmp://192.168.1.8:1935/live/test

Also I am making sure that laptop and Android device both are connected to new Wifi.

connection error
       java.net.SocketTimeoutException: failed to connect to /192.168.1.8 (port 1935) from /192.168.167.57 (port 32844) after 5000ms
      at libcore.io.IoBridge.connectErrno(IoBridge.java:190)
      at libcore.io.IoBridge.connect(IoBridge.java:134)
      at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:142)
      at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
      at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
      at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
      at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
      at java.net.Socket.connect(Socket.java:621)
      at com.pedro.rtmp.utils.socket.TcpSocket.connect(TcpSocket.kt:64)
      at com.pedro.rtmp.rtmp.RtmpClient.establishConnection(RtmpClient.kt:327)
      at com.pedro.rtmp.rtmp.RtmpClient.access$establishConnection(RtmpClient.kt:53)
      at com.pedro.rtmp.rtmp.RtmpClient$connect$1.invokeSuspend(RtmpClient.kt:237)
      at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
pedroSG94 commented 6 months ago

Hello,

If you read the connection error you can check that you are not in the same network:

java.net.SocketTimeoutException: failed to connect to /192.168.1.8 (port 1935) from /192.168.167.57 (port 32844) after 5000ms

192.168.1.8 and 192.168.167.57 is not the same network. If you are the the same network the third number should be the same and only should change the last number. The first one is your server IP as you said and the second one is your device IP. The reason about if you uninstall the app and install again the app is working is maybe because you need wait a bit until your device get the correct IP.

mrdesai109 commented 6 months ago

@pedroSG94 That's a very good observation. I dint know that. Let me check why Android device is not getting the correct IP. I will get back to you as soon as possible.

mrdesai109 commented 6 months ago

@pedroSG94 I observed same thing is happening if I just turn off the Wifi, and reconnect my Android device to the same Wifi. The third digit becomes different.

failed to connect to /192.168.1.8 (port 1935) from /192.168.167.57 (port 55876) after 5000ms

It's only after I uninstall and reinstall the app, it will connect successfully.

As I noticed from the logs, that the library is using java.net.Socket() internally. Maybe problem lies there. Not sure.

      at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
      at java.net.Socket.connect(Socket.java:621)
      at com.pedro.rtmp.utils.socket.TcpSocket.connect(TcpSocket.kt:64)

Can you try to do this and let me know if you are facing the same issue as well?

1) Connect to Wifi from Android device, and stream to RTMP server. Stop the stream. 2) Turn Off Wifi of Android device. 3) Turn On Wifi and connect to the same Wifi again, and try to stream.

pedroSG94 commented 6 months ago

Hello,

I haven't this problem. Socket class is not related with your problem. I'm not using a class that can manipulate IP device or similar.

Try to reproduce the case using my app example to discard that it is an error related with your code. Also, you can check your current IP in the device settings to make sure that you are in the same network (wait until it is correct) and then click to start stream instead of automatize it.

If you have more than one router or wifi hotpots just shutdown all except one. Also, you can configure your router to use always the same IP with your Android device and your server

mrdesai109 commented 6 months ago

@pedroSG94 It is happening with the example app too.

But I believe the issue is not related to library. I think on disabling and enabling Wifi, a different IP is assigned to Android device.

On disabling the Wifi and reconnecting to the Wifi, I can see the Android's IP is 192.168.1.2 in the Wifi settings. But when I try to connect to the RTMP server again, I still get the same error -

failed to connect to /192.168.1.8 (port 1935) from /192.168.167.57 (port 55236) after 5000ms

Definitely Android related issue, not the library I believe. I dont know how 192.168.167.57 is getting displayed. Also, as I mentioned earlier on doing uninstall and reinstall, connection is happening.

Any idea what is the root cause of this issue?

pedroSG94 commented 6 months ago

Also, as I mentioned earlier on doing uninstall and reinstall, connection is happening.

This has no sense for me. If you kill the app totally without uninstall, you should have the same result that uninstall app.

This feels like an error in your network configuration. I recommend you go to your router and configure a static IP to your Android device. Even a factory reset in your device could be a good idea. Shutdown all devices that could be connected to your network except your server to avoid your router asign you other IP

mrdesai109 commented 6 months ago

Thanks @pedroSG94, I will try that. Closing this issue, since this is not a library related issue. Rather an Android side issue.