VideoView black flash before and after playing
I have a VideoView which I want to use to 开发者_StackOverflow中文版play a movieclip. I use it like this to play it and it works.
VideoView vv = new VideoView(this);
vv.setVideoURI(Uri.parse("android.resource://cortex2.hcbj/raw/intro"));
setContentView(vv);
vv.start();
However I see a black flash just before and after the movie clip. The flash in itself isn't a big problem, but the blackness of it is. The background is white, so if the flash is white, or if it dissapears it will be okay.
Today I had the same problem and found a very bad and hacky workaround for this nasty problem: I realized that one can set a background color / drawable onto the VideoView
which blends over the video surface and makes it completely hidden. This only works though while the underlying video is still playing, not when it is stopped (neither when it ended normally nor when stopPlayback()
was called), otherwise you'd again see a black flicker. The background must also not be set in the beginning, otherwise the video would be completely hidden right from the start.
So the only logical step for me was to post a delayed event just before I start the video - and since I know the video length, I let this event happen just a few milliseconds before it ends normally. I took a screenshot of the last frame in VLC and then blended it like this:
private void startVideo()
{
introVideo.setBackgroundDrawable(null);
introVideo.postDelayed(new Runnable() {
public void run()
{
if (!introVideo.isPlaying())
return;
introVideo.setBackgroundResource(R.drawable.video_still_image);
// other stuff here, for example a custom transition to
// another activity
}
}, 7500); // the video is roughly 8000ms long
introVideo.start();
}
This however was not enough, because when the video actually ended, I still got a short black screen flicker, so I also had to set the still image as background of the container that contained the video (in my case it was the layout of the activity):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/video_still_image">
<VideoView android:id="@+id/introVideo"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentRight="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_marginTop="-10dip" />
</RelativeLayout>
This activity is rendered in fullscreen and the video is (mostly) scaled to the total screen size (screen 1024x600, video 960x640). I say mostly, because for some unknown reason the layout's background image blends through for about 10px on top. This was the last hack I had to apply to make it work - move the video container -10dip
into the void on top.
This now looks awesome on my Galaxy Tab, I don't dare to test it on the SGS2 phone, though...
I ended up having to do something very similar to @tommyd to avoid the black surfaceView flash at the beginning and end of my videos. However, I found that setting/nulling the background drawable for the videoView was not occurring instantly on many phones. There could be about a half-second delay between my call to set the background and when it was actually displayed.
What I ended up doing was creating a custom SurfaceView that showed a single, solid color, then overlayed this on top of the VideoView and made use of SurfaceView.setZOrderMediaOverlay().
My custom SurfaceView was heavily informed by: http://android-er.blogspot.com/2010/05/android-surfaceview.html
public class SolidSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = SolidSurfaceView.class.getSimpleName();
private SolidSurfaceThread mThread;
private boolean mSurfaceIsValid;
private int mColor;
public SolidSurfaceView(Context context) {
super(context);
init();
}
public SolidSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SolidSurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
Log.verbose(TAG, "init");
getHolder().addCallback(this);
setZOrderMediaOverlay(true);
}
public void setColor(int color) {
mColor = color;
invalidate();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.verbose(TAG, "surfaceCreated");
mSurfaceIsValid = true;
mThread = new SolidSurfaceThread(getHolder(), this);
mThread.setRunning(true);
mThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.verbose(TAG, "surfaceDestroyed");
mSurfaceIsValid = false;
boolean retry = true;
mThread.setRunning(false);
while (retry) {
try {
mThread.join();
retry = false;
}
catch (InterruptedException e) {
Log.warning(TAG, "Thread join interrupted");
}
}
mThread = null;
}
@Override
protected void onDraw(Canvas canvas) {
if ( ! mSurfaceIsValid) {
return;
}
canvas.drawColor(mColor);
}
private static class SolidSurfaceThread extends Thread {
private final SurfaceHolder mSurfaceHolder;
private final SolidSurfaceView mSurfaceView;
private boolean mIsRunning;
public SolidSurfaceThread(SurfaceHolder surfaceHolder, SolidSurfaceView surfaceView) {
mSurfaceHolder = surfaceHolder;
mSurfaceView = surfaceView;
}
public void setRunning(boolean running) {
mIsRunning = running;
}
@Override
public void run() {
while (mIsRunning) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
mSurfaceView.onDraw(c);
}
}
finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
And in the parent activity that hosts the views:
mVideoView = (VideoView)findViewById(R.id.video_view);
mVideoMask = (SolidSurfaceView)findViewById(R.id.video_mask);
mVideoMask.setColor(Color.BLUE);
You can then do things like mVideoMask.setVisibility(View.GONE)
to hide the mask or mVideoMask.setVisibility(View.VISIBLE)
to show the mask (and hide the black-screened VideoView).
In my experiments on various phones, this method provided very fast showing/hiding of the video mask, as opposed to setting/nulling the background.
i had the same problem and white instead of black was ok for me.. i tried all of solutions above i came up with the following
vv.setBackgroundColor(Color.WHITE);
vv.postDelayed(new Runnable() {
@Override
public void run() {
vv.setVideoURI(videoUri);
}
}, 100);
vv.postDelayed(new Runnable() {
@Override
public void run() {
vv.setBackgroundColor(Color.TRANSPARENT);
}
}, 300);
vv.requestFocus();
vv.start();
and delayed my video 400 ms start fading from white works like a charm for me
My variation on the @tommyd theme:
Set the drawable to a static video frame, then spam the message queue. After some time, clear the drawable so video frames render. Then, before completion, set the static image back.
mMovieView.setBackgroundDrawable(bg);
mMovieView.start();
final int kPollTime= 25;
mMovieView.postDelayed(new Runnable() {
private final int kStartTransitionInThreshold= 50;
private final int kStartTransitionOutThreshold= 250;
private boolean mTransitioned= false;
@Override
public void run() {
if (mMovieView.isPlaying()) {
if (mMovieView.getCurrentPosition() > kStartTransitionInThreshold && !mTransitioned) {
mMovieView.setBackgroundDrawable(null); // clear to video
mTransitioned= true;
}
if (mMovieView.getDuration() - mMovieView.getCurrentPosition() < kStartTransitionOutThreshold)
mMovieView.setBackgroundDrawable(bg);
}
mMovieView.postDelayed(this, kPollTime); // come back in a bit and try again
}
}, kPollTime);
here is simple trick
check the condition media player.getCurrentPosition == 0 in on prepared listener of video view ,black screen will appear when position is zero so display an image untill the video will load
mycode:
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(final MediaPlayer mediaPlayer) {
mVideoView.start();
runOnUiThread(new Runnable() {
@Override
public void run() {
while (mediaPlayer.isPlaying()) {
Log.d("MainActivity", "position " + mediaPlayer.getCurrentPosition());
if (mediaPlayer.getCurrentPosition() == 0) {
videoStillImageView.setVisibility(View.VISIBLE);
} else {
videoStillImageView.setVisibility(View.GONE);
break;
}
}
}
});
}
}
});
My workaround for this diabolical bug utilised a blank View with the background colour of choice over the top of the VideoView.
<View
android:id="@+id/blankView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/white" />
then in my code I did this:
video = (VideoView) findViewById(R.id.video);
// set the video URI, passing the vSourse as a URI
video.setVideoURI(Uri.parse(vSource));
video.setZOrderOnTop(false);
SurfaceHolder sh = video.getHolder();
sh.setFormat(PixelFormat.TRANSPARENT);
ctlr = new BCDMediaController(this);
ctlr.setMediaPlayer(video);
video.setMediaController(ctlr);
video.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
closeActivity();
}
});
blankView = findViewById(R.id.blankView);
video.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
blankView.postDelayed(new Runnable() {
public void run()
{
blankView.setVisibility(View.GONE);
}
}, 500);
}
});
video.start();
video.requestFocus();
That got rid of the beginning black flash for me. Then for the end black flash, I did this:
private void closeActivity() {
blankView.setVisibility(View.VISIBLE);
blankView.postDelayed(new Runnable() {
public void run()
{
video.setOnCompletionListener(null);
video.stopPlayback();
video.suspend();
VideoPlayerViewController.this.finish();
}
}, 1);
}
That flash comes from changing the current content view to another one. You could try adding a VideoView to your layout xml file then referencing it with (VideoView) findViewById(R.id.vid);
instead of new VideoView(this);
and setting the content view to that layout.
Why not using Styling and Themes
Can you try this ? This might help
colors.xml
<drawable name="transparent">#00000000</drawable>
styles.xml
<style name="Transparent">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">
@android:style/Animation.Translucent
</item>
<item name="android:windowBackground">@drawable/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:colorForeground">#fff</item>
</style>
To set a theme for all the activities of your application, open the AndroidManifest.xml file and edit the tag to include the android:theme attribute with the style name.
<application android:theme="@style/Transparent">
Where are you changing the contentView (in which method did you write the four lines of code above)?
You should only set the contentView in the onCreate()
method of an Activity
. If you are doing it somewhere else (for exemple in a button’s callback), you should start a new activity instead.
There is one alternative that you are use media controller for this. Here is sample code
videoView = (VideoView) this.findViewById(R.id.videoView);
MediaController mc = new MediaController(this);
videoView.setMediaController(mc);
videoView.setVideoURI(Uri.parse("http://c421470.r70.cf2.rackcdn.com/video_5983079.m4v"));
videoView.start();
videoView.requestFocus();
Try this.
Another solution that might work for people who are searching for this question:
In my case, the video was a resource in the app (that is, I knew everything about it and it wasn't going to change) and its last 500 ms were the same frame, so I ended up doing the following:
mVideoView.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.splash));
mVideoView.start();
findViewById(android.R.id.content).postDelayed(new WaitForSplashScreenToFinish(), mVideoView.getDuration() - 1000);
and the referenced class is:
private class WaitForSplashScreenToFinish implements Runnable {
@Override
public void run() {
if (mVideoView.isPlaying() && mVideoView.getCurrentPosition() >= mVideoView.getDuration() - 500) {
mVideoView.pause();
// Now do something else, like changing activity
} else {
findViewById(android.R.id.content).postDelayed(this, 100);
}
}
}
Explanation: Immediately after starting the video I create a Runnable and postDelayed it to the root view (android.R.id.content
) to the duration of the video, minus the length at which I'm willing to pause the video (and a generous buffer, because postDelayed isn't guaranteed to play exactly after the requested time)
Then, the runnable checks if the video arrived at its pause-time, if so it pauses it and does whatever else we want it to do. If it doesn't, it runs postDelayed again with itself, for a shortened time (100 ms in my case, but could be shorter)
Granted, this is far from being an ideal solution, but it might help someone with a specific problem similar to the one that stumbled me for half a day :)
I know it's old question but if you can add some code to MediaPlayer listeners there is very simple answer. It turned out that setBackgroundColor for VideoView changes the foreground color ( https://groups.google.com/forum/?fromgroups=#!topic/android-developers/B8CEC64qYhQ ).
So the only thing you have to do is switching between setBackgroundColor(Color.WHITE) and setBackgroundColor(Color.TRANSPARENT).
I had same problem this has worked for me ..
When you want to show video make videoView.setZOrderOnTop(false); and when you want to hide video view just make videoView.setZOrderOnTop(true);
Try this line.This worked for me. if(player!=null)player.release();
@Override
protected void onDestroy() {
super.onDestroy();
player.pause();
if(player!=null)player.release();
player = null;
videoSurface = null;
controller = null;
}
精彩评论