Problem with MediaPlayer onPrepared() not being called after stop() and prepareAsync() - android

I start by loading a Media player into a composition class:
public class MediaPlayerWURI {
private final MediaPlayer mediaPlayer;
final Uri uri;
final ActivityMain activityMain;
boolean isPrepared = true;
MediaPlayerWURI(ActivityMain activityMain, MediaPlayer mediaPlayer, Uri uri){
this.activityMain = activityMain;
this.mediaPlayer = mediaPlayer;
this.uri= uri;
mediaPlayer.setOnPreparedListener(null);
mediaPlayer.setOnErrorListener(null);
mediaPlayer.setOnPreparedListener(new MOnPreparedListener(this));
mediaPlayer.setOnErrorListener(new MOnErrorListener());
}
public void prepareAsync(){
isPrepared = false;
mediaPlayer.prepareAsync();
}
public void start(){
mediaPlayer.start();
}
public void stop(){
isPrepared = false;
mediaPlayer.stop();
}
class MOnPreparedListener implements MediaPlayer.OnPreparedListener{
final MediaPlayerWURI mediaPlayerWURI;
public MOnPreparedListener(MediaPlayerWURI mediaPlayerWURI){
this.mediaPlayerWURI = mediaPlayerWURI;
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayerWURI.isPrepared = true;
}
}
class MOnErrorListener implements MediaPlayer.OnErrorListener {
public MOnErrorListener(){
}
#Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
activityMain.releaseMediaPlayers();
return false;
}
}
}
The media player passed in is created with MediaPlayer.create(getApplicationContext()) and is started successfully.
The following code do not trigger onPrepared() and gets stuck in a loop.
mediaPlayerWURI.stop();
mediaPlayerWURI.prepareAsync();
while (!mediaPlayerWURI.isPrepared) { }
mediaPlayerWURI.start();
I have tried prepareAsync() on another thread:
executorService.submit(new Runnable() {
#Override
public void run() {
mediaPlayerWURI.prepareAsync();
}
});
My guess is it is a threading issue, but I am not sure how to handle this, or if it even is a threading issue. My understanding is that the MediaPlayer is preparing in another thread and that the loop shouldn't prevent it from calling on prepared. I am not sure what thread onPrepare() is ran on, but from the above, I think it means the main thread is supposed to run onPrepare() and is waiting for the loop to end.
Also, I am getting weird behavior where onPrepared() is being called after the construction of the MediaPlayer. Is that normal? My assumption is that onPrepared() is called when setOnPrepared() is called on a prepared MediaPlayer. This means the listener is attached.

The problem was that while waiting for the MediaPlayer to be prepared while (!mediaPlayerWURI.isPrepared) { }, I was hogging the UI thread, which is the same thread that onPrepared() uses.
To fix this, I had to stop hogging the UI thread. I added a boolean to my MediaPlayerWURI wrapper class that indicates to play the MediaPlayer on prepared.
private final MediaPlayer mediaPlayer;
volatile boolean isPrepared;
volatile boolean shouldPlay;
synchronized public void shouldStart(boolean shouldPlay){
if(shouldPlay && isPrepared){
mediaPlayer.start();
} else {
this.shouldPlay = shouldPlay;
}
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
synchronized (mediaPlayerWURI) {
mediaPlayerWURI.isPrepared = true;
if (shouldPlay) {
mediaPlayer.start();
shouldPlay = false;
}
}
}

Related

MediaPlayer and SurfaceView over multiple activities

I have a media player on one activity (called player) and I want to be able to support continuous video playback from when the player is closed into a miniature view on the parent activity.
I am able to do this just fine when it is audio only, the problem lies when I attach a SurfaceView via mediaPlayer.setDisplay();
I can attach the SurfaceView just fine initially but the problems start when I close the Player activity. If I make no changes, the mediaPlayer gets thrown into an error state somehow with the usual unhelpful errors (1, -19) etc.
I have tried using setDisplay(null) when the Player SurfaceView is destroyed which appears to work. But for some reason it resets the video stream. I've tried overriding seekTo() in order to figure out what is happening but seekTo() is not being called. I've also put logging statements everywhere I can think of but nothing is being triggered.
Why would setDisplay(null) cause my video stream to restart?
Here is my current MediaPlayer code (some of the weird stuff is from me trying to solve the issue (like isReallyPlaying()):
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private ScheduledFuture beeperhandle;
private boolean isPaused = false;
private BaseMedia currentMedia;
private PlaybackService.RepeatStatus repeatStatus = PlaybackService.RepeatStatus.REPEAT_NONE;
public void startMedia(BaseMedia model, Integer position) {
Timber.d("Starting media");
startBeeper();
isPaused = false;
lastBeep = -1;
currentMedia = model;
if (position != null) {
seekTo(position);
}
super.start();
}
public BaseMedia getCurrentMedia() {
return currentMedia;
}
#Override
public void start() throws IllegalStateException {
Timber.e("Invalid start called, should request startSong or startVideo");
}
private int lastBeep = -1;
// Because isPlaying is returning false and canceling the beeper. Probably has something to do with the surfaceview being destroyed
private boolean isStillPlaying() {
if (lastBeep != getCurrentPosition()) {
lastBeep = getCurrentPosition();
return true;
}
return false;
}
private final Runnable seekBarCheck = new Runnable() {
public void run() {
if (isStillPlaying() && !beeperhandle.isCancelled()) {
EventBus.getDefault().post(new MusicStatusTimeEvent(
currentMedia, true, GevaldMediaPlayer.this));
} else {
Timber.d("Canceling Beeper, !isPlaying");
beeperhandle.cancel(true);
}
}
};
private void startBeeper() {
Timber.d("Starting Beeper");
beeperhandle = scheduler.scheduleAtFixedRate(seekBarCheck, 100, 100, TimeUnit.MILLISECONDS);
}
#Override
public void seekTo(final int msec) throws IllegalStateException {
Timber.d("Seeking to " + msec);
if (beeperhandle != null) {
Timber.d("Canceling beeper in prep for seek");
beeperhandle.cancel(true);
}
setOnSeekCompleteListener(new OnSeekCompleteListener() {
#Override
public void onSeekComplete(MediaPlayer mp) {
Timber.d("Seek complete to: " + msec);
startBeeper();
}
});
super.seekTo(msec);
}
#Override
public void stop() throws IllegalStateException {
super.stop();
Timber.d("Stopping media");
doStop();
}
private void doStop() {
if (beeperhandle != null) {
Timber.d("Canceling beeper, doStop");
beeperhandle.cancel(true);
}
isPaused = false;
}
#Override
public void pause() throws IllegalStateException {
Timber.d("Pause requested");
if (beeperhandle != null) {
Timber.d("Canceling beeper, pause");
beeperhandle.cancel(true);
}
doStop();
EventBus.getDefault().post(new MusicStatusStoppedEvent(this));
super.pause();
}
public boolean isPaused() {
return isPaused;
}
Figured it out. Apparently closing an activity causes an audio loss with a value of AudioManager.AUDIOFOCUS_LOSS.
Since I was being a good Android citizen, that was set to release the media player. But audio would then be regained which would cause my media player to get reset and hence start from the beginning.
It just happened to line up that this occurred right around the setDisplay() method.

How to start audio streaming from particular position in seekbar android

I am working on audio streaming android application in this I get song url from server which i am playing using audio streaming application in this when I start playing it starts and it shows buffering also but my problem is when i move seekbar to particular position which is not buffered seekbar handle is moving back.actually I need to start playing song from that position.
see my code and please tell me how to handle this scenario
public class StreamingMp3Player extends Activity implements OnClickListener, OnTouchListener, OnCompletionListener, OnBufferingUpdateListener{
private ImageButton buttonPlayPause;
private SeekBar seekBarProgress;
public EditText editTextSongURL;
private MediaPlayer mediaPlayer;
private int mediaFileLengthInMilliseconds; // this value contains the song duration in milliseconds. Look at getDuration() method in MediaPlayer class
private final Handler handler = new Handler();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initView();
}
/** This method initialise all the views in project*/
private void initView() {
buttonPlayPause = (ImageButton)findViewById(R.id.ButtonTestPlayPause);
buttonPlayPause.setOnClickListener(this);
seekBarProgress = (SeekBar)findViewById(R.id.SeekBarTestPlay);
seekBarProgress.setMax(99); // It means 100% .0-99
seekBarProgress.setOnTouchListener(this);
editTextSongURL = (EditText)findViewById(R.id.EditTextSongURL);
editTextSongURL.setText("http://instilltech.netii.net/01LECHIPODAMA.mp3");
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnCompletionListener(this);
}
/** Method which updates the SeekBar primary progress by current song playing position*/
private void primarySeekBarProgressUpdater() {
seekBarProgress.setProgress((int)(((float)mediaPlayer.getCurrentPosition()/mediaFileLengthInMilliseconds)*100)); // This math construction give a percentage of "was playing"/"song length"
if (mediaPlayer.isPlaying()) {
Runnable notification = new Runnable() {
public void run() {
primarySeekBarProgressUpdater();
}
};
handler.postDelayed(notification,1000);
}
}
#Override
public void onClick(View v) {
if(v.getId() == R.id.ButtonTestPlayPause){
/** ImageButton onClick event handler. Method which start/pause mediaplayer playing */
try {
mediaPlayer.setDataSource(editTextSongURL.getText().toString()); // setup song from http://www.hrupin.com/wp-content/uploads/mp3/testsong_20_sec.mp3 URL to mediaplayer data source
mediaPlayer.prepare(); // you must call this method after setup the datasource in setDataSource method. After calling prepare() the instance of MediaPlayer starts load data from URL to internal buffer.
} catch (Exception e) {
e.printStackTrace();
}
mediaFileLengthInMilliseconds = mediaPlayer.getDuration(); // gets the song length in milliseconds from URL
if(!mediaPlayer.isPlaying()){
mediaPlayer.start();
buttonPlayPause.setImageResource(R.drawable.button_pause);
}else {
mediaPlayer.pause();
buttonPlayPause.setImageResource(R.drawable.button_play);
}
primarySeekBarProgressUpdater();
}
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if(v.getId() == R.id.SeekBarTestPlay){
/** Seekbar onTouch event handler. Method which seeks MediaPlayer to seekBar primary progress position*/
if(mediaPlayer.isPlaying()){
SeekBar sb = (SeekBar)v;
int playPositionInMillisecconds = (mediaFileLengthInMilliseconds / 100) * sb.getProgress();
mediaPlayer.seekTo(playPositionInMillisecconds);
}
}
return false;
}
#Override
public void onCompletion(MediaPlayer mp) {
/** MediaPlayer onCompletion event handler. Method which calls then song playing is complete*/
buttonPlayPause.setImageResource(R.drawable.button_play);
}
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
/** Method which updates the SeekBar secondary progress by current song loading from URL position*/
seekBarProgress.setSecondaryProgress(percent);
}
}

Android MediaPlayer Streaming Error: 100: MEDIA_ERROR_SERVER_DIED

I've developed an app which takes an advantage of the native Android's MediaPlayer. The source code of my class making use of Media Player is below.
The problem is that only on some devices after some miliseconds of playback (I hear only voice, the screen remains black) I keep getting error(100,0) which according to the documentation says
public static final int MEDIA_ERROR_SERVER_DIED
Media server died. In this case, the application must release the MediaPlayer object and instantiate a new one.
On forums I've found out that I need to reset the player every time I get it... but I get it after just a short moment and then it dies forever. I cannot reset the player every second since playback is useless. I cannot get why some devices have this problem and others not. The one that I know has Android OS > 4.0.
Of course, first init() and then showVideo() are getting called. The last onError with code 100 is then called. What's a potential solution to make the streams run continuously and not break?
public class NativePlayer extends Player implements OnBufferingUpdateListener,
OnCompletionListener, OnErrorListener, OnInfoListener {
private VideoView videoview;
private PlayerListener listener;
private MainActivity context;
private final Logger logger = LoggerFactory.getLogger(NativePlayer.class);
#Override
public void init(MainActivity activity) {
this.videoview = (VideoView) activity.findViewById(R.id.video);
context = activity;
}
#Override
public void showVideo(final String url, final PlayerListener _listener) {
listener = _listener;
videoview.setVisibility(View.VISIBLE);
try {
Uri video = Uri.parse(url);
videoview.setVideoURI(video);
} catch (Exception e) {
logger.error("Error playing video", e);
listener.onVideoError();
return;
}
videoview.setOnCompletionListener(this);
videoview.setOnErrorListener(this);
videoview.requestFocus();
videoview.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
videoview.start();
if (listener != null) {
listener.onVideoStarted();
}
}
});
}
#Override
public void onStop() {
stop();
}
private void stop() {
if (videoview == null) {
return;
}
if (videoview.isPlaying()) {
videoview.stopPlayback();
}
}
#Override
public void onDestroy() {
}
#Override
public void onCompletion(MediaPlayer mp) {
stop();
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
if (listener != null) {
listener.onVideoError();
}
return false;
}
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (listener != null) {
listener.onInfo(what, extra);
}
return false;
}
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
if (listener != null) {
listener.onBufferingUpdate(percent);
}
}
}
I had same problem (error 100, mediaplayer died, etc.).
I resolve it by using .stopPlayback(), and starting stream again.
Below is my part of code:
private void startWatchVideo(final string video_link) {
videoViewVA.setMediaController(new MediaController(this));
videoViewVA.setVideoURI(Uri.parse(video_link));
videoViewVA.requestFocus();
videoViewVA.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer media) {
media.start();
}
});
videoViewVA.setOnErrorListener(new OnErrorListener() {
#Override
public boolean onError(MediaPlayer media, int what, int extra) {
if (what == 100)
{
videoViewVA.stopPlayback();
startWatchVideo(video_link);
}
return true;
}
});
}
On practice it looks like video is just slows down

Can I set onCompletionListener() to an object that is an instance of the class that extends MediaPlayer?

I have implemented a class that extends MediaPlayer.
public class AudioPlayer extends MediaPlayer {
private String fileName = null;
FileInputStream fileInputStream = null;
private MediaPlayer mediaPlayer = null;
// Constructor
public AudioPlayer(Context context)
{
// Initialization
}
public void onPlay(boolean start)
{
if(start) {
startPlaying(this.fileName);
}else {
stopPlaying(this.fileName);
}
}
private void startPlaying(String fileName) {
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(fileInputStream.getFD());
mediaPlayer.prepare();
mediaPlayer.start();
// mediaPlayer.setOnCompletionListener(new CompletionListener());
}
class CompletionListener implements MediaPlayer.OnCompletionListener {
#Override
public void onCompletion(MediaPlayer mp) {
// Do stuff
}
}
When I setOnCompletionListener on MediaPlayer object inside my custom AudioPlayer class it works just fine. However I would like to set this listener on the object created from this class since it's used in multiple activities. Different actions should be taken in onCompletion() method therefore implementing onCompletionListener inside my custom class is pointless.
When I create an instance of my custom class in each activity where I want to use it
AudioPlayer audioPlayer = new AudioPlayer(context);
and set on it onCompletionListener:
audioPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
}
});
it’s never being called. Is there a way to call onCompletionListener on my custom object?
The solution is to write a method that returns MediaPlayer object and then call setOnCompletionListener() on this object:
public MediaPlayer getActiveMediaPlayer()
{
if ( mediaPlayer != null )
return this.mediaPlayer;
else
return null;
}
and then in the activity:
audioRecorder.getActiveMediaPlayer().setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Log.d("MediaPlayer", "test");
}
});

delayed audio in android

i'm trying to play audio when a button is clicked but there's some delay between when i press the button and when the audio is actually played. I get this error: AudioFlinger(17396): write blocked for 162 msecs, 3 delayed writes, thread 0x15440 This is what i got so far:
ImageButton i = (ImageButton)findViewById(R.id.button);
i.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
s = new AudioPlayer(getApplicationContext());
s.playSound(R.raw.conga1);
}
return true;
}
});
AudioPlayer class
public class AudioPlayer {
private MediaPlayer mediaPlayer;
private final OnCompletionListener mediaPlayerListener = new MediaPlayerListener();
private Context context = null;
public AudioPlayer(Context context)
{
this.context = context;
init();
}
private void init() {
if (mediaPlayer == null) {
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
int streamVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnCompletionListener(mediaPlayerListener);
mediaPlayer.setVolume(streamVolume, streamVolume);
}
}
private void setSound(int id) {
if (mediaPlayer!=null) {
mediaPlayer.reset();
AssetFileDescriptor file = context.getResources().openRawResourceFd(id);
try {
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
file.close();
mediaPlayer.prepare();
} catch (IOException e) {
mediaPlayer = null;
}
}
}
public void playSound(int id) {
if (mediaPlayer!=null) {
setSound(id);
mediaPlayer.start();
}
}
private static class MediaPlayerListener implements OnCompletionListener {
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.seekTo(0);
}
}
}
Any clues?
Thanks in advance.
EDITS:
So i added a singleton and it helped but there's still delay. This is how it looks like now:
AudioPlayer:
public static synchronized AudioPlayer getSingletonObject(Context context, Uri pathToFile) {
if (audioPlayer == null) {
audioPlayer = new AudioPlayer(context, pathToFile);
}
return audioPlayer;
}
public void setSound(String pathToFile) {
if (mediaPlayer!=null) {
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(pathToFile);
mediaPlayer.prepareAsync();
} //catch....
}
}
public void playSound(String path) {
if (mediaPlayer!=null) {
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(path);
mediaPlayer.prepareAsync();
mediaPlayer.start();
} //catch...
}
}
Main:
final String path = "sdcard/myappsounds/snaredrum2.wav";
final AudioPlayer s = AudioPlayer.getSingletonObject(getApplicationContext(), Uri.parse(path));
s.setSound(path);
ImageButton i = (ImageButton)findViewById(R.id.button);
i.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
s.playSound(path);
}
return true;
}
});
Any ideas? What i'm trying to do is like a drumset, that's why the buttons must respond correctly.
I/O on Android is slow. The audio subsystem is being forced to wait for it's data, and is kind enough to warn about it.
Rather than giving MediaPlayer a raw FileDescriptor (which will have no buffering applied) try using a file:// URI. With any luck the system content provider for file: will buffer it up for you the moment it's opened.
I don't really know Android in depth, but from looking at this code what you could try is to use your AudioPlayer as a singleton and reuse your MediaPlayer instance?

Categories

Resources