MediaPlayer.setDataSource(String) not working with local files
Asked Answered
C

7

38

I am able to play a local mp3 if I use the static method MediaPlayer.create(context, id) but it's not working if I use the non-static method MediaPlayer.setDataSource(String). What's happening is that I am getting a synchronous exception when I call MediaPlayer.prepare():

prepare exceptionjava.io.IOException: Prepare failed.: status=0x1

Here is my code (omitted logging):

String filename = "android.resource://" + this.getPackageName() + "/raw/test0";

mp = new MediaPlayer();
try { mp.setDataSource(filename); } catch (Exception e) {}
try { mp.prepare(); } catch (Exception e) {}
mp.start();

Note that I am not getting an errors about file not found or anything. The full name of the file is test0.mp3 and I place it in the /res/raw/ directory in Eclipse.

I assume that I am setting the path incorrectly but all the examples I find online use the FileDescriptor version of setDataPath instead of the String version of setDataPath.

EDIT: I am also able to play a local mp3 if I use the method MediaPlayer.setDataSource(FileDescriptor) and place the files in the /assets/ directory in Eclipse.

EDIT #2: I accepted the answer that this is not possible, but then realized that the library I am using (openFrameworks) actually does use the String method to load a file. See here:

https://github.com/openframeworks/openFrameworks/blob/master/addons/ofxAndroid/ofAndroidLib/src/cc/openframeworks/OFAndroidSoundPlayer.java

Codex answered 12/10, 2015 at 16:55 Comment(4)
I don't know if this will help , so take a look at this post: [play mp3 file ](#5467382)Loyola
@LamaSonmez That code is using the MediaPlayer.create(context, id) method. I am trying to get the version that uses a string file path to work: developer.android.com/reference/android/media/…Codex
set a countdowntimer and call setDataSource may be after 5 seconds not sure may be the resource has not yet loaded so let it cool down and then try to access the resource ... also u can add onerrorlistener and give it around 4 tries to retry playing ... just a shotPewter
Perhaps this answer will help folks looking here: https://mcmap.net/q/391323/-playing-sound-on-the-alarm-channel-on-androidLlovera
A
46

Alternative Solution #1: Using Resources.getIdentifier()

Why not use getResources().getIdentifier() to get id of the resource and use the static MediaPlayer.create() as usual?

public int getIdentifier (String name, String defType, String defPackage)

getIdentifier() takes your resource name (test0), resource type(raw), your package name and returns the actual resource id.

 MediaPlayer mp;
 //String filename = "android.resource://" + this.getPackageName() + "/raw/test0";
 mp=MediaPlayer.create(getApplicationContext(), getResources().getIdentifier("test0","raw",getPackageName()));
 mp.start();

I've tested this code and it works.


Update #1:

Alternative Solution #2: Using Uri.parse()

I've tested this code as well and it works too. Pass your resource path as URI to setDataSource(). I just made that change to your code to get it work.

String filename = "android.resource://" + this.getPackageName() + "/raw/test0";

mp = new MediaPlayer();
try { mp.setDataSource(this,Uri.parse(filename)); } catch (Exception e) {}
try { mp.prepare(); } catch (Exception e) {}
mp.start();


Update #2: Answer is NO

About setDataSource(String) call

After seeing your comment, it looks like you exactly want setDataSource(string) to be used for your purpose. I don't understand why. But, what I assume is, for some reason you are trying to avoid using "context". If that is not the case then the above two solutions should work perfectly for you or if you are trying to avoid context, I'm afraid that is not possible with the function with signature setDataSource(String) call. The reason is as below,

MediaPlayer setDataSource() function has these below options out of which you are only interested in setDataSource(String),

setDataSource Functions

setDataSource(String) internally calls setDataSource(String path, String[] keys, String[] values) function. If you can check its source,

public void setDataSource(String path)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
        setDataSource(path, null, null);
    }

and if you check setDataSource(String path, String[] keys, String[] values) code, you will see the below condition filtering the path based on its scheme, particularly if it is "file" scheme it calls setDataSource(FileDescriptor) or if scheme is non "file", it calls native JNI media function.

{
        final Uri uri = Uri.parse(path);
        final String scheme = uri.getScheme();
        if ("file".equals(scheme)) {
            path = uri.getPath();
        } else if (scheme != null) {
            // handle non-file sources
            nativeSetDataSource(
                MediaHTTPService.createHttpServiceBinderIfNecessary(path),
                path,
                keys,
                values);
            return;
        }
        final File file = new File(path);
        if (file.exists()) {
            FileInputStream is = new FileInputStream(file);
            FileDescriptor fd = is.getFD();
            setDataSource(fd);
            is.close();
        } else {
            throw new IOException("setDataSource failed.");
        }
}

In the above code, your resource file URI scheme will not be null (android.resource://) and setDataSource(String) will try to use native JNI function nativeSetDataSource() thinking that your path is http/https/rtsp and obviously that call will fail as well without throwing any exception. Thats why your call to setDataSource(String) escapes without an exception and gets to prepare() call with the following exception.

Prepare failed.: status=0x1

So setDataSource(String) override cannot handle your resource file. You need to choose another override for that.

On the other side, check setDataSource(Context context, Uri uri, Map headers) which is used by setDataSource(Context context, Uri uri), it uses AssetFileDescriptor, ContentResolver from your context and openAssetFileDescriptor to open the URI which gets success as openAssetFileDescriptor() can open your resource file and finally the resultant fd is used to call setDataSource(FileDescriptor) override.

    AssetFileDescriptor fd = null;
    try {
        ContentResolver resolver = context.getContentResolver();
        fd = resolver.openAssetFileDescriptor(uri, "r");
        //  :
        //  :
        //  :
        if (fd.getDeclaredLength() < 0) {
                setDataSource(fd.getFileDescriptor());
            } else {
                setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength());
            }

To conclude, you cannot use setDataSource(String) override as is to use your resource mp3 file. Instead, if you want use string to play your resource file you can use either MediaPlayer.create() static function with getIdentifier() as given above or setDataSource(context,uri) as given in Update#1.

Refer to the complete source code for more understanding here: Android MediaPlayer


Update #3:

openFrameworks setDataSource(String):

As I have mentioned in the comments below, openFrameworks uses android MediaPlayer code asis. If you can refer to Line no: 4,

import android.media.MediaPlayer;

and Line no: 26, 27, 28 and 218

        player = new MediaPlayer();       //26
        player.setDataSource(fileName);   //27
        player.prepare();                 //28

        private MediaPlayer player;       //218

So, if you try to pass ardroid.resource//+ this.getPackageName() + "raw/test0" to setDataSource() using openFrameworks, you will still get the same exception as I explained in Update#2. Having said that, I just tried my luck searching Google to double sure what I am saying and found this openFrameworks forum link where one of the openFrameworks core developer arturo says,

don't know exactly how the mediaPlayer works but everything in res/raw or bin/data gets copied to /sdcard/cc.openframeworks.packagename

Based on that comment, you may try using the copied path in setDataSource(). Using resource file on setDataSource(String) of MediaPlayer is not possible as it cannot accept resource file path. Please note that, I said "resource file path" starts with the scheme android.resource// which is actually a jar location (within your apk), not a physical location. Local file will work with setDataSource(String) which starts with the scheme file://.

To make you clearly understand what you are trying to do with a resource file, try executing this below code and see the result in logcat,

    try{
      Log.d("RESURI", this.getClass().getClassLoader().getResource("res/raw/test0").toURI().toString());
    } 
    catch(Exception e) {

    }

You'll get the result as,

jar:file:/data/app/<packagename>/<apkname>.apk!/res/raw/test0

that is to show you that the resource file you are trying to access is not actually a file in physical path but a jar location (within apk) which you cannot access using setDataSource(String) method. (Try using 7zip to extract your apk file and you will see the res/raw/test0 in it).

Hope that helps.

PS: I know its bit lengthy answer, but I hope this explains it in detail. Leaving the alternative solutions in the top if that can help others.

Alamode answered 21/10, 2015 at 18:29 Comment(10)
I must have worded my question poorly -- I am asking about MediaPlayer.setDataSource(String) specifically. I am not interested in other versions of the method.Codex
@racarate, do you mean the solution in update#1 is also not working for you. That uses setDataSource(context,uri). As I mentioned in the answer, I just changed your code from mp.setDataSource(filename) to mp.setDataSource(this, Uri.parse(filename)) and it works perfectly with your resource file.Alamode
Please check the updated answer (Update #2). setDataSource(String) override cannot be used as is. You can use setDataSource(context,uri) instead with your string resource path or getIdentifier() with MediaPlayer.create()Alamode
Alright, so the answer is no. Thanks for the details, maybe this will be more helpful if you get rid of the first two parts of the answer and edit #2 becomes the official "this is not possible" answer. I have to say, the official documentation was not clear about this at all.Codex
I just realized that openFrameworks seems to use this method to load a local file. I can't un-award the bounty but I did mark this as unanswered still.Codex
I am sorry to say either you are confused or confusing me by saying openFrameworks loading the file using the same funciton. I agree. Yes, it is loading the file using the setDataSource(String) function. Is that your question? your question is to load a file in resource using setDataSource(String) function. isn't it? Please check the openFrameworks source again - LineNo: 4 where it imports android.media.MediaPlayer; and LineNo: 27 where it uses setDataSource(filename) to load a file. I still can say with the explanation in update#2 that it will fail if it loads file in resource.Alamode
Sorry, what I am trying to say is that the code linked in openFrameworks seems to work with a local file from the SD card. I interpreted your answer to mean that setDataSource(String) will only ever work with http/https/rtsp protocols.Codex
Well, I have mentioned that setDatasource(String) filters it using the scheme "file". So your local file will get through it to use setDatasource(FD) and http/https/rtsp will goto native function call. Updated the answer with more details. Lengthier but I hope it gives your everything you want to know about setDatasource() :)Alamode
Alright, I see the distinction now. Thanks for your reply!Codex
+1 for good explanation. I have a question on RecyclerView + MediaPlayer. How will it work if I have to replace "test0" here to <ArrayList> mp=MediaPlayer.create(getApplicationContext(), getResources().getIdentifier("test0","raw",getPackageName())); #51112233Marysa
G
4

Like the android documentation said

Arbitrary files to save in their raw form. To open these resources with a raw InputStream, call Resources.openRawResource() with the resource ID, which is R.raw.filename.

However, if you need access to original file names and file hierarchy, you might consider saving some resources in the assets/ directory (instead of res/raw/). Files in assets/ are not given a resource ID, so you can read them only using AssetManager.

So you need to use a InputStream to read the audio file before set it to the media player.

I suggest you to put the audio file in the assets folder like you said you played

:)

Gamy answered 12/10, 2015 at 17:42 Comment(2)
I'm confused as to how InputStream fits in? I am trying to get the version that takes a filepath string to work.Codex
maybe this answer could help you :) #15099157Gamy
J
4

Below code working for me i think this code will help you

    player = MediaPlayer.create(this,R.raw.test0);
    player.setLooping(true); // Set looping

    player.setVolume(100,100);
    player.start();

@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
player.stop();
player.release();
}
Jalousie answered 21/10, 2015 at 10:21 Comment(3)
This example is using the MediaPlayer.create(context, id) method, my question is about the MediaPlayer.setDataSource(String) method.Codex
#18978309 see you can not use MadiaPlayer.setDataSource(String) for local folder files.Jalousie
@RaviVGHL, the link on your comment doesn't say setDataSource(String) cannot be used for local files. Its says MediaPlayer.create() internally use setDataSource() and Prepare() methods. Thou your answer is a working code, but your comment's statement is not correct and the question racarate asking is not about using R.raw.test0 either. racarate wants to use string as parameter (not id) to play sound for his purpose.Alamode
D
2

When dealing with a raw resource, you should rely on the following constructor:

public static MediaPlayer create (Context context, int resid)

Convenience method to create a MediaPlayer for a given resource id. On success, prepare() will already have been called and must not be called again.

The code looks like

mediaPlayer = MediaPlayer.create(this, R.raw.test0);
mediaPlayer.start();

Don't forget to call mediaPlayer.release() when you're done with it.

(source)

Ditheism answered 15/10, 2015 at 6:36 Comment(1)
Yes, this method does work for me but I am trying to get the version that takes a string working: developer.android.com/reference/android/media/…Codex
K
1

From here,

When path refers to a local file, the file may actually be opened by a process other than the calling application. This implies that the pathname should be an absolute path (as any other process runs with unspecified current working directory), and that the pathname should reference a world-readable file. As an alternative, the application could first open the file for reading, and then use the file descriptor form setDataSource(FileDescriptor).

Try,

String filePath = "path/file.mp3";
File file = new File(filePath);
FileInputStream inputStream = new FileInputStream(file);
mediaPlayer.setDataSource(inputStream.getFD());
inputStream.close();

Hope it Helps!

Krefetz answered 21/10, 2015 at 18:15 Comment(1)
Yes, this method works for me but I am asking specifically about the method MediaPlayer.setDataSource(String).Codex
N
0

You have to use setDataSource(@NonNull Context context, @NonNull Uri uri) instead of setDataSource(String path)

Since you want to resolve resource internal application resources, you have to provide Context which would be used to resolve this stuff

Also if you will take a look inside these two methods you will notice they use different strategies to find resulting resource.

Nealah answered 27/2, 2018 at 10:35 Comment(0)
F
0

Try this:

  if(mediaPlayer != null ){
    
                        if (mediaPlayer.isPlaying()){mediaPlayer.stop();}
    
                        mediaPlayer.reset(); //this line is important!
    
                        String path = File.separator + "sdcard" + File.separator + utilsFields.repoDirRoot + File.separator + media.mp4;
    
    
                        try {
                            mediaPlayer.setDataSource(path);
                        }catch (Exception ignored){}
    
    
                        try {
                            mediaPlayer.prepare();
                        }catch (Exception ignored){}
    
                        mediaPlayer.start();
    
                    }
Fireproof answered 23/10, 2022 at 13:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.