纹理视图视频和位图显示
Texture View video and Bitmap display
我有一个显示位图(占位符图像)的 TextureView,当用户加载视频时,textureView 应该显示视频。
我设法在纹理视图中播放视频并在纹理视图中显示位图,但按顺序完成后我只看到黑屏。
在我的 onSurfaceTextureAvailable 方法中,我在表面视图上绘制了一个位图,如下所示:
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.ic_add_clip);
Canvas canvas = lockCanvas();
canvas.drawBitmap(bm, (width/2)-(bm.getWidth()/2), (height/2)-(bm.getHeight()/2), null);
unlockCanvasAndPost(canvas);
}
当用户从其图库中选择视频时,我会在媒体播放器中加载 Uri,并将表面设置为媒体播放器。但是位图仍然显示。我试图通过清除 canvas 来删除位图,它确实删除了位图,但纹理视图只是黑色,也不会显示视频。
public void setMediaPlayer(MediaPlayer mediaPlayer) {
// Canvas canvas = lockCanvas();
// canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// unlockCanvasAndPost(canvas);
Surface s = new Surface(getSurfaceTexture());
mMediaPlayer = mediaPlayer;
mMediaPlayer.setSurface(s);
mMediaPlayer.setOnPreparedListener(this);
}
有些事情告诉我,我用来显示视频的表面视图位于我在 TextureAvailble 中绘制的位图后面。当我尝试清除 canvas 时,我也清除了媒体播放器想要使用的表面。有什么解决办法吗?
你的方法行不通。
Surface是一个具有生产者-消费者关系的缓冲区队列。一次只能有一个制作人,而您有两个制作人(Canvas 和 MediaPlayer)。当您调用 mMediaPlayer.setSurface()
时,它会附加 MediaPlayer;当您调用 lockCanvas()
时,它会附加 Canvas。您需要分离一个才能附加另一个。
(检查您的 logcat 输出——其中可能有一些 "already connected" 投诉。)
问题是,虽然您应该能够分离 MediaPlayer,但您无法分离 Canvas。根本没有电话可以做到这一点。这是 API 中的不幸神器。 (通过 4.4 "KitKat" 确认,我怀疑此后它是否发生了变化。)
您有几个选择:
- 改用 GLES 绘制位图。您可以在 Grafika 的 PlayMovieSurface activity 中看到一个示例——参见
clearSurface()
方法,它只是清除屏幕。使用 GLES,您可以在表面上附加和分离。
- 将您的位图转换为单帧视频并通过 MediaPlayer 播放。
- 使用 FrameLayout 将 ImageView 放在视频 window 的顶部。在播放视频时隐藏 ImageView。
我应该补充一点,我实际上并没有尝试从 Surface 分离 MediaPlayer。较低级别的 MediaCodec 以这种方式工作,如 Grafika 播放器所示,因此我假设 MediaPlayer 也将完全分离。不过,对于您的用例,#3 可能是最简单的解决方案,并且不需要 attach/detach 体操。
您可以使用opengl es将位图绘制到显示表面,然后用同样的方法绘制视频帧。我这样做了,效果很好。
我有一个显示位图(占位符图像)的 TextureView,当用户加载视频时,textureView 应该显示视频。
我设法在纹理视图中播放视频并在纹理视图中显示位图,但按顺序完成后我只看到黑屏。
在我的 onSurfaceTextureAvailable 方法中,我在表面视图上绘制了一个位图,如下所示:
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.ic_add_clip);
Canvas canvas = lockCanvas();
canvas.drawBitmap(bm, (width/2)-(bm.getWidth()/2), (height/2)-(bm.getHeight()/2), null);
unlockCanvasAndPost(canvas);
}
当用户从其图库中选择视频时,我会在媒体播放器中加载 Uri,并将表面设置为媒体播放器。但是位图仍然显示。我试图通过清除 canvas 来删除位图,它确实删除了位图,但纹理视图只是黑色,也不会显示视频。
public void setMediaPlayer(MediaPlayer mediaPlayer) {
// Canvas canvas = lockCanvas();
// canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// unlockCanvasAndPost(canvas);
Surface s = new Surface(getSurfaceTexture());
mMediaPlayer = mediaPlayer;
mMediaPlayer.setSurface(s);
mMediaPlayer.setOnPreparedListener(this);
}
有些事情告诉我,我用来显示视频的表面视图位于我在 TextureAvailble 中绘制的位图后面。当我尝试清除 canvas 时,我也清除了媒体播放器想要使用的表面。有什么解决办法吗?
你的方法行不通。
Surface是一个具有生产者-消费者关系的缓冲区队列。一次只能有一个制作人,而您有两个制作人(Canvas 和 MediaPlayer)。当您调用 mMediaPlayer.setSurface()
时,它会附加 MediaPlayer;当您调用 lockCanvas()
时,它会附加 Canvas。您需要分离一个才能附加另一个。
(检查您的 logcat 输出——其中可能有一些 "already connected" 投诉。)
问题是,虽然您应该能够分离 MediaPlayer,但您无法分离 Canvas。根本没有电话可以做到这一点。这是 API 中的不幸神器。 (通过 4.4 "KitKat" 确认,我怀疑此后它是否发生了变化。)
您有几个选择:
- 改用 GLES 绘制位图。您可以在 Grafika 的 PlayMovieSurface activity 中看到一个示例——参见
clearSurface()
方法,它只是清除屏幕。使用 GLES,您可以在表面上附加和分离。 - 将您的位图转换为单帧视频并通过 MediaPlayer 播放。
- 使用 FrameLayout 将 ImageView 放在视频 window 的顶部。在播放视频时隐藏 ImageView。
我应该补充一点,我实际上并没有尝试从 Surface 分离 MediaPlayer。较低级别的 MediaCodec 以这种方式工作,如 Grafika 播放器所示,因此我假设 MediaPlayer 也将完全分离。不过,对于您的用例,#3 可能是最简单的解决方案,并且不需要 attach/detach 体操。
您可以使用opengl es将位图绘制到显示表面,然后用同样的方法绘制视频帧。我这样做了,效果很好。