41

I have my mp3 file in byte[] (downloaded from an service) and I would like to play it on my device similar to how you can play files:

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

But I can't seem to find a way to do it. I wouldn't mind saving file to phone and then playing it. How can I play the file, or download then play it?

Maxim Kachurovskiy
  • 2,992
  • 2
  • 21
  • 24
nikib3ro
  • 20,366
  • 24
  • 120
  • 181
  • and if anyone runs into media volume vs ringer volume problems (sound "seems to play" but you do not hear anything) - check this page: http://stackoverflow.com/questions/628659/how-can-i-manage-audio-volumes-sanely-in-my-android-app – nikib3ro Jan 02 '10 at 19:37

6 Answers6

78

OK, thanks to all of you but I needed to play mp3 from byte[] as I get that from .NET webservice (don't wish to store dynamically generated mp3s on server).

In the end - there are number of "gotchas" to play simple mp3... here is code for anyone who needs it:

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();
    }
}

EDIT: I've wrote this answer more than 4 years ago - obviously lots of things have changed since then. See Justin's comment on how to reuse MediaPlayer instance. Also, I don't know if .deleteOnExit() will work for you now - feel free to suggest improvement so that temp files do not pile up.

nikib3ro
  • 20,366
  • 24
  • 120
  • 181
  • 2
    it doesnt work for me it provides an error 01-27 15:39:44.686: W/System.err(1022): java.io.IOException: setDataSourceFD failed.: status=0x80000000 – Hemant Metalia Jan 27 '12 at 10:12
  • 6
    If you re-use MediaPlayer like this, you'll eventually run out of resources if you call the code too many times. You should use a single instance of MediaPlayer, but call mediaPlayer.reset() before mediaPlayer.setDataResource(..); this way you won't crash, and you won't run out of resources. – Justin Apr 26 '12 at 15:45
  • Can somebody Please help me in this i have read the bytes of the song into a byte[].However i am not not to play that song again using the above method.The following is printed in the logcat: W/KeyCharacterMap(24058): Can't open keycharmap file W/KeyCharacterMap(24058): Error loading keycharmap file '/system/usr/keychars/sec_keypad.kcm.bin'. hw.keyboards.0.devname='sec_keypad' W/KeyCharacterMap(24058): Using default keymap: /system/usr/keychars/qwerty.kcm.bin – Joyson Jan 14 '13 at 10:29
  • @Justin thanks for sharing... luckily lots has changed since Android 1.5 ;) – nikib3ro Sep 13 '13 at 20:12
  • 1
    @kape123 care to elaborate ? – Justin Dec 15 '13 at 15:41
  • The temp files are not deleted at all. There are more an more files in my caches. Any idea? – Bagusflyer Sep 18 '14 at 15:22
  • Prefix String is very odd. I tried to translate it but it gives me a lot of inappropriate words as a result. – Marko Katic Nov 26 '20 at 15:21
25

I found an easy solution by encoding my MP3 file as Base64 (I already receive the data encoded from a Restful API service), and then creating a URL object. I tested it in 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());
        }
    }
Max Vargas
  • 396
  • 3
  • 5
  • 1
    Browsers do have size [limitations](https://stackoverflow.com/questions/12637395/what-is-the-size-limit-of-a-base64-dataurl-image/12637435) for data URLs. Does Android impose any size limits? – ccpizza Jun 14 '20 at 18:57
  • Just great! Works in Android 10 – Alexander L. Aug 16 '20 at 01:59
17

Starting Android MarshMellow (Version Code 23), there is new API that will make this possible.

MediaPlayer.setDataSource(android.media.MediaDataSource)

You can provide a custom implementation of MediaDataSource and wrap a byte[]. A basic implementation given below.

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
    }
}
krishnakumarp
  • 8,967
  • 3
  • 49
  • 55
5

Not sure about bytearrays/bytestreams, but if you have a URL from the service, you can try setting the data source to a network URI by calling

setDataSource(Context context, Uri uri)

See the API docs.

Yoni Samlan
  • 37,905
  • 5
  • 60
  • 62
  • It's wise to always follow the API when it comes to dealing with media, unless you're absolutely sure your way is better/overcomes some glaring flaw in their implementation. In which case you should submit a bug report. – Sneakyness Dec 29 '09 at 00:04
  • So will the player initialize if we pass on a uri like http://server/song.mp3 to it? – Bohemian Dec 29 '09 at 06:17
  • So I was checking out this. And I passed the song location as Uri to the player. N it plays it fine. No file handling. I m testing it on 2.01/emulator – Bohemian Dec 29 '09 at 07:32
  • Has anyone tried: Uri uri = Uri.fromFile(file); Android Docs even give an example of the type of File that might get passed in and there example was: Example: "file:///tmp/android.txt" – JamisonMan111 Apr 27 '18 at 03:52
3

If you target API 23 and above, create a class like this

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() {}

}

and use like this

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

credits to krishnakumarp's answer above and to this article

Dan Alboteanu
  • 9,404
  • 1
  • 52
  • 40
  • This worked for me, although I had to use `setAudioAttributes(AudioAttributes.Builder().setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED).setLegacyStreamType(AudioManager.STREAM_MUSIC).setUsage(AudioAttributes.USAGE_GAME).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build())` instead of the now deprecated `setAudioStreamType(...)`. –  Jun 03 '21 at 12:53
0

wrong code:

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

CORRECT CODE:

 MediaPlayer mp = new MediaPlayer();
 mp.setDataSource(PATH_TO_FILE);
 mp.setOnpreparedListener(this);
 mp.prepare();

//Implement OnPreparedListener 
OnPrepared() {
    mp.start();
 }

see API Demos ..

Janusz
  • 187,060
  • 113
  • 301
  • 369
subbarao
  • 35
  • 1
  • 1
    If you are right, then it seems they need to update documentation -> http://developer.android.com/guide/topics/media/index.html#playfile – nikib3ro Dec 29 '09 at 20:31
  • 3
    According to the docs, prepare() is synchronous. Only if you called prepareAsync() would you need to use the listener. However, if your data source is a stream, "For streams, you should call prepareAsync(), which returns immediately, rather than blocking until enough data has been buffered." – Jerry Aug 09 '11 at 19:19