在2.3.4版本中,使用文件路径设置铃声时,MediaPlayer的setDataSource失败,状态为0x80000000。

25

标题已经说得很清楚了。

我的应用程序一直在使用像content://media/internal/audio/media/387content://media/external/audio/media/1655(我认为是用于SD卡上的自定义铃声)这样的uri来播放铃声,同时使用setDataSource(fileInfo)setDataSource(mContext, Uri.parse(fileInfo))两种方法。

在每种情况下,我都收到了关于在使用Android 4.x的手机上(不同版本)出现setDataSource失败:status=0x80000000异常的日志信息。

由于发生错误仅针对指向内容uri的铃声,而不是指向路径的单个文件,因此我决定对铃声也使用路径,这在上述手机上解决了问题(同时仍然使用setDataSource(mContext, Uri.parse(fileInfo)))。

但是,在Android 2.3.4-2.3.6的手机上(我的2.3.3上没有),问题开始出现:

  • 我收到了几个带有路径文件的异常日志,例如/system/media/audio/ringtones/TwirlAway.ogg,并显示setDataSource失败:status=0x80000000
  • 我还收到了一个关于MediaPlayer.onErrorListener.onError(int what, int extra)方法调用的日志,其中what=1extra=-2147483648,根据我的了解,这意味着文件要么缺失,要么损坏。然而我执行了

    File file = new File(fileInfo);
    if (!file.exists())
    

在这种情况下检查,它返回文件确实存在 - 那么它是损坏了吗?对于内部存储中的音乐文件来说,这是高度不可能的。

总之:

  • 使用setDataSource("content:// media / internal / audio / media / 52")可以工作
  • 对于setDataSource(mContext,"/system/media/audio/ringtones/TwirlAway.ogg"),会抛出异常:setDataSource failed .: status = 0x80000000

有趣的是,被setDataSource(Context context, Uri uri)调用的setDataSource(Context context, Uri uri, Headers headers)方法的前几行内容(从2.3.4版本的GrepCode源代码)如下:

 String scheme = uri.getScheme();
     if(scheme == null || scheme.equals("file")) {
         setDataSource(uri.getPath());
         return;
     }

所以,归根结底,它只是在setDataSource("/system/media/audio/ringtones/TwirlAway.ogg")失败了。我使用以下内容从URI中获取铃声的路径:

private static String getRingtonePathFromContentUri(Context context,
        Uri contentUri) {

    String[] proj = { MediaStore.Audio.Media.DATA };
    Cursor ringtoneCursor = context.getContentResolver().query(contentUri,
            proj, null, null, null);
    ringtoneCursor.moveToFirst();
    return ringtoneCursor.getString(ringtoneCursor
            .getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
}
任何想法是什么导致错误抛出?也许这些问题是由于缺乏读取权限引起的? 我猜本地setDataSource(String路径)函数的源代码会很有帮助,但我找不到它。
6个回答

15

下面Lorne的答案对于解决这个问题非常有帮助。

对于其他人遇到问题的情况,以下是我已经使用了6个多月且几乎不报错的代码:

fileinfo可以是以下任意一个(示例):

/system/media/audio/alarms/Walk_in_the_forest.ogg

content://media/internal/audio/media/20

public static void setMediaPlayerDataSource(Context context,
        MediaPlayer mp, String fileInfo) throws Exception {

    if (fileInfo.startsWith("content://")) {
        try {
            Uri uri = Uri.parse(fileInfo);
            fileInfo = getRingtonePathFromContentUri(context, uri);
        } catch (Exception e) {
        }
    }

    try {
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
            try {
                setMediaPlayerDataSourcePreHoneyComb(context, mp, fileInfo);
            } catch (Exception e) {
                setMediaPlayerDataSourcePostHoneyComb(context, mp, fileInfo);
            }
        else
            setMediaPlayerDataSourcePostHoneyComb(context, mp, fileInfo);

    } catch (Exception e) {
        try {
            setMediaPlayerDataSourceUsingFileDescriptor(context, mp,
                    fileInfo);
        } catch (Exception ee) {
            String uri = getRingtoneUriFromPath(context, fileInfo);
            mp.reset();
            mp.setDataSource(uri);
        }
    }
}

private static void setMediaPlayerDataSourcePreHoneyComb(Context context,
        MediaPlayer mp, String fileInfo) throws Exception {
    mp.reset();
    mp.setDataSource(fileInfo);
}

private static void setMediaPlayerDataSourcePostHoneyComb(Context context,
        MediaPlayer mp, String fileInfo) throws Exception {
    mp.reset();
    mp.setDataSource(context, Uri.parse(Uri.encode(fileInfo)));
}

private static void setMediaPlayerDataSourceUsingFileDescriptor(
        Context context, MediaPlayer mp, String fileInfo) throws Exception {
    File file = new File(fileInfo);
    FileInputStream inputStream = new FileInputStream(file);
    mp.reset();
    mp.setDataSource(inputStream.getFD());
    inputStream.close();
}

private static String getRingtoneUriFromPath(Context context, String path) {
    Uri ringtonesUri = MediaStore.Audio.Media.getContentUriForPath(path);
    Cursor ringtoneCursor = context.getContentResolver().query(
            ringtonesUri, null,
            MediaStore.Audio.Media.DATA + "='" + path + "'", null, null);
    ringtoneCursor.moveToFirst();

    long id = ringtoneCursor.getLong(ringtoneCursor
            .getColumnIndex(MediaStore.Audio.Media._ID));
    ringtoneCursor.close();

    if (!ringtonesUri.toString().endsWith(String.valueOf(id))) {
        return ringtonesUri + "/" + id;
    }
    return ringtonesUri.toString();
}

public static String getRingtonePathFromContentUri(Context context,
        Uri contentUri) {
    String[] proj = { MediaStore.Audio.Media.DATA };
    Cursor ringtoneCursor = context.getContentResolver().query(contentUri,
            proj, null, null, null);
    ringtoneCursor.moveToFirst();

    String path = ringtoneCursor.getString(ringtoneCursor
            .getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));

    ringtoneCursor.close();
    return path;
}

我已经尝试解决这个问题将近一个月了。我们的应用程序甚至是一个闹钟应用程序,早上应该能够完美地播放音乐或铃声,但到现在为止,在播放音乐时仍然有很多崩溃情况。 - Wooseong Kim
2
我因为这个问题一直很沮丧,你救了我的一个月! - Wooseong Kim
如何处理类似于 /storage/emulated/0/IDM/Music/xxx.mp3 的路径?即使这样做也失败了。 - jeevs
@jeevs,有什么问题吗?上述解决方案对我仍然有效。也许你的文件已损坏或者在这个位置找不到了? - Koger
到目前为止最佳答案!非常感谢您的帮助! - Aradhna
显示剩余3条评论

6

您需要明确设置文件的长度。使用重载方法:

AssetFileDescriptor afd = ctx.getAssets().openFd([您的资产名称]); mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());

请注意,以上代码中的"ctx"应替换为您的上下文。


谢谢,从没想过这是导致错误的原因!我以前调用了上面重载的方法,但这次没有思考就调用了简单的方法。 - Jenix

4

setDataSource(String path)方法在Android 4.1.1中由于漏洞修复而行为发生了变化。在4.1.1或更高版本中,您需要使用本地路径(不带协议)。但是,在4.0.4及之前的版本中,您需要使用URI(例如使用file://协议)。

以下是一个不完整的代码片段,应该可以说明解决方法:

// as of 4.1.1 (JELLY_BEAN) we need to use a local path (without protocol)
// on 4.0.4 and earlier we needed a URI (with file:// protocol)
final String cachedFile = android.os.Build.VERSION.SDK_INT >= 16 // android.os.Build.VERSION_CODES.JELLY_BEAN
                        ? getCacheFilePath(file)
                        : getCacheFileUri(file);


// for the purpose of this example 
// assume cacheFolder is a String and getCacheFile returns a String

public String getCacheFilePath(String file) {
    return cacheFolder + getCacheFile(file);
}

public String getCacheFileUri(String file) {
    return "file://" + cacheFolder + getCacheFile(file);
}

2

这可能是由于您尝试播放或压缩的文件格式导致的。我正在压缩一个可以正常工作的mp4文件,但当我压缩mov文件时,应用程序崩溃并出现了setDataSource失败的异常。

建议检查您要处理的文件格式是否受支持,并确保使用的应用程序能够正确处理该格式。

请注意,不同的文件格式可能需要使用不同的编解码器进行处理,因此在处理文件之前,请仔细了解所需的编解码器以及它们的兼容性。

最初的回答:


这是真的。这主要与文件格式有关。在处理视频文件时,我发现以下视频格式:AVI、MPG/MPEG、MOV、mov、mp4、m4v、flv、WMV中,AVI、MPG/MPEG和WMV每次都会抛出异常。最好在运行该方法之前将它们排除,并用try-catch包装它。 - waseefakhtar

0

我曾经遇到过同样的错误,当我试图播放一个.wav文件时。让我们举个例子:

 private void start_player(){
// create raw folder inside the res folder if not present
MediaPlayer mPlayer = MediaPlayer.create(activity, R.raw.your_wave_audio_file);
        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mPlayer.setOnCompletionListener(new OnCompletionListener() {

            @Override
            public void onCompletion(MediaPlayer mp) {
                mp.release();
               // Your Stuff
            }
        });  
         mPlayer.start();
     }
    }

我也遇到了status=0x80000000错误。在我的情况下,解决方案是重新编码音频文件(例如将.wav文件转换为16位PCM),然后一切都按预期工作。


-1
MediaPlayer mPlayer = MediaPlayer.create(activity, R.raw.your_wave_audio_file);
    mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

你做不到这个,因为 prepare 函数是在 create 函数中被调用的,所以在之后你不能再改变音频流类型了。

下面的代码对我来说工作得很好:

sMediaPlayer = new MediaPlayer();
sMediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);
AssetFileDescriptor assetFileDescriptor = context.getResources().
         openRawResourceFd(R.raw.cool_song);

 if(assetFileDescriptor == null) return;

 try {
    sMediaPlayer.setDataSource(assetFileDescriptor.getFileDescriptor(),
                               assetFileDescriptor.getStartOffset(), 
                               assetFileDescriptor.getLength());
    assetFileDescriptor.close();

    sMediaPlayer.setOnCompletionListener(new OnCompletionListener() {           
        @Override
        public void onCompletion(MediaPlayer mp) {
            if(sMediaPlayer != null){
                sMediaPlayer.release();
                sMediaPlayer = null;
            }
        }
    });

    sMediaPlayer.setOnPreparedListener(new OnPreparedListener() {               
        @Override
        public void onPrepared(MediaPlayer mp) {
            sMediaPlayer.start();                   
        }
    });
    sMediaPlayer.prepare();

} catch (IllegalArgumentException e) {
    HelpFunctions.showLog("ERROR = " + e);
} catch (IllegalStateException e) {
    HelpFunctions.showLog("ERROR = " + e);
} catch (IOException e) {
    HelpFunctions.showLog("ERROR = " + e);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接