I'm currently developing the android application ServeStream and I've encountered and problem that I can't fix. My application will stream music and video using the android MediaPlayer class. I've modeled my class after the example found at:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/media/MediaPlayerDemo_Video.html
The difference between this example and my own code is my MediaPlayer is runs in a service which allows it to continue playback in the background. The problem with the example android code is if I'm watching a video and I leave the current window/activity (i.e press the menu button, etc) and return to the playback activity I get a black screen but still receive audio from the video that is playing.
When my playback activity is initially created the code shown below is executed. This code essentially creates the view used for playback and then ties it to the media player:
setContentView(R.layout.mediaplayer_2);
mPreview = (SurfaceView) findViewById(R.id.surface);
holder = mPreview.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
...
mMediaPlayer.setDisplay(holder);
The important line is mMediaPlayer.setDisplay(holder) because it ties the current view/display to the media player. The view (the "holder") is destroyed when you leave the activity. After returning to the activity and recreating the view, executing mMediaPlayer.setDisplay(holder) again doesn't appear to re-attach the newly created view. A black screen is shown instead of the video.
Does anyone have a workaround or solution for this issue. I would appreciate any help or advice.
After lot of googling around and head-scratching over state diagram of MediaPlayer
, I finally managed to avoid this so called black screen. I've already posted in the OP's comments section that this problem seems to be resolved with latest Android versions (greater than 4.x)and so I was opting to have similar behavior on Gingerbread devices as well.
Needless to say, SurfaceHolder
callback methods play a very crucial role in the lifecycle of a bounded MediaPlayer
-SurfaceView
implementation. And those very callback methods came handy to me for getting out of this situation.
First:
inside onCreate(): - Basic initialization stuff..
mVideoSurface = (SurfaceView) findViewById(R.id.videoSurface);
videoHolder = mVideoSurface.getHolder();
videoHolder.addCallback(this);
// For Android 2.2
videoHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// Media Player and Controller
player = new MediaPlayer();
controller = new VideoControllerView(this);
Then, SurfaceView Callbacks :
don't forget to set the Display
to your MediaPlayer
and IMHO, surfaceCreated()
is the best place to do that.
@Override
public void surfaceCreated(SurfaceHolder holder) {
player.setDisplay(holder);
try {
player.prepareAsync();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
And here's the most important thing. When user leaves the current activity by either pressing Home button or by opening a different activity with expecting any results, our SurfaceView
is going to to be get destroyed. What I wanted to achieve was to resume the playback of ongoing video from the position it was playing when the context got switched.
So, in order to do that,
Save the current playback position in a variable so that we can use it later on to seek our playback to that particular position.
And one more. Release the damn MediaPlayer
instance. I tried to avoid releasing the MediaPlayer
instance and it's re-creation but I keep failing over and over. So..point made.
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (player != null) {
mCurrentVideoPosition = player.getCurrentPosition();
player.release();
player = null;
}
}
Now, onPrepared()
Initialize any MediaController
here if you're interested. Check if the video was already playing and so seek the video to that position.
@Override
public void onPrepared(MediaPlayer mp) {
controller.setMediaPlayer(this);
controller.setAnchorView((FrameLayout) findViewById(R.id.videoSurfaceContainer));
player.start();
if (mCurrentVideoPosition != 0) {
player.seekTo(mCurrentVideoPosition);
player.start();
}
}
Finally, to play the video:
void playVideo(){
try {
if (player == null)
player = new MediaPlayer();
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setDataSource("SOME_VIDEO_FILE.3gp");
player.setOnPreparedListener(this);
player.setOnErrorListener(new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.reset();
return false;
}
});
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
And that's all of it. I know the issue is not there with newer versions and all great but... lots of GBs are still out there.
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