视频优化

This commit is contained in:
Lindong 2026-01-27 14:03:32 +08:00
parent 13ee0de952
commit 6f34b5b990
3 changed files with 117 additions and 29 deletions

View File

@ -65,6 +65,8 @@ class ShushuManager private constructor() {
config.setMode(/*if (BuildConfig.DEBUG) TDConfig.ModeEnum.DEBUG else*/ TDConfig.ModeEnum.DEBUG)
// config.setMode(/*if (BuildConfig.DEBUG) TDConfig.ModeEnum.DEBUG else*/ TDConfig.ModeEnum.NORMAL)
mShushuSdk = ThinkingAnalyticsSDK.sharedInstance(config)
StatisticLogger.d("数数DeviceID: ${TDAnalytics.getDeviceId()}")
configAutoTrack()
sdkReady = true
flushPendingEventsAsync()

View File

@ -82,7 +82,7 @@ class HomeFragment : AppViewsFragment<ViewBinding, UiState, ViewModel>(), OnSwit
override fun ViewBinding.initViews() {
viewPager2.setPageTransformer { _, _ -> }
viewPager2.offscreenPageLimit = 1
viewPager2.offscreenPageLimit = 3
viewPager2.adapter = mViewPagerAdapter
@ -236,13 +236,39 @@ class HomeFragment : AppViewsFragment<ViewBinding, UiState, ViewModel>(), OnSwit
if (fragment != null) {
setHomeTabStyle(fragment)
val curFragment: HomeItemFragment = fragment as HomeItemFragment
curFragment.loadVideo()
curFragment.playVideo()
handleEventOneVideoSwiped()
}
// Preload next N videos
val preloadCount = 3
for (i in 1..preloadCount) {
val nextPos = position + i
if (nextPos < mViewPagerAdapter.itemCount) {
val nextFragment = mViewPagerAdapter.getFragmentByIndex(nextPos)
if (nextFragment != null && nextFragment is HomeItemFragment) {
nextFragment.preloadVideo()
}
}
}
// Release players for previous fragments to save memory
// We keep 'preloadCount' fragments after current, and maybe 0 before?
// But offscreenPageLimit keeps them in memory. We should release their heavy resources.
val offscreenLimit = 3
for (i in 1..offscreenLimit) {
val prevPos = position - i
if (prevPos >= 0) {
val prevFragment = mViewPagerAdapter.getFragmentByIndex(prevPos)
if (prevFragment != null && prevFragment is HomeItemFragment) {
prevFragment.releasePlayer()
}
}
}
// load more
if (mViewPagerAdapter.itemCount > 0 && position == mViewPagerAdapter.itemCount - 2) {
if (mViewPagerAdapter.itemCount > 0 && position >= mViewPagerAdapter.itemCount - 3) {
lifecycleScope.launch {
mViewModel.loadVideoList()
}
@ -262,7 +288,6 @@ class HomeFragment : AppViewsFragment<ViewBinding, UiState, ViewModel>(), OnSwit
}, false)
viewPager2.offscreenPageLimit = 3
}
override fun ViewBinding.initObservers() {

View File

@ -19,6 +19,7 @@ import androidx.core.view.isVisible
import com.ama.core.architecture.appBase.AppViewsEmptyViewModelFragment
import com.ama.core.architecture.util.AndroidUtil
import com.ama.core.architecture.util.setOnClickBatch
import com.blankj.utilcode.util.LogUtils
import com.gamedog.statisticreporter.StatisticUtil
import com.viddin.videos.free.R
import com.gamedog.vididin.beans.YoutubeVideo
@ -27,6 +28,7 @@ import com.gamedog.vididin.youtubestatistic.TickerTimer
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.PlayerConstants
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.YouTubePlayerCallback
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.utils.loadOrCueVideo
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
import com.viddin.videos.free.databinding.VididinappFeatureHomeItemLayoutBinding as ViewBinding
@ -43,6 +45,8 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
private var mCurPlayedSecond: Float = 0F
private var mTotalDuration: Float = 0F
private val mTickerTimer = TickerTimer()
private var mPendingPlay: Boolean = false
private var mIsPreloading: Boolean = false
private val mThumbHandler = Handler(Looper.getMainLooper())
@ -100,27 +104,67 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
private fun playVideo() {
if (mPlayer != null && mVideoData != null && !mVideoData?.id.isNullOrEmpty()) {
mPlayer?.loadOrCueVideo(
lifecycle,
mVideoData!!.id,
0f
)
/*mPlayerView?.isVisible?.let {
if (!it) {
mPlayer?.pause()
}
}*/
fun playVideo() {
mIsPreloading = false
binding?.playerContainer?.isVisible = true
if (mPlayerView == null) {
initPlayerView(true)
} else {
if (mPlayer != null) {
mPlayer?.unMute()
mPlayer?.play()
} else {
mPendingPlay = true
}
}
}
fun preloadVideo() {
if (mPlayerView == null) {
mIsPreloading = true
binding?.playerContainer?.isVisible = true
initPlayerView(false)
} else {
if (mPlayer != null && mIsPreloading) {
// Already preloading or preloaded
}
}
}
fun releasePlayer() {
LogUtils.d("[视频Item] releasePlayer ID: ${getVideoId()}")
mIsPreloading = false
mPendingPlay = false
if (mPlayerView != null) {
// Try to save last frame as mask if possible and not already saved
if (mMaskBitmap == null && binding?.playerContainer?.isVisible == true) {
// This is a best-effort sync capture or just skip to avoid async complexity during release
// For now, we just release to free memory immediately.
}
mPlayerView!!.release()
binding?.playerContainer?.removeView(mPlayerView)
mPlayerView = null
mPlayer = null
}
binding?.playerContainer?.isVisible = false
binding?.ivMask?.isVisible = true
if (mMaskBitmap != null) {
binding?.ivMask?.setImageBitmap(mMaskBitmap)
}
showPlayIconAnim()
mTickerTimer.pause()
}
fun getVideoId(): String? {
return mVideoData?.id
}
fun loadVideo() {
private fun initPlayerView(autoPlay: Boolean) {
mPendingPlay = autoPlay
LogUtils.d("[视频Item] initPlayerView ID: ${getVideoId()} autoPlay: $autoPlay")
if (null == mPlayerView) {
mPlayerView = YouTubePlayerView(requireContext())
val layoutParam = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
@ -130,16 +174,20 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
mPlayerView?.enableAutomaticInitialization = true
}
val playerView = mPlayerView
playerView!!.addYouTubePlayerListener(object : AbstractYouTubePlayerListener() {
mPlayerView!!.addYouTubePlayerListener(object : AbstractYouTubePlayerListener() {
override fun onReady(@NonNull youTubePlayer: YouTubePlayer) {
mPlayer = youTubePlayer
val playerUiController = MyPlayerControlView(mPlayerView!!, youTubePlayer)
mPlayerView!!.setCustomPlayerUi(playerUiController.rootView)
val playerUiController = MyPlayerControlView(playerView, youTubePlayer)
playerView.setCustomPlayerUi(playerUiController.rootView)
playVideo()
if (mPendingPlay) {
mPlayer?.loadVideo(mVideoData!!.id, 0f)
} else if (mIsPreloading) {
mPlayer?.mute()
mPlayer?.loadVideo(mVideoData!!.id, 0f)
} else {
mPlayer?.cueVideo(mVideoData!!.id, 0f)
}
}
override fun onCurrentSecond(youTubePlayer: YouTubePlayer, second: Float) {
@ -160,10 +208,20 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
) {
when (state) {
PlayerConstants.PlayerState.PLAYING -> {
togglePlayingState(true)
if (mIsPreloading) {
mPlayer?.pause()
} else {
togglePlayingState(true)
}
}
PlayerConstants.PlayerState.PAUSED -> {
togglePlayingState(false)
if (mIsPreloading) {
// Do nothing or ensure mask is visible?
// togglePlayingState(false) sets mask visible.
togglePlayingState(false)
} else {
togglePlayingState(false)
}
}
PlayerConstants.PlayerState.UNKNOWN -> {
togglePlayingState(false)
@ -173,7 +231,9 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
}
PlayerConstants.PlayerState.ENDED -> {
togglePlayingState(false)
playVideo()
if (!mIsPreloading) {
mPlayer?.loadVideo(mVideoData!!.id, 0f)
}
}
PlayerConstants.PlayerState.BUFFERING -> {
//binding?.circlePb?.isVisible = true
@ -217,7 +277,8 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
}
binding?.ivMask?.isVisible = !mIsPlaying
binding?.playerContainer?.isVisible = mIsPlaying
// Ensure playerContainer is visible when playing OR preloading (masked)
binding?.playerContainer?.isVisible = mIsPlaying || mIsPreloading
if (mIsPlaying) {
hidePlayIconAnim()