anas nadifi
anas nadifi

Reputation: 11

Hls Exoplayer Missing segments are not skipped quickly -- Android Compose

I am playing an HLS stream (m3u8 playlist) in my compose app. The stream is coming from a sensor and is made up of MPEG-TS segments (Video Only, no audio). The issue is when the stream comes, the first few segments are not found, you can see the logs of the sensor (test.ts 404), only subsequent streams are found:

The Android behavior seems to be retrying again and again, resulting in my stream showing up only after a huge delay of 30 seconds. Sensor's Android case logs

The IOS behavior is to simply skip the missing segment, and move on to retrieve the next segment and repeat, as shown in the logs : Sensor's IOS case logs

Here is an example mediaInfo of .ts segment :

General
ID                                       : 1 (0x1)
Complete name                            : test_19.ts
Format                                   : MPEG-TS
File size                                : 51.2 KiB
Duration                                 : 5 s 205 ms
Overall bit rate mode                    : Variable
Overall bit rate                         : 80.6 kb/s
Frame rate                               : 30.000 FPS

Video
ID                                       : 65 (0x41)
Menu ID                                  : 1 (0x1)
Format                                   : AVC
Format/Info                              : Advanced Video Codec
Format profile                           : Baseline@L1
Format settings                          : 1 Ref Frames
Format settings, CABAC                   : No
Format settings, Reference frames        : 1 frame
Format settings, GOP                     : M=1, N=30
Codec ID                                 : 27
Duration                                 : 5 s 49 ms
Bit rate                                 : 76.6 kb/s
Width                                    : 640 pixels
Height                                   : 360 pixels
Display aspect ratio                     : 16:9
Frame rate                               : 30.000 FPS
Color space                              : YUV
Chroma subsampling                       : 4:2:0
Bit depth                                : 8 bits
Scan type                                : Progressive
Bits/(Pixel*Frame)                       : 0.011
Stream size                              : 47.1 KiB (92%)

and here is example of Hls playlist manifest :

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:NO
#EXT-X-MEDIA-SEQUENCE:19
#EXT-X-TARGETDURATION:5

#EXTINF:5.068028450012207,
test_17.ts
#EXTINF:5.0681610107421875,
test_05.ts
#EXTINF:5.067845344543457,
test_10.ts
#EXTINF:5.0680408477783203,
test_21.ts
#EXTINF:5.0680079460144043,
test_22.ts

I tried out multiple things, notably allowingChunklessPreparation/ LoadErrorHandlingPolicy/ autoSeeking / Passing different flags to mediaSource/ Playing the HLS with an implemented package that uses exoplayer internally etc, as can be seen from my code and comments (in code) :

fun LiveStreamPlayer(url: String) {
    val context = LocalContext.current
    val mediaUrl = (context as MainActivity).mainViewModel.livestreamUrl.value!!.data
    Timber.e("media item livestreamurl: $mediaUrl")
    val livestreamLogs = remember { mutableStateListOf<String>() }
    val exoPlayer = remember { ExoPlayer.Builder(context).build()}


    LaunchedEffect(key1 = Unit) {
        val dataSourceFactory = DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true)
        val mediaSource = HlsMediaSource
            .Factory(dataSourceFactory)
            .createMediaSource(
                MediaItem.fromUri(mediaUrl)
            )

        exoPlayer.apply {
            addListener(object : Player.Listener {
                override fun onPlayerError(error: PlaybackException) {
                    Timber.e("ExoPlayer error: ${error.message}")
                    livestreamLogs.add("error ${error.errorCode}: ${error.errorCodeName}")
                    // Handle error
                }
                override fun onPlaybackStateChanged(playbackState: Int) {
                    Timber.e("Playback state changed: $playbackState")
                    livestreamLogs.add("playback state change : ${playbackState}")
                }
            }
            )
            setMediaSource(mediaSource)
            prepare()
            playWhenReady = true
        }
    }


    DisposableEffect(
        Column(
            modifier = Modifier
                .fillMaxSize()
        ) {
            AndroidView(
                modifier = Modifier
                    .height(300.dp)
                    .fillMaxWidth(),
                factory = {
                    val playerView = PlayerView(context)
                    playerView.apply {
                        player = exoPlayer
                        //useController = false
                    }
                    playerView
                }
            )
            Spacer(modifier = Modifier.height(10.dp))
            LazyColumn(
                modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentHeight()
                    .padding(horizontal = 16.dp)
                    .padding(bottom = 80.dp),
            ) {
                items(livestreamLogs.size) { log ->
                    Text(livestreamLogs[log])
                }
            }
        }

    ) {
        onDispose {
            Timber.e("exoplayer disposed")
            exoPlayer.release()
        }
    }
}

In summary how can i force Exoplayer to skip a segment if not found and move on instantly to next segment? (unfortunately i cannot provide the stream URL as it is provided dynamically on command to the sensor)

Any help is appreciated, and if further info are needed or my question is not clear, please do let me know.

Upvotes: 1

Views: 170

Answers (1)

Martin Zeitler
Martin Zeitler

Reputation: 76779

When it replies with test..ts 404, this means: file not found. The double-colon is suspicious ..
While that #EXTM3U does not even have test..ts (that's a whole other). Just fix the playlist.

One could as well check, if any of the segments is 404 before enqueuing them.
But fixing the playlist which the "sensor" sends appeary to be the most reliable.

MediaSource uses Timeline internally: Live stream with multiple periods.
Or to obtain the filenames to check for 404: Accessing the manifest.

Upvotes: 0

Related Questions