Skip to content

Commit 1f99772

Browse files
committed
复用 tv 版的播放器代码
1 parent 92d0f04 commit 1f99772

File tree

7 files changed

+155
-56
lines changed

7 files changed

+155
-56
lines changed

app/src/main/kotlin/dev/aaa1115910/bv/mobile/activities/VideoPlayerActivity.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ import androidx.activity.compose.setContent
88
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
99
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
1010
import androidx.lifecycle.lifecycleScope
11-
import androidx.media3.exoplayer.ExoPlayer
1211
import com.kuaishou.akdanmaku.render.SimpleRenderer
1312
import com.kuaishou.akdanmaku.ui.DanmakuPlayer
13+
import dev.aaa1115910.biliapi.entity.ApiType
14+
import dev.aaa1115910.bv.R
15+
import dev.aaa1115910.bv.entity.PlayerType
1416
import dev.aaa1115910.bv.mobile.screen.VideoPlayerScreen
1517
import dev.aaa1115910.bv.mobile.theme.BVMobileTheme
1618
import dev.aaa1115910.bv.mobile.viewmodel.MobileVideoPlayerViewModel
19+
import dev.aaa1115910.bv.player.VideoPlayerOptions
20+
import dev.aaa1115910.bv.player.impl.exo.ExoPlayerFactory
21+
import dev.aaa1115910.bv.util.Prefs
1722
import dev.aaa1115910.bv.util.fInfo
1823
import dev.aaa1115910.bv.util.toast
1924
import io.github.oshai.kotlinlogging.KotlinLogging
@@ -55,7 +60,20 @@ class VideoPlayerActivity : ComponentActivity() {
5560
private fun initVideoPlayer() {
5661
if (playerViewModel.videoPlayer != null) return
5762
logger.fInfo { "initVideoPlayer" }
58-
playerViewModel.videoPlayer = ExoPlayer.Builder(this).build()
63+
val options = VideoPlayerOptions(
64+
userAgent = when (Prefs.apiType) {
65+
ApiType.Web -> getString(R.string.video_player_user_agent_http)
66+
ApiType.App -> getString(R.string.video_player_user_agent_client)
67+
},
68+
referer = when (Prefs.apiType) {
69+
ApiType.Web -> getString(R.string.video_player_referer)
70+
ApiType.App -> null
71+
}
72+
)
73+
val videoPlayer = when (Prefs.playerType) {
74+
PlayerType.Media3 -> ExoPlayerFactory().create(this, options)
75+
}
76+
playerViewModel.videoPlayer = videoPlayer
5977
//TODO 还没处理旋转后的一些判断,就先放这了
6078
parseIntent()
6179
}

app/src/main/kotlin/dev/aaa1115910/bv/mobile/screen/VideoPlayerScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ fun VideoPlayerScreen(
223223
.aspectRatio(16f / 9f),
224224
isFullScreen = isVideoFullscreen,
225225
videoPlayer = playerViewModel.videoPlayer!!,
226-
danmakuPlayer = playerViewModel.danmakuPlayer!!,
226+
danmakuPlayer = playerViewModel.danmakuPlayer,
227227
onEnterFullScreen = {
228228
isVideoFullscreen = true
229229
},

app/src/main/kotlin/dev/aaa1115910/bv/mobile/viewmodel/MobileVideoPlayerViewModel.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import dev.aaa1115910.bv.BVApp
3232
import dev.aaa1115910.bv.entity.Audio
3333
import dev.aaa1115910.bv.entity.Resolution
3434
import dev.aaa1115910.bv.entity.VideoCodec
35+
import dev.aaa1115910.bv.player.AbstractVideoPlayer
3536
import dev.aaa1115910.bv.player.mobile.component.DanmakuType
3637
import dev.aaa1115910.bv.player.mobile.component.playUrl
3738
import dev.aaa1115910.bv.util.Prefs
@@ -52,7 +53,7 @@ class MobileVideoPlayerViewModel(
5253
) : ViewModel() {
5354
private val logger = KotlinLogging.logger {}
5455

55-
var videoPlayer: ExoPlayer? by mutableStateOf(null)
56+
var videoPlayer: AbstractVideoPlayer? by mutableStateOf(null)
5657
var danmakuPlayer: DanmakuPlayer? by mutableStateOf(null)
5758

5859
var avid: Int = 0
@@ -327,7 +328,6 @@ class MobileVideoPlayerViewModel(
327328
)
328329
})
329330
danmakuPlayer?.updateData(danmakuData)
330-
danmakuPlayer?.start()
331331
}.onFailure {
332332
logger.fWarn { "Load danmaku filed: ${it.stackTraceToString()}" }
333333
}.onSuccess {
@@ -351,10 +351,12 @@ class MobileVideoPlayerViewModel(
351351
}
352352

353353
private suspend fun releaseDanmakuPlayer() = withContext(Dispatchers.Main) {
354+
println("release danmaku player")
354355
danmakuPlayer?.release()
355356
}
356357

357358
private suspend fun reInitDanmakuPlayer() = withContext(Dispatchers.Main) {
359+
println("set new danmaku player")
358360
danmakuPlayer = DanmakuPlayer(SimpleRenderer())
359361
}
360362
}

app/src/main/kotlin/dev/aaa1115910/bv/screen/VideoPlayerV3Screen.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ fun VideoPlayerV3Screen(
273273
}
274274
}
275275

276+
override fun onIdle() {
277+
//TODO("Not yet implemented")
278+
}
279+
276280
override fun onSeekBack(seekBackIncrementMs: Long) {
277281
playerViewModel.danmakuPlayer?.seekTo(currentPosition)
278282
}

bv-player-mobile/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ java {
5959
}
6060

6161
dependencies {
62+
implementation(project(":bv-player-core"))
6263
implementation(androidx.activity.compose)
6364
implementation(androidx.core.ktx)
6465
implementation(androidx.compose.ui)

bv-player-mobile/src/main/kotlin/dev/aaa1115910/bv/player/mobile/component/BvPlayer.kt

Lines changed: 124 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@ package dev.aaa1115910.bv.player.mobile.component
33
import androidx.compose.foundation.layout.fillMaxHeight
44
import androidx.compose.runtime.Composable
55
import androidx.compose.runtime.LaunchedEffect
6-
import androidx.compose.runtime.SideEffect
76
import androidx.compose.runtime.getValue
87
import androidx.compose.runtime.mutableStateOf
98
import androidx.compose.runtime.remember
109
import androidx.compose.runtime.saveable.rememberSaveable
1110
import androidx.compose.runtime.setValue
1211
import androidx.compose.ui.Modifier
1312
import androidx.compose.ui.draw.alpha
14-
import androidx.media3.common.Player
15-
import androidx.media3.exoplayer.ExoPlayer
1613
import com.kuaishou.akdanmaku.DanmakuConfig
1714
import com.kuaishou.akdanmaku.data.DanmakuItemData
1815
import com.kuaishou.akdanmaku.ecs.component.filter.TypeFilter
1916
import com.kuaishou.akdanmaku.ext.RETAINER_BILIBILI
2017
import com.kuaishou.akdanmaku.ui.DanmakuPlayer
18+
import dev.aaa1115910.bv.player.AbstractVideoPlayer
19+
import dev.aaa1115910.bv.player.BvVideoPlayer
20+
import dev.aaa1115910.bv.player.VideoPlayerListener
2121
import dev.aaa1115910.bv.player.mobile.component.controller.BvPlayerController
2222
import dev.aaa1115910.bv.player.mobile.util.LocalMobileVideoPlayerData
2323
import kotlinx.coroutines.delay
@@ -35,11 +35,14 @@ fun BvPlayer(
3535
onDanmakuOpacityChange: (Float) -> Unit,
3636
onDanmakuScaleChange: (Float) -> Unit,
3737
onDanmakuAreaChange: (Float) -> Unit,
38-
videoPlayer: ExoPlayer,
39-
danmakuPlayer: DanmakuPlayer
38+
videoPlayer: AbstractVideoPlayer,
39+
danmakuPlayer: DanmakuPlayer?
4040
) {
4141
val mobileVideoPlayerData = LocalMobileVideoPlayerData.current
4242
var isPlaying by rememberSaveable { mutableStateOf(false) }
43+
var isError by remember { mutableStateOf(false) }
44+
var isBuffering by remember { mutableStateOf(false) }
45+
var exception by remember { mutableStateOf<Exception?>(null) }
4346

4447
var enabledDanmaku by rememberSaveable { mutableStateOf(mobileVideoPlayerData.enabledDanmaku) }
4548
val typeFilter by remember { mutableStateOf(TypeFilter()) }
@@ -75,6 +78,13 @@ fun BvPlayer(
7578
}
7679
}
7780

81+
// 直接调用 danmakuPlayer 会始终为 null
82+
var mDanmakuPlayer: DanmakuPlayer? by remember { mutableStateOf(null) }
83+
84+
LaunchedEffect(danmakuPlayer) {
85+
mDanmakuPlayer = danmakuPlayer
86+
}
87+
7888
val initDanmakuConfig: () -> Unit = {
7989
updateEnabledDanmakuTypeFilter(mobileVideoPlayerData.currentDanmakuTypes)
8090
danmakuConfig = danmakuConfig.copy(
@@ -83,27 +93,122 @@ fun BvPlayer(
8393
dataFilter = listOf(typeFilter)
8494
)
8595
danmakuConfig.updateFilter()
86-
danmakuPlayer.updateConfig(danmakuConfig)
96+
mDanmakuPlayer?.updateConfig(danmakuConfig)
8797
}
8898

8999
val updateDanmakuConfigTypeFilter: () -> Unit = {
90100
updateEnabledDanmakuTypeFilter(mobileVideoPlayerData.currentDanmakuTypes)
91101
danmakuConfig.updateFilter()
92-
danmakuPlayer.updateConfig(danmakuConfig)
102+
mDanmakuPlayer?.updateConfig(danmakuConfig)
93103
}
94104

95105
val toggleDanmakuEnabled: (Boolean) -> Unit = { enabled ->
96106
updateEnabledDanmakuTypeFilter(if (enabled) mobileVideoPlayerData.currentDanmakuTypes else listOf())
97107
danmakuConfig.updateFilter()
98-
danmakuPlayer.updateConfig(danmakuConfig)
108+
mDanmakuPlayer?.updateConfig(danmakuConfig)
99109
}
100110

101111
val updateDanmakuConfig: () -> Unit = {
102112
danmakuConfig = danmakuConfig.copy(
103113
retainerPolicy = RETAINER_BILIBILI,
104114
textSizeScale = mobileVideoPlayerData.currentDanmakuScale,
105115
)
106-
danmakuPlayer.updateConfig(danmakuConfig)
116+
mDanmakuPlayer?.updateConfig(danmakuConfig)
117+
}
118+
119+
val videoPlayerListener = object : VideoPlayerListener {
120+
override fun onError(error: Exception) {
121+
println("onError: $error")
122+
isError = true
123+
exception = error.cause as Exception?
124+
}
125+
126+
override fun onReady() {
127+
println("onReady")
128+
isError = false
129+
exception = null
130+
initDanmakuConfig()
131+
132+
videoPlayer.start()
133+
134+
//updateVideoAspectRatio()
135+
136+
//reset default play speed
137+
//logger.info { "Reset default play speed: $currentPlaySpeed" }
138+
//videoPlayer.speed = currentPlaySpeed
139+
//playerViewModel.danmakuPlayer?.updatePlaySpeed(currentPlaySpeed)
140+
}
141+
142+
override fun onPlay() {
143+
println("onPlay")
144+
//logger.info { "onPlay" }
145+
println("start danmaku player, ${danmakuPlayer != null}")
146+
mDanmakuPlayer?.start()
147+
isPlaying = true
148+
isBuffering = false
149+
150+
//if (playerViewModel.lastPlayed > 0 && hideBackToHistoryTimer == null) {
151+
// showBackToHistory = true
152+
// hideBackToHistoryTimer = countDownTimer(5000, 1000, "hideBackToHistoryTimer") {
153+
// showBackToHistory = false
154+
// hideBackToHistoryTimer = null
155+
// playerViewModel.lastPlayed = 0
156+
// }
157+
//}
158+
}
159+
160+
override fun onPause() {
161+
println("onPause")
162+
println("pause danmaku player 1")
163+
mDanmakuPlayer?.pause()
164+
isPlaying = false
165+
}
166+
167+
override fun onBuffering() {
168+
println("onBuffering")
169+
isBuffering = true
170+
println("pause danmaku player 2")
171+
mDanmakuPlayer?.pause()
172+
}
173+
174+
override fun onEnd() {
175+
println("onEnd")
176+
println("pause danmaku player 3")
177+
mDanmakuPlayer?.pause()
178+
isPlaying = false
179+
//if (!Prefs.incognitoMode) sendHeartbeat()
180+
181+
//val videoListIndex = playerViewModel.availableVideoList.indexOfFirst {
182+
// it.cid == playerViewModel.currentCid
183+
//}
184+
//if (videoListIndex + 1 < playerViewModel.availableVideoList.size) {
185+
// val nextVideo = playerViewModel.availableVideoList[videoListIndex + 1]
186+
// logger.info { "Play next video: $nextVideo" }
187+
// playerViewModel.partTitle = nextVideo.title
188+
// playerViewModel.loadPlayUrl(
189+
// avid = nextVideo.aid,
190+
// cid = nextVideo.cid,
191+
// epid = nextVideo.epid,
192+
// seasonId = nextVideo.seasonId,
193+
// continuePlayNext = true
194+
// )
195+
//}
196+
}
197+
198+
override fun onIdle() {
199+
println("onIdle")
200+
println("pause danmaku player 4")
201+
mDanmakuPlayer?.pause()
202+
}
203+
204+
override fun onSeekBack(seekBackIncrementMs: Long) {
205+
mDanmakuPlayer?.seekTo(currentTime)
206+
}
207+
208+
override fun onSeekForward(seekForwardIncrementMs: Long) {
209+
mDanmakuPlayer?.seekTo(currentTime)
210+
}
211+
107212
}
108213

109214
LaunchedEffect(Unit) {
@@ -113,42 +218,6 @@ fun BvPlayer(
113218
}
114219
}
115220

116-
SideEffect {
117-
videoPlayer.addListener(object : Player.Listener {
118-
override fun onIsPlayingChanged(playing: Boolean) {
119-
isPlaying = playing
120-
if (isPlaying) {
121-
danmakuPlayer.start()
122-
} else {
123-
danmakuPlayer.pause()
124-
}
125-
}
126-
127-
override fun onPlaybackStateChanged(playbackState: Int) {
128-
when (playbackState) {
129-
Player.STATE_READY -> {
130-
initDanmakuConfig()
131-
danmakuPlayer.seekTo(videoPlayer.currentPosition)
132-
if (!isPlaying) danmakuPlayer.pause()
133-
}
134-
135-
Player.STATE_ENDED -> danmakuPlayer.pause()
136-
Player.STATE_IDLE -> {}
137-
Player.STATE_BUFFERING -> danmakuPlayer.pause()
138-
else -> danmakuPlayer.pause()
139-
}
140-
}
141-
142-
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
143-
if (playWhenReady) {
144-
danmakuPlayer.start()
145-
} else {
146-
danmakuPlayer.pause()
147-
}
148-
}
149-
})
150-
}
151-
152221
BvPlayerController(
153222
modifier = modifier,
154223
isPlaying = isPlaying,
@@ -168,11 +237,15 @@ fun BvPlayer(
168237
onEnterFullScreen = onEnterFullScreen,
169238
onExitFullScreen = onExitFullScreen,
170239
onBack = onBack,
171-
onPlay = { videoPlayer.play() },
240+
onPlay = { videoPlayer.start() },
172241
onPause = { videoPlayer.pause() },
173-
onSeekToPosition = videoPlayer::seekTo,
242+
onSeekToPosition = { position ->
243+
mDanmakuPlayer?.seekTo(position)
244+
mDanmakuPlayer?.pause()
245+
videoPlayer.seekTo(position)
246+
},
174247
onChangeResolution = onChangeResolution,
175-
onChangeSpeed = videoPlayer::setPlaybackSpeed,
248+
onChangeSpeed = { videoPlayer.speed = it },
176249
onToggleDanmaku = {
177250
enabledDanmaku = !enabledDanmaku
178251
toggleDanmakuEnabled(enabledDanmaku)
@@ -189,12 +262,13 @@ fun BvPlayer(
189262
},
190263
onDanmakuAreaChange = onDanmakuAreaChange
191264
) {
192-
Media3VideoPlayer(videoPlayer = videoPlayer)
265+
//Media3VideoPlayer(videoPlayer = videoPlayer)
266+
BvVideoPlayer(videoPlayer = videoPlayer, playerListener = videoPlayerListener)
193267
AkDanmakuPlayer(
194268
modifier = Modifier
195269
.alpha(mobileVideoPlayerData.currentDanmakuOpacity)
196270
.fillMaxHeight(mobileVideoPlayerData.currentDanmakuArea),
197-
danmakuPlayer = danmakuPlayer
271+
danmakuPlayer = mDanmakuPlayer
198272
)
199273
}
200274
}

settings.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ rootProject.name = "BV"
3030
include(":app")
3131
include(":bili-api")
3232
include(":bili-subtitle")
33-
include(":bv-player")
33+
include(":bv-player-core")
3434
include(":bv-player-mobile")
3535
include(":libs:av1Decoder")
3636
include(":libs:libVLC")

0 commit comments

Comments
 (0)