【问题标题】:Android - Playing mp3 from byte[]Android - 从 byte[] 播放 mp3
【发布时间】:2010-12-30 15:09:06
【问题描述】:

我有 byte[] 中的 mp3 文件(从服务下载),我想在我的设备上播放它,类似于播放文件的方式:

MediaPlayer mp = new MediaPlayer();
mp.setDataSource(PATH_TO_FILE);
mp.prepare();
mp.start();

但我似乎找不到办法。我不介意将文件保存到手机然后播放。如何播放文件,或下载然后播放?

【问题讨论】:

标签: android bytearray mp3 audio-player


【解决方案1】:

如果您以 API 23 及更高版本为目标,请创建这样的类

class MyMediaDataSource(val data: ByteArray) : MediaDataSource() {

override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {

    if (position >= data.size) return -1 // -1 indicates EOF 

    val endPosition: Int = (position + size).toInt()
    var size2: Int = size
    if (endPosition > data.size)
        size2 -= endPosition - data.size

    System.arraycopy(data, position.toInt(), buffer, offset, size2)
    return size2
}

override fun getSize(): Long {
    return data.size.toLong()
}

override fun close() {}

}

并像这样使用

val mediaSource = MyMediaDataSource(byteArray)
MediaPlayer().apply {
    setAudioStreamType(AudioManager.STREAM_MUSIC)
    setDataSource(mediaSource)
    setOnCompletionListener { release() }
    prepareAsync()
    setOnPreparedListener { start() }
}

感谢 krishnakumarp 的上述回答和 this article

【讨论】:

  • 这对我有用,虽然我不得不使用 setAudioAttributes(AudioAttributes.Builder().setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED).setLegacyStreamType(AudioManager.STREAM_MUSIC).setUsage(AudioAttributes.USAGE_GAME).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build()) 而不是现在已弃用的 setAudioStreamType(...)
【解决方案2】:

好的,谢谢大家,但我需要从 byte[] 播放 mp3,因为我从 .NET webservice 获得它(不希望在服务器上存储动态生成的 mp3)。

最后 - 播放简单的 mp3 有很多“陷阱”...这里是任何需要它的人的代码:

private MediaPlayer mediaPlayer = new MediaPlayer();
private void playMp3(byte[] mp3SoundByteArray) {
    try {
        // create temp file that will hold byte array
        File tempMp3 = File.createTempFile("kurchina", "mp3", getCacheDir());
        tempMp3.deleteOnExit();
        FileOutputStream fos = new FileOutputStream(tempMp3);
        fos.write(mp3SoundByteArray);
        fos.close();

        // resetting mediaplayer instance to evade problems
        mediaPlayer.reset();

        // In case you run into issues with threading consider new instance like:
        // MediaPlayer mediaPlayer = new MediaPlayer();                     

        // Tried passing path directly, but kept getting 
        // "Prepare failed.: status=0x1"
        // so using file descriptor instead
        FileInputStream fis = new FileInputStream(tempMp3);
        mediaPlayer.setDataSource(fis.getFD());

        mediaPlayer.prepare();
        mediaPlayer.start();
    } catch (IOException ex) {
        String s = ex.toString();
        ex.printStackTrace();
    }
}

编辑:我在 4 年多前写了这个答案 - 显然从那时起很多事情都发生了变化。请参阅 Justin 关于如何重用 MediaPlayer 实例的评论。另外,我不知道 .deleteOnExit() 现在是否适合您 - 请随时提出改进建议,以免临时文件堆积。

【讨论】:

  • 它对我不起作用它提供了一个错误 01-27 15:39:44.686: W/System.err(1022): java.io.IOException: setDataSourceFD failed.: status=0x80000000跨度>
  • 如果你像这样重用MediaPlayer,如果你调用代码太多次,你最终会耗尽资源。您应该使用 MediaPlayer 的单个实例,但在 mediaPlayer.setDataResource(..) 之前调用 mediaPlayer.reset();这样您就不会崩溃,也不会耗尽资源。
  • 有人能帮帮我吗,我已经把歌曲的字节读成了一个字节[]。但是我不会再用上面的方法播放那首歌。下面是打印在logcat:W/KeyCharacterMap(24058):无法打开键字符映射文件 W/KeyCharacterMap(24058):加载键字符映射文件“/system/usr/keychars/sec_keypad.kcm.bin”时出错。 hw.keyboards.0.devname='sec_keypad' W/KeyCharacterMap(24058):使用默认键盘映射:/system/usr/keychars/qwerty.kcm.bin
  • @Justin 感谢分享...幸运的是,自 Android 1.5 以来发生了很多变化;)
  • @kape123 需要详细说明吗?
【解决方案3】:

我找到了一个简单的解决方案,将我的 MP3 文件编码为 Base64(我已经收到从 Restful API 服务编码的数据),然后创建一个 URL 对象。我在 Android 4.1 中测试过。

public void PlayAudio(String base64EncodedString){
        try
        {
            String url = "data:audio/mp3;base64,"+base64EncodedString; 
            MediaPlayer mediaPlayer = new MediaPlayer();
            mediaPlayer.setDataSource(url);
            mediaPlayer.prepare();
            mediaPlayer.start();
        }
        catch(Exception ex){
            System.out.print(ex.getMessage());
        }
    }

【讨论】:

  • 浏览器的数据 URL 的大小为 limitations。 Android 是否有任何大小限制?
  • 太棒了!适用于 Android 10
【解决方案4】:

从 Android MarshMellow(版本代码 23)开始,新的 API 将使这成为可能。

MediaPlayer.setDataSource(android.media.MediaDataSource)

您可以提供 MediaDataSource 的自定义实现并包装一个 byte[]。下面给出了一个基本的实现。

import android.annotation.TargetApi;
import android.media.MediaDataSource;
import android.os.Build;
import java.io.IOException;


@TargetApi(Build.VERSION_CODES.M)
public class ByteArrayMediaDataSource extends MediaDataSource {

    private final byte[] data;

    public ByteArrayMediaDataSource(byte []data) {
        assert data != null;
        this.data = data;
    }
    @Override
    public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
        System.arraycopy(data, (int)position, buffer, offset, size);
        return size;
    }

    @Override
    public long getSize() throws IOException {
        return data.length;
    }

    @Override
    public void close() throws IOException {
        // Nothing to do here
    }
}

【讨论】:

    【解决方案5】:

    代码错误:

     MediaPlayer mp = new MediaPlayer();
     mp.setDataSource(PATH_TO_FILE);
     mp.prepare();
     mp.start();
    

    正确代码:

     MediaPlayer mp = new MediaPlayer();
     mp.setDataSource(PATH_TO_FILE);
     mp.setOnpreparedListener(this);
     mp.prepare();
    
    //Implement OnPreparedListener 
    OnPrepared() {
        mp.start();
     }
    

    参见 API 演示 ..

    【讨论】:

    • 如果你是对的,那么他们似乎需要更新文档 -> developer.android.com/guide/topics/media/index.html#playfile
    • 根据文档,prepare() 是同步的。只有当您调用 prepareAsync() 时,您才需要使用侦听器。但是,如果您的数据源是流,“对于流,您应该调用 prepareAsync(),它会立即返回,而不是阻塞直到缓冲足够的数据。”
    【解决方案6】:

    不确定 bytearrays/bytestreams,但如果你有来自服务的 URL,你可以尝试通过调用将数据源设置为网络 URI

    setDataSource(Context context, Uri uri)
    

    请参阅API docs

    【讨论】:

    • 在处理媒体时始终遵循 API 是明智的,除非您绝对确定自己的方式更好/克服了其实现中的一些明显缺陷。在这种情况下,您应该提交错误报告。
    • 那么如果我们将server/song.mp3 之类的uri 传递给它,播放器会初始化吗?
    • 所以我正在检查这个。我将歌曲位置作为 Uri 传递给播放器。 N它玩得很好。没有文件处理。我在 2.01/emulator 上测试它
    • 有没有人试过:Uri uri = Uri.fromFile(file); Android Docs 甚至给出了可能传入的文件类型的示例,例如:示例:“file:///tmp/android.txt”
    猜你喜欢
    • 1970-01-01
    • 2012-04-18
    • 1970-01-01
    • 2014-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多