[gst-devel] Java Binding Plea for Help

Andrew Taylor andy at benow.ca
Sat Mar 13 13:51:12 CET 2004


Ok, I'm breaking down and formally asking for help for this one.

The gstreamer-java bindings are this close -><- to working.  They
compile and present gstreamer pipeline creation to java, have ant build
and javadoc and would be a great backend for java multimedia, if they
worked.  I'm running into a problem where it would seem the java JNI
wrapping stomps on the the environment.  I've written a test program
which works fine natively, but dies when running within JNI.  It does
not use the gst-java binding directly, but results in the same error
that I'm seeing when using the gst-java bindings, so squash this and the
bindings will be that much closer.  Here's a description of the problem,
starting first with the symptom:

-- run output --

$ bin/testPiper -f ../test.mp3
running: LD_LIBRARY_PATH=:src/c java -cp
build:lib/log4j-1.2.4.jar:lib/benow-util.jar:.:/opt/sun-jdk-1.4.2.03/jre/lib:/opt/sun-jdk-1.4.2.03/lib/tools.jar:/opt/sun-jdk-1.4.2.03/jre/lib/rt.jar test.org.benow.bemoan.TestPiper -f ../test.mp3
initialized.
loading file: /usr/lib/gstreamer-0.7/libgstoptscheduler.so
** (process:8090): CRITICAL **: how to remove plugins?
 
(process:8090): GStreamer-CRITICAL **: file gstscheduler.c: line 910
(gst_scheduler_factory_create): assertion `factory->type != 0' failed
 
GStreamer-ERROR **: Critical error: could not get scheduler "opt"
Are you sure you have a registry ?
Run gst-register as root if you haven't done so yet.
aborting...

--

bin/testPiper is a shell script which calls java which calls a native
library which runs the basic 'helloworld' mp3 player pipeline.

The base application is:

-- Bootstrap:

public class TestPiper extends Application {
	private AppArgument fileArg;
	static {
		setInstance(new TestPiper());
	}
	
	public TestPiper() {
		super("test native gstreamer");
		fileArg=addArgument("-f", "File to play");
	}

	// @see org.benow.util.app.Application#run()
	protected void run() throws Exception {
		Piper piper=new Piper();
		piper.init();
		piper.setFile(fileArg.getValue());
		piper.play();
	}
}

Which creates and calls methods in the Piper class, where the native
calls are hidden.

-- Java rep of native backend

public class Piper {

	static {
		Runtime.getRuntime().loadLibrary("piper");
	}

	public Piper() {
		super();
	}
	
	public void init() throws PiperException {
		PiperException.assertError(n_init()==0,"initializing");
	}
	private native int n_init();
	
	public void setFile(String file) throws PiperException {
		PiperException.assertError(n_setFile(file)==0,"setting file");
	}
	private native int n_setFile(String file);
	
	public void play() throws PiperException {
		PiperException.assertError(n_play()==0,"playing");
	}
	private native int n_play();
}

The native calls map to the JNI code, which calls native piper.c
procedures.

-- Java to native mapping

#include <jni.h>
#include <stdlib.h>
#include <gst/gst.h>
#include "piper.h"

piper_t* piper;

/*
 * Class:     org_benow_bemoan_Piper
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT int JNICALL Java_org_benow_bemoan_Piper_n_1init(JNIEnv *env,
jobject obj) 
{
	piper=piper_new();
  if (piper) return 0;
  return 1;
}

/*
 * Class:     org_benow_bemoan_Piper
 * Method:    setFile
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT int JNICALL Java_org_benow_bemoan_Piper_n_1setFile(JNIEnv
*env, jobject obj, jstring file) 
{
  return piper_set_file(piper,(*env)->GetStringUTFChars(env, file, 0));
}

/*
 * Class:     org_benow_bemoan_Piper
 * Method:    play
 * Signature: ()V
 */
JNIEXPORT int JNICALL Java_org_benow_bemoan_Piper_n_1play(JNIEnv *env,
jobject obj) 
{
  return piper_play(piper);
}

--

The native piper library itself contains the gstreamer calls to create
the pipeline, populate arguments and run it.

-- native library

#include <stdlib.h>
#include "piper.h"

piper_t *piper_new()
{
	piper_t *piper;
	
	if (!(piper = (piper_t *)calloc(1, sizeof(piper_t)))) {
		return NULL;
	}

 	const int argc=1;
	const char* argv[argc];
	argv[0]="--gst-debug-level=5";
//	args[0]="--gst-scheduler=basicgthreads";
	gst_init (&argc, &argv);
	g_print("initialized.\n");
//  gst_init (0, NULL);

  /* create a new bin to hold the elements 
	NOTE: dies during this call
  */
  piper->bin = gst_pipeline_new ("pipeline");
  g_assert (piper->bin);

  /* create a disk reader */
  piper->filesrc = gst_element_factory_make ("filesrc", "disk_source");
  g_assert (piper->filesrc);

  /* now it's time to get the decoder */
  piper->decoder = gst_element_factory_make ("mad", "decode");
  g_assert(piper->decoder);
  
  /* and an audio sink */
  piper->output = gst_element_factory_make ("osssink", "play_audio");
  g_assert (piper->output);

  /* add objects to the main pipeline */
  gst_bin_add_many (GST_BIN (piper->bin), piper->filesrc,
piper->decoder, piper->output, NULL);

  /* link the elements */
  gst_element_link_many (piper->filesrc, piper->decoder, piper->output,
NULL);
  
  return piper;
}

int piper_free(piper_t *self)
{
  if (!self) return 1;

  gst_element_set_state (GST_ELEMENT (self->bin), GST_STATE_NULL);
  gst_object_unref (GST_OBJECT (self->bin));

  free(self);

  return 0;
}


int piper_set_file(piper_t *piper, const char* location) {
  g_object_set (G_OBJECT (piper->filesrc), "location", location, NULL);
  return 0;
}

int piper_play(piper_t *piper) {
  /* start playing */
  gst_element_set_state (piper->bin, GST_STATE_PLAYING);

  while (gst_bin_iterate (GST_BIN (piper->bin)));

  /* stop the bin */
  gst_element_set_state (piper->bin, GST_STATE_NULL);
  
  return 0;
}

--

If you've made it this far, congrats!

So, the call filters through all the linkings to result in a call to
gstreamer methods, and fails on creation of the pipeline with the above
message (ie not being able to resolve opt).  As I've built piper.c
rather modularly (architecture based on libshout), I can call it from
native code directly:

--

#include "piper.h"

int main (int argc, char *argv[]) 
{
  if (argc != 2) {
    g_print ("usage: %s <mp3 file>\n", argv[0]);
    exit (-1);
  }

  piper_t *piper=piper_new(argc,argv);

  piper_set_file(piper,argv[1]);

  piper_play(piper);

  piper_free(piper);
  exit (0);
}

--

This has the same effect as the java calls, but without the JNI wrapping
(and environment stomping, it would seem), and runs excellently without
any of the problems seen when wrapped.  So, due to the JNI wrapping the
environment is changed in such a way that pipeline element detection
fails.

My question is this: what could be changed in the startup environment
which could cause failure in pipeline element lookup?  As the native
code works well, it is shown that all elements are in the right place,
registered, etc.  

I hope someone out there has an answer for me... in the mean time, I'm
implementing a native socket server which responds to pipeline building
commands and informs of events... a pain in the butt for this Java
centric, give-me-high-level-or-give-me-a-good-library guy.

Thanks,

Andrew Taylor (benow)









More information about the gstreamer-devel mailing list