Black Screen When Returning To Video Playback Activity In Android
Solution 1:
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 = newMediaPlayer();
controller = newVideoControllerView(this);
Then, SurfaceView Callbacks :
don't forget to set the Display
to your MediaPlayer
and IMHO, surfaceCreated()
is the best place to do that.
@OverridepublicvoidsurfaceCreated(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.
@OverridepublicvoidsurfaceDestroyed(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:
voidplayVideo(){
try {
if (player == null)
player = newMediaPlayer();
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setDataSource("SOME_VIDEO_FILE.3gp");
player.setOnPreparedListener(this);
player.setOnErrorListener(newOnErrorListener() {
@OverridepublicbooleanonError(MediaPlayer mp, int what, int extra) {
mp.reset();
returnfalse;
}
});
} 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.
Solution 2:
I think I know the cause of the problem. It seems that mediaplayer initializes its surface ONLY when you call prepare/Async(). If you will change the surface after that, you will get what you`ve got. So the solution would be to disable standard lifecycle of activity via android:configChanges="orientation|keyboardHidden". This will prevent your activity from recreations. Otherwise you should call for prepare each time you recreate holder.
Solution 3:
You can do: mMediaPlayer.setDisplay(null)
when surfaceDestroy
and when you enter video again setDisplay
for mediaPlayer with new surfaceHolder
.
Remember, always put this code below inside onStart
, because when home pressed or lockscreen, it will force `sufaceCreate' fired with new holder. View will be recreated, video will show instead black screen
holder = mPreview.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
Hope this help!!
Solution 4:
I have the same issue!
While video is playing I press HOME button and then return to the Application. I get in:
public void surfaceCreated(SurfaceHolder holder) {
player.setDisplay(holder);
playVideo();
}
In the playVideo()
I have:
privatevoidplayVideo() {
if (extras.getString("video_path").equals("VIDEO_URI")) {
showToast("Please, set the video URI in HelloAndroidActivity.java in onClick(View v) method");
} else {
try {
if (flag == 0) {
player.setDataSource(extras.getString("video_path"));
player.prepareAsync();
flag = 1;
}
else
{
player.start();
}
} catch (IllegalArgumentException e) {
showToast("Error while playing video");
Log.i(TAG, "========== IllegalArgumentException ===========");
e.printStackTrace();
} catch (IllegalStateException e) {
showToast("Error while playing video");
Log.i(TAG, "========== IllegalStateException ===========");
e.printStackTrace();
} catch (IOException e) {
showToast("Error while playing video. Please, check your network connection.");
Log.i(TAG, "========== IOException ===========");
e.printStackTrace();
}
}
}
And I have blank black background and hear audiostream.
I noticed that if in the first time of launching this Activity if I don't make player.setDisplay(holder);
I will have the same behavior.
A spent two days trying to solve this issue...
Solution 5:
EDIT: Please note this solution does not work on 4.x devices - video simply never appears, only blank screen and I couldn't make it work there. Here's a state machine that does the following: 1. plays short couple of seconds video /res/raw/anim_mp4.mp4 (procedure initiated in onCreate() method) 2. if user presses home button then returns to app it will seek video to start position and pause immediatelly (procedure initiated in onResume() method)
package com.test.video;
import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
publicclassMediaPlayerActivityextendsActivityimplementsOnCompletionListener,
MediaPlayer.OnPreparedListener, MediaPlayer.OnSeekCompleteListener, SurfaceHolder.Callback {
privatefinalintSTATE_NOT_INITIALIZED=0;
privatefinalintSTATE_INITIALIZING=1;
privatefinalintSTATE_INITIALIZED=2;
privatefinalintSTATE_SEEKING=3;
privatefinalintSTATE_DELAYING=4;
privatefinalintSTATE_PLAYING=5;
privatefinalintSTATE_FINISHED=6;
privatefinalintSTATE_RESUMED=7;
privatefinalintSTATE_RESUMED_PREPARING=8;
privatefinalintSTATE_RESUMED_PREPARED=9;
privatefinalintSTATE_RESUMED_SEEKING=10;
privateintstate= STATE_NOT_INITIALIZED;
private SurfaceView surface;
private MediaPlayer player;
private SurfaceHolder holder;
@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
Log.d(Constants.TAG, "onCreate()");
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.mediaplayer);
surface = (SurfaceView) findViewById(R.id.idSurface);
holder = surface.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@OverrideprotectedvoidonStart() {
Log.d(Constants.TAG, "onStart()");
super.onStart();
state();
}
@OverrideprotectedvoidonResume() {
Log.d(Constants.TAG, "onResume()");
super.onResume();
if (STATE_FINISHED == state) {
state = STATE_RESUMED;
state();
}
}
@OverrideprotectedvoidonDestroy() {
Log.d(Constants.TAG, "onDestroy()");
super.onDestroy();
if (player != null) {
player.release();
player = null;
}
}
@OverridepublicvoidsurfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
Log.d(Constants.TAG, "surfaceChanged()");
}
@OverridepublicvoidsurfaceCreated(SurfaceHolder arg0) {
Log.d(Constants.TAG, "surfaceCreated()");
}
@OverridepublicvoidsurfaceDestroyed(SurfaceHolder arg0) {
Log.d(Constants.TAG, "surfaceDestroyed()");
}
@OverridepublicvoidonPrepared(MediaPlayer mp) {
Log.d(Constants.TAG, "onPrepared()");
state();
}
@OverridepublicvoidonCompletion(MediaPlayer mp) {
Log.d(Constants.TAG, "onCompletion()");
state();
}
@OverridepublicvoidonSeekComplete(MediaPlayer mediaplayer) {
Log.d(Constants.TAG, "onSeekComplete()");
state();
}
privateclassResumeDelayedextendsPlayDelayed {
protectedvoidonPostExecute(Void result) {
Log.d(Constants.TAG, "ResumeDelayed.onPostExecute()");
state();
};
}
privatevoidinitPlayer() {
Log.d(Constants.TAG, "initPlayer()");
try {
if (player == null) {
player = newMediaPlayer();
player.setScreenOnWhilePlaying(true);
} else {
player.stop();
player.reset();
}
Stringuri="android.resource://" + getPackageName() + "/" + R.raw.anim_mp4;
player.setDataSource(this, Uri.parse(uri));
player.setDisplay(holder);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnPreparedListener(this);
player.prepareAsync();
player.setOnCompletionListener(this);
player.setOnSeekCompleteListener(this);
} catch (Throwable t) {
Log.e(Constants.TAG, "Exception in media prep", t);
}
}
privateclassPlayDelayedextendsAsyncTask<Void, Void, Void> {
@Overrideprotected Void doInBackground(Void... arg0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Log.e(Constants.TAG, "Can't sleep", e);
}
returnnull;
}
protectedvoidonPostExecute(Void result) {
Log.d(Constants.TAG, "PlayDelayed.onPostExecute()");
initPlayer();
};
}
privatevoidstate() {
switch (state) {
case STATE_NOT_INITIALIZED:
state = STATE_INITIALIZING;
initPlayer();
break;
case STATE_INITIALIZING:
state = STATE_INITIALIZED;
newPlayDelayed().execute();
break;
case STATE_INITIALIZED:
state = STATE_SEEKING;
player.start();
player.seekTo(0);
break;
case STATE_SEEKING:
state = STATE_DELAYING;
player.pause();
newResumeDelayed().execute();
break;
case STATE_DELAYING:
state = STATE_PLAYING;
player.start();
break;
case STATE_PLAYING:
state = STATE_FINISHED;
break;
case STATE_RESUMED:
state = STATE_RESUMED_PREPARING;
initPlayer();
break;
case STATE_RESUMED_PREPARING:
state = STATE_RESUMED_PREPARED;
newPlayDelayed().execute();
break;
case STATE_RESUMED_PREPARED:
state = STATE_RESUMED_SEEKING;
player.start();
player.seekTo(0);
break;
case STATE_RESUMED_SEEKING:
state = STATE_FINISHED;
player.pause();
break;
default:
break;
}
}
mediaplayer.xml layout:
<?xml version="1.0" encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:background="@android:color/black" ><SurfaceViewandroid:id="@+id/idSurface"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center" />
AndroidManifest.xml looks like:
<?xml version="1.0" encoding="utf-8"?><manifestxmlns:android="http://schemas.android.com/apk/res/android"package="com.test.video"android:versionCode="1"android:versionName="1.0" ><uses-sdkandroid:minSdkVersion="10" /><applicationandroid:icon="@drawable/ic_launcher"android:theme="@android:style/Theme.Black.NoTitleBar" ><activityandroid:name=".MediaPlayerActivity"android:label="@string/app_name"android:screenOrientation="portrait"android:theme="@android:style/Theme.Black.NoTitleBar" ><intent-filter><actionandroid:name="android.intent.action.MAIN" /><categoryandroid:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application>
Post a Comment for "Black Screen When Returning To Video Playback Activity In Android"