使用Base64解码播放音频。

6
我的数据库里有很多格式为base64的短音频。我想在按钮被点击时播放音频。基本上,我写了这段代码,但它不起作用。(如果可能的话,最好不要将文件写入存储,因为这个过程会有延迟)
playButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        try{
            String url = "data:audio/mp3;base64,"+base64FormattedString;
            MediaPlayer mediaPlayer = new MediaPlayer();
            mediaPlayer.setDataSource(url);
            mediaPlayer.prepare();
            mediaPlayer.start();
        }
        catch(Exception e){
            e.printStackTrace();
        }

    }
});

以下是堆栈跟踪信息:https://gist.github.com/AliAtes/aa46261aba3d755fbbd1eba300356a5f


什么是"data:audio/mp3;base64,"? - pskink
这是音频/ MP3 文件的前缀,用于 base64 字符串。base64 格式化字符串通常没有它。 - ATES
我的意思是从 MediaPlayer 的角度来看,它不是一个有效的数据源 - "path String: 文件路径,或您要播放的流的 http/rtsp URL" - pskink
文件路径或者你想播放的流的http/rtsp URL。 - pskink
如果您事先创建一个包含音频的文件,例如当用户第一次打开您的应用程序时,而不是在播放声音之前的瞬间创建该文件,则不会有延迟。 - nandsito
显示剩余4条评论
2个回答

7

根据您的API限制,您可以使用AudioTrack。此外,这很容易。使用AudioTrack,您可以自API 3以来播放byte[]音频数据。

您只需使用音频样本的特定参数初始化AudioTrack对象:

AudioTrack audioTrack = new AudioTrack(...);

并调用:

audioTrack.play();

然后,您需要解码您的Base64数据:
byte[] data = Base64.decode(yourBase64AudioSample, Base64.DEFAULT);

将其放入 audioTrack 中以进行播放:
int iRes = audioTrack.write(data, 0, data.length);

并且,TADAAA :)

... 不要忘记在玩耍之后释放:

audioTrack.release();

你的代码完美无缺,它能够正常运行,但是我没有正确地将我的mp3适配到“AudioTrack();”中。我需要最后一点帮助 :)(这是mp3的所有信息:https://gist.github.com/AliAtes/be72817aa0ebea45acd6ef6840c279b1) - ATES
明天我会看一下。在播放前,你可能需要解码你的MP3文件! - N0un
经过一些研究,发现AudioTrack仅支持PCM(未压缩音频格式),而mp3是一种压缩音频格式。解决方案是在播放之前对其进行解码。使用此链接,您可以使用JLayer获得示例:http://mindtherobot.com/blog/624/android-audio-play-an-mp3-file-on-an-audiotrack/ - N0un
三个月过去了,您是否仍然认为这个答案是错误的? - N0un

4
你可以通过扩展MediaDataSource并将其作为参数调用setDataSource来实现这一点。
也许可以尝试类似于以下内容:
// first decode the data to bytes
byte[] data = Base64.decode(base64FormattedString, Base64.DEFAULT);
mediaPlayer.setDataSource(new MediaDataSource() {
    @Override
    public long getSize() {
        return data.length;
    }

    @Override
    public int readAt(long position, byte[] buffer, int offset, int size) {
        int length = getSize();
        if (position >= length) return -1; // EOF
        if (position + size > length) // requested more than available
            size = length - position; // set size to maximum size possible
                                      // at given position

        System.arraycopy(data, (int) position, buffer, offset, size);
        return size;
    }

    @Override
    public synchronized void close() throws IOException {

    }
});

想要了解有关实现MediaDataSource的更多信息,您可以查看这篇文章

编辑:此方法需要API级别23。在23之前,似乎几乎不可能从byte[](或除URL或文件以外的任何内容)提供播放数据。我找到的唯一东西是这个答案,它提供了与问题中显示的相同的代码。

编辑2:N0un在他的评论和回答中指出,自API级别3以来,可以使用AudioTrack代替MediaPlayer实现此功能。


感谢您的代码,但是MediaDataSource的api级别是23。我的项目api是19,所以没有使用MediaDataSource的计划吗? - ATES
1
我进行了更多的研究,似乎不可能在没有MediaDataSource的情况下提供byte[]格式的数据。我找到的唯一答案是这个回答,但由于它提供的代码与您使用的相同,显然对您无效。如果您想坚持使用API级别19,可以采用创建文件的方法(例如在应用程序首次启动时创建文件,并检查是否有助于减少延迟)。另一种选择是将API级别切换到23。 - Leon
是的,"pre-23 API"是可以实现的。实际上,从API 3开始就可以实现了 :) 请看我的回答:这是我为我的应用程序使用的方法(它使用此方法播放通过网络接收的byte[]数据,因此我确认它有效)。 - N0un

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