Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

native_window_api_connect returned an error: Invalid argument (-22)

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?

like image 560
saa Avatar asked Feb 03 '14 12:02

saa


1 Answers

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

like image 170
fadden Avatar answered Oct 26 '22 15:10

fadden