gstreamer-java / gst1-java-core

Java bindings for GStreamer 1.x
GNU Lesser General Public License v3.0
188 stars 72 forks source link

Trying to dispose element pipeline1, but it is in PLAYING instead of the NULL state. #209

Closed sblantipodi closed 3 years ago

sblantipodi commented 3 years ago

Hi, thanks for the wonderful work here guys.

Sometimes when capturing the screen I get this error: Trying to dispose element pipeline1, but it is in PLAYING instead of the NULL state.

Is this a problem of the java bindings?

This is an example of my grabber, it's based on the library examples.

/**
 * This class needs GStreamer: open source multimedia framework
 * This class uses Windows Desktop Duplication API
 */
public class GStreamerGrabber extends javax.swing.JComponent {

    private final Lock bufferLock = new ReentrantLock();
    private final AppSink videosink;
    static LinkedHashMap<Integer, LEDCoordinate> ledMatrix;

    /**
     * Creates a new instance of GstVideoComponent
     */
    public GStreamerGrabber() {

        this(new AppSink("GstVideoComponent"));

    }

    /**
     * Creates a new instance of GstVideoComponent
     */
    public GStreamerGrabber(AppSink appsink) {

        this.videosink = appsink;
        videosink.set(Constants.EMIT_SIGNALS, true);
        AppSinkListener listener = new AppSinkListener();
        videosink.connect(listener);
        String gstreamerPipeline = Constants.GSTREAMER_PIPELINE;

            gstreamerPipeline += Constants.FRAMERATE_PLACEHOLDER.replaceAll("FRAMERATE_PLACEHOLDER", FireflyLuciferin.config.getDesiredFramerate());

        StringBuilder caps = new StringBuilder(gstreamerPipeline);
        // JNA creates ByteBuffer using native byte order, set masks according to that.
        if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
            caps.append(Constants.BYTE_ORDER_BGR);
        } else {
            caps.append(Constants.BYTE_ORDER_RGB);
        }
        videosink.setCaps(new Caps(caps.toString()));
        setLayout(null);
        setOpaque(false);
        setBackground(Color.BLACK);

    }

    /**
     * Return videosink element
     * @return videosink
     */
    public Element getElement() {

        return videosink;

    }

    /**
     * Listener callback triggered every frame
     */
    private class AppSinkListener implements AppSink.NEW_SAMPLE {

        public void rgbFrame(int width, int height, IntBuffer rgbBuffer) {

            // If the EDT is still copying data from the buffer, just drop this frame
            if (!bufferLock.tryLock()) {
                return;
            }

            int intBufferSize = (width*height)-1;

            try {
               my business with rgbBuffer
            } finally {
                bufferLock.unlock();
            }
        }

        /**
         * New sample triggered every frame
         * @param elem appvideosink
         * @return flow
         */
        @Override
        public FlowReturn newSample(AppSink elem) {
            Sample sample = elem.pullSample();
            Structure capsStruct = sample.getCaps().getStructure(0);
            int w = capsStruct.getInteger(Constants.WIDTH);
            int h = capsStruct.getInteger(Constants.HEIGHT);
            Buffer buffer = sample.getBuffer();
            ByteBuffer bb = buffer.map(false);
            if (bb != null) {
                rgbFrame(w, h, bb.asIntBuffer());
                buffer.unmap();
            }
            sample.dispose();
            return FlowReturn.OK;
        }

    }

}
neilcsmith-net commented 3 years ago

This is almost certainly a bug in your code, in something not shared here. You must keep a strong reference to the pipeline, and anything that references that pipeline, or the garbage collector will attempt to clean up the pipeline you're using while it's running.

sblantipodi commented 3 years ago

@neilcsmith-net my Pipeline is declared as static,
as much as I know static variables are not garbage collected.

are there something else that can throw that error? thanks

problem happens when GStreamer "is suffering", for example when a videogame or something heavy is running on the GPU alongside with the dxgiscreencapsrc. during the screen capture I can monitor the framerate and the framerate never goes down 9FPS.

if I close the game and the GPU is not loaded with other workloads, the screen capture framerate returns to 30FPS and the problem never happen.

neilcsmith-net commented 3 years ago

@sblantipodi unless you share that code, can't help you further, sorry. Either the GC is disposing the pipeline or something in your code is, before it's been stopped properly (also make sure to call stop() before disposing).

sblantipodi commented 3 years ago

@neilcsmith-net you're always right, thanks for the help, I've seen that one of my executors where trying to play the same pipeline that is running. problem solved, thanks for helping me finding the solution.

neilcsmith-net commented 3 years ago

@sblantipodi :smile: no problem, this is a common error people encounter.