ExoPlayer 有时会因 java.io.FileNotFoundException 失败:打开失败:ENOENT(没有这样的文件或目录)当文件存在时

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

我正在使用 androidx.media3:media3-exoplayer:1.0.1 中的 ExoPlayer 播放本地存储中的视频(下载文件夹 - 使用 Android DownloadManager 下载的文件)。

override fun downloadFile(title: String, url: String): Long {
        val request = DownloadManager.Request(url.toUri())
            .setMimeType("video/mp4")
            .setNotificationVisibility(
                DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
                    or DownloadManager.Request.VISIBILITY_VISIBLE
            )
            .setTitle(title)
            .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, title)
        return downloadManager.enqueue(request)
    }

下载成功后,我播放视频,当我从后端获取事件时。

override fun playVideo(uuid: String) {
        videoIsPlaying = true
        currentUri = fileParser.getUri(uuid)
        currentUri?.let {
            viewModelScope.launch {
                videoPlayer.playVideo(it)
            }
        }
    }

fileParser 返回下载文件的 URI,如果它可以找到具有该 uuid 的任何文件(在我的例子中,此方法总是返回 URI,它们不为空)

class FileParser(private val context: Context, private val pref: DefaultSharedPref) {

    private val listOfUris = mutableListOf<Uri>()

    fun getUri(uuid: String) : Uri? {
        if (listOfUris.isEmpty()) {
            val manager: DownloadManager =
                context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager

            pref.getVideoIds().forEach {
                val uri = manager.getUriForDownloadedFile(it.toLong())
                uri.path?.let {
                    listOfUris.add(uri)
                }
            }
        }
        listOfUris.forEach {
            val fileName = context.getFileName(it)
            if (fileName == uuid)
                return it
        }
        return null
    }
}

fun Context.getFileName(uri: Uri): String? = when(uri.scheme) {
    ContentResolver.SCHEME_CONTENT -> getContentFileName(uri)
    else -> uri.path?.let(::File)?.name
}

private fun Context.getContentFileName(uri: Uri): String? = runCatching {
    contentResolver.query(uri, null, null, null, null)?.use { cursor ->
        cursor.moveToFirst()
        return@use cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME).let(cursor::getString)
    }
}.getOrNull()

这是我的playVideo方法

fun playVideo(uri: Uri) {
        player.setMediaItem(MediaItem.fromUri(uri))
        player.prepare()
        player.play()
    }

现在,我正面临着这个问题——有时视频播放好几个小时,有时播放几分钟,但过了一段时间后我得到了这个异常。

Playback error androidx.media3.exoplayer.ExoPlaybackException: Source error
at androidx.media3.exoplayer.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:652)
at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:624)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: androidx.media3.datasource.ContentDataSource$ContentDataSourceException: java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
at androidx.media3.datasource.ContentDataSource.open(ContentDataSource.java:157)
at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:272)
at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:86)
at androidx.media3.exoplayer.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1006)
at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:414)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
at java.lang.Thread.run(Thread.java:1012)
Caused by: java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:151)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:780)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:2029)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1967)
at androidx.media3.datasource.ContentDataSource.open(ContentDataSource.java:89)
at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:272) 
at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:86) 
at androidx.media3.exoplayer.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1006) 
at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:414) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) 
at java.lang.Thread.run(Thread.java:1012) 

此时文件仍然存在,我没有更改它的标题。第一次捕获此异常视频后将不再播放。只有两种修复方法 - 删除应用程序并重新安装或清除现金。它有帮助,但只是在很短的时间内。某个点后异常将返回。有人可以说出什么问题了吗? Uri 看起来像 content://downloads/all_downloads/1012。我正在为我的 UI 使用 JetpackCompose

AnimatedVisibility(visible = videoIsPlaying) {
            AndroidView(
                factory = { context ->
                    PlayerView(context).also {
                        it.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
                        it.useController = false
                        it.player = videoPlayer.player
                    }
                },
                update = {
                    when (lifecycle) {
                        Lifecycle.Event.ON_PAUSE -> {
                            it.onPause()
                            it.player?.pause()
                        }
                        Lifecycle.Event.ON_RESUME -> {
                            it.onResume()
                        }
                        else -> Unit
                    }
                },
                modifier = Modifier
                    .fillMaxHeight()
                    .aspectRatio(16 / 9f)
            )
        }

谢谢!

我试过使用 DefaultDataSource 但没有用

 private val defaultDataSourceFactory = DefaultDataSource.Factory(app)
    private val dataSourceFactory: DataSource.Factory = DefaultDataSource.Factory(
        app,
        defaultDataSourceFactory
    )

    fun playVideo(uri: Uri) {
        val source = ProgressiveMediaSource.Factory(dataSourceFactory)
            .createMediaSource(MediaItem.fromUri(uri))
        player.setMediaSource(source)
        player.prepare()
        player.playWhenReady = false
        player.play()
    }

我也尝试添加这个

videoPlayer.player.seekTo(0)
来重新启动视频,但问题仍然没有解决。

我想这可能是因为许可(我没有添加它们,据我所知,如果文件属于您的应用程序,我们不需要它们)但即使我添加它们 - 也没有任何变化。

android exoplayer android-download-manager
© www.soinside.com 2019 - 2024. All rights reserved.