[BUG]: on iOS, onPlaybackStateChanged event is not trigerred when video stops due to lost of connection or limited bandwith
fabienbouchez opened this issue · comments
Version
6.1.2
What platforms are you having the problem on?
iOS
System Version
iOS 17.2
On what device are you experiencing the issue?
Real device, Simulator
Architecture
Old architecture
What happened?
On iOS,
- when playing a streamed video, if we lose the network connectivity and we reach the end of buffer, the video stops but the onPlaybackStateChanged event is not triggered.
- This happens as well when the bandwidth is too low to stream the content, the video is paused while buffering, the onBuffer event is well triggered, but not the onPlaybackStateChanged
For the same situations with Android, the onPlaybackStateChanged event is well triggered
Here is an example of events triggered, when I close the wifi just after the video has started to play :
Logs for Android :
LOG Running "reactNativeVideoTest" with {"rootTag":11}
LOG video1 onBuffer {"isBuffering": true}
LOG video1 onPlaybackStateChanged {"isPlaying": true}
LOG video1 onBuffer {"isBuffering": false}
LOG video1 onError {"error": {"errorCode": "22001", "errorException": "androidx.media3.exoplayer.ExoPlaybackException: Source error", "errorStackTrace": "androidx.media3.exoplayer.ExoPlaybackException: Source error
at androidx.media3.exoplayer.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:684)
at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:656)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loopOnce(Looper.java:232)
at android.os.Looper.loop(Looper.java:317)
at android.os.HandlerThread.run(HandlerThread.java:68)
Caused by: androidx.media3.datasource.HttpDataSource$HttpDataSourceException: java.io.IOException: java.util.concurrent.ExecutionException: java.net.UnknownHostException: Unable to resolve host \"[demo.unified-streaming.com](http://demo.unified-streaming.com/)\": No address associated with hostname
at androidx.media3.datasource.okhttp.OkHttpDataSource.open(OkHttpDataSource.java:321)
at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:272)
at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:86)
at androidx.media3.exoplayer.hls.HlsMediaChunk.prepareExtraction(HlsMediaChunk.java:527)
at androidx.media3.exoplayer.hls.HlsMediaChunk.feedDataToExtractor(HlsMediaChunk.java:500)
at androidx.media3.exoplayer.hls.HlsMediaChunk.loadMedia(HlsMediaChunk.java:469)
at androidx.media3.exoplayer.hls.HlsMediaChunk.load(HlsMediaChunk.java:426)
at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:414)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
Caused by: java.io.IOException: java.util.concurrent.ExecutionException: java.net.UnknownHostException: Unable to resolve host \"[demo.unified-streaming.com](http://demo.unified-streaming.com/)\": No address associated with hostname
at androidx.media3.datasource.okhttp.OkHttpDataSource.executeCall(OkHttpDataSource.java:489)
at androidx.media3.datasource.okhttp.OkHttpDataSource.open(OkHttpDataSource.java:316)
... 10 more
Caused by: java.util.concurrent.ExecutionException: java.net.UnknownHostException: Unable to resolve host \"[demo.unified-streaming.com](http://demo.unified-streaming.com/)\": No address associated with hostname
at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:588)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:567)
at com.google.common.util.concurrent.AbstractFuture$TrustedFuture.get(AbstractFuture.java:113)
at androidx.media3.datasource.okhttp.OkHttpDataSource.executeCall(OkHttpDataSource.java:484)
... 11 more
Caused by: java.net.UnknownHostException: Unable to resolve host \"[demo.unified-streaming.com](http://demo.unified-streaming.com/)\": No address associated with hostname
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:156)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:103)
at java.net.InetAddress.getAllByName(InetAddress.java:1152)
at okhttp3.Dns$Companion$DnsSystem.lookup(Dns.kt:49)
at okhttp3.internal.connection.RouteSelector.resetNextInetSocketAddress(RouteSelector.kt:169)
at okhttp3.internal.connection.RouteSelector.nextProxy(RouteSelector.kt:131)
at okhttp3.internal.connection.RouteSelector.next(RouteSelector.kt:73)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:205)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
... 3 more
Caused by: android.system.GaiException: android_getaddrinfo failed: EAI_NODATA (No address associated with hostname)
at libcore.io.Linux.android_getaddrinfo(Native Method)
at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:133)
at libcore.io.BlockGuardOs.android_getaddrinfo(BlockGuardOs.java:222)
at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:133)
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:135)
... 23 more
", "errorString": "ExoPlaybackException: ERROR_CODE_IO_NETWORK_CONNECTION_FAILED"}}
**LOG video1 onPlaybackStateChanged {"isPlaying": false}**
We have onPlaybackStateChanged {"isPlaying": false} as expected
Logs for iOS:
LOG Running "reactNativeVideoTest" with {"rootTag":1,"initialProps":{"concurrentRoot":false}}
LOG video1 onPlaybackStateChanged {"isPlaying": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onPlaybackStateChanged {"isPlaying": true, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onError {"error": {"code": -1009, "domain": "NSURLErrorDomain", "localizedDescription": "The Internet connection appears to be offline.", "localizedFailureReason": "", "localizedRecoverySuggestion": ""}, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
We don't have onPlaybackStateChanged {"isPlaying": false} as expected
Reproduction
repository link
Reproduction
You can reproduce by running this code and cut the internet connection (ex : by disabling Wifi on the phone) some seconds after the video has started. After a few seconds, due to lack of buffering the video will stop.
import React from 'react';
import { View } from 'react-native';
import Video from 'react-native-video';
function App(): React.JSX.Element {
return (
<View style={{flex:1}}>
<Video
source={{ uri: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8' }}
onBuffer={(onBuffer) => console.log('video1 onBuffer', onBuffer)}
onError={(onError) => console.log('video1 onError', onError)}
onPlaybackStateChanged={(onPlaybackStateChanged) => console.log('video1 onPlaybackStateChanged', onPlaybackStateChanged)}
style={{flex:1}}
/>
</View>
);
}
export default App;
This is the logs on iOS when the bandwidth is limited :
When the videos hangs, the onBuffer is triggered, but not the onPlaybackStateChanged
LOG video1 onPlaybackStateChanged {"isPlaying": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onPlaybackStateChanged {"isPlaying": true, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
LOG video1 onBuffer {"isBuffering": true, "target": 3}
LOG video1 onBuffer {"isBuffering": false, "target": 3}
Thanks for your help