MediaPlayer不能在图像渲染后渲染到TextureView。

6

我有一个 MediaPlayer 将视频渲染到一个 TextureView 上,这个已经可以工作了。

现在,我想要在这个 TextureView 上显示一个静态图片一段时间,然后让 MediaPlayer 再将视频渲染到同样的 TextureView 上。

这是我用于渲染位图的代码:

Canvas canvas = mTextureView.lockCanvas();
canvas.drawBitmap(sourceBitmap, matrix, new Paint());
mTextureView.unlockCanvasAndPost(canvas);

在这之后,任何尝试播放视频的操作都会导致视频播放器触发ERROR_INVALID_OPERATION (-38)错误。
我尝试将对drawBitmap的调用注释掉,但仍然出现错误。似乎只是对lockCanvas的调用,然后紧接着对unlockCanvasAndPost的调用就会导致TextureView无法适用于MediaPlayer使用。
有没有办法可以重置TextureView以使其适用于MediaPlayer使用?
我的Android版本是4.2.2。
2个回答

9
由于Android应用框架的限制(至少在Android 4.4上),您无法执行此操作。
TextureView下面的SurfaceTexture是一个缓冲区消费者。 MediaPlayer是一个缓冲区生产者的例子,Canvas是另一个。一旦您连接了一个生产者,必须在连接第二个生产者之前将其分离。
问题在于没有办法分离基于软件的(Canvas)缓冲区生产者。可能会有,但现在还没有。所以一旦您使用Canvas绘制,您就被卡住了。(这里有一个相关的注释here。)
您可以分离GLES生产者。例如,在Grafika的一个视频播放器类中,您可以找到一个clearSurface()方法,该方法使用GLES将表面清除为黑色。请注意,EGL上下文和窗口是在方法范围内创建并显式释放的。您可以扩展该方法以显示图像。

如果我有一个MediaPlayer作为缓冲区生产者,最简单的方法是将我的表面清除为黑色? - Gilbert
@Gilbert:你应该将它作为一个新问题发布。解决这个问题通常有三种形式:(1)让MediaPlayer来做,也许通过播放单帧黑色视频;(2)断开MediaPlayer,用GLES清除它,重新连接MediaPlayer;(3)在其前面放置一个具有相同尺寸的不透明黑色视图。第三种方法对于TextureView来说非常容易,因为与SurfaceView不同,它可以与其他视图很好地协作。 - fadden
@fadden我们可以使用两种方法将位图绘制到SurfaceView上,一种是仅使用Canvas.drawBitmap() API进行绘制,另一种是使用OpenGL ES手动绘制。两者都具有硬件加速功能,但哪种更快? 我认为通过OpenGL手动绘制需要进行内存复制,因此我们应该首先使用texImage2D API将位图上传到GPU的纹理中。 - dragonfly
据我最近的检查,Canvas渲染到Surface上并没有硬件加速。如果每次绘制的位图都不同,那么最好在CPU上处理。如果您要多次绘制相同的位图,则GPU是更好的选择。 - fadden

3
我最近遇到了类似的问题。我的意图是直接在 TextureView 中显示视频缩略图,然后使用同一 TextureView 播放视频,而不使用另一个 ImageView 来显示视频缩略图。
我实现了 @fadden 的评论中的第二种方法,使用 EGL 将视频缩略图绘制到同一 TextureView 中。
此外,我们还可以在 GLSurfaceView 中使用两个纹理来实现相同的目标。一个外部 OES 纹理用于播放连续视频,另一个 2D 纹理用于显示视频缩略图。
完整演示可以在此 Github 项目中找到:EGLPoster
希望对任何到达这里的人有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接