google / ExoPlayer

This project is deprecated and stale. The latest ExoPlayer code is available in https://github.com/androidx/media
https://developer.android.com/media/media3/exoplayer
Apache License 2.0
21.7k stars 6.02k forks source link

RtspMediaSource with not available RTSP stream will block whole ExoPlayer #10946

Closed MarcinWad closed 1 year ago

MarcinWad commented 1 year ago

Hello, I try to implement a player which mixes local video files in playlist with public RTSP streams for my project. I found that if RTSP Server is not accesible (dead) - Exoplayer will lock whole play thread even at playing an earlier local MP4 file in playlist.

It will finally, after about 2 minutes, fire ExoPlaybackException

I cannot find a timeout option to setup.

var mediaSourceRTSP =
    RtspMediaSource.Factory()
        .setForceUseRtpTcp(true)
        .setTimeoutMs(3000)
        .createMediaSource(newMediaItem)

And the Exception which fires about 2 minutes later:

E/ExoPlayerImplInternal: Playback error
      com.google.android.exoplayer2.ExoPlaybackException: Source error
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:637)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:613)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:223)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: java.net.ConnectException: failed to connect to /11.201.0.110 (port 11035) from /:: (port 58066): connect failed: ETIMEDOUT (Connection timed out)
        at libcore.io.IoBridge.connect(IoBridge.java:142)
        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 java.net.Socket.connect(Socket.java:570)
        at java.net.Socket.<init>(Socket.java:450)
        at java.net.Socket.<init>(Socket.java:218)
        at javax.net.DefaultSocketFactory.createSocket(SocketFactory.java:279)
        at com.google.android.exoplayer2.source.rtsp.RtspClient.getSocket(RtspClient.java:304)
        at com.google.android.exoplayer2.source.rtsp.RtspClient.start(RtspClient.java:198)
        at com.google.android.exoplayer2.source.rtsp.RtspMediaPeriod.prepare(RtspMediaPeriod.java:158)
        at com.google.android.exoplayer2.source.MaskingMediaPeriod.prepare(MaskingMediaPeriod.java:145)
        at com.google.android.exoplayer2.source.MaskingMediaPeriod.prepare(MaskingMediaPeriod.java:145)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.maybeUpdateLoadingPeriod(ExoPlayerImplInternal.java:1981)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.updatePeriods(ExoPlayerImplInternal.java:1961)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:961)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:495)
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
     Caused by: android.system.ErrnoException: connect failed: ETIMEDOUT (Connection timed out)
        at libcore.io.Linux.connect(Native Method)
        at libcore.io.ForwardingOs.connect(ForwardingOs.java:94)
        at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:138)
        at libcore.io.ForwardingOs.connect(ForwardingOs.java:94)
        at libcore.io.IoBridge.connectErrno(IoBridge.java:156)
        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 java.net.Socket.connect(Socket.java:570) 
        at java.net.Socket.<init>(Socket.java:450) 
        at java.net.Socket.<init>(Socket.java:218) 
        at javax.net.DefaultSocketFactory.createSocket(SocketFactory.java:279) 
        at com.google.android.exoplayer2.source.rtsp.RtspClient.getSocket(RtspClient.java:304) 
        at com.google.android.exoplayer2.source.rtsp.RtspClient.start(RtspClient.java:198) 
        at com.google.android.exoplayer2.source.rtsp.RtspMediaPeriod.prepare(RtspMediaPeriod.java:158) 
        at com.google.android.exoplayer2.source.MaskingMediaPeriod.prepare(MaskingMediaPeriod.java:145) 
        at com.google.android.exoplayer2.source.MaskingMediaPeriod.prepare(MaskingMediaPeriod.java:145) 
        at com.google.android.exoplayer2.ExoPlayerImplInternal.maybeUpdateLoadingPeriod(ExoPlayerImplInternal.java:1981) 
        at com.google.android.exoplayer2.ExoPlayerImplInternal.updatePeriods(ExoPlayerImplInternal.java:1961) 
        at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:961) 
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:495) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
marcbaechinger commented 1 year ago

@claincly May I ask you to take a look? Thanks!

claincly commented 1 year ago

First, the timeoutMs setter is not for the network, so as you have observed, it's doing no good.

In your specific use case, IIUC you need to set a timeout on the Socket itself, and this timeout will ensure the connection error is surfaced earlier than the current value (which is the platform specific, and two minutes as you said.)

I would suggest make your own SocketFactory, which can be as easy as

    new RtspMediaSource.Factory()
        .setSocketFactory(
            new SocketFactory() {
              SocketFactory defaultSocketFactory = SocketFactory.getDefault();

              @Override
              public Socket createSocket(String host, int port) throws IOException {
                Socket socket = defaultSocketFactory.createSocket();
                socket.connect(
                    new InetSocketAddress(InetAddress.getByName(host), port), /* timeout= */ 1000);
                return socket;
              }

              @Override
              public Socket createSocket(
                  String host, int port, InetAddress localHost, int localPort) {
                throw new UnsupportedOperationException();
              }

              @Override
              public Socket createSocket(InetAddress host, int port) {
                throw new UnsupportedOperationException();
              }

              @Override
              public Socket createSocket(
                  InetAddress address, int port, InetAddress localAddress, int localPort) {
                throw new UnsupportedOperationException();
              }
            });
MarcinWad commented 1 year ago

Hello, Thank you. Adding own implementation of SocketFactory fixes the problem and Exo is not blocking main thread anymore.

I think that adding this functionality to a Builder of RTSPMediaSource would be better idea. I digged almost whole Internet before i wrote here :)

claincly commented 1 year ago

Glad it helped!