Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.gstreamer.GObject Maven / Gradle / Ivy
Go to download
Java binding for the Gstreamer framework (0.10 compatible)
/*
* Copyright (C) 2009 Levente Farkas
* Copyright (C) 2009 Tamas Korodi
* Copyright (c) 2009 Andres Colubri
* Copyright (c) 2007 Wayne Meissner
*
* This file is part of gstreamer-java.
*
* This code is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* version 3 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with this work. If not, see .
*/
package org.gstreamer;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.gstreamer.lowlevel.EnumMapper;
import org.gstreamer.lowlevel.GObjectAPI;
import org.gstreamer.lowlevel.GSignalAPI;
import org.gstreamer.lowlevel.GType;
import org.gstreamer.lowlevel.IntPtr;
import org.gstreamer.lowlevel.NativeObject;
import org.gstreamer.lowlevel.RefCountedObject;
import org.gstreamer.lowlevel.GValueAPI.GValue;
import com.sun.jna.Callback;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.PointerByReference;
import static org.gstreamer.lowlevel.GObjectAPI.GOBJECT_API;
import static org.gstreamer.lowlevel.GSignalAPI.GSIGNAL_API;
import static org.gstreamer.lowlevel.GValueAPI.GVALUE_API;
/**
* This is an abstract class providing some GObject-like facilities in a common
* base class. Not intended for direct use.
*/
public abstract class GObject extends RefCountedObject {
private static final Logger logger = Logger.getLogger(GObject.class.getName());
private static final Level LIFECYCLE = Level.FINE;
private static final Map strongReferences = new ConcurrentHashMap();
private Map, Map> callbackListeners;
private Map> signalClosures;
private final IntPtr objectID = new IntPtr(System.identityHashCode(this));
public GObject(Initializer init) {
super(init.needRef ? initializer(init.ptr, false, init.ownsHandle) : init);
logger.entering("GObject", "", new Object[] { init });
if (init.ownsHandle) {
strongReferences.put(this, Boolean.TRUE);
GOBJECT_API.g_object_add_toggle_ref(init.ptr, toggle, objectID);
if (!init.needRef) {
unref();
}
}
}
/**
* Sets the value of a GObject property.
*
* @param property The property to set.
* @param data The value for the property. This must be of the type expected
* by gstreamer.
*/
public void set(String property, Object data) {
logger.entering("GObject", "set", new Object[] { property, data });
GObjectAPI.GParamSpec propertySpec = findProperty(property);
if (propertySpec == null) {
throw new IllegalArgumentException("Unknown property: " + property);
}
final GType propType = propertySpec.value_type;
GValue propValue = new GValue();
GVALUE_API.g_value_init(propValue, propType);
if (propType.equals(GType.INT)) {
GVALUE_API.g_value_set_int(propValue, intValue(data));
} else if (propType.equals(GType.UINT)) {
GVALUE_API.g_value_set_uint(propValue, intValue(data));
} else if (propType.equals(GType.CHAR)) {
GVALUE_API.g_value_set_char(propValue, (byte) intValue(data));
} else if (propType.equals(GType.UCHAR)) {
GVALUE_API.g_value_set_uchar(propValue, (byte) intValue(data));
} else if (propType.equals(GType.LONG)) {
GVALUE_API.g_value_set_long(propValue, new NativeLong(longValue(data)));
} else if (propType.equals(GType.ULONG)) {
GVALUE_API.g_value_set_ulong(propValue, new NativeLong(longValue(data)));
} else if (propType.equals(GType.INT64)) {
GVALUE_API.g_value_set_int64(propValue, longValue(data));
} else if (propType.equals(GType.UINT64)) {
GVALUE_API.g_value_set_uint64(propValue, longValue(data));
} else if (propType.equals(GType.BOOLEAN)) {
GVALUE_API.g_value_set_boolean(propValue, booleanValue(data));
} else if (propType.equals(GType.FLOAT)) {
GVALUE_API.g_value_set_float(propValue, floatValue(data));
} else if (propType.equals(GType.DOUBLE)) {
GVALUE_API.g_value_set_double(propValue, doubleValue(data));
} else if (propType.equals(GType.STRING)) {
//
// Special conversion of java URI to gstreamer compatible uri
//
if (data instanceof URI) {
URI uri = (URI) data;
String uriString = uri.toString();
// Need to fixup file:/ to be file:/// for gstreamer
if ("file".equals(uri.getScheme()) && uri.getHost() == null) {
final String path = uri.getRawPath();
uriString = "file://" + path;
}
GVALUE_API.g_value_set_string(propValue, uriString);
} else {
GVALUE_API.g_value_set_string(propValue, data.toString());
}
} else if (propType.equals(GType.OBJECT)) {
GVALUE_API.g_value_set_object(propValue, (GObject) data);
} else if (GVALUE_API.g_value_type_transformable(GType.INT64, propType)) {
transform(data, GType.INT64, propValue);
} else if (GVALUE_API.g_value_type_transformable(GType.LONG, propType)) {
transform(data, GType.LONG, propValue);
} else if (GVALUE_API.g_value_type_transformable(GType.INT, propType)) {
transform(data, GType.INT, propValue);
} else if (GVALUE_API.g_value_type_transformable(GType.DOUBLE, propType)) {
transform(data, GType.DOUBLE, propValue);
} else if (GVALUE_API.g_value_type_transformable(GType.FLOAT, propType)) {
transform(data, GType.FLOAT, propValue);
} else {
// Old behaviour
GOBJECT_API.g_object_set(this, property, data);
return;
}
GOBJECT_API.g_object_set_property(this, property, propValue);
GVALUE_API.g_value_unset(propValue); // Release any memory
}
/**
* Gets the default value set to GObject property.
* @param property The name of the property.
* @return A java value representing the GObject property's default value.
*/
public Object getPropertyDefaultValue(String property) {
GObjectAPI.GParamSpec propertySpec = findProperty(property);
if (propertySpec == null) {
throw new IllegalArgumentException("Unknown property: " + property);
}
final GType propType = propertySpec.value_type;
return findProperty(property, propType).getDefault();
}
/**
* Gets the minimum value should be set to GObject property.
* @param property The name of the property.
* @return A java value representing the GObject property's minimum value.
*/
public Object getPropertyMinimumValue(String property) {
GObjectAPI.GParamSpec propertySpec = findProperty(property);
if (propertySpec == null) {
throw new IllegalArgumentException("Unknown property: " + property);
}
final GType propType = propertySpec.value_type;
return findProperty(property, propType).getMinimum();
}
/**
* Gets the maximum value should be set to GObject property.
* @param property The name of the property.
* @return A java value representing the GObject property's maximum value.
*/
public Object getPropertyMaximumValue(String property) {
GObjectAPI.GParamSpec propertySpec = findProperty(property);
if (propertySpec == null) {
throw new IllegalArgumentException("Unknown property: " + property);
}
final GType propType = propertySpec.value_type;
return findProperty(property, propType).getMaximum();
}
/**
* Gets the current value of a GObject property.
*
* @param property The name of the property to get.
*
* @return A java value representing the GObject property value.
*/
public Object get(String property) {
logger.entering("GObject", "get", new Object[] { property });
GObjectAPI.GParamSpec propertySpec = findProperty(property);
if (propertySpec == null) {
throw new IllegalArgumentException("Unknown property: " + property);
}
final GType propType = propertySpec.value_type;
GValue propValue = new GValue();
GVALUE_API.g_value_init(propValue, propType);
GOBJECT_API.g_object_get_property(this, property, propValue);
if (propType.equals(GType.INT)) {
return GVALUE_API.g_value_get_int(propValue);
} else if (propType.equals(GType.UINT)) {
return GVALUE_API.g_value_get_uint(propValue);
} else if (propType.equals(GType.CHAR)) {
return Integer.valueOf(GVALUE_API.g_value_get_char(propValue));
} else if (propType.equals(GType.UCHAR)) {
return Integer.valueOf(GVALUE_API.g_value_get_uchar(propValue));
} else if (propType.equals(GType.LONG)) {
return GVALUE_API.g_value_get_long(propValue).longValue();
} else if (propType.equals(GType.ULONG)) {
return GVALUE_API.g_value_get_ulong(propValue).longValue();
} else if (propType.equals(GType.INT64)) {
return GVALUE_API.g_value_get_int64(propValue);
} else if (propType.equals(GType.UINT64)) {
return GVALUE_API.g_value_get_uint64(propValue);
} else if (propType.equals(GType.BOOLEAN)) {
return GVALUE_API.g_value_get_boolean(propValue);
} else if (propType.equals(GType.FLOAT)) {
return GVALUE_API.g_value_get_float(propValue);
} else if (propType.equals(GType.DOUBLE)) {
return GVALUE_API.g_value_get_double(propValue);
} else if (propType.equals(GType.STRING)) {
return GVALUE_API.g_value_get_string(propValue);
} else if (propType.equals(GType.OBJECT)) {
return GVALUE_API.g_value_dup_object(propValue);
} else if (GVALUE_API.g_value_type_transformable(propType, GType.OBJECT)) {
return GVALUE_API.g_value_dup_object(transform(propValue, GType.OBJECT));
} else if (GVALUE_API.g_value_type_transformable(propType, GType.INT)) {
return GVALUE_API.g_value_get_int(transform(propValue, GType.INT));
} else if (GVALUE_API.g_value_type_transformable(propType, GType.INT64)) {
return GVALUE_API.g_value_get_int64(transform(propValue, GType.INT64));
}
else {
throw new IllegalArgumentException("Unknown conversion from GType=" + propType);
}
}
/**
* Gets the pointer to the the value of the specified property.
*
* @param property The name of the property to get.
*
* @return A java pointer.
*/
public Pointer getPointer(String property) {
logger.entering("GObject", "getPointer", new Object[] { property });
GObjectAPI.GParamSpec propertySpec = findProperty(property);
if (propertySpec == null) {
throw new IllegalArgumentException("Unknown property: " + property);
}
PointerByReference refPtr = new PointerByReference();
GOBJECT_API.g_object_get(this, property, refPtr, null);
if (refPtr != null) {
Pointer ptr = refPtr.getValue();
return ptr;
} else {
throw new IllegalArgumentException("Got NULL pointer for property="+property);
}
}
private static GValue transform(GValue src, GType dstType) {
GValue dst = new GValue();
GVALUE_API.g_value_init(dst, dstType);
GVALUE_API.g_value_transform(src, dst);
return dst;
}
private static void transform(Object data, GType type, GValue dst) {
GValue src = new GValue();
GVALUE_API.g_value_init(src, type);
setGValue(src, type, data);
GVALUE_API.g_value_transform(src, dst);
}
private static boolean setGValue(GValue value, GType type, Object data) {
if (type.equals(GType.INT)) {
GVALUE_API.g_value_set_int(value, intValue(data));
} else if (type.equals(GType.UINT)) {
GVALUE_API.g_value_set_uint(value, intValue(data));
} else if (type.equals(GType.CHAR)) {
GVALUE_API.g_value_set_char(value, (byte) intValue(data));
} else if (type.equals(GType.UCHAR)) {
GVALUE_API.g_value_set_uchar(value, (byte) intValue(data));
} else if (type.equals(GType.LONG)) {
GVALUE_API.g_value_set_long(value, new NativeLong(longValue(data)));
} else if (type.equals(GType.ULONG)) {
GVALUE_API.g_value_set_ulong(value, new NativeLong(longValue(data)));
} else if (type.equals(GType.INT64)) {
GVALUE_API.g_value_set_int64(value, longValue(data));
} else if (type.equals(GType.UINT64)) {
GVALUE_API.g_value_set_uint64(value, longValue(data));
} else if (type.equals(GType.BOOLEAN)) {
GVALUE_API.g_value_set_boolean(value, booleanValue(data));
} else if (type.equals(GType.FLOAT)) {
GVALUE_API.g_value_set_float(value, floatValue(data));
} else if (type.equals(GType.DOUBLE)) {
GVALUE_API.g_value_set_double(value, doubleValue(data));
} else {
return false;
}
return true;
}
private static boolean booleanValue(Object value) {
if (value instanceof Boolean) {
return ((Boolean) value).booleanValue();
} else if (value instanceof Number) {
return ((Number) value).intValue() != 0;
} else if (value instanceof String) {
return Boolean.parseBoolean((String) value);
}
throw new IllegalArgumentException("Expected boolean value, not " + value.getClass());
}
private static int intValue(Object value) {
if (value instanceof Number) {
return ((Number) value).intValue();
} else if (value instanceof String) {
return Integer.parseInt((String) value);
}
throw new IllegalArgumentException("Expected integer value, not " + value.getClass());
}
private static long longValue(Object value) {
if (value instanceof Number) {
return ((Number) value).longValue();
} else if (value instanceof String) {
return Long.parseLong((String) value);
}
throw new IllegalArgumentException("Expected long value, not " + value.getClass());
}
private static float floatValue(Object value) {
if (value instanceof Number) {
return ((Number) value).floatValue();
} else if (value instanceof String) {
return Float.parseFloat((String) value);
}
throw new IllegalArgumentException("Expected float value, not " + value.getClass());
}
private static double doubleValue(Object value) {
if (value instanceof Number) {
return ((Number) value).doubleValue();
} else if (value instanceof String) {
return Double.parseDouble((String) value);
}
throw new IllegalArgumentException("Expected double value, not " + value.getClass());
}
protected void disposeNativeHandle(Pointer ptr) {
logger.log(LIFECYCLE, "Removing toggle ref " + getClass().getSimpleName() + " (" + ptr + ")");
GOBJECT_API.g_object_remove_toggle_ref(ptr, toggle, objectID);
}
@Override
protected void ref() {
GOBJECT_API.g_object_ref(this);
}
@Override
protected void unref() {
GOBJECT_API.g_object_unref(this);
}
protected void invalidate() {
try {
// Need to increase the ref count before removing the toggle ref, so
// ensure the native object is not destroyed.
if (ownsHandle.get()) {
ref();
// Disconnect the callback.
GOBJECT_API.g_object_remove_toggle_ref(handle(), toggle, objectID);
}
strongReferences.remove(this);
} finally {
super.invalidate();
}
}
protected NativeLong g_signal_connect(String signal, Callback callback) {
logger.entering("GObject", "g_signal_connect", new Object[] { signal, callback });
return GOBJECT_API.g_signal_connect_data(this, signal, callback, null, null, 0);
}
/* private GList objectFor(Pointer ptr) {
return GList.valueOf(ptr);
}*/
abstract protected class GCallback {
protected final Callback cb;
protected final NativeLong id;
volatile boolean connected = false;
protected GCallback(NativeLong id, Callback cb) {
this.id = id != null ? id : new NativeLong(0);
this.cb = cb;
this.connected = this.id.intValue() != 0;
}
void remove() {
if (connected) {
disconnect();
connected = false;
}
}
abstract protected void disconnect();
@Override
protected final void finalize() {
// Ensure the native callback is removed
remove();
}
}
private final class SignalCallback extends GCallback {
protected SignalCallback(String signal, Callback cb) {
super(g_signal_connect(signal, cb), cb);
if (!connected) {
throw new IllegalArgumentException(String.format("Failed to connect signal '%s'", signal));
}
}
synchronized protected void disconnect() {
GOBJECT_API.g_signal_handler_disconnect(GObject.this, id);
}
}
private synchronized final Map, Map> getCallbackMap() {
if (callbackListeners == null) {
callbackListeners = new ConcurrentHashMap, Map>();
}
return callbackListeners;
}
private synchronized final Map> getClosureMap() {
if (signalClosures == null) {
signalClosures = new ConcurrentHashMap>();
}
return signalClosures;
}
protected synchronized void addCallback(Class listenerClass, T listener, GCallback cb) {
final Map, Map> signals = getCallbackMap();
Map m = signals.get(listenerClass);
if (m == null) {
m = new HashMap();
signals.put(listenerClass, m);
}
m.put(listener, cb);
}
public synchronized void removeCallback(Class listenerClass, T listener) {
final Map, Map> signals = getCallbackMap();
Map map = signals.get(listenerClass);
if (map != null) {
GCallback cb = map.remove(listener);
if (cb != null) {
cb.remove();
}
if (map.isEmpty()) {
signals.remove(listenerClass);
if (callbackListeners.isEmpty()) {
callbackListeners = null;
}
}
}
}
public void connect(Class listenerClass, T listener, Callback cb) {
String signal = listenerClass.getSimpleName().toLowerCase().replaceAll("_", "-");
connect(signal, listenerClass, listener, cb);
}
public synchronized void connect(String signal, Class listenerClass, T listener, Callback cb) {
addCallback(listenerClass, listener, new SignalCallback(signal, cb));
}
public synchronized void disconnect(Class listenerClass, T listener) {
removeCallback(listenerClass, listener);
}
private final class ClosureProxy implements GSignalAPI.GSignalCallbackProxy {
private final Closure closure;
private final Method method;
private final Class>[] parameterTypes;
NativeLong id;
protected ClosureProxy(String signal, Closure closure) {
this.closure = closure;
Method invoke = null;
for (Method m : closure.getClass().getDeclaredMethods()) {
if (m.getName().equals(Closure.METHOD_NAME)) {
invoke = m;
break;
}
}
if (invoke == null) {
throw new IllegalArgumentException(closure.getClass()
+ " does not have an invoke method");
}
invoke.setAccessible(true);
this.method = invoke;
//
// The closure does not have a 'user_data' pointer, so push it in as the
// last arg. The last arg will be dropped later in callback()
//
parameterTypes = new Class[method.getParameterTypes().length + 1];
parameterTypes[parameterTypes.length - 1] = Pointer.class;
for (int i = 0; i < method.getParameterTypes().length; ++i) {
Class> paramType = method.getParameterTypes()[i];
Class> nativeType = paramType;
if (ClockTime.class.isAssignableFrom(paramType)) {
nativeType = long.class;
} else if (NativeObject.class.isAssignableFrom(paramType)) {
nativeType = Pointer.class;
} else if (Enum.class.isAssignableFrom(paramType)) {
nativeType = int.class;
} else if (String.class.isAssignableFrom(paramType)) {
nativeType = Pointer.class;
} else if (Boolean.class.isAssignableFrom(paramType)) {
nativeType = int.class;
}
parameterTypes[i] = nativeType;
}
NativeLong connectID = GSIGNAL_API.g_signal_connect_data(GObject.this,
signal, this, null, null, 0);
if (connectID.intValue() == 0) {
throw new IllegalArgumentException(String.format("Failed to connect signal '%s'", signal));
}
this.id = connectID;
}
synchronized protected void disconnect() {
if (id != null && id.intValue() != 0) {
GOBJECT_API.g_signal_handler_disconnect(GObject.this, id);
id = null;
}
}
@Override
protected void finalize() {
// Ensure the native callback is removed
disconnect();
}
@SuppressWarnings("unchecked")
public Object callback(Object[] parameters) {
try {
// Drop the last arg - it is the 'user_data' pointer
Object[] methodParameters = new Object[parameters.length - 1];
for (int i = 0; i < methodParameters.length; ++i) {
Class paramType = method.getParameterTypes()[i];
Object nativeParam = parameters[i];
Object javaParam = nativeParam;
if (nativeParam == null) {
continue;
}
if (ClockTime.class.isAssignableFrom(paramType)) {
javaParam = ClockTime.valueOf((Long) nativeParam,
TimeUnit.NANOSECONDS);
} else if (NativeObject.class.isAssignableFrom(paramType)) {
javaParam = NativeObject.objectFor((Pointer) nativeParam,
paramType, 1, true);
} else if (Enum.class.isAssignableFrom(paramType)) {
javaParam = EnumMapper.getInstance().valueOf((Integer) nativeParam,
paramType);
} else if (String.class.isAssignableFrom(paramType)) {
javaParam = ((Pointer) nativeParam).getString(0);
} else if (Boolean.class.isAssignableFrom(paramType)) {
javaParam = Boolean.valueOf(((Integer) nativeParam).intValue() != 0);
} else {
javaParam = nativeParam;
}
methodParameters[i] = javaParam;
}
return method.invoke(closure, methodParameters);
} catch (Throwable t) {
return Integer.valueOf(0);
}
}
public Class>[] getParameterTypes() {
return parameterTypes;
}
public Class> getReturnType() {
return method.getReturnType();
}
}
public synchronized void connect(String signal, Closure closure) {
final Map> signals = getClosureMap();
Map m = signals.get(signal);
if (m == null) {
m = new HashMap();
signals.put(signal, m);
}
m.put(closure, new ClosureProxy(signal, closure));
}
public synchronized void disconnect(String signal, Closure closure) {
final Map> signals = signalClosures;
if (signals == null) {
return;
}
Map map = signals.get(signal);
if (map != null) {
ClosureProxy cb = map.remove(signal);
if (cb != null) {
cb.disconnect();
}
if (map.isEmpty()) {
signals.remove(signal);
if (signalClosures.isEmpty()) {
signalClosures = null;
}
}
}
}
public static GObject objectFor(Pointer ptr, Class extends GObject> defaultClass) {
return GObject.objectFor(ptr, defaultClass, true);
}
public static T objectFor(Pointer ptr, Class defaultClass, boolean needRef) {
logger.entering("GObject", "objectFor", new Object[] { ptr, defaultClass, needRef });
return NativeObject.objectFor(ptr, defaultClass, needRef);
}
private GObjectAPI.GParamSpec findProperty(String propertyName) {
Pointer ptr = GOBJECT_API.g_object_class_find_property(handle().getPointer(0), propertyName);
if (ptr == null)
return null;
return new GObjectAPI.GParamSpec(ptr);
}
private GObjectAPI.GParamSpecTypeSpecific findProperty(String propertyName, GType type) {
Pointer ptr = GOBJECT_API.g_object_class_find_property(handle().getPointer(0), propertyName);
if (type.equals(GType.INT))
return new GObjectAPI.GParamSpecInt(ptr);
else if(type.equals(GType.UINT))
return new GObjectAPI.GParamSpecUInt(ptr);
else if(type.equals(GType.CHAR))
return new GObjectAPI.GParamSpecChar(ptr);
else if(type.equals(GType.UCHAR))
return new GObjectAPI.GParamSpecUChar(ptr);
else if(type.equals(GType.BOOLEAN))
return new GObjectAPI.GParamSpecBoolean(ptr);
else if(type.equals(GType.LONG))
return new GObjectAPI.GParamSpecLong(ptr);
else if(type.equals(GType.INT64))
return new GObjectAPI.GParamSpecInt64(ptr);
else if(type.equals(GType.FLOAT))
return new GObjectAPI.GParamSpecFloat(ptr);
else if(type.equals(GType.DOUBLE))
return new GObjectAPI.GParamSpecDouble(ptr);
else if(type.equals(GType.STRING))
return new GObjectAPI.GParamSpecString(ptr);
throw new IllegalArgumentException("Unknown conversion from GType=" + type);
}
/*
* Hooks to/from native disposal
*/
private static final GObjectAPI.GToggleNotify toggle = new GObjectAPI.GToggleNotify() {
public void callback(Pointer data, Pointer ptr, boolean is_last_ref) {
/*
* Manage the strong reference to this instance. When this is the last
* reference to the underlying object, remove the strong reference so
* it can be garbage collected. If it is owned by someone else, then make
* it a strong ref, so the java GObject for the underlying C object can
* be retained for later retrieval
*/
GObject o = (GObject) NativeObject.instanceFor(ptr);
if (o == null) {
return;
}
logger.log(LIFECYCLE, "toggle_ref " + o.getClass().getSimpleName() +
" (" + ptr + ")" + " last_ref=" + is_last_ref);
if (is_last_ref) {
strongReferences.remove(o);
} else {
strongReferences.put(o, Boolean.TRUE);
}
}
};
}