GStreamer Java Binding cleanup issue after playback from AppSrc

804 views Asked by At

I'm trying to use GStreamer Java Binding on Raspberry Pi to play some dynamic audio data which is Opus encoded. I'm almost done. My final issue is I cannot have GStreamer clean up nicely when the data reaching the end. My testing code is as below.

package org.freedesktop.gstreamer.examples;

import java.awt.Dimension;
import java.awt.EventQueue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;

import javax.swing.JFrame;
import org.freedesktop.gstreamer.Bin;
import org.freedesktop.gstreamer.Buffer;
import org.freedesktop.gstreamer.Bus;
import org.freedesktop.gstreamer.Element;
import org.freedesktop.gstreamer.ElementFactory;
import org.freedesktop.gstreamer.FlowReturn;
import org.freedesktop.gstreamer.Gst;
import org.freedesktop.gstreamer.GstObject;
import org.freedesktop.gstreamer.Message;
import org.freedesktop.gstreamer.Pad;
import org.freedesktop.gstreamer.PadLinkReturn;
import org.freedesktop.gstreamer.Pipeline;
import org.freedesktop.gstreamer.State;
import org.freedesktop.gstreamer.elements.AppSrc;
import org.freedesktop.gstreamer.lowlevel.MainLoop;

/**
 *
 * @author Superrei
 */
public class AudioTest {

    //private static final int BUFFER_SIZE = 4096;
    private static byte[] soundBytes = null;
    private static int pointer = 0;
    private static String filename = null;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        Gst.init("AudioTest", args);
        filename = args[0];
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                File soundFile = new File(filename);
                FileInputStream inStream = null;

                if (soundFile.exists()){
                    try {
                        System.out.println("Read media file.");
                        long fileSize = soundFile.length();
                        soundBytes = new byte[(int)fileSize];
                        inStream = new FileInputStream(soundFile);
                        int byteCount = inStream.read(soundBytes);
                        System.out.println("Number of byte read: " + byteCount);
                    } catch (IOException e) {
                        e.printStackTrace();
                        return;
                    }
                }
                final MainLoop loop = new MainLoop(); 
                AppSrc source = (AppSrc)ElementFactory.make("appsrc", "app-source");
                Element demuxer = ElementFactory.make("oggdemux", "ogg-demuxer");
                final Element decoder = ElementFactory.make("opusdec", "opus-decoder");
                Element conv = ElementFactory.make("audioconvert", "converter");
                Element sink = ElementFactory.make("autoaudiosink", "audio-output");

                //source.set("location", "/home/pi/gstJavaTest/transcript.ogg");

                Pipeline pipe = new Pipeline();
                Bus bus = pipe.getBus();
                bus.connect(new Bus.EOS() {

                    @Override
                    public void endOfStream(GstObject source) {
                        loop.quit();
                    }

                });
                bus.connect(new Bus.ERROR() {

                    @Override
                    public void errorMessage(GstObject source, int code, String message) {
                        System.out.println("Error detected");
                        System.out.println("Error source: " + source.getName());
                        System.out.println("Error code: " + code);
                        System.out.println("Message: " + message);
                        loop.quit();
                    }
                });

                pipe.addMany(source, demuxer, decoder, conv, sink);
                source.link(demuxer);
                source.set("emit-signals", true);
                source.connect(new AppSrc.NEED_DATA() {

                    private ByteBuffer bb = ByteBuffer.wrap(soundBytes);

                    @Override
                    public void needData(AppSrc elem, int size) {
                        if (bb.hasRemaining()) {
                            // TODO Auto-generated method stub
                            System.out.println("needData: size = " + size);
                            byte[] tempBuffer;
                            Buffer buf;
                            if (bb.remaining() > size) {
                                tempBuffer = new byte[size];
                                buf = new Buffer(size);
                            } else {
                                tempBuffer = new byte[bb.remaining()];
                                buf = new Buffer(bb.remaining());
                            }
                            //System.out.println("Buffer size: " + buf.map(true).remaining());
                            //System.arraycopy(soundBytes, pointer, tempBuffer, 0, size);
                            bb.get(tempBuffer);
                            System.out.println("Temp Buffer remaining bytes: " + bb.remaining());
                            buf.map(true).put(ByteBuffer.wrap(tempBuffer));
                            elem.pushBuffer(buf);
                        } else {
                            elem.emit("end-of-stream", elem);
                        }
                        //pointer += size;
                    }
                });

                source.connect(new AppSrc.ENOUGH_DATA() {

                    @Override
                    public void enoughData(AppSrc elem) {
                        System.out.println("enoughData: " + elem.toString());

                    }
                });
                source.connect(new AppSrc.END_OF_STREAM() {

                    @Override
                    public FlowReturn endOfStream(AppSrc elem) {
                        // TODO Auto-generated method stub
                        return FlowReturn.OK;
                    }
                });
                Element.linkMany(decoder, conv, sink);
                demuxer.connect(new Element.PAD_ADDED() {

                    @Override
                    public void padAdded(Element element, Pad pad) {
                        System.out.println("Dynamic pad created, linking demuxer/decoder");
                        System.out.println("Pad name: " + pad.getName());
                        System.out.println("Pad type: " + pad.getTypeName());
                        Pad sinkPad = decoder.getStaticPad("sink");
                        //boolean linked = element.link(decoder);
                        PadLinkReturn ret = pad.link(sinkPad);
                        if (ret.equals(PadLinkReturn.OK)){
                            System.out.println("Pad linked.");
                        } else {
                            System.out.println("Pad link failed");
                        }
                        //System.out.println("Pad Link Return: " + ret.toString());
                    }

                });

                System.out.println("Now Playing...");
                pipe.play();
                System.out.println("Running...");
                loop.run();
                System.out.println("Returned, stopping playback");
                pipe.stop();
            }
        });
    }

}

I don't use playbin since the audio data will come in byte stream finally.

The test runs well until the end. When I reach the end of file. I get error messages and follow by JVM crush. The error message is like this:

(AudioTest:4643): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed

(AudioTest:4643): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed Returned, stopping playback

(AudioTest:4643): GLib-GObject-CRITICAL **: g_object_ref: assertion 'G_IS_OBJECT (object)' failed

(AudioTest:4643): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed

(AudioTest:4643): GLib-GObject-CRITICAL **: g_object_ref: assertion 'G_IS_OBJECT (object)' failed

(AudioTest:4643): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed

(AudioTest:4643): GLib-GObject-CRITICAL **: g_value_set_object: assertion 'G_IS_OBJECT (v_object)' failed
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x63d8b8ec, pid=4643, tid=1658819680
#
# JRE version: Java(TM) SE Runtime Environment (8.0_65-b17) (build
1.8.0_65-b17)
# Java VM: Java HotSpot(TM) Client VM (25.65-b01 mixed mode linux-arm )
# Problematic frame:
# C  [libglib-2.0.so.0+0x928ec]  g_rec_mutex_lock+0x8
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /home/pi/gstJavaTest/hs_err_pid4643.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
# Aborted

I cannot find any example on using AppSrc to play something with an end. Any help will be appreciated.

1

There are 1 answers

0
Alex Hui On

I fixed the problem. The solution is using AppSrc.endOfStream() method rather than emitting a signal. Also I removed the callback for AppSrc.END_OF_STREAM, since it is not necessary. So my callback for needData is like this:

            @Override
            public void needData(AppSrc elem, int size) {
                if (bb.hasRemaining()) {
                    System.out.println("needData: size = " + size);
                    byte[] tempBuffer;
                    Buffer buf;
                    int copyLength = (bb.remaining() >= size) ? size : bb.remaining();
                    tempBuffer = new byte[copyLength];
                    buf = new Buffer(copyLength);
                    //System.arraycopy(soundBytes, pointer, tempBuffer, 0, copyLength);
                    System.out.println("Buffer size: " + buf.map(true).remaining());
                    bb.get(tempBuffer);
                    System.out.println("Temp Buffer remaining bytes: " + bb.remaining());
                    buf.map(true).put(ByteBuffer.wrap(tempBuffer));
                    elem.pushBuffer(buf);
                    //pointer += size;
                } else {
                    elem.endOfStream();
                }
            }