5

I have a MediaPlayer rendering videos to a TextureView. This is working.

Now, I want to display a still image on this TextureView for a given time, then get the MediaPlayer to render a video to the same TextureView.

Here's my code to render the bitmap:

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

After this, any attempts to play videos result in ERROR_INVALID_OPERATION (-38) being triggered from the video player.

I tried commenting out the call to drawBitmap, and the error still happened. It seems that the simple act of calling lockCanvas followed by unlockCanvasAndPost results in the TextureView being unsuitable for the MediaPlayer to use.

Is there some way that I can reset the TextureView to a state that allows the MediaPlayer to use it?

I'm working on Android 4.2.2.

Andrew Shepherd
  • 44,254
  • 30
  • 139
  • 205

2 Answers2

9

You can't do this, due to a limitation of the Android app framework (as of Android 4.4 at least).

The SurfaceTexture that underlies the TextureView is a buffer consumer. The MediaPlayer is one example of a buffer producer, Canvas is another. Once you attach a producer, you have to detach it before you can attach a second producer.

The trouble is that there is no way to detach a software-based (Canvas) buffer producer. There could be, but isn't. So once you draw with Canvas, you're stuck. (There's a note to that effect here.)

You can detach a GLES producer. For example, in one of Grafika's video player classes you can find a clearSurface() method that clears the surface to black using GLES. Note the EGL context and window are created and explicitly released within the scope of the method. You could expand the method to show an image instead.

fadden
  • 51,356
  • 5
  • 116
  • 166
  • If I have a MediaPlayer as the buffer producer, what's the easiest way to clear my surface to black? – Gilbert Oct 13 '14 at 06:06
  • @Gilbert: you should probably post that as a new question. Solutions to that problem generally have three forms: (1) get MediaPlayer to do it, perhaps by playing a single-frame black video; (2) disconnect MediaPlayer, clear it with GLES, reconnect MediaPlayer; (3) put an opaque black View with identical dimensions in front of it. The third is pretty easy with TextureView since, unlike SurfaceView, it plays nice with other Views. – fadden Oct 13 '14 at 14:40
  • @fadden We can use 2 ways of drawing a bitmap onto the SurfaceView, just using Canvas.drawBitmap() API and manually drawing with opengl es. Both are hardware accelerated, but which is faster? I think drawing manually with opengl needs memory copying, we should use texImage2D APIs to upload bitmap to texture in GPU at first. – dragonfly Dec 06 '16 at 15:08
  • Last I checked, Canvas rendering onto a Surface is not hardware-accelerated. If the bitmap is different every time you draw it then you might be better off doing the work on the CPU. If you're drawing the same bitmap multiple times then the GPU is a better choice. – fadden Dec 06 '16 at 20:07
3

I have encountered similar problems recently. My intention was to show video thumbnail directly into TextureView and then use the same TextureView to play video, without using another ImageView to display video thumbnail.

I implemented the second approach in @fadden's comments, use EGL to draw video thumbnail into the same TextureView.

In addition, we can also use two textures in GLSurfaceView to achieve the same goal. One external OES texture to play continuous video, and another 2D texture to display video thumbnail.

The full demo can be found in this github project EGLPoster.

Hopefully it will be helpful for anyone reaches here.

alijandro
  • 11,627
  • 2
  • 58
  • 74