11

I am writing a Video player application using MediaCodec API. I have to show blank screen before video decoding started. So i am using following code to show blank screen.

Canvas c=null;
            c = surfaceView.getHolder().lockCanvas();
            c.drawColor(Color.BLACK);
            surfaceView.getHolder().unlockCanvasAndPost(c);

After this Video decoding will start. But at configure time

videoDecoder.configure(format, surfaceView.getHolder().getSurface(), null, 0);

it giving following errors

02-03 03:52:37.542: E/MediaCodec(9655): native_window_api_connect returned an error: Invalid argument (-22)
02-03 03:52:37.542: E/Video Decoder Configuration(9655): java.lang.IllegalStateException

So my app crash with that error. Without that blank screen code decoder is working fine. How can i resolve this problem?

fadden
  • 51,356
  • 5
  • 116
  • 166
saa
  • 1,538
  • 2
  • 17
  • 35

1 Answers1

8

Here's my theory, based on a quick look through the framework code.

The Canvas lock operation is (eventually) calling into Surface::lock() (code here). That has a piece of code that says:

if (!mConnectedToCpu) {
    int err = Surface::connect(NATIVE_WINDOW_API_CPU);

This is connecting a "CPU producer", i.e. code that runs on the CPU and generates graphic data, to the producer side of the buffer queue that feeds into the Surface. That producer is not disconnected in unlockAndPost(). You can actually find the disconnect call in the Surface destructor, which is a bit late for your purposes.

You can't have two producers on one buffer queue, so when you hand the Surface to the MediaCodec decoder it's unable to connect.

I believe you have a couple of options:

  1. Blank the surface with OpenGL ES. When you destroy the EGLSurface it will disconnect. This requires setting up EGL/GLES and getting the EGL release code right.
  2. Put up a blank rectangle using an approach other than drawing on the SurfaceView itself (h/t my office-mate).

For approach #2, you just need a second view (maybe an ImageView) with the same position and dimensions as the SurfaceView, and fill it with opaque black. The SurfaceView layer is always under everything else (assuming you haven't configured it to be on top), so the UI elements will draw on top of it. When it's time to start the movie playing, you disable the other view.

Update: you can now see approach #1 in Grafika. In the "Play movie (SurfaceView)" activity, it creates an EGL context, clears the surface, and destroys the context. (It's necessary to destroy the EGL context and surface immediately to avoid the "two producer" problem.)

fadden
  • 51,356
  • 5
  • 116
  • 166
  • 2
    I got it to work. Many thanks! I've written a snippet to do that and left it here. I hope this helps someone else. https://gist.github.com/hisui/433e09e899175844cfc4 – findall Oct 02 '15 at 08:56
  • 2
    I could write a book about all the times that a problem of mine was solved by reading an answer by you @fadden . Your contributions and work are an invaluable resource, thank you for sharing! I love how sometimes your office-mate pops up in these answers, usually bringing in some tricky solution or a different view. Almost feel like I know you two. Thanks! – natario Sep 22 '19 at 21:58
  • Canvas was locked due to prior drawing/multiple producers. Thank you! – Z. Bagley May 24 '21 at 18:36
  • I have the same issues facing while playing(video) screen saver. But not play get log for "SurfaceUtils: Failed to connect to surface". When I call clear() and where i call from my code side. @findall – Sakthivel Appavu Mar 01 '22 at 07:34