我正在使用 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)
来重新启动视频,但问题仍然没有解决。
我想这可能是因为许可(我没有添加它们,据我所知,如果文件属于您的应用程序,我们不需要它们)但即使我添加它们 - 也没有任何变化。