android – MediaPlayer setDataSource, better to use path or FileDescriptor?-ThrowExceptions

Exception or error:

Let’s say I have a full path to a file. Which is the better approach to loading that file into a MediaPlayer?

String filePath = "somepath/somefile.mp3";
mediaPlayer.setDataSource(filePath);

OR

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

Does it matter? Simply using the path seems easier but is there a reason to do the extra work to use a FileDescriptor?

How to solve:

Actually, it turns out that there IS a difference in certain situations.

mediaPlayer.setDataSource(String path) will fail when you call mediaPlayer.prepare(), if you are trying to load a file from getApplicationContext().getFilesDir(), depending on how the file was saved. For example, if I write a file using new RandomAccessFile(filePath, "rw"), the file is not actually readable by the mediaplayer if you use mediaPlayer.setDataSource(String path). The prepare() will immediately trigger error(1, -2147483648) from the mediaplayer; essentially a file permission error. SDK 9 introduced file.setReadable (boolean readable, boolean ownerOnly) which would presumably allow you to resolve this issue by setting ownerOnly to false…but that doesn’t help you if you need to support older SDKs.

HOWEVER, mediaPlayer.setDataSource(FileDescriptor fd) does NOT have this problem and mediaplayer will successfully prepare the same exact file without a permission problem.

###

MediaPlayer.java has setDataSource() signatures that accept both a String (path) and an FD.
They both eventually go into native C code. Although one of these may be SLIGHTLY more efficient it will be negligible unless you’re setting your data source more often than once a second.

/**
 * Sets the data source (file-path or http/rtsp URL) to use. Call this after 
 * reset(), or before any other method (including setDataSource()) that might
 * throw IllegalStateException in this class.
 * 
 * @param path the path of the file, or the http/rtsp URL of the stream you want to play
 * @throws IllegalStateException if it is called
 * in an order other than the one specified above
 */
public native void setDataSource(String path) throws IOException, IllegalArgumentException, IllegalStateException;

/**
 * Sets the data source (FileDescriptor) to use. It is the caller's responsibility
 * to close the file descriptor. It is safe to do so as soon as this call returns.
 * Call this after reset(), or before any other method (including setDataSource()) 
 * that might throw IllegalStateException in this class.
 * 
 * @param fd the FileDescriptor for the file you want to play
 * @throws IllegalStateException if it is called
 * in an order other than the one specified above
 */
public void setDataSource(FileDescriptor fd) 
        throws IOException, IllegalArgumentException, IllegalStateException {
    // intentionally less than LONG_MAX
    setDataSource(fd, 0, 0x7ffffffffffffffL);
}

/**
 * Sets the data source (FileDescriptor) to use.  It is the caller's responsibility
 * to close the file descriptor. It is safe to do so as soon as this call returns.
 * Call this after reset(), or before any other method (including setDataSource()) 
 * that might throw IllegalStateException in this class.
 * 
 * @param fd the FileDescriptor for the file you want to play
 * @param offset the offset into the file where the data to be played starts, in bytes
 * @param length the length in bytes of the data to be played
 * @throws IllegalStateException if it is called
 * in an order other than the one specified above
 */
public native void setDataSource(FileDescriptor fd, long offset, long length) 
        throws IOException, IllegalArgumentException, IllegalStateException;

Leave a Reply

Your email address will not be published. Required fields are marked *