From aab2eda6f1cd2cc6ab8dfb419ff5836e86bdb073 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 7 Mar 2016 15:28:04 -0800 Subject: [PATCH] Add jnienet JNI library --- .gitmodules | 3 + jni/jnienet/enet | 1 + jni/jnienet/jnienet.c | 146 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 .gitmodules create mode 160000 jni/jnienet/enet create mode 100644 jni/jnienet/jnienet.c diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3cac579 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "jni/jnienet/enet"] + path = jni/jnienet/enet + url = https://github.com/cgutman/enet.git diff --git a/jni/jnienet/enet b/jni/jnienet/enet new file mode 160000 index 0000000..8b24595 --- /dev/null +++ b/jni/jnienet/enet @@ -0,0 +1 @@ +Subproject commit 8b24595dbd9ad0a026edf2047b4d1eaceaf09123 diff --git a/jni/jnienet/jnienet.c b/jni/jnienet/jnienet.c new file mode 100644 index 0000000..d6d72d2 --- /dev/null +++ b/jni/jnienet/jnienet.c @@ -0,0 +1,146 @@ +#include "enet/enet.h" + +#include +#include + +#define CLIENT_TO_LONG(x) ((intptr_t)(x)) +#define LONG_TO_CLIENT(x) ((ENetHost*)(intptr_t)(x)) + +#define PEER_TO_LONG(x) ((intptr_t)(x)) +#define LONG_TO_PEER(x) ((ENetPeer*)(intptr_t)(x)) + +JNIEXPORT jint JNICALL +Java_com_limelight_nvstream_enet_EnetConnection_initializeEnet(JNIEnv *env, jobject class) { + return enet_initialize(); +} + +JNIEXPORT jlong JNICALL +Java_com_limelight_nvstream_enet_EnetConnection_createClient(JNIEnv *env, jobject class, jstring address) { + ENetAddress enetAddress; + const char *addrStr; + int err; + + // Perform a lookup on the address to determine the address family + addrStr = (*env)->GetStringUTFChars(env, address, 0); + err = enet_address_set_host(&enetAddress, addrStr); + (*env)->ReleaseStringUTFChars(env, address, addrStr); + if (err < 0) { + return CLIENT_TO_LONG(NULL); + } + + // Create a client that can use 1 outgoing connection and 1 channel + return CLIENT_TO_LONG(enet_host_create(enetAddress.address.ss_family, NULL, 1, 1, 0, 0)); +} + +JNIEXPORT jlong JNICALL +Java_com_limelight_nvstream_enet_EnetConnection_connectToPeer(JNIEnv *env, jobject class, jlong client, jstring address, jint port, jint timeout) { + ENetPeer* peer; + ENetAddress enetAddress; + ENetEvent event; + const char *addrStr; + int err; + + // Initialize the ENet address + addrStr = (*env)->GetStringUTFChars(env, address, 0); + err = enet_address_set_host(&enetAddress, addrStr); + enet_address_set_port(&enetAddress, port); + (*env)->ReleaseStringUTFChars(env, address, addrStr); + if (err < 0) { + return PEER_TO_LONG(NULL); + } + + // Start the connection + peer = enet_host_connect(LONG_TO_CLIENT(client), &enetAddress, 1, 0); + if (peer == NULL) { + return PEER_TO_LONG(NULL); + } + + // Wait for the connect to complete + if (enet_host_service(LONG_TO_CLIENT(client), &event, timeout) <= 0 || event.type != ENET_EVENT_TYPE_CONNECT) { + enet_peer_reset(peer); + return PEER_TO_LONG(NULL); + } + + // Ensure the connect verify ACK is sent immediately + enet_host_flush(LONG_TO_CLIENT(client)); + + // Set the max peer timeout to 10 seconds + enet_peer_timeout(peer, ENET_PEER_TIMEOUT_LIMIT, ENET_PEER_TIMEOUT_MINIMUM, 10000); + + return PEER_TO_LONG(peer); +} + +JNIEXPORT jint JNICALL +Java_com_limelight_nvstream_enet_EnetConnection_readPacket(JNIEnv *env, jobject class, jlong client, jbyteArray data, jint length, jint timeout) { + jint err; + jbyte* dataPtr; + ENetEvent event; + + // Wait for a receive event, timeout, or disconnect + err = enet_host_service(LONG_TO_CLIENT(client), &event, timeout); + if (err <= 0) { + return err; + } + else if (event.type != ENET_EVENT_TYPE_RECEIVE) { + return -1; + } + + // Check that the packet isn't too large + if (event.packet->dataLength > length) { + enet_packet_destroy(event.packet); + return event.packet->dataLength; + } + + // Copy the packet data into the caller's buffer + dataPtr = (*env)->GetByteArrayElements(env, data, 0); + memcpy(dataPtr, event.packet->data, event.packet->dataLength); + err = event.packet->dataLength; + (*env)->ReleaseByteArrayElements(env, data, dataPtr, 0); + + // Free the packet + enet_packet_destroy(event.packet); + + return err; +} + +JNIEXPORT jboolean JNICALL +Java_com_limelight_nvstream_enet_EnetConnection_writePacket(JNIEnv *env, jobject class, jlong client, jlong peer, jbyteArray data, jint length, jint packetFlags) { + ENetPacket* packet; + jboolean ret; + jbyte* dataPtr; + + dataPtr = (*env)->GetByteArrayElements(env, data, 0); + + // Create the reliable packet that describes our outgoing message + packet = enet_packet_create(dataPtr, length, packetFlags); + if (packet != NULL) { + // Send the message to the peer + if (enet_peer_send(LONG_TO_PEER(peer), 0, packet) < 0) { + // This can fail if the peer has been disconnected + enet_packet_destroy(packet); + ret = JNI_FALSE; + } + else { + // Force the client to send the packet now + enet_host_flush(LONG_TO_CLIENT(client)); + ret = JNI_TRUE; + } + } + else { + ret = JNI_FALSE; + } + + (*env)->ReleaseByteArrayElements(env, data, dataPtr, JNI_ABORT); + + return ret; +} + +JNIEXPORT jint JNICALL +Java_com_limelight_nvstream_enet_EnetConnection_destroyClient(JNIEnv *env, jobject class, jlong client) { + enet_host_destroy(LONG_TO_CLIENT(client)); +} + +JNIEXPORT jint JNICALL +Java_com_limelight_nvstream_enet_EnetConnection_disconnectPeer(JNIEnv *env, jobject class, jlong peer) { + enet_peer_disconnect_now(LONG_TO_PEER(peer), 0); +}