Hls Exoplayer 丢失的片段不会被快速跳过——Android Compose

问题描述 投票:0回答:1

我正在我的撰写应用程序中播放 HLS 流(m3u8 播放列表)。该流来自传感器,由 MPEG-TS 片段组成(仅视频,无音频)。问题是当流到来时,找不到第一个段,您可以看到传感器的日志(test..ts 404),只找到后续流:

Sensor 的 Android 案例日志 Android 行为似乎一次又一次地重试,导致我的流仅在 30 秒的巨大延迟后才显示。

IOS 的行为是简单地跳过丢失的段,然后继续检索下一个段并重复,如日志中所示: Sensor的IOS案例日志

这是 .ts 段的 mediaInfo 示例:

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%)

这是 Hls 播放列表清单的示例:

#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

我尝试了多种方法,特别是允许ChunklessPreparation / LoadErrorHandlingPolicy / autoSeeking /将不同的标志传递给mediaSource /使用内部使用exoplayer的已实现包播放HLS等,从我的代码和注释(在代码中)可以看出:

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()}

    var autoSought = false;

    LaunchedEffect(key1 = Unit) {
        val dataSourceFactory =     DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true)
        val extractorsFactory = DefaultHlsExtractorFactory(
            FLAG_DETECT_ACCESS_UNITS or DefaultTsPayloadReaderFactory.FLAG_ALLOW_NON_IDR_KEYFRAMES or DefaultTsPayloadReaderFactory.FLAG_IGNORE_AAC_STREAM,
            true
        )
        val mediaSource = HlsMediaSource
            .Factory(dataSourceFactory)
            /*.setLoadErrorHandlingPolicy(
                object : DefaultLoadErrorHandlingPolicy(0) {
                    override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo): Long {
                        return when (loadErrorInfo.exception) {
                            is FileNotFoundException -> {
                                C.TIME_UNSET
                            }
                            else -> {
                                // Handle other exceptions or apply default retry logic
                                super.getRetryDelayMsFor(loadErrorInfo)
                            }
                        }
                    }
                }
            )*/
            //.setExtractorFactory(extractorsFactory)
            .setAllowChunklessPreparation(true)
            .createMediaSource(
                MediaItem.fromUri(mediaUrl)
                    /*.Builder()
                    .setMimeType(MimeTypes.APPLICATION_M3U8)
                    .setUri(mediaUrl)
                    .build()*/
            )

        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}")
                    if(!autoSought){
                        seekTo(11000)
                        autoSought = true
                        /*Handler(Looper.getMainLooper()).postDelayed(
                            {
                                val currentPosition = exoPlayer.currentPosition
                                exoPlayer.setMediaItem(MediaItem.fromUri(mediaUrl))
                                exoPlayer.seekTo(currentPosition + 5000)
                                exoPlayer.play()
                                autoSought = true
                            }, 1000
                        )*/
                     }
                }
            }
            )
            setMediaSource(mediaSource)
            seekTo(1)
            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
                }
            )

            /*VideoPlayer(
                mediaItems = listOf(
                    VideoPlayerMediaItem.NetworkMediaItem(
                        url = mediaUrl,
                        mediaMetadata = MediaMetadata.Builder()
                            .setTitle("Widevine DASH cbcs: Tears").build(),
                        mimeType = MimeTypes.APPLICATION_M3U8
                    )
                ),
                handleLifecycle = true,
                autoPlay = true,
                usePlayerController = true,
                enablePip = true,
                handleAudioFocus = true,
                controllerConfig = VideoPlayerControllerConfig(
                    showSpeedAndPitchOverlay = false,
                    showSubtitleButton = false,
                    showCurrentTimeAndTotalTime = true,
                    showBufferingProgress = false,
                    showForwardIncrementButton = true,
                    showBackwardIncrementButton = true,
                    showBackTrackButton = true,
                    showNextTrackButton = true,
                    showRepeatModeButton = true,
                    controllerShowTimeMilliSeconds = 5_000,
                    controllerAutoShow = true,
                    showFullScreenButton = false
                ),
                volume = 0.5f,  // volume 0.0f to 1.0f
                repeatMode = RepeatMode.NONE,       // or RepeatMode.ALL, RepeatMode.ONE
                onCurrentTimeChanged = { // long type, current player time (millisec)
                },
                playerInstance = { // ExoPlayer instance (Experimental)

                },
                modifier = Modifier
                    .height(300.dp)
                    .fillMaxWidth(),
            )*/

            /*AndroidView(
                    modifier = Modifier.fillMaxWidth(),
                    factory = { context ->
                        VideoView(context).apply {
                            setVideoURI(Uri.parse(mediaUrl))
                            setOnPreparedListener {
                                setZOrderOnTop(true)
                                it.start()
                            }
                        }
                    },
                )*/
            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()
        }
    }
}

总而言之,如果没有找到,我如何强制 Exoplayer 跳过一个片段并立即转到下一个片段? (不幸的是,我无法提供流 URL,因为它是根据传感器的命令动态提供的)

感谢任何帮助,如果需要更多信息或我的问题不清楚,请告诉我。

android kotlin android-jetpack-compose http-live-streaming exoplayer
1个回答
0
投票

当它回复

test..ts 404
时,这意味着:文件未找到。双冒号很可疑
..
.

虽然

#EXTM3U
甚至没有它(那是完全不同的)。只需修复播放列表即可。

© www.soinside.com 2019 - 2024. All rights reserved.