I've succeeded uploading an audio file (3gp) into Google-Drive.
Now I want to be able to play the file within the app.
The Google Drive API only allows to get the input stream of the file that's stored there.
All MediaPlayer capabilities of inputs aren't available in my case, which is only InputSteam:
http://developer.android.com/reference/android/media/MediaPlayer.html#setDataSource(java.io.FileDescriptor)
I know I can save the file from the Google-Drive to the cache and play it, but I want to avoid the storage handling, and play the file on the fly.
I tried to search for this issue, and only found that it might be possible using AudioTrack (here). It might also be possible using new Jelly-Bean features (shown here, found from here), but I'm not sure as it's quite low level.
Sadly, using AudioTrack I got wrong sounds being played (noise).
I've also noticed that MediaPlayer has the option to set the dataSource to be MediaDataSource (here) , but not only I'm not sure how to use it, it also requires API 23 and above.
Of course, I tried using a url that is given in Google-Drive, but this is only used for other purposes and isn't being directed to the audio file, so it can't be used using MediaPlayer.
Given an InputStream, is it possible to use AudioTrack or something else, to play an audio 3gp file ?
Is there maybe a support library solution for this?
If your minSdkVersion
is 23 or higher, you can use setDataSource(MediaDataSource)
and supply your own subclass of the abstract
MediaDataSource
class.
For older devices, you should be able to use a pipe created from ParcelFileDescriptor
. You would have a thread that writes data to your end of the pipe, and pass the FileDescriptor
(from getFileDescriptor()
) for the player's end to setDataSource(FileDescriptor)
.
The simplest MediaDataSource implementation example:
import android.media.MediaDataSource;
import android.os.Build;
import android.support.annotation.RequiresApi;
import java.io.IOException;
import java.io.InputStream;
@RequiresApi(api = Build.VERSION_CODES.M)
public class InputStreamMediaDataSource extends MediaDataSource {
private InputStream is;
private long streamLength = -1, lastReadEndPosition;
public InputStreamMediaDataSource(InputStream is, long streamLength) {
this.is = is;
this.streamLength = streamLength;
if (streamLength <= 0){
try {
this.streamLength = is.available(); //Correct value of InputStream#available() method not always supported by InputStream implementation!
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public synchronized void close() throws IOException {
is.close();
}
@Override
public synchronized int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
if (position >= streamLength)
return -1;
if (position + size > streamLength)
size -= (position + size) - streamLength;
if (position < lastReadEndPosition) {
is.close();
lastReadEndPosition = 0;
is = getNewCopyOfInputStreamSomeHow();//new FileInputStream(mediaFile) for example.
}
long skipped = is.skip(position - lastReadEndPosition);
if (skipped == position - lastReadEndPosition) {
int bytesRead = is.read(buffer, offset, size);
lastReadEndPosition = position + bytesRead;
return bytesRead;
} else {
return -1;
}
}
@Override
public synchronized long getSize() throws IOException {
return streamLength;
}
}
To use it with API >= 23 you have to provide streamLength
value and if (when) MediaPlayer goes back - i.e. position < lastReadEndPosition
, you have to know how to create a new copy of InputStream
.
Usage example:
You have to create Activity, initialize MediaPlayer
class (there are a lot of examples of a file playback) and place following code istead of old player.setDataSource("/path/to/media/file.3gp")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
File file = new File("/path/to/media/file.3gp");//It is just an example! If you have real file on the phone memory, you don't need to wrap it to the InputStream to play it in MediaPlayer!
player.setDataSource(new InputStreamMediaDataSource(new FileInputStream(file), file.length()));
} else
player.setDataSource(this, mediaUri);
If your file is an object com.google.api.services.drive.model.File
on Google Drive and com.google.api.services.drive.Drive drive
you can get
InputStream is = drive.getRequestFactory().buildGetRequest(new GenericUrl(file.getDownloadUrl())).execute().getContent();
In the case of Build.VERSION.SDK_INT < Build.VERSION_CODES.M
I had to setup HTTP sever on the local host of an Android device (by means of NanoHTTPd) and transfer a byte stream thru this server to MediaPlayer by uri - player.setDataSource(this, mediaUri)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With