JavaOSC Multicast fähig machen

Will man unter Java oder Android OSC nutzen, dann kommt man an Libraries wie JavaOSC nicht vorbei, möchte man die ganze OSC Message Logik nicht selbst implementieren. Leider können die wenigsten OSC Libs mit Multicast IP`s umgehen. Hier ein Beispiel wie man JavaOSC doch noch Multicast fähig bekommt.

Die Klasse OSCPortIn kann man entweder mittels Port oder DatagramSocket instanziieren. Für unseren Multicast ist das leider keine Option. Daher werde ich in meinem Beispiel auch keine Erweiterung der Klasse OSCPortIn machen, sondern meine eigene OSCPortInMulticast Klasse erstellen. Und hier das coding meiner Multicast Klasse:

import com.illposed.osc.AddressSelector;
import com.illposed.osc.OSCListener;
import com.illposed.osc.OSCMessage;
import com.illposed.osc.OSCPacket;
import com.illposed.osc.OSCPort;
import com.illposed.osc.OSCPortIn;
import com.illposed.osc.utility.OSCByteArrayToJavaConverter;
import com.illposed.osc.utility.OSCPacketDispatcher;
import com.illposed.osc.utility.OSCPatternAddressSelector;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;

/**
 * Created by Anton Hartl on 03.02.2018.
 */

public class OSCPortInMulticast implements Runnable {
    static final int BUFFER_SIZE = 65507;

    /**
     * state for listening
     */
    private boolean listening;
    private final OSCByteArrayToJavaConverter converter;
    private final OSCPacketDispatcher dispatcher;
    private Thread listeningThread;
    private MulticastSocket socket;

    public OSCPortInMulticast(String multiCastIp, int port) throws UnknownHostException {
        InetAddress ip = InetAddress.getByName(multiCastIp);

        byte[] buffer = new byte[BUFFER_SIZE];

        try {
            this.socket = new MulticastSocket(port);
            this.socket.setLoopbackMode(true);
            this.socket.joinGroup(ip);
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        this.converter = new OSCByteArrayToJavaConverter();
        this.dispatcher = new OSCPacketDispatcher();
        this.listeningThread = null;
    }

    public void run() {
        final byte[] buffer = new byte[BUFFER_SIZE];
        final DatagramPacket packet = new DatagramPacket(buffer, BUFFER_SIZE);

        while (listening) {
            try {
                try {
                    this.socket.receive(packet);
                } catch (SocketException ex) {
                    if (listening) {
                        throw ex;
                    } else {
                        // if we closed the socket while receiving data,
                        // the exception is expected/normal, so we hide it
                        continue;
                    }
                }
                final OSCPacket oscPacket = converter.convert(buffer, packet.getLength());
                dispatcher.dispatchPacket(oscPacket);
            } catch (IOException ex) {
                ex.printStackTrace(); 
            }
        }
    }

    public void close() {
        try {
            this.socket.leaveGroup(this.socket.getInetAddress());
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.socket.close();
    }

    public void startListening() {

        if (!isListening()) { // NOTE This is not thread-save
            listening = true;
            listeningThread = new Thread(this);
            // The JVM exits when the only threads running are all daemon threads.
            listeningThread.setDaemon(true);
            listeningThread.start();
        }
    }

    public void stopListening() {

        listening = false;
        if (listeningThread != null) { // NOTE This is not thread-save
            listeningThread.interrupt();
        }
        listeningThread = null;
    }

    public boolean isListening() {
        return listening;
    }

    public void addListener(final String addressSelector, final OSCListener listener) {
        this.addListener(new OSCPatternAddressSelector(addressSelector), listener);
    }

    public void addListener(final AddressSelector addressSelector, final OSCListener listener) {
        dispatcher.addListener(addressSelector, listener);
    }

Und das wars auch schon wieder. Nun einfach eine Instanz der neuen Klasse erstellen und Multicast Nachrichten empfangen. Möchte man die Klasse auch in Android nutzen, dann sind da noch ein paar Kleinigkeiten zu beachten.

Im Manifest muss die Permission <uses-permission android:name=“android.permission.CHANGE_WIFI_MULTICAST_STATE“ /> für eure Applikation beantragt werden. Wenn eure App startet, dann solltet Ihr das Wifi für die Multicast Funktionalität vorbereiten mit:

        WifiManager wifi = (WifiManager)getSystemService(WIFI_SERVICE);
        this._multicastLock = wifi.createMulticastLock("multicastLock");
        this._multicastLock.setReferenceCounted(true);
        this._multicastLock.acquire();

Bevor die App beendet wird, dann solltet Ihr den Multicast Lock wieder entfernen mit:

this._multicastLock.release();

Posted on 3. Februar 2018 in Blog, Java, Tipps & Tricks

Back to Top