Clear video frame from surfaceview on video comple

2019-01-19 00:03发布

问题:

I have created a media player which plays video on the surface view. After completion of video the last frame of the video remains on the surface. I want to remove the video frame from the surface because after some delay another video starts.

The flow of the video is:

NOW: Video on surface -> last video frame on surface -> another video on surface.

But the required flow is:

REQUIRED: Video on surface -> clear surface (black) -> another video on surface.

can anyone help to solve this problem.

Thanks Ishan jain

回答1:

Building on Fadden's answer, and Andreimarinescu's question, here is a version for API 16 and below:

private void clearSurface(Surface surface) {
    EGL10 egl = (EGL10) EGLContext.getEGL();
    EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    egl.eglInitialize(display, null);

    int[] attribList = {
            EGL10.EGL_RED_SIZE, 8,
            EGL10.EGL_GREEN_SIZE, 8,
            EGL10.EGL_BLUE_SIZE, 8,
            EGL10.EGL_ALPHA_SIZE, 8,
            EGL10.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
            EGL10.EGL_NONE, 0,      // placeholder for recordable [@-3]
            EGL10.EGL_NONE
    };
    EGLConfig[] configs = new EGLConfig[1];
    int[] numConfigs = new int[1];
    egl.eglChooseConfig(display, attribList, configs, configs.length, numConfigs);
    EGLConfig config = configs[0];
    EGLContext context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, new int[]{
            EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL10.EGL_NONE
    });
    EGLSurface eglSurface = egl.eglCreateWindowSurface(display, config, surface,
            new int[]{
                    EGL14.EGL_NONE
            });

    egl.eglMakeCurrent(display, eglSurface, eglSurface, context);
    GLES20.glClearColor(0, 0, 0, 1);
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    egl.eglSwapBuffers(display, eglSurface);
    egl.eglDestroySurface(display, eglSurface);
    egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, 
            EGL10.EGL_NO_CONTEXT);
    egl.eglDestroyContext(display, context);
    egl.eglTerminate(display);
}

Pretty crude, lacks error checking, but it works for me.



回答2:

You can clear it with GLES. You can't clear it with Canvas draw commands because that will prevent you from playing movies on that surface again.

An example can be found in Grafika's PlayMovieSurfaceActivity class. The clearSurface() method does this:

    EglCore eglCore = new EglCore();
    WindowSurface win = new WindowSurface(eglCore, surface, false);
    win.makeCurrent();
    GLES20.glClearColor(0, 0, 0, 0);
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    win.swapBuffers();
    win.release();
    eglCore.release();

The EglCore and WindowSurface classes are part of Grafika. The key thing is that it attaches to the surface, does the clear, and then detaches from the surface. Make sure the video player has released the surface before doing this, or GLES won't be able to attach.

If you want to understand why the attach / detach stuff is necessary, see the system-level graphics architecture doc.



回答3:

I had a similar issue. In my case, I was showing a title card that completely covered the video view followed by the video itself. The first title card and video that followed played fine. However, when any subsequent title card was hidden, the last frame of the previous video would flash before the next video started. I added an info listener to my VideoView to listen for the first frame render before hiding the title card. This covers up the video until the first correct frame has rendered. I hope this helps!

mVideoView.setOnInfoListener(new MediaPlayer.OnInfoListener() 
{
    @Override
    public boolean onInfo(final MediaPlayer mp, final int what, final int extra) 
    {
        if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) 
        {
            mTitleCardView.setVisibility(View.GONE);

            return true;
        }

        return false;
    }
});


回答4:

I have a sample problem and i fixed it by using these two lines after player release.

surfaceViewHolder.setFormat(PixelFormat.TRANSPARENT); surfaceViewHolder.setFormat(PixelFormat.OPAQUE);



回答5:

I have the same problem. Maybe help someone. I found solution here https://stackoverflow.com/a/18906637

I have player in Service, in Fragment I have onComplete callback and after video finished:

videoHolder.setFormat(PixelFormat.TRANSPARENT);
videoHolder.setFormat(PixelFormat.OPAQUE);
videoService.setVideoDisplay(videoHolder);


回答6:

I solved the problem with very simple way - just set Surface invisible until video is prepared. If you want to black screen, you can add a black screen view(curtainView).

public void play(String path) {
    try {
        status =  STATUS_PREPARING;
        mSurface.setVisibility(View.INVISIBLE);
        mCurtainView.setVisibility(View.VISIBLE);  // simple black view
        mPlayer = new MediaPlayer();
        if(mSurfaceHolder!=null) mPlayer.setDisplay(mSurfaceHolder);
        mPlayer.setDataSource(path);
        mPlayer.prepareAsync();
        mPlayer.setOnPreparedListener(this);
    } catch (Exception e) {
        return;
    }
}

...

@Override
public void onPrepared(MediaPlayer mp) {
    mSurface.setVisibility(View.VISIBLE);
    if(mSurfaceHolder != null){     
        mPlayer.start();
        mCurtainView.setVisibility(View.GONE);
        status = STATUS_PLAYING;
    } else {
        status = STATUS_DATA_PREPARED;
    }
}

private final class SurfaceCallback implements Callback {
    public void surfaceCreated(SurfaceHolder holder) {
        mSurfaceHolder = holder;
        mPlayer.setDisplay(holder);
        if(status==STATUS_DATA_PREPARED){
            mPlayer.start();
            mCurtainView.setVisibility(View.GONE);
            status = STATUS_PLAYING;
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        mSurfaceHolder = null;
    }
    ...
}


回答7:

Clear surface when playback of video is completed (MediaPlayer onCompletion callback):

    Canvas canvas = surfaceView.getHolder().lockCanvas();

    // Clear surface by drawing on it
    canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); 

   // option
   // canvas.drawColor(Color.BLACK);

   surfaceView.getHolder().unlockCanvasAndPost(canvas);

Another option (but I don't remember clearly right now) is call one of MediaPlayer class function when onComplition() callback called:

MediaPlayer.stop();

// reset to clear ready to reuse state.
MediaPlayer.reset();

// release all releted objects and memory
MediaPlayer.release()

One of them cause clear surface view. After release() you have to create another MediaPlayer instance again.