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

Android MediaPlayer调用setDataSource时出现“Failed to convert to modern format file descriptor”异常求助

Android MediaPlayer setDataSource 报错:Failed to convert to modern format file descriptor

我之前也碰到过这个一模一样的错误,当时折腾了好一阵才搞定,先帮你分析下问题,再给几个可行的解决方案:

首先先把你遇到的错误信息贴出来方便对照:

Failed to convert to modern format file descriptor
java.io.IOException: Input file descriptor already original
at android.provider.MediaStore.getOriginalMediaFormatFileDescriptor(MediaStore.java:943)
at android.os.FileUtils.convertToModernFd(FileUtils.java:1470)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1271)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1254)

错误原因分析

这个异常主要出现在Android 10(API 29)及以上版本,核心原因是系统引入了媒体格式适配机制:

  • 当你直接用FileInputStream获取的FileDescriptor传入MediaPlayer时,系统会尝试将其转换为“现代格式FD”,但如果这个FD没有关联正确的媒体元数据,或者已经是原始状态,就会触发转换失败。
  • 你尝试的Uri.fromFile()方式,在Android 10+因为Scoped Storage(分区存储)的限制,这种File Uri无法被MediaPlayer正确识别,同样会触发类似的底层异常。

解决方案

方案一:用ContentResolver + FileProvider获取适配的FD(推荐,全版本兼容)

如果你的音频文件在应用私有目录之外,或者需要适配Android 10+的分区存储,这是最稳妥的方式。通过FileProvider生成合法的Content Uri,再用ContentResolver获取可被MediaPlayer识别的FD:

if (myAudio.exists()) {
    mp.reset();
    // 生成FileProvider的Content Uri,替换成你的应用包名
    Uri contentUri = FileProvider.getUriForFile(getContext(), "com.your.app.id.fileprovider", myAudio);
    ParcelFileDescriptor pfd = null;
    try {
        pfd = getContext().getContentResolver().openFileDescriptor(contentUri, "r");
        if (pfd != null) {
            mp.setDataSource(pfd.getFileDescriptor());
            mp.setOnErrorListener(this);
            mp.setOnPreparedListener(this);
            mp.prepareAsync();
            btnPlay.setImageResource(R.drawable.pause);
        }
    } catch (IOException e) {
        e.printStackTrace();
        btnPlay.setImageResource(R.drawable.play);
        errorGot += 1;
    } finally {
        if (pfd != null) {
            try {
                pfd.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
} else {
    btnPlay.setImageResource(R.drawable.play);
    errorGot += 1;
}

注意:需要先配置FileProvider:

  1. 在AndroidManifest.xml中添加Provider声明:
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.your.app.id.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>
  1. 在res/xml目录下创建file_paths.xml,定义允许共享的文件路径:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 根据你的文件位置选择对应标签,比如外部存储根目录 -->
    <external-path name="external_files" path="."/>
    <!-- 或者应用私有目录 -->
    <files-path name="internal_files" path="."/>
</paths>

方案二:直接使用文件绝对路径(仅适应用私有目录文件)

如果你的音频文件存储在应用私有目录(比如getFilesDir()、getCacheDir()),可以直接用文件路径调用setDataSource,绕开FD和Uri的适配问题:

if (myAudio.exists()) {
    mp.reset();
    try {
        mp.setDataSource(myAudio.getAbsolutePath());
        mp.setOnErrorListener(this);
        mp.setOnPreparedListener(this);
        mp.prepareAsync();
        btnPlay.setImageResource(R.drawable.pause);
    } catch (IOException e) {
        e.printStackTrace();
        btnPlay.setImageResource(R.drawable.play);
        errorGot += 1;
    }
} else {
    btnPlay.setImageResource(R.drawable.play);
    errorGot += 1;
}

这个方法简单直接,而且不会触发分区存储的权限问题,适合私有目录内的媒体文件。

方案三:延迟关闭FileInputStream(不推荐,兼容性差)

你之前的代码里提前关闭了FileInputStream,可能导致FD被系统提前回收,无法完成格式转换。可以尝试把流的关闭操作放到onPrepared回调里:

if (myAudio.exists()) {
    mp.reset();
    FileInputStream is = null;
    try {
        is = new FileInputStream(myAudio);
        FileDescriptor fd = is.getFD();
        mp.setDataSource(fd);
        mp.setOnErrorListener(this);
        mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                btnPlay.setImageResource(R.drawable.pause);
                // 播放完成或释放时再关闭流
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        mp.prepareAsync();
    } catch (IOException e) {
        e.printStackTrace();
        btnPlay.setImageResource(R.drawable.play);
        errorGot += 1;
        // 异常时也要关闭流
        if (is != null) {
            try {
                is.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
} else {
    btnPlay.setImageResource(R.drawable.play);
    errorGot += 1;
}

不过这个方法在不同Android版本上表现不稳定,不建议作为长期方案。

总结

优先选择方案一或方案二,根据你的文件存储位置来决定:如果是外部公共目录用方案一,私有目录用方案二,这样既能解决当前的FD转换错误,也能适配新的Android版本规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 08:04:07