【问题标题】:Clear video frame from surfaceview on video complete视频完成时从表面视图清除视频帧
【发布时间】:2014-10-28 22:31:17
【问题描述】:

我创建了一个在表面视图上播放视频的媒体播放器。视频完成后,视频的最后一帧仍保留在表面上。我想从表面移除视频帧,因为经过一段时间的延迟,另一个视频开始播放。

视频的流程是:

现在表面上的视频 -> 表面上的最后一个视频帧 -> 表面上的另一个视频。

但是需要的流程是:

必需: 表面上的视频 -> 透明表面(黑色)-> 表面上的另一个视频。

谁能帮忙解决这个问题。

谢谢 伊山耆那教

【问题讨论】:

    标签: android video media-player surfaceview


    【解决方案1】:

    基于 Fadden 的回答和 Andreimarinescu 的问题,这里有一个 API 16 及以下版本:

    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);
    }
    

    相当粗糙,缺乏错误检查,但对我有用。

    【讨论】:

    • EGL14.EGL_OPENGL_ES2_BIT 应替换为 EGL10.EGL_WINDOW_BIT - EGL14.EGL_NONE 应替换为 EGL10.EGL_NONE - 不知道如何替换 EGL14.EGL_CONTEXT_CLIENT_VERSION。目前您的解决方案需要 API 17...但它在 API 17 上运行良好。
    • 我已使其与 API 16 兼容:gist.github.com/HugoGresse/5ca05821444353a823bb
    • 哦,有趣。感谢更新。我认为如果您使用 SDK 17 进行编译,您仍然可以在 API 16 上运行它(我认为),因为常量将在编译时被折叠起来。虽然不是 100% 确定。
    • 在 API 16 上运行时,我有一些警告或错误取决于
    【解决方案2】:

    您可以使用 GLES 清除它。您无法使用 Canvas 绘制命令清除它,因为这会阻止您再次在该表面上播放电影。

    可以在GrafikaPlayMovieSurfaceActivity 类中找到一个示例。 clearSurface() 方法是这样做的:

        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();
    

    EglCoreWindowSurface 类是 Grafika 的一部分。关键是它附着在表面上,进行清除,然后从表面分离。在执行此操作之前,请确保视频播放器已释放表面,否则 GLES 将无法附加。

    如果您想了解为什么需要附加/分离内容,请参阅system-level graphics architecture 文档。

    【讨论】:

    • 这可能适用于 API 级别 15+ 吗?我尝试从 Grafika 移植 EglCore 类,它似乎绑定到 API 级别 17。
    • Grafika 使用了 EGL 1.4 中引入的一些特性,这些特性是在 API 17 中添加的。我认为上面的内容很简单,它应该可以在 EGL 1.0 中使用,但你只想取出Grafika gles 代码中的必要位。如果您不确定 EGL 1.0 / 1.4 API 的区别是什么,bigflake.com/mediacodec/#ExtractMpegFramesTest 针对 API 16 和 17 编写了两次,这样可以轻松进行并排比较。
    • 这太好了,感谢@fadden 的指点!这对我来说也是一个很好的学习练习:)
    【解决方案3】:

    我遇到了类似的问题。在我的例子中,我展示了一个完全覆盖视频视图的标题卡,然后是视频本身。随后的第一张标题卡和视频播放良好。但是,当任何后续标题卡被隐藏时,上一个视频的最后一帧会在下一个视频开始之前闪烁。我在 VideoView 中添加了一个信息侦听器,以在隐藏标题卡之前侦听第一帧渲染。这会覆盖视频,直到第一个正确的帧被渲染。我希望这会有所帮助!

    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】:

      我有一个示例问题,我在播放器发布后使用这两行来修复它。

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

      【讨论】:

        【解决方案5】:

        我也有同样的问题。也许可以帮助某人。我在这里找到了解决方案https://stackoverflow.com/a/18906637

        我在服务中有播放器,在片段中我有 onComplete 回调和视频完成后:

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

        【讨论】:

          【解决方案6】:

          我用非常简单的方法解决了这个问题 - 只需将 Surface 设置为不可见,直到准备好视频。 如果你想黑屏,你可以添加一个黑屏视图(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】:

            视频播放完成时清除表面(MediaPlayer onCompletion 回调):

                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);
            

            另一种选择(但我现在记不太清楚)是在调用 onComplition() 回调时调用 MediaPlayer 类函数之一:

            MediaPlayer.stop();
            
            // reset to clear ready to reuse state.
            MediaPlayer.reset();
            
            // release all releted objects and memory
            MediaPlayer.release()
            

            其中一个会导致清晰的表面视图。在release() 之后,您必须再次创建另一个MediaPlayer 实例。

            【讨论】:

            • 我已经尝试并得到以下异常:09-04 20:28:34.164: A/libc(545): Fatal signal 11 (SIGSEGV) at 0x5cd14000 (code=1) ,线程 545 () 和应用程序崩溃。
            • 从 logcat 发布整个堆栈跟踪和日志。还尝试调用 MediaPlayer.stop() 和 MediaPlayer.reset() / MediaPlayer.release() 因为我记得其中一个功能会导致清除黑屏。我确定。
            • 没有。他们都没有在我的情况下工作。我尝试在 Android 4.3 设备上播放视频。
            • 不,我们不能使用 Canvas 来清理用于显示视频的 Surface:“问题是无法分离基于软件 (Canvas) 的缓冲区生成器。”所以下次使用会失败。见stackoverflow.com/a/24914966/1377145
            猜你喜欢
            • 2011-06-21
            • 1970-01-01
            • 1970-01-01
            • 2017-04-23
            • 2015-07-25
            • 1970-01-01
            • 2017-03-01
            • 1970-01-01
            • 2013-03-04
            相关资源
            最近更新 更多