I'm writing an application to play audio from remote server. I tried several ways to implement streaming audio, but they all are not good enough for me. That's what I've tried:
Something like:
MediaPlayer player = new MediaPlayer();
player.setDataSource(context, Uri.parse("http://whatever.com/track.mp3"));
player.prepare();
player.start();
(or prepareAsync
, no matter)
But standard MediaPlayer
is quite unstable when playing remote content. It is often falls or stops playback and I can't process this. On the other side, I want to implement media caching. But I haven't found any way to get buffered content from MediaPlayer to save it somewhere on device.
Then there became an idea to download media file by chunks, combine them into one local file and play this file. Downloading the whole file can be slow because of bad connection, so it will be fine to download enough initially piece, then start playback and continue downloading and appending local file. Besides, we get caching functionality.
Sounds like a plan, but it didn't always work. It works perfectly on HTC Sensation XE but didn't on 4.1 tablet playback stopped after finishing this initial piece. Don't know, why is so. I've asked question about this, but received no answers.
I've created two MediaPlayer instances and tried to make them change each other. The logic is following:
The source:
private static final String FILE_NAME="local.mp3";
private static final String URL = ...;
private static final long FILE_SIZE = 7084032;
private static final long PREPARE_NEXT_PLAYER_OFFSET = 1000;
private static final int START_NEXT_OFFSET = 261;
private static final int INIT_PERCENTAGE = 3;
private MediaPlayer mPlayer;
private MediaPlayer mSecondaryPlayer;
private Handler mHandler = new Handler();
public void startDownload() {
mDownloader = new Mp3Downloader(FILE_NAME, URL, getExternalCacheDir());
mDownloader.setDownloadListener(mInitDownloadListener);
mDownloader.startDownload();
}
private Mp3Downloader.DownloadListener mInitDownloadListener = new Mp3Downloader.DownloadListener() {
public void onDownloaded(long bytes) {
int percentage = Math.round(bytes * 100f / FILE_SIZE);
// Start playback when appropriate piece of media downloaded
if (percentage >= INIT_PERCENTAGE) {
mPlayer = new MediaPlayer();
try {
mPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath());
mPlayer.prepare();
mPlayer.start();
mHandler.postDelayed(prepareSecondaryPlayerRunnable, mPlayer.getDuration() - PREPARE_NEXT_PLAYER_OFFSET);
mHandler.postDelayed(startNextPlayerRunnable, mPlayer.getDuration() - START_NEXT_OFFSET);
} catch (IOException e) {
Log.e(e);
}
mDownloader.setDownloadListener(null);
}
}
};
// Starting to prepare secondary MediaPlayer
private Runnable prepareSecondaryPlayerRunnable = new Runnable() {
public void run() {
mSecondaryPlayer = new MediaPlayer();
try {
mSecondaryPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath());
mSecondaryPlayer.prepare();
mSecondaryPlayer.seekTo(mPlayer.getDuration() - START_NEXT_OFFSET);
} catch (IOException e) {
Log.e(e);
}
}
};
// Starting secondary MediaPlayer playback, scheduling creating next MediaPlayer
private Runnable startNextPlayerRunnable = new Runnable() {
public void run() {
mSecondaryPlayer.start();
mHandler.postDelayed(prepareSecondaryPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - PREPARE_NEXT_PLAYER_OFFSET);
mHandler.postDelayed(startNextPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - START_NEXT_OFFSET);
mPlayer.pause();
mPlayer.release();
mPlayer = mSecondaryPlayer;
}
};
Again – sounds, like a plan, but works not perfectly. The moments of switching MediaPlayers are quite hearable. Here I have opposite situation: on 4.1 tablet it's ok, but on HTC Sensation there are evident lags.
I also tried to implement different download techniques. I've implemented download by 10Kb chunks and by MP3 frames. I don't know exactly, but it seems that in case of MP3 frames seekTo and start work better. But it's just a feeling, I don't know explanation.
I saw to this word several times while googling, and found this implementation: https://code.google.com/p/mynpr/source/browse/trunk/mynpr/src/com/webeclubbin/mynpr/StreamingMediaPlayer.java?r=18
It is a solution everybody use?
If yes, it's sad, because it is not working good for me too. And I don't see any fresh ideas in implementation.
How do you guys implement audio streaming in your applications? I don't beleive I am the only person who faced problems like this. There should be some good practices.
In my case I use FFMPEG with OpenSL ES. The disadvantage is complexity. You must be familiar with a lot of things: JNI, OpenSL, FFMPEG. It's also hard to debug(comparing with pure java android app). In your case I suggest you to try low level Media API. The only thing is lack of examples. But there is a unit test which shows how you can handle audio(you need to change InputStream
reference - line 82).
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