You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose中ExoPlayer在Android11及以下仅播音频无画面问题

问题:Android 11及以下版本播放视频仅输出音频,画面呈白色

我使用ExoPlayer Media3库播放视频,先将视频下载保存至本地存储(路径:/storage/emulated/0/Android/data/packagename/files/video/BigBuckBunny.mp4)。播放时,Android 12及以上版本正常,但Android 11及以下版本仅能播放音频,视频画面呈白色。

我尝试了自定义的VideoPlayerScreenNew组件,也试过系统默认的VideoView,两种方式均出现相同问题;同时直接播放网络视频链接(https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4)也存在该问题。

相关代码如下:


VideoPlayerScreenNew.kt

import android.net.Uri
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.Player.Listener
import androidx.media3.common.Player.STATE_BUFFERING
import androidx.media3.common.util.Log
import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.dash.DashMediaSource
import androidx.media3.exoplayer.hls.HlsMediaSource
import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.exoplayer.source.ProgressiveMediaSource
import androidx.media3.ui.PlayerView
import app.mashmari.miniplayer.ui.theme.purpleTheme
import app.mashmari.miniplayer.utilitys.CustomCircularProgressBar

@androidx.annotation.OptIn(UnstableApi::class)
@Composable
fun VideoPlayerScreenNew(
    videoUrl: String?,
    onBackPressed: () -> Unit,
    onError: () -> Unit = {},
) {
    Log.e("TAG", "VideoPlayerScreenNew: video link : $videoUrl")
    val isShowLoader = remember { mutableStateOf(true) }
    val context = LocalContext.current
    var exoPlayer by remember { mutableStateOf<ExoPlayer?>(null) }

    BackHandler(onBack = onBackPressed)

    // Create the ExoPlayer only once
    DisposableEffect(Unit) {
        android.util.Log.e("TAG", "VideoPlayerScreenNew: DisposableEffect")
        val player = ExoPlayer.Builder(context).build().also { exoPlayer = it }
        exoPlayer?.repeatMode = Player.REPEAT_MODE_ONE
//        player.addAnalyticsListener(EventLogger())
        exoPlayer?.addListener(object : Listener {
            override fun onPlaybackStateChanged(playbackState: Int) {
                super.onPlaybackStateChanged(playbackState)
                if (playbackState == STATE_BUFFERING) {
//                    Log.e("TAG", "onPlaybackStateChanged: isbuffering")
                    isShowLoader.value = true
                } else {
//                    Log.e("TAG", "onPlaybackStateChanged: isbuffering not $playbackState ")
                    isShowLoader.value = false
                }
            }

            override fun onPlayerError(error: PlaybackException) {
                super.onPlayerError(error)
                Log.e("TAG", "VideoPlayerScreenNew onPlayerError: called  ${error.errorCode}")
                Toast.makeText(
                    context, "Video playback issue ${error.message}", Toast.LENGTH_SHORT
                ).show()
                onError()
            }
        })

        onDispose {
            player.release()
        }
    }

    // Update the media source when videoUrl changes
    LaunchedEffect(videoUrl) {
        android.util.Log.e("TAG", "VideoPlayerScreenNew: LaunchedEffect$videoUrl")
        exoPlayer?.let { player ->
            val isOnline: Boolean = videoUrl?.startsWith("http") == true
            val url = videoUrl
                ?: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
            val mediaSourceFactory = when {
                url.contains(".m3u8") -> HlsMediaSource.Factory(DefaultHttpDataSource.Factory())
                url.contains(".mpd") -> DashMediaSource.Factory(DefaultHttpDataSource.Factory())
                isOnline -> ProgressiveMediaSource.Factory(DefaultHttpDataSource.Factory())
                else -> ProgressiveMediaSource.Factory(DefaultDataSource.Factory(context))
            }

            val mediaSource: MediaSource =
                mediaSourceFactory.createMediaSource(MediaItem.fromUri(Uri.parse(url)))

            player.setMediaSource(mediaSource)
            player.prepare()
            player.playWhenReady = true
        }
    }

    Box(modifier = Modifier.fillMaxSize()) {
        android.util.Log.e("TAG", "VideoPlayerScreenNew: Box called")
        exoPlayer?.let { player ->
            AndroidView(
                factory = {
                    PlayerView(context).apply {
                        useController = false
                        hideController()
                        this.player = player
                    }
                }, modifier = Modifier.fillMaxSize()
            )
        }
        CustomCircularProgressBar(
            isShowLoader = isShowLoader, progressColor = purpleTheme
        )
    }
}

VideoPlayerScreenNew调用代码

composable<VideoPlay> {
    val allVideo = getAllVideoLink()
    val file = appUtils.getLocalFileFromUri(allVideo.get(1))
//            VideoPlayer(file.absolutePath)
    Log.e("TAG", "VideoPlayerScreenNew: App: VideoPlay called")
    VideoPlayerScreenNew(videoUrl = file.absolutePath, onBackPressed = {
        navController.popBackStack()
    }, onError = {
        navController.popBackStack()
    })
}

VideoPlayer.kt

@Composable
fun VideoPlayer(videoUrl: String) {
    AndroidView(factory = { context ->
        VideoView(context).apply {
            // Set up MediaController for playback controls
            val mediaController = MediaController(context)
            mediaController.setAnchorView(this)
            setMediaController(mediaController)

            // Set the video URL
            setVideoPath(videoUrl)

            // Start playing the video
            setOnPreparedListener {
                start()
            }
        }
    }, update = { videoView ->
        // Update logic if needed, e.g., change video source
    })
}

内容的提问来源于stack exchange,提问作者Vishal Bhimporwala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 19:37:31