@@ -3,6 +3,7 @@ package com.donut.mixfile.activity.video.player
33
44import android.net.Uri
55import android.widget.Toast
6+ import androidx.annotation.OptIn
67import androidx.compose.foundation.background
78import androidx.compose.foundation.gestures.detectTapGestures
89import androidx.compose.foundation.layout.Box
@@ -26,13 +27,21 @@ import androidx.lifecycle.Lifecycle
2627import androidx.lifecycle.LifecycleEventObserver
2728import androidx.lifecycle.compose.LocalLifecycleOwner
2829import androidx.media3.common.MediaItem
30+ import androidx.media3.common.PlaybackException
31+ import androidx.media3.common.Player
2932import androidx.media3.common.Player.REPEAT_MODE_ALL
33+ import androidx.media3.common.util.UnstableApi
34+ import androidx.media3.exoplayer.DefaultRenderersFactory
3035import androidx.media3.exoplayer.ExoPlayer
36+ import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
37+ import androidx.media3.extractor.DefaultExtractorsFactory
38+ import androidx.media3.extractor.ts.DefaultTsPayloadReaderFactory
3139import androidx.media3.ui.PlayerView
3240import com.donut.mixfile.activity.video.VideoHistory
3341import com.donut.mixfile.activity.video.playHistory
3442import com.donut.mixfile.ui.theme.mainColorScheme
3543import com.donut.mixfile.util.ForceUpdateMutable
44+ import com.donut.mixfile.util.showErrorDialog
3645import com.donut.mixfile.util.showToast
3746import kotlinx.coroutines.delay
3847import java.util.Locale
@@ -56,6 +65,7 @@ val playerColorScheme
5665 onSecondaryContainer = mainColorScheme.primary.copy(0.8f )
5766 )
5867
68+ @OptIn(UnstableApi ::class )
5969@Composable
6070fun VideoPlayerScreen (
6171 videoUris : List <Uri >,
@@ -64,21 +74,42 @@ fun VideoPlayerScreen(
6474) {
6575 val context = LocalContext .current
6676 val player = remember {
67- ExoPlayer .Builder (context).build().apply {
68- setMediaItems(videoUris.map { MediaItem .fromUri(it) })
69- repeatMode = REPEAT_MODE_ALL
70- val cached = playHistory.firstOrNull { it.hash.contentEquals(hash) }
71- if (cached != null ) {
72- setMediaItems(
73- videoUris.map { MediaItem .fromUri(it) },
74- cached.episode,
75- (cached.time - 2000L ).coerceAtLeast(0 )
76- )
77- showToast(" 已跳转到上次播放位置" , length = Toast .LENGTH_SHORT )
78- }
79- prepare()
80- playWhenReady = true
77+ // 1. 创建渲染器工厂
78+ val renderersFactory = DefaultRenderersFactory (context).apply {
79+ setExtensionRendererMode(DefaultRenderersFactory .EXTENSION_RENDERER_MODE_PREFER )
80+
81+ }
82+
83+
84+ val extractorsFactory = DefaultExtractorsFactory ().apply {
85+ // 开启对所有可能的容器支持
86+ setConstantBitrateSeekingEnabled(true ) // 允许对没有索引的流进行粗略进度拖动
87+ setTsExtractorFlags(DefaultTsPayloadReaderFactory .FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS )
8188 }
89+
90+ ExoPlayer .Builder (context, renderersFactory)
91+ .setMediaSourceFactory(DefaultMediaSourceFactory (context, extractorsFactory))
92+ .build()
93+ .apply {
94+ addListener(object : Player .Listener {
95+ override fun onPlayerError (error : PlaybackException ) {
96+ showErrorDialog(error, " 播放出错" , playerColorScheme)
97+ }
98+ })
99+ setMediaItems(videoUris.map { MediaItem .fromUri(it) })
100+ repeatMode = REPEAT_MODE_ALL
101+ val cached = playHistory.firstOrNull { it.hash.contentEquals(hash) }
102+ if (cached != null ) {
103+ setMediaItems(
104+ videoUris.map { MediaItem .fromUri(it) },
105+ cached.episode,
106+ (cached.time - 2000L ).coerceAtLeast(0 )
107+ )
108+ showToast(" 已跳转到上次播放位置" , length = Toast .LENGTH_SHORT )
109+ }
110+ prepare()
111+ playWhenReady = true
112+ }
82113 }
83114
84115 var currentMediaItem by remember { mutableIntStateOf(player.currentMediaItemIndex) }
0 commit comments