# HG changeset patch # User nikita@nikita-laptop # Date 1264034117 -3600 # Node ID af3a788344f937859ff36699e30d880a805e90ba # Parent 032cd7e2c938e97835633132b362035f64fe2053# Parent 047b5e2b99049febeb2a08bb6419db3d57bc58be merge diff -r 047b5e2b9904 -r af3a788344f9 .classpath --- a/.classpath Sat Jan 16 21:32:29 2010 +0100 +++ b/.classpath Thu Jan 21 01:35:17 2010 +0100 @@ -18,10 +18,6 @@ - - - - diff -r 047b5e2b9904 -r af3a788344f9 AndroidManifest.xml --- a/AndroidManifest.xml Sat Jan 16 21:32:29 2010 +0100 +++ b/AndroidManifest.xml Thu Jan 21 01:35:17 2010 +0100 @@ -14,6 +14,7 @@ + + + + + diff -r 047b5e2b9904 -r af3a788344f9 build.xml --- a/build.xml Sat Jan 16 21:32:29 2010 +0100 +++ b/build.xml Thu Jan 21 01:35:17 2010 +0100 @@ -66,7 +66,7 @@ - + diff -r 047b5e2b9904 -r af3a788344f9 jni/Android.mk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/Android.mk Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,50 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := gsm + +SRC_FILES := gsm/add.c\ + gsm/code.c\ + gsm/debug.c\ + gsm/decode.c\ + gsm/gsm_create.c\ + gsm/gsm_decode.c\ + gsm/gsm_destroy.c\ + gsm/gsm_encode.c\ + gsm/gsm_explode.c\ + gsm/gsm_implode.c\ + gsm/gsm_option.c\ + gsm/gsm_print.c\ + gsm/long_term.c\ + gsm/lpc.c\ + gsm/preprocess.c\ + gsm/rpe.c\ + gsm/short_term.c\ + gsm/table.c\ + gsm/gsm_jni.c +LOCAL_SRC_FILES := $(SRC_FILES) + +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_MODULE := g722 + +SRC_FILES := g722/g722_encode.c\ + g722/g722_decode.c\ + g722/g722_jni.c +LOCAL_SRC_FILES := $(SRC_FILES) + +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_MODULE := OSNetworkSystem + +SRC_FILES := OSNetworkSystem.cpp + +#LOCAL_LDLIBS := -llog +LOCAL_SRC_FILES := $(SRC_FILES) + +include $(BUILD_SHARED_LIBRARY) diff -r 047b5e2b9904 -r af3a788344f9 jni/Application.mk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/Application.mk Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,3 @@ +APP_PROJECT_PATH := /home/nikita/devel/beem/beem-audio/ +APP_BUILD_SCRIPT := ${APP_PROJECT_PATH}/jni/Android.mk +APP_MODULES := gsm g722 OSNetworkSystem diff -r 047b5e2b9904 -r af3a788344f9 jni/OSNetworkSystem.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/OSNetworkSystem.cpp Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,3688 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * Copyright (C) 2007 The Android Open Source Project + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define LOG_TAG "OSNetworkSystem" + +#include +#include "jni.h" +#include "errno.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include +//#include +//#include +//#include "AndroidSystemNatives.h" + +/** + * @name Socket Errors + * Error codes for socket operations + * + * @internal SOCKERR* range from -200 to -299 avoid overlap + */ +#define SOCKERR_BADSOCKET -200 /* generic error */ +#define SOCKERR_NOTINITIALIZED -201 /* socket library uninitialized */ +#define SOCKERR_BADAF -202 /* bad address family */ +#define SOCKERR_BADPROTO -203 /* bad protocol */ +#define SOCKERR_BADTYPE -204 /* bad type */ +#define SOCKERR_SYSTEMBUSY -205 /* system busy handling requests */ +#define SOCKERR_SYSTEMFULL -206 /* too many sockets */ +#define SOCKERR_NOTCONNECTED -207 /* socket is not connected */ +#define SOCKERR_INTERRUPTED -208 /* the call was cancelled */ +#define SOCKERR_TIMEOUT -209 /* the operation timed out */ +#define SOCKERR_CONNRESET -210 /* the connection was reset */ +#define SOCKERR_WOULDBLOCK -211 /* the socket is marked as nonblocking operation would block */ +#define SOCKERR_ADDRNOTAVAIL -212 /* address not available */ +#define SOCKERR_ADDRINUSE -213 /* address already in use */ +#define SOCKERR_NOTBOUND -214 /* the socket is not bound */ +#define SOCKERR_UNKNOWNSOCKET -215 /* resolution of fileDescriptor to socket failed */ +#define SOCKERR_INVALIDTIMEOUT -216 /* the specified timeout is invalid */ +#define SOCKERR_FDSETFULL -217 /* Unable to create an FDSET */ +#define SOCKERR_TIMEVALFULL -218 /* Unable to create a TIMEVAL */ +#define SOCKERR_REMSOCKSHUTDOWN -219 /* The remote socket has shutdown gracefully */ +#define SOCKERR_NOTLISTENING -220 /* listen() was not invoked prior to accept() */ +#define SOCKERR_NOTSTREAMSOCK -221 /* The socket does not support connection-oriented service */ +#define SOCKERR_ALREADYBOUND -222 /* The socket is already bound to an address */ +#define SOCKERR_NBWITHLINGER -223 /* The socket is marked non-blocking & SO_LINGER is non-zero */ +#define SOCKERR_ISCONNECTED -224 /* The socket is already connected */ +#define SOCKERR_NOBUFFERS -225 /* No buffer space is available */ +#define SOCKERR_HOSTNOTFOUND -226 /* Authoritative Answer Host not found */ +#define SOCKERR_NODATA -227 /* Valid name, no data record of requested type */ +#define SOCKERR_BOUNDORCONN -228 /* The socket has not been bound or is already connected */ +#define SOCKERR_OPNOTSUPP -229 /* The socket does not support the operation */ +#define SOCKERR_OPTUNSUPP -230 /* The socket option is not supported */ +#define SOCKERR_OPTARGSINVALID -231 /* The socket option arguments are invalid */ +#define SOCKERR_SOCKLEVELINVALID -232 /* The socket level is invalid */ +#define SOCKERR_TIMEOUTFAILURE -233 +#define SOCKERR_SOCKADDRALLOCFAIL -234 /* Unable to allocate the sockaddr structure */ +#define SOCKERR_FDSET_SIZEBAD -235 /* The calculated maximum size of the file descriptor set is bad */ +#define SOCKERR_UNKNOWNFLAG -236 /* The flag is unknown */ +#define SOCKERR_MSGSIZE -237 /* The datagram was too big to fit the specified buffer & was truncated. */ +#define SOCKERR_NORECOVERY -238 /* The operation failed with no recovery possible */ +#define SOCKERR_ARGSINVALID -239 /* The arguments are invalid */ +#define SOCKERR_BADDESC -240 /* The socket argument is not a valid file descriptor */ +#define SOCKERR_NOTSOCK -241 /* The socket argument is not a socket */ +#define SOCKERR_HOSTENTALLOCFAIL -242 /* Unable to allocate the hostent structure */ +#define SOCKERR_TIMEVALALLOCFAIL -243 /* Unable to allocate the timeval structure */ +#define SOCKERR_LINGERALLOCFAIL -244 /* Unable to allocate the linger structure */ +#define SOCKERR_IPMREQALLOCFAIL -245 /* Unable to allocate the ipmreq structure */ +#define SOCKERR_FDSETALLOCFAIL -246 /* Unable to allocate the fdset structure */ +#define SOCKERR_OPFAILED -247 /* Operation failed */ +#define SOCKERR_VALUE_NULL -248 /* The value indexed was NULL */ +#define SOCKERR_CONNECTION_REFUSED -249 /* connection was refused */ +#define SOCKERR_ENETUNREACH -250 /* network is not reachable */ +#define SOCKERR_EACCES -251 /* permissions do not allow action on socket */ +#define SOCKERR_EHOSTUNREACH -252 /* no route to host */ +#define SOCKERR_EPIPE -253 /* broken pipe */ + +#define JAVASOCKOPT_TCP_NODELAY 1 +#define JAVASOCKOPT_IP_TOS 3 +#define JAVASOCKOPT_SO_REUSEADDR 4 +#define JAVASOCKOPT_SO_KEEPALIVE 8 +#define JAVASOCKOPT_MCAST_TIME_TO_LIVE 10 /* Currently unused */ +#define JAVASOCKOPT_SO_BINDADDR 15 +#define JAVASOCKOPT_MCAST_INTERFACE 16 +#define JAVASOCKOPT_MCAST_TTL 17 +#define JAVASOCKOPT_IP_MULTICAST_LOOP 18 +#define JAVASOCKOPT_MCAST_ADD_MEMBERSHIP 19 +#define JAVASOCKOPT_MCAST_DROP_MEMBERSHIP 20 +#define JAVASOCKOPT_IP_MULTICAST_IF2 31 +#define JAVASOCKOPT_SO_BROADCAST 32 +#define JAVASOCKOPT_SO_LINGER 128 +#define JAVASOCKOPT_REUSEADDR_AND_REUSEPORT 10001 +#define JAVASOCKOPT_SO_SNDBUF 4097 +#define JAVASOCKOPT_SO_RCVBUF 4098 +#define JAVASOCKOPT_SO_RCVTIMEOUT 4102 +#define JAVASOCKOPT_SO_OOBINLINE 4099 + +/* constants for calling multi-call functions */ +#define SOCKET_STEP_START 10 +#define SOCKET_STEP_CHECK 20 +#define SOCKET_STEP_DONE 30 + +#define BROKEN_MULTICAST_IF 1 +#define BROKEN_MULTICAST_TTL 2 +#define BROKEN_TCP_NODELAY 4 + +#define SOCKET_CONNECT_STEP_START 0 +#define SOCKET_CONNECT_STEP_CHECK 1 + +#define SOCKET_OP_NONE 0 +#define SOCKET_OP_READ 1 +#define SOCKET_OP_WRITE 2 +#define SOCKET_READ_WRITE 3 + +#define SOCKET_MSG_PEEK 1 +#define SOCKET_MSG_OOB 2 + +#define SOCKET_NOFLAGS 0 + +#undef BUFFERSIZE +#define BUFFERSIZE 2048 + +// wait for 500000 usec = 0.5 second +#define SEND_RETRY_TIME 500000 + + +struct CachedFields { + jfieldID fd_descriptor; + jclass iaddr_class; + jmethodID iaddr_class_init; + jmethodID iaddr_getbyaddress; + jfieldID iaddr_ipaddress; + jclass genericipmreq_class; + jclass integer_class; + jmethodID integer_class_init; + jfieldID integer_class_value; + jclass boolean_class; + jmethodID boolean_class_init; + jfieldID boolean_class_value; + jclass byte_class; + jmethodID byte_class_init; + jfieldID byte_class_value; + jclass string_class; + jmethodID string_class_init; + jfieldID socketimpl_address; + jfieldID socketimpl_port; + jclass dpack_class; + jfieldID dpack_address; + jfieldID dpack_port; + jfieldID dpack_length; + jclass fd_class; + jfieldID descriptor; +} gCachedFields; + +static int useAdbNetworking = 0; + +/* needed for connecting with timeout */ +typedef struct selectFDSet { + int nfds; + int sock; + fd_set writeSet; + fd_set readSet; + fd_set exceptionSet; +} selectFDSet; + +static const char * netLookupErrorString(int anErrorNum); + +#define log_socket_close(a,b) +#define log_socket_connect(a,b,c) +#define add_send_stats(a,b) +#define add_recv_stats(a,b) +#define adb_networking_connect_fd(a,b) 0 +#define adb_networking_gethostbyname(a,b) 0 +#define PROPERTY_VALUE_MAX 1 +#define property_get(a,b,c) +#define assert(a) +/* + * Throw an exception with the specified class and an optional message. + */ +int jniThrowException(JNIEnv* env, const char* className, const char* msg) +{ + jclass exceptionClass; + + exceptionClass = env->FindClass(className); + if (exceptionClass == NULL) { +// LOGE("Unable to find exception class %s\n", className); + assert(0); /* fatal during dev; should always be fatal? */ + return -1; + } + + if (env->ThrowNew(exceptionClass, msg) != JNI_OK) { +// LOGE("Failed throwing '%s' '%s'\n", className, msg); + assert(!"failed to throw"); + } + return 0; +} + +/* + * Internal helper function. + * + * Get the file descriptor. + */ +static inline int getFd(JNIEnv* env, jobject obj) +{ + return env->GetIntField(obj, gCachedFields.descriptor); +} + +/* + * Internal helper function. + * + * Set the file descriptor. + */ +static inline void setFd(JNIEnv* env, jobject obj, jint value) +{ + env->SetIntField(obj, gCachedFields.descriptor, value); +} + +/* + * For JNIHelp.c + * Get an int file descriptor from a java.io.FileDescriptor + */ + +static int jniGetFDFromFileDescriptor (JNIEnv* env, jobject fileDescriptor) { + + return getFd(env, fileDescriptor); +} + +/* + * For JNIHelp.c + * Set the descriptor of a java.io.FileDescriptor + */ + +static void jniSetFileDescriptorOfFD (JNIEnv* env, jobject fileDescriptor, int value) { + + setFd(env, fileDescriptor, value); +} + +/** + * Throws an SocketException with the message affiliated with the errorCode. + */ +static void throwSocketException(JNIEnv *env, int errorCode) { + jniThrowException(env, "java/net/SocketException", + netLookupErrorString(errorCode)); +} + +/** + * Throws an IOException with the given message. + */ +static void throwIOExceptionStr(JNIEnv *env, const char *message) { + jniThrowException(env, "java/io/IOException", message); +} + +/** + * Throws a NullPointerException. + */ +static void throwNullPointerException(JNIEnv *env) { + jniThrowException(env, "java/lang/NullPointerException", NULL); +} + +/** + * Converts a 4-byte array to a native address structure. Throws a + * NullPointerException or an IOException in case of error. This is + * signaled by a return value of -1. The normal return value is 0. + */ +static int javaAddressToStructIn( + JNIEnv *env, jbyteArray java_address, struct in_addr *address) { + + memset(address, 0, sizeof(address)); + + if (java_address == NULL) { + return -1; + } + + if (env->GetArrayLength(java_address) != sizeof(address->s_addr)) { + return -1; + } + + jbyte * java_address_bytes + = env->GetByteArrayElements(java_address, NULL); + + memcpy(&(address->s_addr), + java_address_bytes, + sizeof(address->s_addr)); + + env->ReleaseByteArrayElements(java_address, java_address_bytes, JNI_ABORT); + + return 0; +} + +/** + * Converts a native address structure to a 4-byte array. Throws a + * NullPointerException or an IOException in case of error. This is + * signaled by a return value of -1. The normal return value is 0. + */ +static int structInToJavaAddress( + JNIEnv *env, struct in_addr *address, jbyteArray java_address) { + + if (java_address == NULL) { + return -1; + } + + if (env->GetArrayLength(java_address) != sizeof(address->s_addr)) { + return -1; + } + + jbyte *java_address_bytes; + + java_address_bytes = env->GetByteArrayElements(java_address, NULL); + + memcpy(java_address_bytes, &(address->s_addr), sizeof(address->s_addr)); + + env->ReleaseByteArrayElements(java_address, java_address_bytes, 0); + + return 0; +} + +/** + * Converts a native address structure to an InetAddress object. + * Throws a NullPointerException or an IOException in case of + * error. This is signaled by a return value of -1. The normal + * return value is 0. + */ +static int socketAddressToInetAddress(JNIEnv *env, + struct sockaddr_in *sockaddress, jobject inetaddress, int *port) { + + jbyteArray ipaddress; + int result; + + ipaddress = (jbyteArray)env->GetObjectField(inetaddress, + gCachedFields.iaddr_ipaddress); + + if (structInToJavaAddress(env, &sockaddress->sin_addr, ipaddress) < 0) { + return -1; + } + + *port = ntohs(sockaddress->sin_port); + + return 0; +} + +/** + * Converts an InetAddress object to a native address structure. + * Throws a NullPointerException or an IOException in case of + * error. This is signaled by a return value of -1. The normal + * return value is 0. + */ +static int inetAddressToSocketAddress(JNIEnv *env, + jobject inetaddress, int port, struct sockaddr_in *sockaddress) { + + jbyteArray ipaddress; + int result; + + ipaddress = (jbyteArray)env->GetObjectField(inetaddress, + gCachedFields.iaddr_ipaddress); + + memset(sockaddress, 0, sizeof(sockaddress)); + + sockaddress->sin_family = AF_INET; + sockaddress->sin_port = htons(port); + + if (javaAddressToStructIn(env, ipaddress, &(sockaddress->sin_addr)) < 0) { + return -1; + } + + return 0; +} + +static jobject structInToInetAddress(JNIEnv *env, struct in_addr *address) { + jbyteArray bytes; + int success; + + bytes = env->NewByteArray(4); + + if (bytes == NULL) { + return NULL; + } + + if (structInToJavaAddress(env, address, bytes) < 0) { + return NULL; + } + + return env->CallStaticObjectMethod(gCachedFields.iaddr_class, + gCachedFields.iaddr_getbyaddress, bytes); +} + +/** + * Answer a new java.lang.Boolean object. + * + * @param env pointer to the JNI library + * @param anInt the Boolean constructor argument + * + * @return the new Boolean + */ + +static jobject newJavaLangBoolean(JNIEnv * env, jint anInt) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.boolean_class; + tempMethod = gCachedFields.boolean_class_init; + return env->NewObject(tempClass, tempMethod, (jboolean) (anInt != 0)); +} + +/** + * Answer a new java.lang.Byte object. + * + * @param env pointer to the JNI library + * @param anInt the Byte constructor argument + * + * @return the new Byte + */ + +static jobject newJavaLangByte(JNIEnv * env, jbyte val) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.byte_class; + tempMethod = gCachedFields.byte_class_init; + return env->NewObject(tempClass, tempMethod, val); +} + +/** + * Answer a new java.lang.Integer object. + * + * @param env pointer to the JNI library + * @param anInt the Integer constructor argument + * + * @return the new Integer + */ + +static jobject newJavaLangInteger(JNIEnv * env, jint anInt) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.integer_class; + tempMethod = gCachedFields.integer_class_init; + return env->NewObject(tempClass, tempMethod, anInt); +} + +/** + * Answer a new java.lang.String object. + * + * @param env pointer to the JNI library + * @param anInt the byte[] constructor argument + * + * @return the new String + */ + +static jobject newJavaLangString(JNIEnv * env, jbyteArray bytes) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.string_class; + tempMethod = gCachedFields.string_class_init; + return env->NewObject(tempClass, tempMethod, (jbyteArray) bytes); +} + +/** + * Query OS for timestamp. + * Retrieve the current value of system clock and convert to milliseconds. + * + * @param[in] portLibrary The port library. + * + * @return 0 on failure, time value in milliseconds on success. + * @deprecated Use @ref time_hires_clock and @ref time_hires_delta + * + * technically, this should return I_64 since both timeval.tv_sec and + * timeval.tv_usec are long + */ + +static int time_msec_clock() { + struct timeval tp; + struct timezone tzp; + + gettimeofday(&tp, &tzp); + return (tp.tv_sec * 1000) + (tp.tv_usec / 1000); +} + +/** + * check if the passed sockaddr_in struct contains a localhost address + * + * @param[in] address pointer to the address to check + * + * @return 0 if the passed address isn't a localhost address + */ +static int isLocalhost(struct sockaddr_in *address) { + // return address == 127.0.0.1 + return (unsigned int) address->sin_addr.s_addr == 16777343; +} + +/** + * Answer the errorString corresponding to the errorNumber, if available. + * This function will answer a default error string, if the errorNumber is not + * recognized. + * + * This function will have to be reworked to handle internationalization + * properly, removing the explicit strings. + * + * @param anErrorNum the error code to resolve to a human readable string + * + * @return a human readable error string + */ + +static const char * netLookupErrorString(int anErrorNum) { + switch (anErrorNum) { + case SOCKERR_BADSOCKET: + return "Bad socket"; + case SOCKERR_NOTINITIALIZED: + return "Socket library uninitialized"; + case SOCKERR_BADAF: + return "Bad address family"; + case SOCKERR_BADPROTO: + return "Bad protocol"; + case SOCKERR_BADTYPE: + return "Bad type"; + case SOCKERR_SYSTEMBUSY: + return "System busy handling requests"; + case SOCKERR_SYSTEMFULL: + return "Too many sockets allocated"; + case SOCKERR_NOTCONNECTED: + return "Socket is not connected"; + case SOCKERR_INTERRUPTED: + return "The system call was cancelled"; + case SOCKERR_TIMEOUT: + return "The operation timed out"; + case SOCKERR_CONNRESET: + return "The connection was reset"; + case SOCKERR_WOULDBLOCK: + return "The nonblocking operation would block"; + case SOCKERR_ADDRNOTAVAIL: + return "The address is not available"; + case SOCKERR_ADDRINUSE: + return "The address is already in use"; + case SOCKERR_NOTBOUND: + return "The socket is not bound"; + case SOCKERR_UNKNOWNSOCKET: + return "Resolution of the FileDescriptor to socket failed"; + case SOCKERR_INVALIDTIMEOUT: + return "The specified timeout is invalid"; + case SOCKERR_FDSETFULL: + return "Unable to create an FDSET"; + case SOCKERR_TIMEVALFULL: + return "Unable to create a TIMEVAL"; + case SOCKERR_REMSOCKSHUTDOWN: + return "The remote socket has shutdown gracefully"; + case SOCKERR_NOTLISTENING: + return "Listen() was not invoked prior to accept()"; + case SOCKERR_NOTSTREAMSOCK: + return "The socket does not support connection-oriented service"; + case SOCKERR_ALREADYBOUND: + return "The socket is already bound to an address"; + case SOCKERR_NBWITHLINGER: + return "The socket is marked non-blocking & SO_LINGER is non-zero"; + case SOCKERR_ISCONNECTED: + return "The socket is already connected"; + case SOCKERR_NOBUFFERS: + return "No buffer space is available"; + case SOCKERR_HOSTNOTFOUND: + return "Authoritative Answer Host not found"; + case SOCKERR_NODATA: + return "Valid name, no data record of requested type"; + case SOCKERR_BOUNDORCONN: + return "The socket has not been bound or is already connected"; + case SOCKERR_OPNOTSUPP: + return "The socket does not support the operation"; + case SOCKERR_OPTUNSUPP: + return "The socket option is not supported"; + case SOCKERR_OPTARGSINVALID: + return "The socket option arguments are invalid"; + case SOCKERR_SOCKLEVELINVALID: + return "The socket level is invalid"; + case SOCKERR_TIMEOUTFAILURE: + return "The timeout operation failed"; + case SOCKERR_SOCKADDRALLOCFAIL: + return "Failed to allocate address structure"; + case SOCKERR_FDSET_SIZEBAD: + return "The calculated maximum size of the file descriptor set is bad"; + case SOCKERR_UNKNOWNFLAG: + return "The flag is unknown"; + case SOCKERR_MSGSIZE: + return "The datagram was too big to fit the specified buffer, so truncated"; + case SOCKERR_NORECOVERY: + return "The operation failed with no recovery possible"; + case SOCKERR_ARGSINVALID: + return "The arguments are invalid"; + case SOCKERR_BADDESC: + return "The socket argument is not a valid file descriptor"; + case SOCKERR_NOTSOCK: + return "The socket argument is not a socket"; + case SOCKERR_HOSTENTALLOCFAIL: + return "Unable to allocate the hostent structure"; + case SOCKERR_TIMEVALALLOCFAIL: + return "Unable to allocate the timeval structure"; + case SOCKERR_LINGERALLOCFAIL: + return "Unable to allocate the linger structure"; + case SOCKERR_IPMREQALLOCFAIL: + return "Unable to allocate the ipmreq structure"; + case SOCKERR_FDSETALLOCFAIL: + return "Unable to allocate the fdset structure"; + case SOCKERR_OPFAILED: + return "Operation failed"; + case SOCKERR_CONNECTION_REFUSED: + return "Connection refused"; + case SOCKERR_ENETUNREACH: + return "Network unreachable"; + case SOCKERR_EHOSTUNREACH: + return "No route to host"; + case SOCKERR_EPIPE: + return "Broken pipe"; + case SOCKERR_EACCES: + return "Permission denied (maybe missing INTERNET permission)"; + + default: +// LOGE("unknown socket error %d", anErrorNum); + return "unknown error"; + } +} + +static int convertError(int errorCode) { + switch (errorCode) { + case EBADF: + return SOCKERR_BADDESC; + case ENOBUFS: + return SOCKERR_NOBUFFERS; + case EOPNOTSUPP: + return SOCKERR_OPNOTSUPP; + case ENOPROTOOPT: + return SOCKERR_OPTUNSUPP; + case EINVAL: + return SOCKERR_SOCKLEVELINVALID; + case ENOTSOCK: + return SOCKERR_NOTSOCK; + case EINTR: + return SOCKERR_INTERRUPTED; + case ENOTCONN: + return SOCKERR_NOTCONNECTED; + case EAFNOSUPPORT: + return SOCKERR_BADAF; + /* note: CONNRESET not included because it has the same + * value as ECONNRESET and they both map to SOCKERR_CONNRESET */ + case ECONNRESET: + return SOCKERR_CONNRESET; + case EAGAIN: + return SOCKERR_WOULDBLOCK; + case EPROTONOSUPPORT: + return SOCKERR_BADPROTO; + case EFAULT: + return SOCKERR_ARGSINVALID; + case ETIMEDOUT: + return SOCKERR_TIMEOUT; + case ECONNREFUSED: + return SOCKERR_CONNECTION_REFUSED; + case ENETUNREACH: + return SOCKERR_ENETUNREACH; + case EACCES: + return SOCKERR_EACCES; + case EPIPE: + return SOCKERR_EPIPE; + case EHOSTUNREACH: + return SOCKERR_EHOSTUNREACH; + case EADDRINUSE: + return SOCKERR_ADDRINUSE; + case EADDRNOTAVAIL: + return SOCKERR_ADDRNOTAVAIL; + case EMSGSIZE: + return SOCKERR_MSGSIZE; + default: +// LOGE("unclassified errno %d (%s)", errorCode, strerror(errorCode)); + return SOCKERR_OPFAILED; + } +} + +static int sockSelect(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) { + + int result = select(nfds, readfds, writefds, exceptfds, timeout); + + if (result < 0) { + if (errno == EINTR) { + result = SOCKERR_INTERRUPTED; + } else { + result = SOCKERR_OPFAILED; + } + } else if (result == 0) { + result = SOCKERR_TIMEOUT; + } + return result; +} + +#define SELECT_READ_TYPE 0 +#define SELECT_WRITE_TYPE 1 + +static int selectWait(int handle, int uSecTime, int type) { + fd_set fdset; + struct timeval time, *timePtr; + int result = 0; + int size = handle + 1; + + FD_ZERO(&fdset); + FD_SET(handle, &fdset); + + if (0 <= uSecTime) { + /* Use a timeout if uSecTime >= 0 */ + memset(&time, 0, sizeof(time)); + time.tv_usec = uSecTime; + timePtr = &time; + } else { + /* Infinite timeout if uSecTime < 0 */ + timePtr = NULL; + } + + if (type == SELECT_READ_TYPE) { + result = sockSelect(size, &fdset, NULL, NULL, timePtr); + } else { + result = sockSelect(size, NULL, &fdset, NULL, timePtr); + } + return result; +} + +static int pollSelectWait(JNIEnv *env, jobject fileDescriptor, int timeout, int type) { + /* now try reading the socket for the timespan timeout. + * if timeout is 0 try forever until the soclets gets ready or until an + * exception occurs. + */ + int pollTimeoutUSec = 100000, pollMsec = 100; + int finishTime = 0; + int timeLeft = timeout; + int hasTimeout = timeout > 0 ? 1 : 0; + int result = 0; + int handle; + + if (hasTimeout) { + finishTime = time_msec_clock() + timeout; + } + + int poll = 1; + + while (poll) { /* begin polling loop */ + + /* + * Fetch the handle every time in case the socket is closed. + */ + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_INTERRUPTED); + return -1; + } + + if (hasTimeout) { + + if (timeLeft - 10 < pollMsec) { + pollTimeoutUSec = timeLeft <= 0 ? 0 : (timeLeft * 1000); + } + + result = selectWait(handle, pollTimeoutUSec, type); + + /* + * because we are polling at a time smaller than timeout + * (presumably) lets treat an interrupt and timeout the same - go + * see if we're done timewise, and then just try again if not. + */ + if (SOCKERR_TIMEOUT == result || + SOCKERR_INTERRUPTED == result) { + + timeLeft = finishTime - time_msec_clock(); + + if (timeLeft <= 0) { + /* + * Always throw the "timeout" message because that is + * effectively what has happened, even if we happen to + * have been interrupted. + */ + jniThrowException(env, "java/net/SocketTimeoutException", + netLookupErrorString(SOCKERR_TIMEOUT)); + } else { + continue; // try again + } + + } else if (0 > result) { + log_socket_close(handle, result); + throwSocketException(env, result); + } + poll = 0; + + } else { /* polling with no timeout (why would you do this?)*/ + + result = selectWait(handle, pollTimeoutUSec, type); + + /* + * if interrupted (or a timeout) just retry + */ + if (SOCKERR_TIMEOUT == result || + SOCKERR_INTERRUPTED == result) { + + continue; // try again + } else if (0 > result) { + log_socket_close(handle, result); + throwSocketException(env, result); + } + poll = 0; + } + } /* end polling loop */ + + return result; +} + +/** + * A helper method, to set the connect context to a Long object. + * + * @param env pointer to the JNI library + * @param longclass Java Long Object + */ +void setConnectContext(JNIEnv *env,jobject longclass,jbyte * context) { + jclass descriptorCLS; + jfieldID descriptorFID; + descriptorCLS = env->FindClass("java/lang/Long"); + descriptorFID = env->GetFieldID(descriptorCLS, "value", "J"); + env->SetLongField(longclass, descriptorFID, (jlong)((jint)context)); +}; + +/** + * A helper method, to get the connect context. + * + * @param env pointer to the JNI library + * @param longclass Java Long Object + */ +jbyte *getConnectContext(JNIEnv *env, jobject longclass) { + jclass descriptorCLS; + jfieldID descriptorFID; + descriptorCLS = env->FindClass("java/lang/Long"); + descriptorFID = env->GetFieldID(descriptorCLS, "value", "J"); + return (jbyte*) ((jint)env->GetLongField(longclass, descriptorFID)); +}; + +// typical ip checksum +unsigned short ip_checksum(unsigned short* buffer, int size) { + register unsigned short * buf = buffer; + register int bufleft = size; + register unsigned long sum = 0; + + while (bufleft > 1) { + sum = sum + (*buf++); + bufleft = bufleft - sizeof(unsigned short ); + } + if (bufleft) { + sum = sum + (*(unsigned char*)buf); + } + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + return (unsigned short )(~sum); +} + +/** + * Establish a connection to a peer with a timeout. This function is called + * repeatedly in order to carry out the connect and to allow other tasks to + * proceed on certain platforms. The caller must first call with + * step = SOCKET_STEP_START, if the result is SOCKERR_NOTCONNECTED it will then + * call it with step = CHECK until either another error or 0 is returned to + * indicate the connect is complete. Each time the function should sleep for no + * more than timeout milliseconds. If the connect succeeds or an error occurs, + * the caller must always end the process by calling the function with + * step = SOCKET_STEP_DONE + * + * @param[in] portLibrary The port library. + * @param[in] sock pointer to the unconnected local socket. + * @param[in] addr pointer to the sockaddr, specifying remote host/port. + * @param[in] timeout the timeout in milliseconds. If timeout is negative, + * perform a block operation. + * @param[in,out] pointer to context pointer. Filled in on first call and then + * to be passed into each subsequent call. + * + * @return 0, if no errors occurred, otherwise the (negative) error code. + */ +static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, + unsigned int timeout, unsigned int step, jbyte *ctxt) { + int rc = 0; + struct timeval passedTimeout; + int errorVal; + socklen_t errorValLen = sizeof(int); + struct selectFDSet *context = NULL; + + if (SOCKET_STEP_START == step) { + + context = (struct selectFDSet *) ctxt; + + context->sock = handle; + context->nfds = handle + 1; + + if (useAdbNetworking && !isLocalhost(&addr)) { + + // LOGD("+connect to address 0x%08x (via adb)", + // addr.sin_addr.s_addr); + rc = adb_networking_connect_fd(handle, &addr); + // LOGD("-connect ret %d errno %d (via adb)", rc, errno); + + } else { + log_socket_connect(handle, ntohl(addr.sin_addr.s_addr), + ntohs(addr.sin_port)); + /* set the socket to non-blocking */ + int block = JNI_TRUE; + rc = ioctl(handle, FIONBIO, &block); + if (0 != rc) { + return convertError(rc); + } + + // LOGD("+connect to address 0x%08x (via normal) on handle %d", + // addr.sin_addr.s_addr, handle); + do { + rc = connect(handle, (struct sockaddr *) &addr, + sizeof(struct sockaddr)); + } while (rc < 0 && errno == EINTR); + // LOGD("-connect to address 0x%08x (via normal) returned %d", + // addr.sin_addr.s_addr, (int) rc); + + } + + if (rc == -1) { + rc = errno; + switch (rc) { + case EINTR: + return SOCKERR_ALREADYBOUND; + case EAGAIN: + case EINPROGRESS: + return SOCKERR_NOTCONNECTED; + default: + return convertError(rc); + } + } + + /* we connected right off the bat so just return */ + return rc; + + } else if (SOCKET_STEP_CHECK == step) { + /* now check if we have connected yet */ + + context = (struct selectFDSet *) ctxt; + + /* + * set the timeout value to be used. Because on some unix platforms we + * don't get notified when a socket is closed we only sleep for 100ms + * at a time + */ + passedTimeout.tv_sec = 0; + if (timeout > 100) { + passedTimeout.tv_usec = 100 * 1000; + } else if ((int)timeout >= 0) { + passedTimeout.tv_usec = timeout * 1000; + } + + /* initialize the FD sets for the select */ + FD_ZERO(&(context->exceptionSet)); + FD_ZERO(&(context->writeSet)); + FD_ZERO(&(context->readSet)); + FD_SET(context->sock, &(context->writeSet)); + FD_SET(context->sock, &(context->readSet)); + FD_SET(context->sock, &(context->exceptionSet)); + + rc = select(context->nfds, + &(context->readSet), + &(context->writeSet), + &(context->exceptionSet), + (int)timeout >= 0 ? &passedTimeout : NULL); + + /* if there is at least one descriptor ready to be checked */ + if (0 < rc) { + /* if the descriptor is in the write set we connected or failed */ + if (FD_ISSET(context->sock, &(context->writeSet))) { + + if (!FD_ISSET(context->sock, &(context->readSet))) { + /* ok we have connected ok */ + return 0; + } else { + /* ok we have more work to do to figure it out */ + if (getsockopt(context->sock, SOL_SOCKET, SO_ERROR, + &errorVal, &errorValLen) >= 0) { + return errorVal ? convertError(errorVal) : 0; + } else { + return convertError(errno); + } + } + } + + /* if the descriptor is in the exception set the connect failed */ + if (FD_ISSET(context->sock, &(context->exceptionSet))) { + if (getsockopt(context->sock, SOL_SOCKET, SO_ERROR, &errorVal, + &errorValLen) >= 0) { + return errorVal ? convertError(errorVal) : 0; + } + rc = errno; + return convertError(rc); + } + + } else if (rc < 0) { + /* something went wrong with the select call */ + rc = errno; + + /* if it was EINTR we can just try again. Return not connected */ + if (EINTR == rc) { + return SOCKERR_NOTCONNECTED; + } + + /* some other error occured so look it up and return */ + return convertError(rc); + } + + /* + * if we get here the timeout expired or the connect had not yet + * completed just indicate that the connect is not yet complete + */ + return SOCKERR_NOTCONNECTED; + } else if (SOCKET_STEP_DONE == step) { + /* we are done the connect or an error occured so clean up */ + if (handle != -1) { + int block = JNI_FALSE; + ioctl(handle, FIONBIO, &block); + } + return 0; + } + return SOCKERR_ARGSINVALID; +} + +/** + * Join/Leave the nominated multicast group on the specified socket. + * Implemented by setting the multicast 'add membership'/'drop membership' + * option at the HY_IPPROTO_IP level on the socket. + * + * Implementation note for multicast sockets in general: + * + * - This code is untested, because at the time of this writing multicast can't + * be properly tested on Android due to GSM routing restrictions. So it might + * or might not work. + * + * - The REUSEPORT socket option that Harmony employs is not supported on Linux + * and thus also not supported on Android. It's is not needed for multicast + * to work anyway (REUSEADDR should suffice). + * + * @param env pointer to the JNI library. + * @param socketP pointer to the hysocket to join/leave on. + * @param optVal pointer to the InetAddress, the multicast group to join/drop. + * + * @exception SocketException if an error occurs during the call + */ +static void mcastAddDropMembership (JNIEnv * env, int handle, jobject optVal, + int ignoreIF, int setSockOptVal) { + int result; + struct ip_mreq ipmreqP; + struct sockaddr_in sockaddrP; + int length = sizeof(struct ip_mreq); + socklen_t lengthIF = sizeof(struct sockaddr_in); + + /* + * JNI objects needed to access the information in the optVal oject + * passed in. The object passed in is a GenericIPMreq object + */ + jclass cls; + jfieldID multiaddrID; + jfieldID interfaceAddrID; + jobject multiaddr; + jobject interfaceAddr; + + /* + * check whether we are getting an InetAddress or an Generic IPMreq, for now + * we support both so that we will not break the tests + */ + if (env->IsInstanceOf (optVal, gCachedFields.iaddr_class)) { + + ipmreqP.imr_interface.s_addr = htonl(INADDR_ANY); + if (!ignoreIF) { + + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockaddrP, + &lengthIF); + + if (0 != result) { + throwSocketException (env, convertError(errno)); + return; + } + + memcpy(&(ipmreqP.imr_interface.s_addr), &(sockaddrP.sin_addr), 4); + } + + result = inetAddressToSocketAddress(env, optVal, 0, &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + memcpy(&(ipmreqP.imr_multiaddr.s_addr), &(sockaddrP.sin_addr), 4); + + result = setsockopt(handle, IPPROTO_IP, setSockOptVal, &ipmreqP, length); + if (0 != result) { + throwSocketException (env, convertError(errno)); + return; + } + + } else { + + /* we need the multicast address regardless of the type of address */ + cls = env->GetObjectClass(optVal); + multiaddrID = env->GetFieldID(cls, "multiaddr", "Ljava/net/InetAddress;"); + multiaddr = env->GetObjectField(optVal, multiaddrID); + + result = inetAddressToSocketAddress(env, multiaddr, 0, &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + memcpy(&(ipmreqP.imr_multiaddr.s_addr), &(sockaddrP.sin_addr), 4); + + /* we need to use an IP_MREQ as it is an IPV4 address */ + interfaceAddrID = env->GetFieldID(cls, "interfaceAddr", + "Ljava/net/InetAddress;"); + interfaceAddr = env->GetObjectField(optVal, interfaceAddrID); + + ipmreqP.imr_interface.s_addr = htonl(INADDR_ANY); + + /* + * if an interfaceAddr was passed then use that value, otherwise set the + * interface to all 0 to indicate the system should select the interface + * used + */ + if (!ignoreIF) { + if (NULL != interfaceAddr) { + + result = inetAddressToSocketAddress(env, interfaceAddr, 0, + &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + memcpy(&(ipmreqP.imr_interface.s_addr), &(sockaddrP.sin_addr), 4); + + } + } + + /* join/drop the multicast address */ + result = setsockopt(handle, IPPROTO_IP, setSockOptVal, &ipmreqP, length); + if (0 != result) { + throwSocketException (env, convertError(errno)); + return; + } + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj, + jboolean jcl_supports_ipv6) { + // LOGD("ENTER oneTimeInitializationImpl of OSNetworkSystem"); + + char useAdbNetworkingProperty[PROPERTY_VALUE_MAX]; + char adbConnectedProperty[PROPERTY_VALUE_MAX]; + + property_get("android.net.use-adb-networking", useAdbNetworkingProperty, ""); + property_get("adb.connected", adbConnectedProperty, ""); + + if (strlen((char *)useAdbNetworkingProperty) > 0 + && strlen((char *)adbConnectedProperty) > 0) { + useAdbNetworking = 1; + } + + memset(&gCachedFields, 0, sizeof(gCachedFields)); + + // initializing InetAddress + + jclass iaddrclass = env->FindClass("java/net/InetAddress"); + + if (iaddrclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.net.InetAddress"); + return; + } + + gCachedFields.iaddr_class = (jclass) env->NewGlobalRef(iaddrclass); + + jmethodID iaddrclassinit = env->GetMethodID(iaddrclass, "", "()V"); + + if (iaddrclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "InetAddress.()"); + return; + } + + gCachedFields.iaddr_class_init = iaddrclassinit; + + jmethodID iaddrgetbyaddress = env->GetStaticMethodID(iaddrclass, + "getByAddress", "([B)Ljava/net/InetAddress;"); + + if (iaddrgetbyaddress == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "InetAddress.getByAddress(byte[] val)"); + return; + } + + gCachedFields.iaddr_getbyaddress = iaddrgetbyaddress; + + jmethodID iaddrgetaddress = env->GetStaticMethodID(iaddrclass, + "getByAddress", "([B)Ljava/net/InetAddress;"); + + + + + jfieldID iaddripaddress = env->GetFieldID(iaddrclass, "ipaddress", "[B"); + + if (iaddripaddress == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "Can't find field InetAddress.ipaddress"); + return; + } + + gCachedFields.iaddr_ipaddress = iaddripaddress; + + // get the GenericIPMreq class + + jclass genericipmreqclass = env->FindClass("org/apache/harmony/luni/net/GenericIPMreq"); + + if (genericipmreqclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "org.apache.harmony.luni.net.GenericIPMreq"); + return; + } + + gCachedFields.genericipmreq_class = (jclass) env->NewGlobalRef(genericipmreqclass); + + // initializing Integer + + jclass integerclass = env->FindClass("java/lang/Integer"); + + if (integerclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.Integer"); + return; + } + + jmethodID integerclassinit = env->GetMethodID(integerclass, "", "(I)V"); + + if (integerclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "Integer.(int val)"); + return; + } + + jfieldID integerclassvalue = env->GetFieldID(integerclass, "value", "I"); + + if (integerclassvalue == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "Integer.value"); + return; + } + + gCachedFields.integer_class = (jclass) env->NewGlobalRef(integerclass); + gCachedFields.integer_class_init = integerclassinit; + gCachedFields.integer_class_value = integerclassvalue; + + // initializing Boolean + + jclass booleanclass = env->FindClass("java/lang/Boolean"); + + if (booleanclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.Boolean"); + return; + } + + jmethodID booleanclassinit = env->GetMethodID(booleanclass, "", "(Z)V"); + + if (booleanclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "Boolean.(boolean val)"); + return; + } + + jfieldID booleanclassvalue = env->GetFieldID(booleanclass, "value", "Z"); + + if (booleanclassvalue == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "Boolean.value"); + return; + } + + gCachedFields.boolean_class = (jclass) env->NewGlobalRef(booleanclass); + gCachedFields.boolean_class_init = booleanclassinit; + gCachedFields.boolean_class_value = booleanclassvalue; + + // initializing Byte + + jclass byteclass = env->FindClass("java/lang/Byte"); + + if (byteclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.Byte"); + return; + } + + jmethodID byteclassinit = env->GetMethodID(byteclass, "", "(B)V"); + + if (byteclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "Byte.(byte val)"); + return; + } + + jfieldID byteclassvalue = env->GetFieldID(byteclass, "value", "B"); + + if (byteclassvalue == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "Byte.value"); + return; + } + + gCachedFields.byte_class = (jclass) env->NewGlobalRef(byteclass); + gCachedFields.byte_class_init = byteclassinit; + gCachedFields.byte_class_value = byteclassvalue; + + // initializing String + + jclass stringclass = env->FindClass("java/lang/String"); + + if (stringclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.String"); + return; + } + + jmethodID stringclassinit = env->GetMethodID(stringclass, "", "([B)V"); + + if (stringclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "String.(byte[] val)"); + return; + } + + gCachedFields.string_class = (jclass) env->NewGlobalRef(stringclass); + gCachedFields.string_class_init = stringclassinit; + + // initializing ScoketImpl + + jclass socketimplclass = env->FindClass("java/net/SocketImpl"); + + if (socketimplclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.net.SocketImpl"); + return; + } + + jfieldID socketimplport = env->GetFieldID(socketimplclass, "port", "I"); + + if (socketimplport == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", "SocketImpl.port"); + return; + } + + jfieldID socketimpladdress = env->GetFieldID(socketimplclass, "address", + "Ljava/net/InetAddress;"); + + if (socketimpladdress == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "SocketImpl.address"); + return; + } + + gCachedFields.socketimpl_address = socketimpladdress; + gCachedFields.socketimpl_port = socketimplport; + + gCachedFields.dpack_class = env->FindClass("java/net/DatagramPacket"); + if (gCachedFields.dpack_class == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.net.DatagramPacket"); + return; + } + + gCachedFields.dpack_address = env->GetFieldID(gCachedFields.dpack_class, + "address", "Ljava/net/InetAddress;"); + if (gCachedFields.dpack_address == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "DatagramPacket.address"); + return; + } + + gCachedFields.dpack_port = env->GetFieldID(gCachedFields.dpack_class, + "port", "I"); + if (gCachedFields.dpack_port == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "DatagramPacket.port"); + return; + } + + gCachedFields.dpack_length = env->GetFieldID(gCachedFields.dpack_class, + "length", "I"); + if (gCachedFields.dpack_length == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "DatagramPacket.length"); + return; + } + + gCachedFields.fd_class = env->FindClass("java/io/FileDescriptor"); + if (gCachedFields.fd_class == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.io.FileDescriptor"); + return; + } + gCachedFields.descriptor = env->GetFieldID(gCachedFields.fd_class, "descriptor", "I"); + if (gCachedFields.descriptor == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "FileDescriptor.descriptor"); + return; + } + +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_createSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createSocketImpl"); + + int ret = socket(PF_INET, SOCK_STREAM, 0); + + if (ret < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, ret); + + return; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_createDatagramSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createDatagramSocketImpl"); + + int ret = socket(PF_INET, SOCK_DGRAM, 0); + + if (ret < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, ret); + + return; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_readSocketDirectImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint address, jint offset, jint count, + jint timeout) { + // LOGD("ENTER readSocketDirectImpl"); + + int handle; + jbyte *message = (jbyte *)address; + int result, ret, localCount; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + result = selectWait(handle, timeout, SELECT_READ_TYPE); + + if (0 > result) { + return 0; + } + + localCount = (count < 65536) ? count : 65536; + + do { + ret = recv(handle, (jbyte *) message, localCount, SOCKET_NOFLAGS); + } while (ret < 0 && errno == EINTR); + + if (0 == ret) { + return -1; + } else if (ret == -1) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + add_recv_stats(handle, ret); + return ret; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_readSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count, + jint timeout) { + // LOGD("ENTER readSocketImpl"); + + jbyte *message; + int result, localCount; + + jbyte internalBuffer[BUFFERSIZE]; + + localCount = (count < 65536) ? count : 65536; + + if (localCount > BUFFERSIZE) { + message = (jbyte*)malloc(localCount * sizeof(jbyte)); + if (message == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for readSocket"); + return 0; + } + } else { + message = (jbyte *)internalBuffer; + } + + result = Java_org_sipdroid_net_impl_OSNetworkSystem_readSocketDirectImpl(env, clazz, fileDescriptor, + (jint) message, offset, count, timeout); + + if (result > 0) { + env->SetByteArrayRegion(data, offset, result, (jbyte *)message); + } + + if (((jbyte *)message) != internalBuffer) { + free(( jbyte *)message); + } + + return result; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint address, jint offset, jint count) { + // LOGD("ENTER writeSocketDirectImpl"); + + int handle; + jbyte *message = (jbyte *)address; + int result = 0, sent = 0; + + if (count <= 0) { + return 0; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + result = send(handle, (jbyte *) message, (int) count, SOCKET_NOFLAGS); + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + + if (SOCKERR_WOULDBLOCK == err){ + jclass socketExClass,errorCodeExClass; + jmethodID errorCodeExConstructor, socketExConstructor,socketExCauseMethod; + jobject errorCodeEx, socketEx; + const char* errorMessage = netLookupErrorString(err); + jstring errorMessageString = env->NewStringUTF(errorMessage); + + errorCodeExClass = env->FindClass("org/apache/harmony/luni/util/ErrorCodeException"); + if (!errorCodeExClass){ + return 0; + } + errorCodeExConstructor = env->GetMethodID(errorCodeExClass,"","(I)V"); + if (!errorCodeExConstructor){ + return 0; + } + errorCodeEx = env->NewObject(errorCodeExClass,errorCodeExConstructor,err); + + socketExClass = env->FindClass("java/net/SocketException"); + if (!socketExClass) { + return 0; + } + socketExConstructor = env->GetMethodID(socketExClass,"","(Ljava/lang/String;)V"); + if (!socketExConstructor) { + return 0; + } + socketEx = env->NewObject(socketExClass, socketExConstructor, errorMessageString); + socketExCauseMethod = env->GetMethodID(socketExClass,"initCause","(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); + env->CallObjectMethod(socketEx,socketExCauseMethod,errorCodeEx); + env->Throw((jthrowable)socketEx); + return 0; + } + throwSocketException(env, err); + return 0; + } + + add_send_stats(handle, result); + return result; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_writeSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count) { + // LOGD("ENTER writeSocketImpl"); + + jbyte *message; + int sent = 0; + jint result = 0; + +/* TODO: ARRAY PINNING */ +#define INTERNAL_SEND_BUFFER_MAX 512 + jbyte internalBuffer[INTERNAL_SEND_BUFFER_MAX]; + + if (count > INTERNAL_SEND_BUFFER_MAX) { + message = (jbyte*)malloc(count * sizeof( jbyte)); + if (message == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for writeSocket"); + return 0; + } + } else { + message = (jbyte *)internalBuffer; + } + + env->GetByteArrayRegion(data, offset, count, message); + + result = Java_org_sipdroid_net_impl_OSNetworkSystem_writeSocketDirectImpl(env, clazz, fileDescriptor, + (jint) message, offset, count); + + if (( jbyte *)message != internalBuffer) { + free(( jbyte *)message); + } +#undef INTERNAL_SEND_BUFFER_MAX + return result; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_setNonBlockingImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean nonblocking) { + // LOGD("ENTER setNonBlockingImpl"); + + int handle; + int result; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + int block = nonblocking; + + result = ioctl(handle, FIONBIO, &block); + + if (result == -1) { + throwSocketException(env, convertError(errno)); + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_connectSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint trafficClass, jobject inetAddr, jint port); + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_connectWithTimeoutSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jint timeout, jint trafficClass, + jobject inetAddr, jint port, jint step, jbyteArray passContext) { + // LOGD("ENTER connectWithTimeoutSocketImpl"); + + int handle; + int result = 0; + struct sockaddr_in address; + jbyte *context = NULL; + + memset(&address, 0, sizeof(address)); + + address.sin_family = AF_INET; + + result = inetAddressToSocketAddress(env, inetAddr, port, + (struct sockaddr_in *) &address); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return result; + } + + // Check if we're using adb networking and redirect in case it is used. + if (useAdbNetworking && !isLocalhost(&address)) { + return Java_org_sipdroid_net_impl_OSNetworkSystem_connectSocketImpl(env, clazz, fileDescriptor, + trafficClass, inetAddr, port); + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return -1; + } + + address.sin_port = htons(port); + + context = (jbyte *)env->GetPrimitiveArrayCritical(passContext, NULL); + + switch (step) { + case SOCKET_CONNECT_STEP_START: + result = sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_START, context); + break; + case SOCKET_CONNECT_STEP_CHECK: + result = sockConnectWithTimeout(handle, address, timeout, + SOCKET_STEP_CHECK, context); + break; + } + + env->ReleasePrimitiveArrayCritical(passContext, context, JNI_ABORT); + + if (0 == result) { + /* connected , so stop here */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, NULL); + } else if (result != SOCKERR_NOTCONNECTED) { + /* can not connect... */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, NULL); + if (result == SOCKERR_EACCES) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(result)); + } + } + + return result; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_connectStreamWithTimeoutSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jint remotePort, jint timeout, + jint trafficClass, jobject inetAddr) { + // LOGD("ENTER connectStreamWithTimeoutSocketImpl"); + + int result = 0; + int handle; + struct sockaddr_in address; + jbyte *context = NULL; + int remainingTimeout = timeout; + int passedTimeout = 0; + int finishTime = 0; + int blocking = 0; + char hasTimeout = timeout > 0; + + /* if a timeout was specified calculate the finish time value */ + if (hasTimeout) { + finishTime = time_msec_clock() + (int) timeout; + } + + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } else { + result = inetAddressToSocketAddress(env, inetAddr, remotePort, + (struct sockaddr_in *) &address); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + // Check if we're using adb networking and redirect in case it is used. + if (useAdbNetworking && !isLocalhost(&address)) { + int retVal = Java_org_sipdroid_net_impl_OSNetworkSystem_connectSocketImpl(env, clazz, + fileDescriptor, trafficClass, inetAddr, remotePort); + if (retVal != 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + } + return; + } + + /* + * we will be looping checking for when we are connected so allocate + * the descriptor sets that we will use + */ + context =(jbyte *) malloc(sizeof(struct selectFDSet)); + + if (NULL == context) { + throwSocketException(env, SOCKERR_NOBUFFERS); + return; + } + + result = sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_START, context); + if (0 == result) { + /* ok we connected right away so we are done */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, context); + goto bail; + } else if (result != SOCKERR_NOTCONNECTED) { + log_socket_close(handle, result); + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, + context); + /* we got an error other than NOTCONNECTED so we cannot continue */ + if (SOCKERR_EACCES == result) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + throwSocketException(env, result); + } + goto bail; + } + + while (SOCKERR_NOTCONNECTED == result) { + passedTimeout = remainingTimeout; + + /* + * ok now try and connect. Depending on the platform this may sleep + * for up to passedTimeout milliseconds + */ + result = sockConnectWithTimeout(handle, address, passedTimeout, + SOCKET_STEP_CHECK, context); + + /* + * now check if the socket is still connected. + * Do it here as some platforms seem to think they + * are connected if the socket is closed on them. + */ + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + throwSocketException(env, SOCKERR_BADSOCKET); + goto bail; + } + + /* + * check if we are now connected, + * if so we can finish the process and return + */ + if (0 == result) { + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + goto bail; + } + + /* + * if the error is SOCKERR_NOTCONNECTED then we have not yet + * connected and we may not be done yet + */ + if (SOCKERR_NOTCONNECTED == result) { + /* check if the timeout has expired */ + if (hasTimeout) { + remainingTimeout = finishTime - time_msec_clock(); + if (remainingTimeout <= 0) { + log_socket_close(handle, result); + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + jniThrowException(env, + "java/net/SocketTimeoutException", + netLookupErrorString(result)); + goto bail; + } + } else { + remainingTimeout = 100; + } + } else { + log_socket_close(handle, result); + sockConnectWithTimeout(handle, address, remainingTimeout, + SOCKET_STEP_DONE, context); + if ((SOCKERR_CONNRESET == result) || + (SOCKERR_CONNECTION_REFUSED == result) || + (SOCKERR_ADDRNOTAVAIL == result) || + (SOCKERR_ADDRINUSE == result) || + (SOCKERR_ENETUNREACH == result)) { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(result)); + } else if (SOCKERR_EACCES == result) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + throwSocketException(env, result); + } + goto bail; + } + } + } + +bail: + + /* free the memory for the FD set */ + if (context != NULL) { + free(context); + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_connectSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint trafficClass, jobject inetAddr, jint port) { + //LOGD("ENTER direct-call connectSocketImpl\n"); + + struct sockaddr_in address; + int ret; + int handle; + jbyteArray java_in_addr; + + memset(&address, 0, sizeof(address)); + + address.sin_family = AF_INET; + + ret = inetAddressToSocketAddress(env, inetAddr, port, + (struct sockaddr_in *) &address); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return ret; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return -1; + } + + address.sin_port = htons(port); + + if (useAdbNetworking && !isLocalhost(&address)) { + + // LOGD("+connect to address 0x%08x port %d (via adb)", + // address.sin_addr.s_addr, (int) port); + ret = adb_networking_connect_fd(handle, &address); + // LOGD("-connect ret %d errno %d (via adb)", ret, errno); + + } else { + + // call this method with a timeout of zero + Java_org_sipdroid_net_impl_OSNetworkSystem_connectStreamWithTimeoutSocketImpl(env, clazz, + fileDescriptor, port, 0, trafficClass, inetAddr); + if (env->ExceptionOccurred() != 0) { + return -1; + } else { + return 0; + } + + } + + if (ret < 0) { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(convertError(errno))); + return ret; + } + + return ret; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_socketBindImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint port, jobject inetAddress) { + // LOGD("ENTER socketBindImpl"); + + struct sockaddr_in sockaddress; + int ret; + int handle; + + ret = inetAddressToSocketAddress(env, inetAddress, port, + (struct sockaddr_in *) &sockaddress); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + ret = bind(handle, (const sockaddr*)&sockaddress, sizeof(sockaddress)); + + if (ret < 0) { + jniThrowException(env, "java/net/BindException", + netLookupErrorString(convertError(errno))); + return; + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_listenStreamSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint backlog) { + // LOGD("ENTER listenStreamSocketImpl"); + + int ret; + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + ret = listen(handle, backlog); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_availableStreamImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor) { + // LOGD("ENTER availableStreamImpl"); + + int handle; + char message[BUFFERSIZE]; + + int result; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + do { + result = selectWait(handle, 1, SELECT_READ_TYPE); + + if (SOCKERR_TIMEOUT == result) { + // The read operation timed out, so answer 0 bytes available + return 0; + } else if (SOCKERR_INTERRUPTED == result) { + continue; + } else if (0 > result) { + log_socket_close(handle, result); + throwSocketException(env, result); + return 0; + } + } while (SOCKERR_INTERRUPTED == result); + + result = recv(handle, (jbyte *) message, BUFFERSIZE, MSG_PEEK); + + if (0 > result) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + add_recv_stats(handle, result); + return result; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_acceptSocketImpl(JNIEnv* env, jclass clazz, + jobject fdServer, jobject newSocket, jobject fdnewSocket, jint timeout) { + // LOGD("ENTER acceptSocketImpl"); + + union { + struct sockaddr address; + struct sockaddr_in in_address; + } sa; + + int ret; + int retFD; + int result; + int handle; + socklen_t addrlen; + + if (newSocket == NULL) { + throwNullPointerException(env); + return; + } + + result = pollSelectWait(env, fdServer, timeout, SELECT_READ_TYPE); + + if (0 > result) { + return; + } + + handle = jniGetFDFromFileDescriptor(env, fdServer); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + do { + addrlen = sizeof(sa); + ret = accept(handle, &(sa.address), &addrlen); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } + + retFD = ret; + + /* For AF_INET / inetOrLocal == true only: put + * peer address and port in instance variables + * We don't bother for UNIX domain sockets, since most peers are + * anonymous anyway + */ + if (sa.address.sa_family == AF_INET) { + // inetOrLocal should also be true + + jobject inetAddress; + + inetAddress = structInToInetAddress(env, &(sa.in_address.sin_addr)); + + if (inetAddress == NULL) { + close(retFD); + newSocket = NULL; + return; + } + + env->SetObjectField(newSocket, + gCachedFields.socketimpl_address, inetAddress); + + env->SetIntField(newSocket, gCachedFields.socketimpl_port, + ntohs(sa.in_address.sin_port)); + } + + jniSetFileDescriptorOfFD(env, fdnewSocket, retFD); +} + +extern "C" jboolean Java_org_sipdroid_net_impl_OSNetworkSystem_supportsUrgentDataImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor) { + // LOGD("ENTER supportsUrgentDataImpl"); + + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + return JNI_FALSE; + } + + return JNI_TRUE; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_sendUrgentDataImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyte value) { + // LOGD("ENTER sendUrgentDataImpl"); + + int handle; + int result; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + result = send(handle, (jbyte *) &value, 1, MSG_OOB); + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_connectDatagramImpl2(JNIEnv* env, jclass clazz, + jobject fd, jint port, jint trafficClass, jobject inetAddress) { + // LOGD("ENTER connectDatagramImpl2"); + + int handle = jniGetFDFromFileDescriptor(env, fd); + + struct sockaddr_in sockAddr; + int ret; + + ret = inetAddressToSocketAddress(env, inetAddress, port, &sockAddr); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + log_socket_connect(handle, ntohl(sockAddr.sin_addr.s_addr), port); + int result = connect(handle, (struct sockaddr *)&sockAddr, sizeof(sockAddr)); + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_disconnectDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd) { + // LOGD("ENTER disconnectDatagramImpl"); + + int handle = jniGetFDFromFileDescriptor(env, fd); + + struct sockaddr_in *sockAddr; + socklen_t sockAddrLen = sizeof(struct sockaddr_in); + sockAddr = (struct sockaddr_in *) malloc(sockAddrLen); + memset(sockAddr, 0, sockAddrLen); + + sockAddr->sin_family = AF_UNSPEC; + int result = connect(handle, (struct sockaddr *)sockAddr, sockAddrLen); + free(sockAddr); + + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } +} + +extern "C" jboolean Java_org_sipdroid_net_impl_OSNetworkSystem_socketBindImpl2(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint port, jboolean bindToDevice, + jobject inetAddress) { + // LOGD("ENTER socketBindImpl2"); + + struct sockaddr_in sockaddress; + int ret; + int handle; + + ret = inetAddressToSocketAddress(env, inetAddress, port, + (struct sockaddr_in *) &sockaddress); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + ret = bind(handle, (const sockaddr*)&sockaddress, sizeof(sockaddress)); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + jniThrowException(env, "java/net/BindException", netLookupErrorString(err)); + return 0; + } + + return 0; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_peekDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject sender, jint receiveTimeout) { + // LOGD("ENTER peekDatagramImpl"); + + int port = -1; + + int result = pollSelectWait (env, fd, receiveTimeout, SELECT_READ_TYPE); + if (0> result) { + return (jint) 0; + } + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + struct sockaddr_in sockAddr; + socklen_t sockAddrLen = sizeof(sockAddr); + + int length = recvfrom(handle, NULL, 0, MSG_PEEK, + (struct sockaddr *)&sockAddr, &sockAddrLen); + + if (length < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + + if (socketAddressToInetAddress(env, &sockAddr, sender, &port) < 0) { + throwIOExceptionStr(env, "Address conversion failed"); + return -1; + } + add_recv_stats(handle, length); + return port; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_receiveDatagramDirectImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject packet, jint address, jint offset, jint length, + jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveDatagramDirectImpl"); + + int result = pollSelectWait (env, fd, receiveTimeout, SELECT_READ_TYPE); + if (0 > result) { + return (jint) 0; + } + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + struct sockaddr_in sockAddr; + socklen_t sockAddrLen = sizeof(sockAddr); + + int mode = peek ? MSG_PEEK : 0; + + int actualLength = recvfrom(handle, (char*)(address + offset), length, mode, + (struct sockaddr *)&sockAddr, &sockAddrLen); + + if (actualLength < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + + if (packet != NULL) { + int port = ntohs(sockAddr.sin_port); + jobject sender = env->GetObjectField(packet, gCachedFields.dpack_address); + //__android_log_print(ANDROID_LOG_INFO,"OSNetwork","Sender address %x ", sender); + if (sender != 0) { + jbyteArray addr = (jbyteArray)env->GetObjectField(sender, gCachedFields.iaddr_ipaddress); + //__android_log_print(ANDROID_LOG_INFO,"OSNetwork","Sender Recup"); + if ((structInToJavaAddress(env, &sockAddr.sin_addr, addr)) < 0) { + jniThrowException(env, "java/net/SocketException", + "Could not set address of packet."); + return 0; + } + } + else { + jbyteArray addr = env->NewByteArray(sizeof(struct in_addr)); + if ((structInToJavaAddress(env, &sockAddr.sin_addr, addr)) < 0) { + jniThrowException(env, "java/net/SocketException", + "Could not set address of packet."); + return 0; + } + sender = env->CallStaticObjectMethod( + gCachedFields.iaddr_class, gCachedFields.iaddr_getbyaddress, + addr); + + } + env->SetObjectField(packet, gCachedFields.dpack_address, sender); + env->SetIntField(packet, gCachedFields.dpack_port, port); + env->SetIntField(packet, gCachedFields.dpack_length, actualLength); + } + add_recv_stats(handle, actualLength); + return actualLength; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_receiveDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject packet, jbyteArray data, jint offset, jint length, + jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveDatagramImpl"); + + int localLength = (length < 65536) ? length : 65536; + jbyte *bytes = (jbyte*) malloc(localLength); + if (bytes == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for receiveDatagram"); + return 0; + } + + int actualLength = Java_org_sipdroid_net_impl_OSNetworkSystem_receiveDatagramDirectImpl(env, clazz, fd, + packet, (jint)bytes, offset, localLength, receiveTimeout, peek); + + if (actualLength > 0) { + env->SetByteArrayRegion(data, offset, actualLength, bytes); + } + free(bytes); + + return actualLength; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_recvConnectedDatagramDirectImpl(JNIEnv* env, + jclass clazz, jobject fd, jobject packet, jint address, jint offset, + jint length, jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveConnectedDatagramDirectImpl"); + + int result = pollSelectWait (env, fd, receiveTimeout, SELECT_READ_TYPE); + + if (0 > result) { + return 0; + } + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + int mode = peek ? MSG_PEEK : 0; + + int actualLength = recvfrom(handle, + (char*)(address + offset), length, mode, NULL, NULL); + + if (actualLength < 0) { + jniThrowException(env, "java/net/PortUnreachableException", ""); + return 0; + } + + if ( packet != NULL) { + env->SetIntField(packet, gCachedFields.dpack_length, actualLength); + } + add_recv_stats(handle, actualLength); + return actualLength; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_recvConnectedDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject packet, jbyteArray data, jint offset, jint length, + jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveConnectedDatagramImpl"); + + int localLength = (length < 65536) ? length : 65536; + jbyte *bytes = (jbyte*) malloc(localLength); + if (bytes == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for recvConnectedDatagram"); + return 0; + } + + int actualLength = Java_org_sipdroid_net_impl_OSNetworkSystem_recvConnectedDatagramDirectImpl(env, + clazz, fd, packet, (jint)bytes, offset, localLength, + receiveTimeout, peek); + + if (actualLength > 0) { + env->SetByteArrayRegion(data, offset, actualLength, bytes); + } + free(bytes); + + return actualLength; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendDatagramDirectImpl(JNIEnv* env, jclass clazz, + jobject fd, jint address, jint offset, jint length, jint port, + jboolean bindToDevice, jint trafficClass, jobject inetAddress) { + // LOGD("ENTER sendDatagramDirectImpl"); + + int result = 0; + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + struct sockaddr_in receiver; + + if (inetAddressToSocketAddress(env, inetAddress, port, &receiver) < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + result = sendto(handle, (char*)(address + offset), length, SOCKET_NOFLAGS, + (struct sockaddr*)&receiver, sizeof(receiver)); + + if (result < 0) { + int err = convertError(errno); + if ((SOCKERR_CONNRESET == err) + || (SOCKERR_CONNECTION_REFUSED == err)) { + return 0; + } else { + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + } + add_send_stats(handle, result); + return result; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jbyteArray data, jint offset, jint length, jint port, + jboolean bindToDevice, jint trafficClass, jobject inetAddress) { + // LOGD("ENTER sendDatagramImpl"); + + jbyte *bytes = env->GetByteArrayElements(data, NULL); + int actualLength = Java_org_sipdroid_net_impl_OSNetworkSystem_sendDatagramDirectImpl(env, clazz, fd, + (jint)bytes, offset, length, port, bindToDevice, trafficClass, + inetAddress); + env->ReleaseByteArrayElements(data, bytes, JNI_ABORT); + + return actualLength; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendConnectedDatagramDirectImpl(JNIEnv* env, + jclass clazz, jobject fd, jint address, jint offset, jint length, + jboolean bindToDevice) { + // LOGD("ENTER sendConnectedDatagramDirectImpl"); + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + int result = send(handle, (char*)(address + offset), length, 0); + + if (result < 0) { + int err = convertError(errno); + if ((SOCKERR_CONNRESET == err) || (SOCKERR_CONNECTION_REFUSED == err)) { + return 0; + } else { + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + } + add_send_stats(handle, length); + return result; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendConnectedDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jbyteArray data, jint offset, jint length, + jboolean bindToDevice) { + // LOGD("ENTER sendConnectedDatagramImpl"); + + jbyte *bytes = env->GetByteArrayElements(data, NULL); + int actualLength = Java_org_sipdroid_net_impl_OSNetworkSystem_sendConnectedDatagramDirectImpl(env, + clazz, fd, (jint)bytes, offset, length, bindToDevice); + env->ReleaseByteArrayElements(data, bytes, JNI_ABORT); + + return actualLength; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_createServerStreamSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createServerStreamSocketImpl"); + + if (fileDescriptor == NULL) { + throwNullPointerException(env); + return; + } + + int handle = socket(PF_INET, SOCK_STREAM, 0); + + if (handle < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, handle); + + int value = 1; + + setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)); +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_createMulticastSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createMulticastSocketImpl"); + + int handle = socket(PF_INET, SOCK_DGRAM, 0); + + if (handle < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, handle); + + int value = 1; + + // setsockopt(handle, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(jbyte)); + setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)); +} + +/* + * @param timeout in milliseconds. If zero, block until data received + */ +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_receiveStreamImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count, + jint timeout) { + // LOGD("ENTER receiveStreamImpl"); + + int result; + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + // Cap read length to available buf size + int spaceAvailable = env->GetArrayLength(data) - offset; + int localCount = count < spaceAvailable? count : spaceAvailable; + + jboolean isCopy; + jbyte *body = env->GetByteArrayElements(data, &isCopy); + + // set timeout + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, + sizeof(struct timeval)); + + do { + result = recv(handle, body + offset, localCount, SOCKET_NOFLAGS); + } while (result < 0 && errno == EINTR); + + env->ReleaseByteArrayElements(data, body, 0); + + /* + * If no bytes are read, return -1 to signal 'endOfFile' + * to the Java input stream + */ + if (0 < result) { + add_recv_stats(handle, result); + return result; + } else if (0 == result) { + return -1; + } else { + // If EAGAIN or EWOULDBLOCK, read timed out + if (errno == EAGAIN || errno == EWOULDBLOCK) { + jniThrowException(env, "java/net/SocketTimeoutException", + netLookupErrorString(SOCKERR_TIMEOUT)); + } else { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } + return 0; + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendStreamImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count) { + // LOGD("ENTER sendStreamImpl"); + + int handle = 0; + int result = 0, sent = 0; + + jboolean isCopy; + jbyte *message = env->GetByteArrayElements(data, &isCopy); + + // Cap write length to available buf size + int spaceAvailable = env->GetArrayLength(data) - offset; + if (count > spaceAvailable) count = spaceAvailable; + + while (sent < count) { + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, + sent == 0 ? SOCKERR_BADSOCKET : SOCKERR_INTERRUPTED); + env->ReleaseByteArrayElements(data, message, 0); + return 0; + } + + // LOGD("before select %d", count); + selectWait(handle, SEND_RETRY_TIME, SELECT_WRITE_TYPE); + result = send(handle, (jbyte *)message + offset + sent, + (int) count - sent, SOCKET_NOFLAGS); + + if (result < 0) { + result = errno; + if (result == EAGAIN ||result == EWOULDBLOCK) { + // LOGD("write blocked %d", sent); + continue; + } + env->ReleaseByteArrayElements(data, message, 0); + int err = convertError(result); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + sent += result; + } + + env->ReleaseByteArrayElements(data, message, 0); + add_send_stats(handle, sent); + return sent; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_shutdownInputImpl(JNIEnv* env, jobject obj, + jobject fileDescriptor) { + // LOGD("ENTER shutdownInputImpl"); + + int ret; + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + ret = shutdown(handle, SHUT_RD); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_shutdownOutputImpl(JNIEnv* env, jobject obj, + jobject fileDescriptor) { + // LOGD("ENTER shutdownOutputImpl"); + + int ret; + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + return; + } + + ret = shutdown(handle, SHUT_WR); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, + jobject fd, jbyteArray data, jint offset, jint length, jint port, + jobject inetAddress) { + // LOGD("ENTER sendDatagramImpl2"); + + jbyte *message; + jbyte nhostAddrBytes[4]; + unsigned short nPort; + int result = 0, sent = 0; + int handle = 0; + struct sockaddr_in sockaddrP; + + if (inetAddress != NULL) { + + result = inetAddressToSocketAddress(env, inetAddress, port, + (struct sockaddr_in *) &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + } + + message = (jbyte*) malloc(length * sizeof(jbyte)); + if (message == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for readSocket"); + return 0; + } + + env->GetByteArrayRegion(data, offset, length, message); + + while (sent < length) { + handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, + sent == 0 ? SOCKERR_BADSOCKET : SOCKERR_INTERRUPTED); + free(message); + return 0; + } + + result = sendto(handle, (char *) (message + sent), + (int) (length - sent), SOCKET_NOFLAGS, + (struct sockaddr *) &sockaddrP, sizeof(sockaddrP)); + + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + free(message); + return 0; + } + + sent += result; + } + + free(message); + add_send_stats(handle, sent); + return sent; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_selectImpl(JNIEnv* env, jclass clazz, + jobjectArray readFDArray, jobjectArray writeFDArray, jint countReadC, + jint countWriteC, jintArray outFlags, jlong timeout) { + // LOGD("ENTER selectImpl"); + + struct timeval timeP; + int result = 0; + int size = 0; + jobject gotFD; + fd_set *fdset_read,*fdset_write; + int handle; + jboolean isCopy ; + jint *flagArray; + int val; + unsigned int time_sec = (unsigned int)timeout/1000; + unsigned int time_msec = (unsigned int)(timeout%1000); + + fdset_read = (fd_set *)malloc(sizeof(fd_set)); + fdset_write = (fd_set *)malloc(sizeof(fd_set)); + + FD_ZERO(fdset_read); + FD_ZERO(fdset_write); + + for (val = 0; valGetObjectArrayElement(readFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + FD_SET(handle, fdset_read); + + if (0 > (size - handle)) { + size = handle; + } + } + + for (val = 0; valGetObjectArrayElement(writeFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + FD_SET(handle, fdset_write); + + if (0 > (size - handle)) { + size = handle; + } + } + + /* the size is the max_fd + 1 */ + size =size + 1; + + if (0 > size) { + result = SOCKERR_FDSET_SIZEBAD; + } else { + /* only set when timeout >= 0 (non-block)*/ + if (0 <= timeout) { + + timeP.tv_sec = time_sec; + timeP.tv_usec = time_msec*1000; + + result = sockSelect(size, fdset_read, fdset_write, NULL, &timeP); + + } else { + result = sockSelect(size, fdset_read, fdset_write, NULL, NULL); + } + } + + if (0 < result) { + /*output the result to a int array*/ + flagArray = env->GetIntArrayElements(outFlags, &isCopy); + + for (val=0; valGetObjectArrayElement(readFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + if (FD_ISSET(handle,fdset_read)) { + flagArray[val] = SOCKET_OP_READ; + } else { + flagArray[val] = SOCKET_OP_NONE; + } + } + + for (val=0; valGetObjectArrayElement(writeFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + if (FD_ISSET(handle,fdset_write)) { + flagArray[val+countReadC] = SOCKET_OP_WRITE; + } else { + flagArray[val+countReadC] = SOCKET_OP_NONE; + } + } + + env->ReleaseIntArrayElements(outFlags, flagArray, 0); + } + + free(fdset_write); + free(fdset_read); + + /* return both correct and error result, let java handle the exception*/ + return result; +} + +extern "C" jobject Java_org_sipdroid_net_impl_OSNetworkSystem_getSocketLocalAddressImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jboolean preferIPv6Addresses) { + // LOGD("ENTER getSocketLocalAddressImpl"); + + struct sockaddr_in addr; + socklen_t addrLen = sizeof(addr); + + memset(&addr, 0, addrLen); + + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + int result; + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_UNKNOWNSOCKET); + return NULL; + } + + result = getsockname(handle, (struct sockaddr *)&addr, &addrLen); + + // Spec says ignore all errors + + return structInToInetAddress(env, &(addr.sin_addr)); + +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_getSocketLocalPortImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean preferIPv6Addresses) { + // LOGD("ENTER getSocketLocalPortImpl"); + + struct sockaddr_in addr; + socklen_t addrLen = sizeof(addr); + + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + int result; + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_UNKNOWNSOCKET); + return 0; + } + + result = getsockname(handle, (struct sockaddr *)&addr, &addrLen); + + if (0 != result) { + // The java spec does not indicate any exceptions on this call + return 0; + } else { + return ntohs(addr.sin_port); + } +} + +extern "C" jobject Java_org_sipdroid_net_impl_OSNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint anOption) { + // LOGD("ENTER getSocketOptionImpl"); + + int handle; + int intValue = 0; + socklen_t intSize = sizeof(int); + unsigned char byteValue = 0; + socklen_t byteSize = sizeof(unsigned char); + int result; + struct sockaddr_in sockVal; + socklen_t sockSize = sizeof(sockVal); + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return NULL; + } + + switch ((int) anOption & 0xffff) { + case JAVASOCKOPT_SO_LINGER: { + struct linger lingr; + socklen_t size = sizeof(struct linger); + result = getsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr, &size); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + if (!lingr.l_onoff) { + intValue = -1; + } else { + intValue = lingr.l_linger; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_TCP_NODELAY: { + if ((anOption >> 16) & BROKEN_TCP_NODELAY) { + return NULL; + } + result = getsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_MCAST_TTL: { + if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { + return newJavaLangByte(env, 0); + } + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, &byteValue, &byteSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangByte(env, (jbyte)(byteValue & 0xFF)); + } + case JAVASOCKOPT_MCAST_INTERFACE: { + if ((anOption >> 16) & BROKEN_MULTICAST_IF) { + return NULL; + } + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockVal, &sockSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return structInToInetAddress(env, &(sockVal.sin_addr)); + } + case JAVASOCKOPT_SO_SNDBUF: { + result = getsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_SO_RCVBUF: { + result = getsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_SO_BROADCAST: { + result = getsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_SO_REUSEADDR: { + result = getsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_SO_KEEPALIVE: { + result = getsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_SO_OOBINLINE: { + result = getsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_IP_MULTICAST_LOOP: { + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_IP_TOS: { + result = getsockopt(handle, IPPROTO_IP, IP_TOS, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_SO_RCVTIMEOUT: { + struct timeval timeout; + socklen_t size = sizeof(timeout); + result = getsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, &size); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, timeout.tv_sec * 1000 + timeout.tv_usec/1000); + } + default: { + throwSocketException(env, SOCKERR_OPTUNSUPP); + return NULL; + } + } + +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint anOption, jobject optVal) { + // LOGD("ENTER setSocketOptionImpl"); + + int handle, result; + int intVal, intSize = sizeof(int); + unsigned char byteVal, byteSize = sizeof(unsigned char); + struct sockaddr_in sockVal; + int sockSize = sizeof(sockVal); + + if (env->IsInstanceOf(optVal, gCachedFields.integer_class)) { + intVal = (int) env->GetIntField(optVal, gCachedFields.integer_class_value); + } else if (env->IsInstanceOf(optVal, gCachedFields.boolean_class)) { + intVal = (int) env->GetBooleanField(optVal, gCachedFields.boolean_class_value); + } else if (env->IsInstanceOf(optVal, gCachedFields.byte_class)) { + byteVal = (int) env->GetByteField(optVal, gCachedFields.byte_class_value); + } else if (env->IsInstanceOf(optVal, gCachedFields.iaddr_class)) { + if (inetAddressToSocketAddress(env, optVal, 0, &sockVal) < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + } else if (env->IsInstanceOf(optVal, gCachedFields.genericipmreq_class)) { + // we'll use optVal directly + } else { + throwSocketException(env, SOCKERR_OPTUNSUPP); + return; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + switch ((int) anOption & 0xffff) { + case JAVASOCKOPT_SO_LINGER: { + struct linger lingr; + lingr.l_onoff = intVal > 0 ? 1 : 0; + lingr.l_linger = intVal; + result = setsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr, + sizeof(struct linger)); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_TCP_NODELAY: { + if ((anOption >> 16) & BROKEN_TCP_NODELAY) { + return; + } + result = setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_MCAST_TTL: { + if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { + return; + } + result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, &byteVal, byteSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP: { + mcastAddDropMembership(env, handle, optVal, + (anOption >> 16) & BROKEN_MULTICAST_IF, IP_ADD_MEMBERSHIP); + return; + } + + case JAVASOCKOPT_MCAST_DROP_MEMBERSHIP: { + mcastAddDropMembership(env, handle, optVal, + (anOption >> 16) & BROKEN_MULTICAST_IF, IP_DROP_MEMBERSHIP); + return; + } + + case JAVASOCKOPT_MCAST_INTERFACE: { + if ((anOption >> 16) & BROKEN_MULTICAST_IF) { + return; + } + result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockVal, sockSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_SNDBUF: { + result = setsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_RCVBUF: { + result = setsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_BROADCAST: { + result = setsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_REUSEADDR: { + result = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + case JAVASOCKOPT_SO_KEEPALIVE: { + result = setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_OOBINLINE: { + result = setsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_IP_MULTICAST_LOOP: { + result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_IP_TOS: { + result = setsockopt(handle, IPPROTO_IP, IP_TOS, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_REUSEADDR_AND_REUSEPORT: { + // SO_REUSEPORT doesn't need to get set on this System + result = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_RCVTIMEOUT: { + struct timeval timeout; + timeout.tv_sec = intVal / 1000; + timeout.tv_usec = (intVal % 1000) * 1000; + result = setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(struct timeval)); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + default: { + throwSocketException(env, SOCKERR_OPTUNSUPP); + } + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_getSocketFlagsImpl(JNIEnv* env, jclass clazz) { + // LOGD("ENTER getSocketFlagsImpl"); + + // Not implemented by harmony + return 0; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_socketCloseImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor) { + // LOGD("ENTER socketCloseImpl"); + + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + log_socket_close(handle, SOCKET_CLOSE_LOCAL); + + jniSetFileDescriptorOfFD(env, fileDescriptor, -1); + + close(handle); +} + +extern "C" jobject Java_org_sipdroid_net_impl_OSNetworkSystem_getHostByAddrImpl(JNIEnv* env, jclass clazz, + jbyteArray addrStr) { + // LOGD("ENTER getHostByAddrImpl"); + + if (addrStr == NULL) { + throwNullPointerException(env); + return JNI_FALSE; + } + + jstring address = (jstring)newJavaLangString(env, addrStr); + jstring result; + const char* addr = env->GetStringUTFChars(address, NULL); + + struct hostent* ent = gethostbyaddr(addr, strlen(addr), AF_INET); + + if (ent != NULL && ent->h_name != NULL) { + result = env->NewStringUTF(ent->h_name); + } else { + result = NULL; + } + + env->ReleaseStringUTFChars(address, addr); + + return result; +} + +extern "C" jobject Java_org_sipdroid_net_impl_OSNetworkSystem_getHostByNameImpl(JNIEnv* env, jclass clazz, + jstring nameStr, jboolean preferIPv6Addresses) { + // LOGD("ENTER getHostByNameImpl"); + + if (nameStr == NULL) { + throwNullPointerException(env); + return NULL; + } + + const char* name = env->GetStringUTFChars(nameStr, NULL); + + if (useAdbNetworking) { + + union { + struct in_addr a; + jbyte j[4]; + } outaddr; + + // LOGD("ADB networking: +gethostbyname '%s'", name); + int err; + err = adb_networking_gethostbyname(name, &(outaddr.a)); + + env->ReleaseStringUTFChars(nameStr, name); +#if 0 + LOGD("ADB networking: -gethostbyname err %d addr 0x%08x %u.%u.%u.%u", + err, (unsigned int)outaddr.a.s_addr, + outaddr.j[0],outaddr.j[1], + outaddr.j[2],outaddr.j[3]); +#endif + + if (err < 0) { + return NULL; + } else { + jbyteArray addr = env->NewByteArray(4); + env->SetByteArrayRegion(addr, 0, 4, outaddr.j); + return addr; + } + } else { + + // normal case...no adb networking + struct hostent* ent = gethostbyname(name); + + env->ReleaseStringUTFChars(nameStr, name); + + if (ent != NULL && ent->h_length > 0) { + jbyteArray addr = env->NewByteArray(4); + jbyte v[4]; + memcpy(v, ent->h_addr, 4); + env->SetByteArrayRegion(addr, 0, 4, v); + return addr; + } else { + return NULL; + } + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_setInetAddressImpl(JNIEnv* env, jobject obj, + jobject sender, jbyteArray address) { + // LOGD("ENTER setInetAddressImpl"); + + env->SetObjectField(sender, gCachedFields.iaddr_ipaddress, address); +} + +/* +extern "C" jobject Java_org_sipdroid_net_impl_OSNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { + // LOGD("ENTER inheritedChannelImpl"); + + int socket = 0; + int opt; + socklen_t length = sizeof(opt); + int socket_type; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + jclass channel_class, socketaddr_class, serverSocket_class, socketImpl_class; + jobject channel_object = NULL, socketaddr_object, serverSocket_object; + jobject fd_object, addr_object, localAddr_object, socketImpl_object; + jfieldID port_field, socketaddr_field, bound_field, fd_field; + jfieldID serverSocket_field, socketImpl_field, addr_field, localAddr_field; + jmethodID channel_new; + jbyteArray addr_array; + struct sockaddr_in *sock; + jbyte * address; + jbyte * localAddr; + jboolean jtrue = JNI_TRUE; + + if (0 != getsockopt(socket, SOL_SOCKET, SO_TYPE, &opt, &length)) { + return NULL; + } + if (SOCK_STREAM !=opt && SOCK_DGRAM !=opt) { + return NULL; + } + socket_type = opt; + + length = sizeof(struct sockaddr); + if (0 != getsockname(socket, (struct sockaddr *)&local_addr, &length)) { + return NULL; + } else { + if (AF_INET != local_addr.sin_family || length != sizeof(struct sockaddr)) { + return NULL; + } + localAddr = (jbyte*) malloc(sizeof(jbyte)*4); + if (NULL == localAddr) { + return NULL; + } + memcpy (localAddr, &(local_addr.sin_addr.s_addr), 4); + } + if (0 != getpeername(socket, (struct sockaddr *)&remote_addr, &length)) { + remote_addr.sin_port = 0; + remote_addr.sin_addr.s_addr = 0; + address = (jbyte*) malloc(sizeof(jbyte)*4); + bzero(address, sizeof(jbyte)*4); + } else { + if (AF_INET != remote_addr.sin_family + || length != sizeof(struct sockaddr)) { + return NULL; + } + address = (jbyte*) malloc(sizeof(jbyte)*4); + memcpy (address, &(remote_addr.sin_addr.s_addr), 4); + } + + // analysis end, begin pack to java + if (SOCK_STREAM == opt) { + if (remote_addr.sin_port!=0) { + //socket + channel_class = env->FindClass( + "org/apache/harmony/nio/internal/SocketChannelImpl"); + if (NULL == channel_class) { + goto clean; + } + + channel_new = env->GetMethodID(channel_class, "", "()V"); + if (NULL == channel_new) { + goto clean; + } + channel_object = env->NewObject(channel_class, channel_new); + if (NULL == channel_object) { + goto clean; + } + // new and set FileDescript + + fd_field = env->GetFieldID(channel_class, "fd", + "java/io/FielDescriptor"); + fd_object = env->GetObjectField(channel_object, fd_field); + if (NULL == fd_object) { + goto clean; + } + + jniSetFileDescriptorOfFD(env, fd_object, socket); + + // local port + port_field = env->GetFieldID(channel_class, "localPort", "I"); + env->SetIntField(channel_object, port_field, + ntohs(local_addr.sin_port)); + + // new and set remote addr + addr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + if (NULL == addr_object) { + goto clean; + } + socketaddr_class = env->FindClass("java/net/InetSocketAddress"); + socketaddr_field = env->GetFieldID(channel_class, "connectAddress", + "Ljava/net/InetSocketAddress;"); + socketaddr_object = env->GetObjectField(channel_object, + socketaddr_field); + if (NULL == socketaddr_object) { + goto clean; + } + addr_field = env->GetFieldID(socketaddr_class, "addr", + "Ljava/net/InetAddress;"); + env->SetObjectField(socketaddr_object, addr_field, addr_object); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); + env->SetObjectField(addr_object, gCachedFields.iaddr_ipaddress, + addr_array); + + // localAddr + socketaddr_class = env->FindClass("java/net/InetSocketAddress"); + socketaddr_field = env->GetFieldID(channel_class, "connectAddress", + "Ljava/net/InetSocketAddress;"); + socketaddr_object = env->GetObjectField(channel_object, + socketaddr_field); + + localAddr_field = env->GetFieldID(channel_class, "localAddress", + "Ljava/net/InetAddress;"); + localAddr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + jfieldID socketaddr_field = env->GetFieldID(channel_class, + "connectAddress", "Ljava/net/InetSocketAddress;"); + jobject socketaddr_object = env->GetObjectField(channel_object, + socketaddr_field); + env->SetObjectField(socketaddr_object, localAddr_field, + localAddr_object); + if (NULL == localAddr_object) { + goto clean; + } + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, localAddr); + env->SetObjectField(localAddr_object, gCachedFields.iaddr_ipaddress, + addr_array); + + + // set port + port_field = env->GetFieldID(socketaddr_class, "port", "I"); + env->SetIntField(socketaddr_object, port_field, + ntohs(remote_addr.sin_port)); + + // set bound + if (0 != local_addr.sin_port) { + bound_field = env->GetFieldID(channel_class, "isBound", "Z"); + env->SetBooleanField(channel_object, bound_field, jtrue); + } + + } else { + //serverSocket + channel_class = env->FindClass( + "org/apache/harmony/nio/internal/ServerSocketChannelImpl"); + if (NULL == channel_class) { + goto clean; + } + + channel_new = env->GetMethodID(channel_class, "", "()V"); + if (NULL == channel_new) { + goto clean; + } + channel_object = env->NewObject(channel_class, channel_new); + if (NULL == channel_object) { + goto clean; + } + + serverSocket_field = env->GetFieldID(channel_class, "socket", + "Ljava/net/ServerSocket;"); + serverSocket_class = env->FindClass("Ljava/net/ServerSocket;"); + serverSocket_object = env->GetObjectField(channel_object, + serverSocket_field); + // set bound + if (0 != local_addr.sin_port) { + bound_field = env->GetFieldID(channel_class, "isBound", "Z"); + env->SetBooleanField(channel_object, bound_field, jtrue); + bound_field = env->GetFieldID(serverSocket_class, "isBound", "Z"); + env->SetBooleanField(serverSocket_object, bound_field, jtrue); + } + // localAddr + socketImpl_class = env->FindClass("java/net/SocketImpl"); + socketImpl_field = env->GetFieldID(channel_class, "impl", + "Ljava/net/SocketImpl;"); + socketImpl_object = env->GetObjectField(channel_object, + socketImpl_field); + if (NULL == socketImpl_object) { + goto clean; + } + + localAddr_field = env->GetFieldID(channel_class, "localAddress", + "Ljava/net/InetAddress;"); + localAddr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + if (NULL == localAddr_object) { + goto clean; + } + env->SetObjectField(socketImpl_object, localAddr_field, + localAddr_object); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, localAddr); + env->SetObjectField(localAddr_object, + gCachedFields.iaddr_ipaddress, addr_array); + + // set port + port_field = env->GetFieldID(socketImpl_class, "localport", "I"); + env->SetIntField(socketImpl_object, port_field, + ntohs(local_addr.sin_port)); + } + } else { + //Datagram Socket + // new DatagramChannel + channel_class = env->FindClass( + "org/apache/harmony/nio/internal/DatagramChannelImpl"); + if (NULL == channel_class) { + goto clean; + } + + channel_new = env->GetMethodID(channel_class, "", "()V"); + if (NULL == channel_new) { + goto clean; + } + channel_object = env->NewObject(channel_class, channel_new); + if (NULL == channel_object) { + goto clean; + } + + // new and set FileDescript + fd_field = env->GetFieldID(channel_class, "fd", "java/io/FileDescriptor"); + fd_object = env->GetObjectField(channel_object, fd_field); + if (NULL == fd_object) { + goto clean; + } + + jniSetFileDescriptorOfFD(env, fd_object, socket); + + port_field = env->GetFieldID(channel_class, "localPort", "I"); + env->SetIntField(channel_object, port_field, ntohs(local_addr.sin_port)); + + // new and set remote addr + addr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + if (NULL == addr_object) { + goto clean; + } + socketaddr_class = env->FindClass("java/net/InetSocketAddress"); + socketaddr_field = env->GetFieldID(channel_class, "connectAddress", + "Ljava/net/InetSocketAddress;"); + socketaddr_object = env->GetObjectField(channel_object, socketaddr_field); + if (NULL == socketaddr_object) { + goto clean; + } + addr_field = env->GetFieldID(socketaddr_class, "addr", + "Ljava/net/InetAddress;"); + env->SetObjectField(socketaddr_object, addr_field, addr_object); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); + env->SetObjectField(addr_object, gCachedFields.iaddr_ipaddress, addr_array); + + // set bound + if (0 != local_addr.sin_port) { + bound_field = env->GetFieldID(channel_class, "isBound", "Z"); + env->SetBooleanField(channel_object, bound_field, jtrue); + } + } +clean: + free(address); + free(localAddr); + return channel_object; +} +*/ diff -r 047b5e2b9904 -r af3a788344f9 jni/g722/g722.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/g722/g722.h Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,159 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * g722.h - The ITU G.722 codec. + * + * Written by Steve Underwood + * + * Copyright (C) 2005 Steve Underwood + * + * Despite my general liking of the GPL, I place my own contributions + * to this code in the public domain for the benefit of all mankind - + * even the slimy ones who might try to proprietize my work and use it + * to my detriment. + * + * Based on a single channel G.722 codec which is: + * + ***** Copyright (c) CMU 1993 ***** + * Computer Science, Speech Group + * Chengxiang Lu and Alex Hauptmann + * + * $Id: g722.h 48959 2006-12-25 06:42:15Z rizzo $ + */ + + +/*! \file */ + +#if !defined(_G722_H_) +#define _G722_H_ + +/*! \page g722_page G.722 encoding and decoding +\section g722_page_sec_1 What does it do? +The G.722 module is a bit exact implementation of the ITU G.722 specification for all three +specified bit rates - 64000bps, 56000bps and 48000bps. It passes the ITU tests. + +To allow fast and flexible interworking with narrow band telephony, the encoder and decoder +support an option for the linear audio to be an 8k samples/second stream. In this mode the +codec is considerably faster, and still fully compatible with wideband terminals using G.722. + +\section g722_page_sec_2 How does it work? +???. +*/ + +#if !defined(__STDINT_MACROS) +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned uint32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +enum +{ + G722_SAMPLE_RATE_8000 = 0x0001, + G722_PACKED = 0x0002 +}; + +#ifndef INT16_MAX +#define INT16_MAX 32767 +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32768) +#endif + +typedef struct +{ + /*! TRUE if the operating in the special ITU test mode, with the band split filters + disabled. */ + int itu_test_mode; + /*! TRUE if the G.722 data is packed */ + int packed; + /*! TRUE if encode from 8k samples/second */ + int eight_k; + /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */ + int bits_per_sample; + + /*! Signal history for the QMF */ + int x[24]; + + struct + { + int s; + int sp; + int sz; + int r[3]; + int a[3]; + int ap[3]; + int p[3]; + int d[7]; + int b[7]; + int bp[7]; + int sg[7]; + int nb; + int det; + } band[2]; + + unsigned int in_buffer; + int in_bits; + unsigned int out_buffer; + int out_bits; +} g722_encode_state_t; + +typedef struct +{ + /*! TRUE if the operating in the special ITU test mode, with the band split filters + disabled. */ + int itu_test_mode; + /*! TRUE if the G.722 data is packed */ + int packed; + /*! TRUE if decode to 8k samples/second */ + int eight_k; + /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */ + int bits_per_sample; + + /*! Signal history for the QMF */ + int x[24]; + + struct + { + int s; + int sp; + int sz; + int r[3]; + int a[3]; + int ap[3]; + int p[3]; + int d[7]; + int b[7]; + int bp[7]; + int sg[7]; + int nb; + int det; + } band[2]; + + unsigned int in_buffer; + int in_bits; + unsigned int out_buffer; + int out_bits; +} g722_decode_state_t; + +#ifdef __cplusplus +extern "C" { +#endif + +g722_encode_state_t *g722_encode_init(g722_encode_state_t *s, int rate, int options); +int g722_encode_release(g722_encode_state_t *s); +int g722_encode(g722_encode_state_t *s, uint8_t g722_data[], const int16_t amp[], int len); + +g722_decode_state_t *g722_decode_init(g722_decode_state_t *s, int rate, int options); +int g722_decode_release(g722_decode_state_t *s); +int g722_decode(g722_decode_state_t *s, int16_t amp[], const uint8_t g722_data[], int len); + +#ifdef __cplusplus +} +#endif + +#endif diff -r 047b5e2b9904 -r af3a788344f9 jni/g722/g722_decode.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/g722/g722_decode.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,398 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * g722_decode.c - The ITU G.722 codec, decode part. + * + * Written by Steve Underwood + * + * Copyright (C) 2005 Steve Underwood + * + * Despite my general liking of the GPL, I place my own contributions + * to this code in the public domain for the benefit of all mankind - + * even the slimy ones who might try to proprietize my work and use it + * to my detriment. + * + * Based in part on a single channel G.722 codec which is: + * + * Copyright (c) CMU 1993 + * Computer Science, Speech Group + * Chengxiang Lu and Alex Hauptmann + * + * $Id: g722_decode.c 48661 2006-12-21 00:08:21Z mattf $ + */ + +/*! \file */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +//#include +#include +#include +#if 0 +#include +#endif + +#include "g722.h" + +#if !defined(FALSE) +#define FALSE 0 +#endif +#if !defined(TRUE) +#define TRUE (!FALSE) +#endif + +static int16_t saturate(int32_t amp) +{ + int16_t amp16; + + /* Hopefully this is optimised for the common case - not clipping */ + amp16 = (int16_t) amp; + if (amp == amp16) + return amp16; + if (amp > INT16_MAX) + return INT16_MAX; + return INT16_MIN; +} +/*- End of function --------------------------------------------------------*/ + +static void block4(g722_decode_state_t *s, int band, int d); + +static void block4(g722_decode_state_t *s, int band, int d) +{ + int wd1; + int wd2; + int wd3; + int i; + + /* Block 4, RECONS */ + s->band[band].d[0] = d; + s->band[band].r[0] = saturate(s->band[band].s + d); + + /* Block 4, PARREC */ + s->band[band].p[0] = saturate(s->band[band].sz + d); + + /* Block 4, UPPOL2 */ + for (i = 0; i < 3; i++) + s->band[band].sg[i] = s->band[band].p[i] >> 15; + wd1 = saturate(s->band[band].a[1] << 2); + + wd2 = (s->band[band].sg[0] == s->band[band].sg[1]) ? -wd1 : wd1; + if (wd2 > 32767) + wd2 = 32767; + wd3 = (s->band[band].sg[0] == s->band[band].sg[2]) ? 128 : -128; + wd3 += (wd2 >> 7); + wd3 += (s->band[band].a[2]*32512) >> 15; + if (wd3 > 12288) + wd3 = 12288; + else if (wd3 < -12288) + wd3 = -12288; + s->band[band].ap[2] = wd3; + + /* Block 4, UPPOL1 */ + s->band[band].sg[0] = s->band[band].p[0] >> 15; + s->band[band].sg[1] = s->band[band].p[1] >> 15; + wd1 = (s->band[band].sg[0] == s->band[band].sg[1]) ? 192 : -192; + wd2 = (s->band[band].a[1]*32640) >> 15; + + s->band[band].ap[1] = saturate(wd1 + wd2); + wd3 = saturate(15360 - s->band[band].ap[2]); + if (s->band[band].ap[1] > wd3) + s->band[band].ap[1] = wd3; + else if (s->band[band].ap[1] < -wd3) + s->band[band].ap[1] = -wd3; + + /* Block 4, UPZERO */ + wd1 = (d == 0) ? 0 : 128; + s->band[band].sg[0] = d >> 15; + for (i = 1; i < 7; i++) + { + s->band[band].sg[i] = s->band[band].d[i] >> 15; + wd2 = (s->band[band].sg[i] == s->band[band].sg[0]) ? wd1 : -wd1; + wd3 = (s->band[band].b[i]*32640) >> 15; + s->band[band].bp[i] = saturate(wd2 + wd3); + } + + /* Block 4, DELAYA */ + for (i = 6; i > 0; i--) + { + s->band[band].d[i] = s->band[band].d[i - 1]; + s->band[band].b[i] = s->band[band].bp[i]; + } + + for (i = 2; i > 0; i--) + { + s->band[band].r[i] = s->band[band].r[i - 1]; + s->band[band].p[i] = s->band[band].p[i - 1]; + s->band[band].a[i] = s->band[band].ap[i]; + } + + /* Block 4, FILTEP */ + wd1 = saturate(s->band[band].r[1] + s->band[band].r[1]); + wd1 = (s->band[band].a[1]*wd1) >> 15; + wd2 = saturate(s->band[band].r[2] + s->band[band].r[2]); + wd2 = (s->band[band].a[2]*wd2) >> 15; + s->band[band].sp = saturate(wd1 + wd2); + + /* Block 4, FILTEZ */ + s->band[band].sz = 0; + for (i = 6; i > 0; i--) + { + wd1 = saturate(s->band[band].d[i] + s->band[band].d[i]); + s->band[band].sz += (s->band[band].b[i]*wd1) >> 15; + } + s->band[band].sz = saturate(s->band[band].sz); + + /* Block 4, PREDIC */ + s->band[band].s = saturate(s->band[band].sp + s->band[band].sz); +} +/*- End of function --------------------------------------------------------*/ + +g722_decode_state_t *g722_decode_init(g722_decode_state_t *s, int rate, int options) +{ + if (s == NULL) + { + if ((s = (g722_decode_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + } + memset(s, 0, sizeof(*s)); + if (rate == 48000) + s->bits_per_sample = 6; + else if (rate == 56000) + s->bits_per_sample = 7; + else + s->bits_per_sample = 8; + if ((options & G722_SAMPLE_RATE_8000)) + s->eight_k = TRUE; + if ((options & G722_PACKED) && s->bits_per_sample != 8) + s->packed = TRUE; + else + s->packed = FALSE; + s->band[0].det = 32; + s->band[1].det = 8; + return s; +} +/*- End of function --------------------------------------------------------*/ + +int g722_decode_release(g722_decode_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int g722_decode(g722_decode_state_t *s, int16_t amp[], const uint8_t g722_data[], int len) +{ + static const int wl[8] = {-60, -30, 58, 172, 334, 538, 1198, 3042 }; + static const int rl42[16] = {0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 }; + static const int ilb[32] = + { + 2048, 2093, 2139, 2186, 2233, 2282, 2332, + 2383, 2435, 2489, 2543, 2599, 2656, 2714, + 2774, 2834, 2896, 2960, 3025, 3091, 3158, + 3228, 3298, 3371, 3444, 3520, 3597, 3676, + 3756, 3838, 3922, 4008 + }; + static const int wh[3] = {0, -214, 798}; + static const int rh2[4] = {2, 1, 2, 1}; + static const int qm2[4] = {-7408, -1616, 7408, 1616}; + static const int qm4[16] = + { + 0, -20456, -12896, -8968, + -6288, -4240, -2584, -1200, + 20456, 12896, 8968, 6288, + 4240, 2584, 1200, 0 + }; + static const int qm5[32] = + { + -280, -280, -23352, -17560, + -14120, -11664, -9752, -8184, + -6864, -5712, -4696, -3784, + -2960, -2208, -1520, -880, + 23352, 17560, 14120, 11664, + 9752, 8184, 6864, 5712, + 4696, 3784, 2960, 2208, + 1520, 880, 280, -280 + }; + static const int qm6[64] = + { + -136, -136, -136, -136, + -24808, -21904, -19008, -16704, + -14984, -13512, -12280, -11192, + -10232, -9360, -8576, -7856, + -7192, -6576, -6000, -5456, + -4944, -4464, -4008, -3576, + -3168, -2776, -2400, -2032, + -1688, -1360, -1040, -728, + 24808, 21904, 19008, 16704, + 14984, 13512, 12280, 11192, + 10232, 9360, 8576, 7856, + 7192, 6576, 6000, 5456, + 4944, 4464, 4008, 3576, + 3168, 2776, 2400, 2032, + 1688, 1360, 1040, 728, + 432, 136, -432, -136 + }; + static const int qmf_coeffs[12] = + { + 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11, + }; + + int dlowt; + int rlow; + int ihigh; + int dhigh; + int rhigh; + int xout1; + int xout2; + int wd1; + int wd2; + int wd3; + int code; + int outlen; + int i; + int j; + + outlen = 0; + rhigh = 0; + for (j = 0; j < len; ) + { + if (s->packed) + { + /* Unpack the code bits */ + if (s->in_bits < s->bits_per_sample) + { + s->in_buffer |= (g722_data[j++] << s->in_bits); + s->in_bits += 8; + } + code = s->in_buffer & ((1 << s->bits_per_sample) - 1); + s->in_buffer >>= s->bits_per_sample; + s->in_bits -= s->bits_per_sample; + } + else + { + code = g722_data[j++]; + } + + switch (s->bits_per_sample) + { + default: + case 8: + wd1 = code & 0x3F; + ihigh = (code >> 6) & 0x03; + wd2 = qm6[wd1]; + wd1 >>= 2; + break; + case 7: + wd1 = code & 0x1F; + ihigh = (code >> 5) & 0x03; + wd2 = qm5[wd1]; + wd1 >>= 1; + break; + case 6: + wd1 = code & 0x0F; + ihigh = (code >> 4) & 0x03; + wd2 = qm4[wd1]; + break; + } + /* Block 5L, LOW BAND INVQBL */ + wd2 = (s->band[0].det*wd2) >> 15; + /* Block 5L, RECONS */ + rlow = s->band[0].s + wd2; + /* Block 6L, LIMIT */ + if (rlow > 16383) + rlow = 16383; + else if (rlow < -16384) + rlow = -16384; + + /* Block 2L, INVQAL */ + wd2 = qm4[wd1]; + dlowt = (s->band[0].det*wd2) >> 15; + + /* Block 3L, LOGSCL */ + wd2 = rl42[wd1]; + wd1 = (s->band[0].nb*127) >> 7; + wd1 += wl[wd2]; + if (wd1 < 0) + wd1 = 0; + else if (wd1 > 18432) + wd1 = 18432; + s->band[0].nb = wd1; + + /* Block 3L, SCALEL */ + wd1 = (s->band[0].nb >> 6) & 31; + wd2 = 8 - (s->band[0].nb >> 11); + wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); + s->band[0].det = wd3 << 2; + + block4(s, 0, dlowt); + + if (!s->eight_k) + { + /* Block 2H, INVQAH */ + wd2 = qm2[ihigh]; + dhigh = (s->band[1].det*wd2) >> 15; + /* Block 5H, RECONS */ + rhigh = dhigh + s->band[1].s; + /* Block 6H, LIMIT */ + if (rhigh > 16383) + rhigh = 16383; + else if (rhigh < -16384) + rhigh = -16384; + + /* Block 2H, INVQAH */ + wd2 = rh2[ihigh]; + wd1 = (s->band[1].nb*127) >> 7; + wd1 += wh[wd2]; + if (wd1 < 0) + wd1 = 0; + else if (wd1 > 22528) + wd1 = 22528; + s->band[1].nb = wd1; + + /* Block 3H, SCALEH */ + wd1 = (s->band[1].nb >> 6) & 31; + wd2 = 10 - (s->band[1].nb >> 11); + wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); + s->band[1].det = wd3 << 2; + + block4(s, 1, dhigh); + } + + if (s->itu_test_mode) + { + amp[outlen++] = (int16_t) (rlow << 1); + amp[outlen++] = (int16_t) (rhigh << 1); + } + else + { + if (s->eight_k) + { + amp[outlen++] = (int16_t) rlow; + } + else + { + /* Apply the receive QMF */ + for (i = 0; i < 22; i++) + s->x[i] = s->x[i + 2]; + s->x[22] = rlow + rhigh; + s->x[23] = rlow - rhigh; + + xout1 = 0; + xout2 = 0; + for (i = 0; i < 12; i++) + { + xout2 += s->x[2*i]*qmf_coeffs[i]; + xout1 += s->x[2*i + 1]*qmf_coeffs[11 - i]; + } + amp[outlen++] = (int16_t) (xout1 >> 12); + amp[outlen++] = (int16_t) (xout2 >> 12); + } + } + } + return outlen; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff -r 047b5e2b9904 -r af3a788344f9 jni/g722/g722_encode.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/g722/g722_encode.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,400 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * g722_encode.c - The ITU G.722 codec, encode part. + * + * Written by Steve Underwood + * + * Copyright (C) 2005 Steve Underwood + * + * All rights reserved. + * + * Despite my general liking of the GPL, I place my own contributions + * to this code in the public domain for the benefit of all mankind - + * even the slimy ones who might try to proprietize my work and use it + * to my detriment. + * + * Based on a single channel 64kbps only G.722 codec which is: + * + ***** Copyright (c) CMU 1993 ***** + * Computer Science, Speech Group + * Chengxiang Lu and Alex Hauptmann + * + * $Id: g722_encode.c 48661 2006-12-21 00:08:21Z mattf $ + */ + +/*! \file */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +//#include +#include +#include +#if 0 +#include +#endif + +#include "g722.h" + +#if !defined(FALSE) +#define FALSE 0 +#endif +#if !defined(TRUE) +#define TRUE (!FALSE) +#endif + +static int16_t saturate(int32_t amp) +{ + int16_t amp16; + + /* Hopefully this is optimised for the common case - not clipping */ + amp16 = (int16_t) amp; + if (amp == amp16) + return amp16; + if (amp > INT16_MAX) + return INT16_MAX; + return INT16_MIN; +} +/*- End of function --------------------------------------------------------*/ + +static void block4(g722_encode_state_t *s, int band, int d) +{ + int wd1; + int wd2; + int wd3; + int i; + + /* Block 4, RECONS */ + s->band[band].d[0] = d; + s->band[band].r[0] = saturate(s->band[band].s + d); + + /* Block 4, PARREC */ + s->band[band].p[0] = saturate(s->band[band].sz + d); + + /* Block 4, UPPOL2 */ + for (i = 0; i < 3; i++) + s->band[band].sg[i] = s->band[band].p[i] >> 15; + wd1 = saturate(s->band[band].a[1] << 2); + + wd2 = (s->band[band].sg[0] == s->band[band].sg[1]) ? -wd1 : wd1; + if (wd2 > 32767) + wd2 = 32767; + wd3 = (wd2 >> 7) + ((s->band[band].sg[0] == s->band[band].sg[2]) ? 128 : -128); + wd3 += (s->band[band].a[2]*32512) >> 15; + if (wd3 > 12288) + wd3 = 12288; + else if (wd3 < -12288) + wd3 = -12288; + s->band[band].ap[2] = wd3; + + /* Block 4, UPPOL1 */ + s->band[band].sg[0] = s->band[band].p[0] >> 15; + s->band[band].sg[1] = s->band[band].p[1] >> 15; + wd1 = (s->band[band].sg[0] == s->band[band].sg[1]) ? 192 : -192; + wd2 = (s->band[band].a[1]*32640) >> 15; + + s->band[band].ap[1] = saturate(wd1 + wd2); + wd3 = saturate(15360 - s->band[band].ap[2]); + if (s->band[band].ap[1] > wd3) + s->band[band].ap[1] = wd3; + else if (s->band[band].ap[1] < -wd3) + s->band[band].ap[1] = -wd3; + + /* Block 4, UPZERO */ + wd1 = (d == 0) ? 0 : 128; + s->band[band].sg[0] = d >> 15; + for (i = 1; i < 7; i++) + { + s->band[band].sg[i] = s->band[band].d[i] >> 15; + wd2 = (s->band[band].sg[i] == s->band[band].sg[0]) ? wd1 : -wd1; + wd3 = (s->band[band].b[i]*32640) >> 15; + s->band[band].bp[i] = saturate(wd2 + wd3); + } + + /* Block 4, DELAYA */ + for (i = 6; i > 0; i--) + { + s->band[band].d[i] = s->band[band].d[i - 1]; + s->band[band].b[i] = s->band[band].bp[i]; + } + + for (i = 2; i > 0; i--) + { + s->band[band].r[i] = s->band[band].r[i - 1]; + s->band[band].p[i] = s->band[band].p[i - 1]; + s->band[band].a[i] = s->band[band].ap[i]; + } + + /* Block 4, FILTEP */ + wd1 = saturate(s->band[band].r[1] + s->band[band].r[1]); + wd1 = (s->band[band].a[1]*wd1) >> 15; + wd2 = saturate(s->band[band].r[2] + s->band[band].r[2]); + wd2 = (s->band[band].a[2]*wd2) >> 15; + s->band[band].sp = saturate(wd1 + wd2); + + /* Block 4, FILTEZ */ + s->band[band].sz = 0; + for (i = 6; i > 0; i--) + { + wd1 = saturate(s->band[band].d[i] + s->band[band].d[i]); + s->band[band].sz += (s->band[band].b[i]*wd1) >> 15; + } + s->band[band].sz = saturate(s->band[band].sz); + + /* Block 4, PREDIC */ + s->band[band].s = saturate(s->band[band].sp + s->band[band].sz); +} +/*- End of function --------------------------------------------------------*/ + +g722_encode_state_t *g722_encode_init(g722_encode_state_t *s, int rate, int options) +{ + if (s == NULL) + { + if ((s = (g722_encode_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + } + memset(s, 0, sizeof(*s)); + if (rate == 48000) + s->bits_per_sample = 6; + else if (rate == 56000) + s->bits_per_sample = 7; + else + s->bits_per_sample = 8; + if ((options & G722_SAMPLE_RATE_8000)) + s->eight_k = TRUE; + if ((options & G722_PACKED) && s->bits_per_sample != 8) + s->packed = TRUE; + else + s->packed = FALSE; + s->band[0].det = 32; + s->band[1].det = 8; + return s; +} +/*- End of function --------------------------------------------------------*/ + +int g722_encode_release(g722_encode_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int g722_encode(g722_encode_state_t *s, uint8_t g722_data[], const int16_t amp[], int len) +{ + static const int q6[32] = + { + 0, 35, 72, 110, 150, 190, 233, 276, + 323, 370, 422, 473, 530, 587, 650, 714, + 786, 858, 940, 1023, 1121, 1219, 1339, 1458, + 1612, 1765, 1980, 2195, 2557, 2919, 0, 0 + }; + static const int iln[32] = + { + 0, 63, 62, 31, 30, 29, 28, 27, + 26, 25, 24, 23, 22, 21, 20, 19, + 18, 17, 16, 15, 14, 13, 12, 11, + 10, 9, 8, 7, 6, 5, 4, 0 + }; + static const int ilp[32] = + { + 0, 61, 60, 59, 58, 57, 56, 55, + 54, 53, 52, 51, 50, 49, 48, 47, + 46, 45, 44, 43, 42, 41, 40, 39, + 38, 37, 36, 35, 34, 33, 32, 0 + }; + static const int wl[8] = + { + -60, -30, 58, 172, 334, 538, 1198, 3042 + }; + static const int rl42[16] = + { + 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 + }; + static const int ilb[32] = + { + 2048, 2093, 2139, 2186, 2233, 2282, 2332, + 2383, 2435, 2489, 2543, 2599, 2656, 2714, + 2774, 2834, 2896, 2960, 3025, 3091, 3158, + 3228, 3298, 3371, 3444, 3520, 3597, 3676, + 3756, 3838, 3922, 4008 + }; + static const int qm4[16] = + { + 0, -20456, -12896, -8968, + -6288, -4240, -2584, -1200, + 20456, 12896, 8968, 6288, + 4240, 2584, 1200, 0 + }; + static const int qm2[4] = + { + -7408, -1616, 7408, 1616 + }; + static const int qmf_coeffs[12] = + { + 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11, + }; + static const int ihn[3] = {0, 1, 0}; + static const int ihp[3] = {0, 3, 2}; + static const int wh[3] = {0, -214, 798}; + static const int rh2[4] = {2, 1, 2, 1}; + + int dlow; + int dhigh; + int el; + int wd; + int wd1; + int ril; + int wd2; + int il4; + int ih2; + int wd3; + int eh; + int mih; + int i; + int j; + /* Low and high band PCM from the QMF */ + int xlow; + int xhigh; + int g722_bytes; + /* Even and odd tap accumulators */ + int sumeven; + int sumodd; + int ihigh; + int ilow; + int code; + + g722_bytes = 0; + xhigh = 0; + for (j = 0; j < len; ) + { + if (s->itu_test_mode) + { + xlow = + xhigh = amp[j++] >> 1; + } + else + { + if (s->eight_k) + { + xlow = amp[j++]; + } + else + { + /* Apply the transmit QMF */ + /* Shuffle the buffer down */ + for (i = 0; i < 22; i++) + s->x[i] = s->x[i + 2]; + s->x[22] = amp[j++]; + s->x[23] = amp[j++]; + + /* Discard every other QMF output */ + sumeven = 0; + sumodd = 0; + for (i = 0; i < 12; i++) + { + sumodd += s->x[2*i]*qmf_coeffs[i]; + sumeven += s->x[2*i + 1]*qmf_coeffs[11 - i]; + } + xlow = (sumeven + sumodd) >> 13; + xhigh = (sumeven - sumodd) >> 13; + } + } + /* Block 1L, SUBTRA */ + el = saturate(xlow - s->band[0].s); + + /* Block 1L, QUANTL */ + wd = (el >= 0) ? el : -(el + 1); + + for (i = 1; i < 30; i++) + { + wd1 = (q6[i]*s->band[0].det) >> 12; + if (wd < wd1) + break; + } + ilow = (el < 0) ? iln[i] : ilp[i]; + + /* Block 2L, INVQAL */ + ril = ilow >> 2; + wd2 = qm4[ril]; + dlow = (s->band[0].det*wd2) >> 15; + + /* Block 3L, LOGSCL */ + il4 = rl42[ril]; + wd = (s->band[0].nb*127) >> 7; + s->band[0].nb = wd + wl[il4]; + if (s->band[0].nb < 0) + s->band[0].nb = 0; + else if (s->band[0].nb > 18432) + s->band[0].nb = 18432; + + /* Block 3L, SCALEL */ + wd1 = (s->band[0].nb >> 6) & 31; + wd2 = 8 - (s->band[0].nb >> 11); + wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); + s->band[0].det = wd3 << 2; + + block4(s, 0, dlow); + + if (s->eight_k) + { + /* Just leave the high bits as zero */ + code = (0xC0 | ilow) >> (8 - s->bits_per_sample); + } + else + { + /* Block 1H, SUBTRA */ + eh = saturate(xhigh - s->band[1].s); + + /* Block 1H, QUANTH */ + wd = (eh >= 0) ? eh : -(eh + 1); + wd1 = (564*s->band[1].det) >> 12; + mih = (wd >= wd1) ? 2 : 1; + ihigh = (eh < 0) ? ihn[mih] : ihp[mih]; + + /* Block 2H, INVQAH */ + wd2 = qm2[ihigh]; + dhigh = (s->band[1].det*wd2) >> 15; + + /* Block 3H, LOGSCH */ + ih2 = rh2[ihigh]; + wd = (s->band[1].nb*127) >> 7; + s->band[1].nb = wd + wh[ih2]; + if (s->band[1].nb < 0) + s->band[1].nb = 0; + else if (s->band[1].nb > 22528) + s->band[1].nb = 22528; + + /* Block 3H, SCALEH */ + wd1 = (s->band[1].nb >> 6) & 31; + wd2 = 10 - (s->band[1].nb >> 11); + wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); + s->band[1].det = wd3 << 2; + + block4(s, 1, dhigh); + code = ((ihigh << 6) | ilow) >> (8 - s->bits_per_sample); + } + + if (s->packed) + { + /* Pack the code bits */ + s->out_buffer |= (code << s->out_bits); + s->out_bits += s->bits_per_sample; + if (s->out_bits >= 8) + { + g722_data[g722_bytes++] = (uint8_t) (s->out_buffer & 0xFF); + s->out_bits -= 8; + s->out_buffer >>= 8; + } + } + else + { + g722_data[g722_bytes++] = (uint8_t) code; + } + } + return g722_bytes; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff -r 047b5e2b9904 -r af3a788344f9 jni/g722/g722_jni.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/g722/g722_jni.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,80 @@ +/** + +g722_encode_state_t *g722_encode_init(g722_encode_state_t *s, int rate, int options); +int g722_encode_release(g722_encode_state_t *s); +int g722_encode(g722_encode_state_t *s, uint8_t g722_data[], const int16_t amp[], int len); + +g722_decode_state_t *g722_decode_init(g722_decode_state_t *s, int rate, int options); +int g722_decode_release(g722_decode_state_t *s); +int g722_decode(g722_decode_state_t *s, int16_t amp[], const uint8_t g722_data[], int len); + +*/ + +#include +#include "g722.h" + +jlong +Java_org_sipdroid_media_codecs_G722JNI_encodeInit(JNIEnv *env, jlong jg722State, jint rate, jint options) +{ + return g722_encode_init((void *) jg722State, rate, options); +} + +jint +Java_org_sipdroid_media_codecs_G722JNI_encodeRelease(JNIEnv *env, jlong jg722State) +{ + return g722_encode_release((void *) jg722State); +} + +jint +Java_org_sipdroid_media_codecs_G722JNI_encode(JNIEnv *env, jlong jg722State, jshortArray jsignal, jlong jsrcPos, jbyteArray jg722Byte, jlong jdestPos, jint len) +{ + jshort *signal; + jbyte *g722Byte; + jboolean isCopyByte; + jboolean isCopySignal; + void *ctx = (void *) jg722State; + jint res; + + g722Byte = (*env)->GetByteArrayElements(env, jg722Byte, &isCopyByte); + signal = (*env)->GetShortArrayElements(env, jsignal, &isCopySignal); + + res = g722_encode(ctx, g722Byte + jdestPos, signal + jsrcPos, len); + if (isCopyByte == JNI_TRUE) + (*env)->ReleaseByteArrayElements(env, jg722Byte, g722Byte, 0); + if (isCopySignal == JNI_TRUE) + (*env)->ReleaseShortArrayElements(env, jsignal, signal, 0); + return res; +} + +jlong +Java_org_sipdroid_media_codecs_G722JNI_decodeInit(JNIEnv *env, jlong jg722State, jint rate, jint options) +{ + return g722_decode_init((void *) jg722State, rate, options); +} + +jint +Java_org_sipdroid_media_codecs_G722JNI_decodeRelease(JNIEnv *env, jlong jg722State) +{ + return g722_decode_release((void *) jg722State); +} + +jint +Java_org_sipdroid_media_codecs_G722JNI_decode(JNIEnv *env, jlong jg722State, jbyteArray jg722Byte, jlong jsrcPos, jshortArray jsignal, jlong jdestPos, jint len) +{ + jshort *signal; + jbyte *g722Byte; + jboolean isCopyByte; + jboolean isCopySignal; + void *ctx = (void *) jg722State; + jint res; + + g722Byte = (*env)->GetByteArrayElements(env, jg722Byte, &isCopyByte); + signal = (*env)->GetShortArrayElements(env, jsignal, &isCopySignal); + + res = g722_decode(ctx, signal + jdestPos, g722Byte + jsrcPos, len); + if (isCopyByte == JNI_TRUE) + (*env)->ReleaseByteArrayElements(env, jg722Byte, g722Byte, 0); + if (isCopySignal == JNI_TRUE) + (*env)->ReleaseShortArrayElements(env, jsignal, signal, 0); + return res; +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/Android.mk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/Android.mk Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,19 @@ +SRC_FILES := add.c\ + code.c\ + debug.c\ + decode.c\ + gsm_create.c\ + gsm_decode.c\ + gsm_destroy.c\ + gsm_encode.c\ + gsm_explode.c\ + gsm_implode.c\ + gsm_option.c\ + gsm_print.c\ + long_term.c\ + lpc.c\ + preprocess.c\ + rpe.c\ + short_term.c\ + table.c\ + gsm_jni.c diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/add.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/add.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,235 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/add.c,v 1.6 1996/07/02 09:57:33 jutta Exp $ */ + +/* + * See private.h for the more commonly used macro versions. + */ + +#include +#include + +#include "private.h" +#include "gsm.h" +#include "proto.h" + +#define saturate(x) \ + ((x) < MIN_WORD ? MIN_WORD : (x) > MAX_WORD ? MAX_WORD: (x)) + +word gsm_add P2((a,b), word a, word b) +{ + longword sum = (longword)a + (longword)b; + return saturate(sum); +} + +word gsm_sub P2((a,b), word a, word b) +{ + longword diff = (longword)a - (longword)b; + return saturate(diff); +} + +word gsm_mult P2((a,b), word a, word b) +{ + if (a == MIN_WORD && b == MIN_WORD) return MAX_WORD; + else return SASR( (longword)a * (longword)b, 15 ); +} + +word gsm_mult_r P2((a,b), word a, word b) +{ + if (b == MIN_WORD && a == MIN_WORD) return MAX_WORD; + else { + longword prod = (longword)a * (longword)b + 16384; + prod >>= 15; + return prod & 0xFFFF; + } +} + +word gsm_abs P1((a), word a) +{ + return a < 0 ? (a == MIN_WORD ? MAX_WORD : -a) : a; +} + +longword gsm_L_mult P2((a,b),word a, word b) +{ + assert( a != MIN_WORD || b != MIN_WORD ); + return ((longword)a * (longword)b) << 1; +} + +longword gsm_L_add P2((a,b), longword a, longword b) +{ + if (a < 0) { + if (b >= 0) return a + b; + else { + ulongword A = (ulongword)-(a + 1) + (ulongword)-(b + 1); + return A >= MAX_LONGWORD ? MIN_LONGWORD :-(longword)A-2; + } + } + else if (b <= 0) return a + b; + else { + ulongword A = (ulongword)a + (ulongword)b; + return A > MAX_LONGWORD ? MAX_LONGWORD : A; + } +} + +longword gsm_L_sub P2((a,b), longword a, longword b) +{ + if (a >= 0) { + if (b >= 0) return a - b; + else { + /* a>=0, b<0 */ + + ulongword A = (ulongword)a + -(b + 1); + return A >= MAX_LONGWORD ? MAX_LONGWORD : (A + 1); + } + } + else if (b <= 0) return a - b; + else { + /* a<0, b>0 */ + + ulongword A = (ulongword)-(a + 1) + b; + return A >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)A - 1; + } +} + +static unsigned char const bitoff[ 256 ] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +word gsm_norm P1((a), longword a ) +/* + * the number of left shifts needed to normalize the 32 bit + * variable L_var1 for positive values on the interval + * + * with minimum of + * minimum of 1073741824 (01000000000000000000000000000000) and + * maximum of 2147483647 (01111111111111111111111111111111) + * + * + * and for negative values on the interval with + * minimum of -2147483648 (-10000000000000000000000000000000) and + * maximum of -1073741824 ( -1000000000000000000000000000000). + * + * in order to normalize the result, the following + * operation must be done: L_norm_var1 = L_var1 << norm( L_var1 ); + * + * (That's 'ffs', only from the left, not the right..) + */ +{ + assert(a != 0); + + if (a < 0) { + if (a <= -1073741824) return 0; + a = ~a; + } + + return a & 0xffff0000 + ? ( a & 0xff000000 + ? -1 + bitoff[ 0xFF & (a >> 24) ] + : 7 + bitoff[ 0xFF & (a >> 16) ] ) + : ( a & 0xff00 + ? 15 + bitoff[ 0xFF & (a >> 8) ] + : 23 + bitoff[ 0xFF & a ] ); +} + +longword gsm_L_asl P2((a,n), longword a, int n) +{ + if (n >= 32) return 0; + if (n <= -32) return -(a < 0); + if (n < 0) return gsm_L_asr(a, -n); + return a << n; +} + +word gsm_asl P2((a,n), word a, int n) +{ + if (n >= 16) return 0; + if (n <= -16) return -(a < 0); + if (n < 0) return gsm_asr(a, -n); + return a << n; +} + +longword gsm_L_asr P2((a,n), longword a, int n) +{ + if (n >= 32) return -(a < 0); + if (n <= -32) return 0; + if (n < 0) return a << -n; + +# ifdef SASR + return a >> n; +# else + if (a >= 0) return a >> n; + else return -(longword)( -(ulongword)a >> n ); +# endif +} + +word gsm_asr P2((a,n), word a, int n) +{ + if (n >= 16) return -(a < 0); + if (n <= -16) return 0; + if (n < 0) return a << -n; + +# ifdef SASR + return a >> n; +# else + if (a >= 0) return a >> n; + else return -(word)( -(uword)a >> n ); +# endif +} + +/* + * (From p. 46, end of section 4.2.5) + * + * NOTE: The following lines gives [sic] one correct implementation + * of the div(num, denum) arithmetic operation. Compute div + * which is the integer division of num by denum: with denum + * >= num > 0 + */ + +word gsm_div P2((num,denum), word num, word denum) +{ + longword L_num = num; + longword L_denum = denum; + word div = 0; + int k = 15; + + /* The parameter num sometimes becomes zero. + * Although this is explicitly guarded against in 4.2.5, + * we assume that the result should then be zero as well. + */ + + /* assert(num != 0); */ + + assert(num >= 0 && denum >= num); + if (num == 0) + return 0; + + while (k--) { + div <<= 1; + L_num <<= 1; + + if (L_num >= L_denum) { + L_num -= L_denum; + div++; + } + } + + return div; +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/code.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/code.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,104 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/code.c,v 1.3 1996/07/02 09:59:05 jutta Exp $ */ + +#include "config.h" + +#include + +#ifdef HAS_STDLIB_H +#include +#else +# include "proto.h" + extern char * memcpy P((char *, char *, int)); +#endif + +#include "private.h" +#include "gsm.h" +#include "proto.h" + + +/* + * 4.2 FIXED POINT IMPLEMENTATION OF THE RPE-LTP CODER + */ + +void Gsm_Coder P8((S,s,LARc,Nc,bc,Mc,xmaxc,xMc), + + struct gsm_state * S, + + word * s, /* [0..159] samples IN */ + +/* + * The RPE-LTD coder works on a frame by frame basis. The length of + * the frame is equal to 160 samples. Some computations are done + * once per frame to produce at the output of the coder the + * LARc[1..8] parameters which are the coded LAR coefficients and + * also to realize the inverse filtering operation for the entire + * frame (160 samples of signal d[0..159]). These parts produce at + * the output of the coder: + */ + + word * LARc, /* [0..7] LAR coefficients OUT */ + +/* + * Procedure 4.2.11 to 4.2.18 are to be executed four times per + * frame. That means once for each sub-segment RPE-LTP analysis of + * 40 samples. These parts produce at the output of the coder: + */ + + word * Nc, /* [0..3] LTP lag OUT */ + word * bc, /* [0..3] coded LTP gain OUT */ + word * Mc, /* [0..3] RPE grid selection OUT */ + word * xmaxc,/* [0..3] Coded maximum amplitude OUT */ + word * xMc /* [13*4] normalized RPE samples OUT */ +) +{ + int k; + word * dp = S->dp0 + 120; /* [ -120...-1 ] */ + word * dpp = dp; /* [ 0...39 ] */ + + static word e[50]; + + word so[160]; + + Gsm_Preprocess (S, s, so); + Gsm_LPC_Analysis (S, so, LARc); + Gsm_Short_Term_Analysis_Filter (S, LARc, so); + + for (k = 0; k <= 3; k++, xMc += 13) { + + Gsm_Long_Term_Predictor ( S, + so+k*40, /* d [0..39] IN */ + dp, /* dp [-120..-1] IN */ + e + 5, /* e [0..39] OUT */ + dpp, /* dpp [0..39] OUT */ + Nc++, + bc++); + + Gsm_RPE_Encoding ( S, + e + 5, /* e ][0..39][ IN/OUT */ + xmaxc++, Mc++, xMc ); + /* + * Gsm_Update_of_reconstructed_short_time_residual_signal + * ( dpp, e + 5, dp ); + */ + + { register int i; + register longword ltmp; + for (i = 0; i <= 39; i++) + dp[ i ] = GSM_ADD( e[5 + i], dpp[i] ); + } + dp += 40; + dpp += 40; + + } + + (void)memcpy( (char *)S->dp0, (char *)(S->dp0 + 160), + 120 * sizeof(*S->dp0) ); + + +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/config.h Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,37 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/config.h,v 1.5 1996/07/02 11:26:20 jutta Exp $*/ + +#ifndef CONFIG_H +#define CONFIG_H + +//efine SIGHANDLER_T int /* signal handlers are void */ +//efine HAS_SYSV_SIGNAL 1 /* sigs not blocked/reset? */ + +#define HAS_STDLIB_H 1 /* /usr/include/stdlib.h */ +//efine HAS_LIMITS_H 1 /* /usr/include/limits.h */ +#define HAS_FCNTL_H 1 /* /usr/include/fcntl.h */ +//efine HAS_ERRNO_DECL 1 /* errno.h declares errno */ + +#define HAS_FSTAT 1 /* fstat syscall */ +#define HAS_FCHMOD 1 /* fchmod syscall */ +#define HAS_CHMOD 1 /* chmod syscall */ +#define HAS_FCHOWN 1 /* fchown syscall */ +#define HAS_CHOWN 1 /* chown syscall */ +//efine HAS__FSETMODE 1 /* _fsetmode -- set file mode */ + +#define HAS_STRING_H 1 /* /usr/include/string.h */ +//efine HAS_STRINGS_H 1 /* /usr/include/strings.h */ + +#define HAS_UNISTD_H 1 /* /usr/include/unistd.h */ +#define HAS_UTIME 1 /* POSIX utime(path, times) */ +//efine HAS_UTIMES 1 /* use utimes() syscall instead */ +#define HAS_UTIME_H 1 /* UTIME header file */ +//efine HAS_UTIMBUF 1 /* struct utimbuf */ +//efine HAS_UTIMEUSEC 1 /* microseconds in utimbuf? */ + +#endif /* CONFIG_H */ diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/debug.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/debug.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,76 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/debug.c,v 1.2 1993/01/29 18:22:20 jutta Exp $ */ + +#include "private.h" + +#ifndef NDEBUG + +/* If NDEBUG _is_ defined and no debugging should be performed, + * calls to functions in this module are #defined to nothing + * in private.h. + */ + +#include +#include "proto.h" + +void gsm_debug_words P4( (name, from, to, ptr), + char * name, + int from, + int to, + word * ptr) +{ + int nprinted = 0; + + fprintf( stderr, "%s [%d .. %d]: ", name, from, to ); + while (from <= to) { + fprintf(stderr, "%d ", ptr[ from ] ); + from++; + if (nprinted++ >= 7) { + nprinted = 0; + if (from < to) putc('\n', stderr); + } + } + putc('\n', stderr); +} + +void gsm_debug_longwords P4( (name, from, to, ptr), + char * name, + int from, + int to, + longword * ptr) +{ + int nprinted = 0; + + fprintf( stderr, "%s [%d .. %d]: ", name, from, to ); + while (from <= to) { + + fprintf(stderr, "%ld ", ptr[ from ] ); + from++; + if (nprinted++ >= 7) { + nprinted = 0; + if (from < to) putc('\n', stderr); + } + } + putc('\n', stderr); +} + +void gsm_debug_longword P2( (name, value), + char * name, + longword value ) +{ + fprintf(stderr, "%s: %ld\n", name, (long)value ); +} + +void gsm_debug_word P2( (name, value), + char * name, + word value ) +{ + fprintf(stderr, "%s: %ld\n", name, (long)value); +} + +#endif diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/decode.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/decode.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,63 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/decode.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */ + +#include + +#include "private.h" +#include "gsm.h" +#include "proto.h" + +/* + * 4.3 FIXED POINT IMPLEMENTATION OF THE RPE-LTP DECODER + */ + +static void Postprocessing P2((S,s), + struct gsm_state * S, + register word * s) +{ + register int k; + register word msr = S->msr; + register longword ltmp; /* for GSM_ADD */ + register word tmp; + + for (k = 160; k--; s++) { + tmp = GSM_MULT_R( msr, 28180 ); + msr = GSM_ADD(*s, tmp); /* Deemphasis */ + *s = GSM_ADD(msr, msr) & 0xFFF8; /* Truncation & Upscaling */ + } + S->msr = msr; +} + +void Gsm_Decoder P8((S,LARcr, Ncr,bcr,Mcr,xmaxcr,xMcr,s), + struct gsm_state * S, + + word * LARcr, /* [0..7] IN */ + + word * Ncr, /* [0..3] IN */ + word * bcr, /* [0..3] IN */ + word * Mcr, /* [0..3] IN */ + word * xmaxcr, /* [0..3] IN */ + word * xMcr, /* [0..13*4] IN */ + + word * s) /* [0..159] OUT */ +{ + int j, k; + word erp[40], wt[160]; + word * drp = S->dp0 + 120; + + for (j=0; j <= 3; j++, xmaxcr++, bcr++, Ncr++, Mcr++, xMcr += 13) { + + Gsm_RPE_Decoding( S, *xmaxcr, *Mcr, xMcr, erp ); + Gsm_Long_Term_Synthesis_Filtering( S, *Ncr, *bcr, erp, drp ); + + for (k = 0; k <= 39; k++) wt[ j * 40 + k ] = drp[ k ]; + } + + Gsm_Short_Term_Synthesis_Filter( S, LARcr, wt, s ); + Postprocessing(S, s); +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/gsm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/gsm.h Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,71 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/*$Header: /home/kbs/jutta/src/gsm/gsm-1.0/inc/RCS/gsm.h,v 1.11 1996/07/05 18:02:56 jutta Exp $*/ + +#ifndef GSM_H +#define GSM_H + +#ifdef __cplusplus +# define NeedFunctionPrototypes 1 +#endif + +#if __STDC__ +# define NeedFunctionPrototypes 1 +#endif + +#ifdef _NO_PROTO +# undef NeedFunctionPrototypes +#endif + +#ifdef NeedFunctionPrototypes +# include /* for FILE * */ +#endif + +#undef GSM_P +#if NeedFunctionPrototypes +# define GSM_P( protos ) protos +#else +# define GSM_P( protos ) ( /* protos */ ) +#endif + +/* + * Interface + */ + +typedef struct gsm_state * gsm; +typedef short gsm_signal; /* signed 16 bit */ +typedef unsigned char gsm_byte; +typedef gsm_byte gsm_frame[33]; /* 33 * 8 bits */ + +#define GSM_MAGIC 0xD /* 13 kbit/s RPE-LTP */ + +#define GSM_PATCHLEVEL 10 +#define GSM_MINOR 0 +#define GSM_MAJOR 1 + +#define GSM_OPT_VERBOSE 1 +#define GSM_OPT_FAST 2 +#define GSM_OPT_LTP_CUT 3 +#define GSM_OPT_WAV49 4 +#define GSM_OPT_FRAME_INDEX 5 +#define GSM_OPT_FRAME_CHAIN 6 + +extern gsm gsm_create GSM_P((void)); +extern void gsm_destroy GSM_P((gsm)); + +extern int gsm_print GSM_P((FILE *, gsm, gsm_byte *)); +extern int gsm_option GSM_P((gsm, int, int *)); + +extern void gsm_encode GSM_P((gsm, gsm_signal *, gsm_byte *)); +extern int gsm_decode GSM_P((gsm, gsm_byte *, gsm_signal *)); + +extern int gsm_explode GSM_P((gsm, gsm_byte *, gsm_signal *)); +extern void gsm_implode GSM_P((gsm, gsm_signal *, gsm_byte *)); + +#undef GSM_P + +#endif /* GSM_H */ diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/gsm_create.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/gsm_create.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,45 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +static char const ident[] = "$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_create.c,v 1.4 1996/07/02 09:59:05 jutta Exp $"; + +#include "config.h" + +#ifdef HAS_STRING_H +#include +#else +# include "proto.h" + extern char * memset P((char *, int, int)); +#endif + +#ifdef HAS_STDLIB_H +# include +#else +# ifdef HAS_MALLOC_H +# include +# else + extern char * malloc(); +# endif +#endif + +#include + +#include "gsm.h" +#include "private.h" +#include "proto.h" + +gsm gsm_create P0() +{ + gsm r; + + r = (gsm)malloc(sizeof(struct gsm_state)); + if (!r) return r; + + memset((char *)r, 0, sizeof(*r)); + r->nrp = 40; + + return r; +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/gsm_decode.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/gsm_decode.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,361 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_decode.c,v 1.2 1996/07/02 09:59:05 jutta Exp $ */ + +#include "private.h" + +#include "gsm.h" +#include "proto.h" + +int gsm_decode P3((s, c, target), gsm s, gsm_byte * c, gsm_signal * target) +{ + word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4]; + +#ifdef WAV49 + if (s->wav_fmt) { + + uword sr = 0; + + s->frame_index = !s->frame_index; + if (s->frame_index) { + + sr = *c++; + LARc[0] = sr & 0x3f; sr >>= 6; + sr |= (uword)*c++ << 2; + LARc[1] = sr & 0x3f; sr >>= 6; + sr |= (uword)*c++ << 4; + LARc[2] = sr & 0x1f; sr >>= 5; + LARc[3] = sr & 0x1f; sr >>= 5; + sr |= (uword)*c++ << 2; + LARc[4] = sr & 0xf; sr >>= 4; + LARc[5] = sr & 0xf; sr >>= 4; + sr |= (uword)*c++ << 2; /* 5 */ + LARc[6] = sr & 0x7; sr >>= 3; + LARc[7] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[0] = sr & 0x7f; sr >>= 7; + bc[0] = sr & 0x3; sr >>= 2; + Mc[0] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[0] = sr & 0x3f; sr >>= 6; + xmc[0] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[1] = sr & 0x7; sr >>= 3; + xmc[2] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[3] = sr & 0x7; sr >>= 3; + xmc[4] = sr & 0x7; sr >>= 3; + xmc[5] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 10 */ + xmc[6] = sr & 0x7; sr >>= 3; + xmc[7] = sr & 0x7; sr >>= 3; + xmc[8] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[9] = sr & 0x7; sr >>= 3; + xmc[10] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[11] = sr & 0x7; sr >>= 3; + xmc[12] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[1] = sr & 0x7f; sr >>= 7; + bc[1] = sr & 0x3; sr >>= 2; + Mc[1] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[1] = sr & 0x3f; sr >>= 6; + xmc[13] = sr & 0x7; sr >>= 3; + sr = *c++; /* 15 */ + xmc[14] = sr & 0x7; sr >>= 3; + xmc[15] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[16] = sr & 0x7; sr >>= 3; + xmc[17] = sr & 0x7; sr >>= 3; + xmc[18] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[19] = sr & 0x7; sr >>= 3; + xmc[20] = sr & 0x7; sr >>= 3; + xmc[21] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[22] = sr & 0x7; sr >>= 3; + xmc[23] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[24] = sr & 0x7; sr >>= 3; + xmc[25] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; /* 20 */ + Nc[2] = sr & 0x7f; sr >>= 7; + bc[2] = sr & 0x3; sr >>= 2; + Mc[2] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[2] = sr & 0x3f; sr >>= 6; + xmc[26] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[27] = sr & 0x7; sr >>= 3; + xmc[28] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[29] = sr & 0x7; sr >>= 3; + xmc[30] = sr & 0x7; sr >>= 3; + xmc[31] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[32] = sr & 0x7; sr >>= 3; + xmc[33] = sr & 0x7; sr >>= 3; + xmc[34] = sr & 0x7; sr >>= 3; + sr = *c++; /* 25 */ + xmc[35] = sr & 0x7; sr >>= 3; + xmc[36] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[37] = sr & 0x7; sr >>= 3; + xmc[38] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[3] = sr & 0x7f; sr >>= 7; + bc[3] = sr & 0x3; sr >>= 2; + Mc[3] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[3] = sr & 0x3f; sr >>= 6; + xmc[39] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[40] = sr & 0x7; sr >>= 3; + xmc[41] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; /* 30 */ + xmc[42] = sr & 0x7; sr >>= 3; + xmc[43] = sr & 0x7; sr >>= 3; + xmc[44] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[45] = sr & 0x7; sr >>= 3; + xmc[46] = sr & 0x7; sr >>= 3; + xmc[47] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[48] = sr & 0x7; sr >>= 3; + xmc[49] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[50] = sr & 0x7; sr >>= 3; + xmc[51] = sr & 0x7; sr >>= 3; + + s->frame_chain = sr & 0xf; + } + else { + sr = s->frame_chain; + sr |= (uword)*c++ << 4; /* 1 */ + LARc[0] = sr & 0x3f; sr >>= 6; + LARc[1] = sr & 0x3f; sr >>= 6; + sr = *c++; + LARc[2] = sr & 0x1f; sr >>= 5; + sr |= (uword)*c++ << 3; + LARc[3] = sr & 0x1f; sr >>= 5; + LARc[4] = sr & 0xf; sr >>= 4; + sr |= (uword)*c++ << 2; + LARc[5] = sr & 0xf; sr >>= 4; + LARc[6] = sr & 0x7; sr >>= 3; + LARc[7] = sr & 0x7; sr >>= 3; + sr = *c++; /* 5 */ + Nc[0] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[0] = sr & 0x3; sr >>= 2; + Mc[0] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[0] = sr & 0x3f; sr >>= 6; + xmc[0] = sr & 0x7; sr >>= 3; + xmc[1] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[2] = sr & 0x7; sr >>= 3; + xmc[3] = sr & 0x7; sr >>= 3; + xmc[4] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[5] = sr & 0x7; sr >>= 3; + xmc[6] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; /* 10 */ + xmc[7] = sr & 0x7; sr >>= 3; + xmc[8] = sr & 0x7; sr >>= 3; + xmc[9] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[10] = sr & 0x7; sr >>= 3; + xmc[11] = sr & 0x7; sr >>= 3; + xmc[12] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[1] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[1] = sr & 0x3; sr >>= 2; + Mc[1] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[1] = sr & 0x3f; sr >>= 6; + xmc[13] = sr & 0x7; sr >>= 3; + xmc[14] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 15 */ + xmc[15] = sr & 0x7; sr >>= 3; + xmc[16] = sr & 0x7; sr >>= 3; + xmc[17] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[18] = sr & 0x7; sr >>= 3; + xmc[19] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[20] = sr & 0x7; sr >>= 3; + xmc[21] = sr & 0x7; sr >>= 3; + xmc[22] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[23] = sr & 0x7; sr >>= 3; + xmc[24] = sr & 0x7; sr >>= 3; + xmc[25] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[2] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; /* 20 */ + bc[2] = sr & 0x3; sr >>= 2; + Mc[2] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[2] = sr & 0x3f; sr >>= 6; + xmc[26] = sr & 0x7; sr >>= 3; + xmc[27] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[28] = sr & 0x7; sr >>= 3; + xmc[29] = sr & 0x7; sr >>= 3; + xmc[30] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[31] = sr & 0x7; sr >>= 3; + xmc[32] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[33] = sr & 0x7; sr >>= 3; + xmc[34] = sr & 0x7; sr >>= 3; + xmc[35] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 25 */ + xmc[36] = sr & 0x7; sr >>= 3; + xmc[37] = sr & 0x7; sr >>= 3; + xmc[38] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[3] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[3] = sr & 0x3; sr >>= 2; + Mc[3] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[3] = sr & 0x3f; sr >>= 6; + xmc[39] = sr & 0x7; sr >>= 3; + xmc[40] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[41] = sr & 0x7; sr >>= 3; + xmc[42] = sr & 0x7; sr >>= 3; + xmc[43] = sr & 0x7; sr >>= 3; + sr = *c++; /* 30 */ + xmc[44] = sr & 0x7; sr >>= 3; + xmc[45] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[46] = sr & 0x7; sr >>= 3; + xmc[47] = sr & 0x7; sr >>= 3; + xmc[48] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[49] = sr & 0x7; sr >>= 3; + xmc[50] = sr & 0x7; sr >>= 3; + xmc[51] = sr & 0x7; sr >>= 3; + } + } + else +#endif + { + /* GSM_MAGIC = (*c >> 4) & 0xF; */ + + if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1; + + LARc[0] = (*c++ & 0xF) << 2; /* 1 */ + LARc[0] |= (*c >> 6) & 0x3; + LARc[1] = *c++ & 0x3F; + LARc[2] = (*c >> 3) & 0x1F; + LARc[3] = (*c++ & 0x7) << 2; + LARc[3] |= (*c >> 6) & 0x3; + LARc[4] = (*c >> 2) & 0xF; + LARc[5] = (*c++ & 0x3) << 2; + LARc[5] |= (*c >> 6) & 0x3; + LARc[6] = (*c >> 3) & 0x7; + LARc[7] = *c++ & 0x7; + Nc[0] = (*c >> 1) & 0x7F; + bc[0] = (*c++ & 0x1) << 1; + bc[0] |= (*c >> 7) & 0x1; + Mc[0] = (*c >> 5) & 0x3; + xmaxc[0] = (*c++ & 0x1F) << 1; + xmaxc[0] |= (*c >> 7) & 0x1; + xmc[0] = (*c >> 4) & 0x7; + xmc[1] = (*c >> 1) & 0x7; + xmc[2] = (*c++ & 0x1) << 2; + xmc[2] |= (*c >> 6) & 0x3; + xmc[3] = (*c >> 3) & 0x7; + xmc[4] = *c++ & 0x7; + xmc[5] = (*c >> 5) & 0x7; + xmc[6] = (*c >> 2) & 0x7; + xmc[7] = (*c++ & 0x3) << 1; /* 10 */ + xmc[7] |= (*c >> 7) & 0x1; + xmc[8] = (*c >> 4) & 0x7; + xmc[9] = (*c >> 1) & 0x7; + xmc[10] = (*c++ & 0x1) << 2; + xmc[10] |= (*c >> 6) & 0x3; + xmc[11] = (*c >> 3) & 0x7; + xmc[12] = *c++ & 0x7; + Nc[1] = (*c >> 1) & 0x7F; + bc[1] = (*c++ & 0x1) << 1; + bc[1] |= (*c >> 7) & 0x1; + Mc[1] = (*c >> 5) & 0x3; + xmaxc[1] = (*c++ & 0x1F) << 1; + xmaxc[1] |= (*c >> 7) & 0x1; + xmc[13] = (*c >> 4) & 0x7; + xmc[14] = (*c >> 1) & 0x7; + xmc[15] = (*c++ & 0x1) << 2; + xmc[15] |= (*c >> 6) & 0x3; + xmc[16] = (*c >> 3) & 0x7; + xmc[17] = *c++ & 0x7; + xmc[18] = (*c >> 5) & 0x7; + xmc[19] = (*c >> 2) & 0x7; + xmc[20] = (*c++ & 0x3) << 1; + xmc[20] |= (*c >> 7) & 0x1; + xmc[21] = (*c >> 4) & 0x7; + xmc[22] = (*c >> 1) & 0x7; + xmc[23] = (*c++ & 0x1) << 2; + xmc[23] |= (*c >> 6) & 0x3; + xmc[24] = (*c >> 3) & 0x7; + xmc[25] = *c++ & 0x7; + Nc[2] = (*c >> 1) & 0x7F; + bc[2] = (*c++ & 0x1) << 1; /* 20 */ + bc[2] |= (*c >> 7) & 0x1; + Mc[2] = (*c >> 5) & 0x3; + xmaxc[2] = (*c++ & 0x1F) << 1; + xmaxc[2] |= (*c >> 7) & 0x1; + xmc[26] = (*c >> 4) & 0x7; + xmc[27] = (*c >> 1) & 0x7; + xmc[28] = (*c++ & 0x1) << 2; + xmc[28] |= (*c >> 6) & 0x3; + xmc[29] = (*c >> 3) & 0x7; + xmc[30] = *c++ & 0x7; + xmc[31] = (*c >> 5) & 0x7; + xmc[32] = (*c >> 2) & 0x7; + xmc[33] = (*c++ & 0x3) << 1; + xmc[33] |= (*c >> 7) & 0x1; + xmc[34] = (*c >> 4) & 0x7; + xmc[35] = (*c >> 1) & 0x7; + xmc[36] = (*c++ & 0x1) << 2; + xmc[36] |= (*c >> 6) & 0x3; + xmc[37] = (*c >> 3) & 0x7; + xmc[38] = *c++ & 0x7; + Nc[3] = (*c >> 1) & 0x7F; + bc[3] = (*c++ & 0x1) << 1; + bc[3] |= (*c >> 7) & 0x1; + Mc[3] = (*c >> 5) & 0x3; + xmaxc[3] = (*c++ & 0x1F) << 1; + xmaxc[3] |= (*c >> 7) & 0x1; + xmc[39] = (*c >> 4) & 0x7; + xmc[40] = (*c >> 1) & 0x7; + xmc[41] = (*c++ & 0x1) << 2; + xmc[41] |= (*c >> 6) & 0x3; + xmc[42] = (*c >> 3) & 0x7; + xmc[43] = *c++ & 0x7; /* 30 */ + xmc[44] = (*c >> 5) & 0x7; + xmc[45] = (*c >> 2) & 0x7; + xmc[46] = (*c++ & 0x3) << 1; + xmc[46] |= (*c >> 7) & 0x1; + xmc[47] = (*c >> 4) & 0x7; + xmc[48] = (*c >> 1) & 0x7; + xmc[49] = (*c++ & 0x1) << 2; + xmc[49] |= (*c >> 6) & 0x3; + xmc[50] = (*c >> 3) & 0x7; + xmc[51] = *c & 0x7; /* 33 */ + } + + Gsm_Decoder(s, LARc, Nc, bc, Mc, xmaxc, xmc, target); + + return 0; +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/gsm_destroy.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/gsm_destroy.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,26 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_destroy.c,v 1.3 1994/11/28 19:52:25 jutta Exp $ */ + +#include "gsm.h" +#include "config.h" +#include "proto.h" + +#ifdef HAS_STDLIB_H +# include +#else +# ifdef HAS_MALLOC_H +# include +# else + extern void free(); +# endif +#endif + +void gsm_destroy P1((S), gsm S) +{ + if (S) free((char *)S); +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/gsm_encode.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/gsm_encode.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,451 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_encode.c,v 1.2 1996/07/02 09:59:05 jutta Exp $ */ + +#include "private.h" +#include "gsm.h" +#include "proto.h" + +void gsm_encode P3((s, source, c), gsm s, gsm_signal * source, gsm_byte * c) +{ + word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4]; + + Gsm_Coder(s, source, LARc, Nc, bc, Mc, xmaxc, xmc); + + + /* variable size + + GSM_MAGIC 4 + + LARc[0] 6 + LARc[1] 6 + LARc[2] 5 + LARc[3] 5 + LARc[4] 4 + LARc[5] 4 + LARc[6] 3 + LARc[7] 3 + + Nc[0] 7 + bc[0] 2 + Mc[0] 2 + xmaxc[0] 6 + xmc[0] 3 + xmc[1] 3 + xmc[2] 3 + xmc[3] 3 + xmc[4] 3 + xmc[5] 3 + xmc[6] 3 + xmc[7] 3 + xmc[8] 3 + xmc[9] 3 + xmc[10] 3 + xmc[11] 3 + xmc[12] 3 + + Nc[1] 7 + bc[1] 2 + Mc[1] 2 + xmaxc[1] 6 + xmc[13] 3 + xmc[14] 3 + xmc[15] 3 + xmc[16] 3 + xmc[17] 3 + xmc[18] 3 + xmc[19] 3 + xmc[20] 3 + xmc[21] 3 + xmc[22] 3 + xmc[23] 3 + xmc[24] 3 + xmc[25] 3 + + Nc[2] 7 + bc[2] 2 + Mc[2] 2 + xmaxc[2] 6 + xmc[26] 3 + xmc[27] 3 + xmc[28] 3 + xmc[29] 3 + xmc[30] 3 + xmc[31] 3 + xmc[32] 3 + xmc[33] 3 + xmc[34] 3 + xmc[35] 3 + xmc[36] 3 + xmc[37] 3 + xmc[38] 3 + + Nc[3] 7 + bc[3] 2 + Mc[3] 2 + xmaxc[3] 6 + xmc[39] 3 + xmc[40] 3 + xmc[41] 3 + xmc[42] 3 + xmc[43] 3 + xmc[44] 3 + xmc[45] 3 + xmc[46] 3 + xmc[47] 3 + xmc[48] 3 + xmc[49] 3 + xmc[50] 3 + xmc[51] 3 + */ + +#ifdef WAV49 + + if (s->wav_fmt) { + s->frame_index = !s->frame_index; + if (s->frame_index) { + + uword sr; + + sr = 0; + sr = sr >> 6 | LARc[0] << 10; + sr = sr >> 6 | LARc[1] << 10; + *c++ = sr >> 4; + sr = sr >> 5 | LARc[2] << 11; + *c++ = sr >> 7; + sr = sr >> 5 | LARc[3] << 11; + sr = sr >> 4 | LARc[4] << 12; + *c++ = sr >> 6; + sr = sr >> 4 | LARc[5] << 12; + sr = sr >> 3 | LARc[6] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | LARc[7] << 13; + sr = sr >> 7 | Nc[0] << 9; + *c++ = sr >> 5; + sr = sr >> 2 | bc[0] << 14; + sr = sr >> 2 | Mc[0] << 14; + sr = sr >> 6 | xmaxc[0] << 10; + *c++ = sr >> 3; + sr = sr >> 3 | xmc[0] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[1] << 13; + sr = sr >> 3 | xmc[2] << 13; + sr = sr >> 3 | xmc[3] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[4] << 13; + sr = sr >> 3 | xmc[5] << 13; + sr = sr >> 3 | xmc[6] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[7] << 13; + sr = sr >> 3 | xmc[8] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[9] << 13; + sr = sr >> 3 | xmc[10] << 13; + sr = sr >> 3 | xmc[11] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[12] << 13; + sr = sr >> 7 | Nc[1] << 9; + *c++ = sr >> 5; + sr = sr >> 2 | bc[1] << 14; + sr = sr >> 2 | Mc[1] << 14; + sr = sr >> 6 | xmaxc[1] << 10; + *c++ = sr >> 3; + sr = sr >> 3 | xmc[13] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[14] << 13; + sr = sr >> 3 | xmc[15] << 13; + sr = sr >> 3 | xmc[16] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[17] << 13; + sr = sr >> 3 | xmc[18] << 13; + sr = sr >> 3 | xmc[19] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[20] << 13; + sr = sr >> 3 | xmc[21] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[22] << 13; + sr = sr >> 3 | xmc[23] << 13; + sr = sr >> 3 | xmc[24] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[25] << 13; + sr = sr >> 7 | Nc[2] << 9; + *c++ = sr >> 5; + sr = sr >> 2 | bc[2] << 14; + sr = sr >> 2 | Mc[2] << 14; + sr = sr >> 6 | xmaxc[2] << 10; + *c++ = sr >> 3; + sr = sr >> 3 | xmc[26] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[27] << 13; + sr = sr >> 3 | xmc[28] << 13; + sr = sr >> 3 | xmc[29] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[30] << 13; + sr = sr >> 3 | xmc[31] << 13; + sr = sr >> 3 | xmc[32] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[33] << 13; + sr = sr >> 3 | xmc[34] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[35] << 13; + sr = sr >> 3 | xmc[36] << 13; + sr = sr >> 3 | xmc[37] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[38] << 13; + sr = sr >> 7 | Nc[3] << 9; + *c++ = sr >> 5; + sr = sr >> 2 | bc[3] << 14; + sr = sr >> 2 | Mc[3] << 14; + sr = sr >> 6 | xmaxc[3] << 10; + *c++ = sr >> 3; + sr = sr >> 3 | xmc[39] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[40] << 13; + sr = sr >> 3 | xmc[41] << 13; + sr = sr >> 3 | xmc[42] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[43] << 13; + sr = sr >> 3 | xmc[44] << 13; + sr = sr >> 3 | xmc[45] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[46] << 13; + sr = sr >> 3 | xmc[47] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[48] << 13; + sr = sr >> 3 | xmc[49] << 13; + sr = sr >> 3 | xmc[50] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[51] << 13; + sr = sr >> 4; + *c = sr >> 8; + s->frame_chain = *c; + } + else { + uword sr; + + sr = 0; + sr = sr >> 4 | s->frame_chain << 12; + sr = sr >> 6 | LARc[0] << 10; + *c++ = sr >> 6; + sr = sr >> 6 | LARc[1] << 10; + *c++ = sr >> 8; + sr = sr >> 5 | LARc[2] << 11; + sr = sr >> 5 | LARc[3] << 11; + *c++ = sr >> 6; + sr = sr >> 4 | LARc[4] << 12; + sr = sr >> 4 | LARc[5] << 12; + *c++ = sr >> 6; + sr = sr >> 3 | LARc[6] << 13; + sr = sr >> 3 | LARc[7] << 13; + *c++ = sr >> 8; + sr = sr >> 7 | Nc[0] << 9; + sr = sr >> 2 | bc[0] << 14; + *c++ = sr >> 7; + sr = sr >> 2 | Mc[0] << 14; + sr = sr >> 6 | xmaxc[0] << 10; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[0] << 13; + sr = sr >> 3 | xmc[1] << 13; + sr = sr >> 3 | xmc[2] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[3] << 13; + sr = sr >> 3 | xmc[4] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[5] << 13; + sr = sr >> 3 | xmc[6] << 13; + sr = sr >> 3 | xmc[7] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[8] << 13; + sr = sr >> 3 | xmc[9] << 13; + sr = sr >> 3 | xmc[10] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[11] << 13; + sr = sr >> 3 | xmc[12] << 13; + *c++ = sr >> 8; + sr = sr >> 7 | Nc[1] << 9; + sr = sr >> 2 | bc[1] << 14; + *c++ = sr >> 7; + sr = sr >> 2 | Mc[1] << 14; + sr = sr >> 6 | xmaxc[1] << 10; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[13] << 13; + sr = sr >> 3 | xmc[14] << 13; + sr = sr >> 3 | xmc[15] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[16] << 13; + sr = sr >> 3 | xmc[17] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[18] << 13; + sr = sr >> 3 | xmc[19] << 13; + sr = sr >> 3 | xmc[20] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[21] << 13; + sr = sr >> 3 | xmc[22] << 13; + sr = sr >> 3 | xmc[23] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[24] << 13; + sr = sr >> 3 | xmc[25] << 13; + *c++ = sr >> 8; + sr = sr >> 7 | Nc[2] << 9; + sr = sr >> 2 | bc[2] << 14; + *c++ = sr >> 7; + sr = sr >> 2 | Mc[2] << 14; + sr = sr >> 6 | xmaxc[2] << 10; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[26] << 13; + sr = sr >> 3 | xmc[27] << 13; + sr = sr >> 3 | xmc[28] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[29] << 13; + sr = sr >> 3 | xmc[30] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[31] << 13; + sr = sr >> 3 | xmc[32] << 13; + sr = sr >> 3 | xmc[33] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[34] << 13; + sr = sr >> 3 | xmc[35] << 13; + sr = sr >> 3 | xmc[36] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[37] << 13; + sr = sr >> 3 | xmc[38] << 13; + *c++ = sr >> 8; + sr = sr >> 7 | Nc[3] << 9; + sr = sr >> 2 | bc[3] << 14; + *c++ = sr >> 7; + sr = sr >> 2 | Mc[3] << 14; + sr = sr >> 6 | xmaxc[3] << 10; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[39] << 13; + sr = sr >> 3 | xmc[40] << 13; + sr = sr >> 3 | xmc[41] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[42] << 13; + sr = sr >> 3 | xmc[43] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[44] << 13; + sr = sr >> 3 | xmc[45] << 13; + sr = sr >> 3 | xmc[46] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[47] << 13; + sr = sr >> 3 | xmc[48] << 13; + sr = sr >> 3 | xmc[49] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[50] << 13; + sr = sr >> 3 | xmc[51] << 13; + *c++ = sr >> 8; + } + } + + else + +#endif /* WAV49 */ + { + + *c++ = ((GSM_MAGIC & 0xF) << 4) /* 1 */ + | ((LARc[0] >> 2) & 0xF); + *c++ = ((LARc[0] & 0x3) << 6) + | (LARc[1] & 0x3F); + *c++ = ((LARc[2] & 0x1F) << 3) + | ((LARc[3] >> 2) & 0x7); + *c++ = ((LARc[3] & 0x3) << 6) + | ((LARc[4] & 0xF) << 2) + | ((LARc[5] >> 2) & 0x3); + *c++ = ((LARc[5] & 0x3) << 6) + | ((LARc[6] & 0x7) << 3) + | (LARc[7] & 0x7); + *c++ = ((Nc[0] & 0x7F) << 1) + | ((bc[0] >> 1) & 0x1); + *c++ = ((bc[0] & 0x1) << 7) + | ((Mc[0] & 0x3) << 5) + | ((xmaxc[0] >> 1) & 0x1F); + *c++ = ((xmaxc[0] & 0x1) << 7) + | ((xmc[0] & 0x7) << 4) + | ((xmc[1] & 0x7) << 1) + | ((xmc[2] >> 2) & 0x1); + *c++ = ((xmc[2] & 0x3) << 6) + | ((xmc[3] & 0x7) << 3) + | (xmc[4] & 0x7); + *c++ = ((xmc[5] & 0x7) << 5) /* 10 */ + | ((xmc[6] & 0x7) << 2) + | ((xmc[7] >> 1) & 0x3); + *c++ = ((xmc[7] & 0x1) << 7) + | ((xmc[8] & 0x7) << 4) + | ((xmc[9] & 0x7) << 1) + | ((xmc[10] >> 2) & 0x1); + *c++ = ((xmc[10] & 0x3) << 6) + | ((xmc[11] & 0x7) << 3) + | (xmc[12] & 0x7); + *c++ = ((Nc[1] & 0x7F) << 1) + | ((bc[1] >> 1) & 0x1); + *c++ = ((bc[1] & 0x1) << 7) + | ((Mc[1] & 0x3) << 5) + | ((xmaxc[1] >> 1) & 0x1F); + *c++ = ((xmaxc[1] & 0x1) << 7) + | ((xmc[13] & 0x7) << 4) + | ((xmc[14] & 0x7) << 1) + | ((xmc[15] >> 2) & 0x1); + *c++ = ((xmc[15] & 0x3) << 6) + | ((xmc[16] & 0x7) << 3) + | (xmc[17] & 0x7); + *c++ = ((xmc[18] & 0x7) << 5) + | ((xmc[19] & 0x7) << 2) + | ((xmc[20] >> 1) & 0x3); + *c++ = ((xmc[20] & 0x1) << 7) + | ((xmc[21] & 0x7) << 4) + | ((xmc[22] & 0x7) << 1) + | ((xmc[23] >> 2) & 0x1); + *c++ = ((xmc[23] & 0x3) << 6) + | ((xmc[24] & 0x7) << 3) + | (xmc[25] & 0x7); + *c++ = ((Nc[2] & 0x7F) << 1) /* 20 */ + | ((bc[2] >> 1) & 0x1); + *c++ = ((bc[2] & 0x1) << 7) + | ((Mc[2] & 0x3) << 5) + | ((xmaxc[2] >> 1) & 0x1F); + *c++ = ((xmaxc[2] & 0x1) << 7) + | ((xmc[26] & 0x7) << 4) + | ((xmc[27] & 0x7) << 1) + | ((xmc[28] >> 2) & 0x1); + *c++ = ((xmc[28] & 0x3) << 6) + | ((xmc[29] & 0x7) << 3) + | (xmc[30] & 0x7); + *c++ = ((xmc[31] & 0x7) << 5) + | ((xmc[32] & 0x7) << 2) + | ((xmc[33] >> 1) & 0x3); + *c++ = ((xmc[33] & 0x1) << 7) + | ((xmc[34] & 0x7) << 4) + | ((xmc[35] & 0x7) << 1) + | ((xmc[36] >> 2) & 0x1); + *c++ = ((xmc[36] & 0x3) << 6) + | ((xmc[37] & 0x7) << 3) + | (xmc[38] & 0x7); + *c++ = ((Nc[3] & 0x7F) << 1) + | ((bc[3] >> 1) & 0x1); + *c++ = ((bc[3] & 0x1) << 7) + | ((Mc[3] & 0x3) << 5) + | ((xmaxc[3] >> 1) & 0x1F); + *c++ = ((xmaxc[3] & 0x1) << 7) + | ((xmc[39] & 0x7) << 4) + | ((xmc[40] & 0x7) << 1) + | ((xmc[41] >> 2) & 0x1); + *c++ = ((xmc[41] & 0x3) << 6) /* 30 */ + | ((xmc[42] & 0x7) << 3) + | (xmc[43] & 0x7); + *c++ = ((xmc[44] & 0x7) << 5) + | ((xmc[45] & 0x7) << 2) + | ((xmc[46] >> 1) & 0x3); + *c++ = ((xmc[46] & 0x1) << 7) + | ((xmc[47] & 0x7) << 4) + | ((xmc[48] & 0x7) << 1) + | ((xmc[49] >> 2) & 0x1); + *c++ = ((xmc[49] & 0x3) << 6) + | ((xmc[50] & 0x7) << 3) + | (xmc[51] & 0x7); + + } +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/gsm_explode.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/gsm_explode.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,419 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_explode.c,v 1.2 1996/07/02 14:32:42 jutta Exp jutta $ */ + +#include "private.h" +#include "gsm.h" +#include "proto.h" + +int gsm_explode P3((s, c, target), gsm s, gsm_byte * c, gsm_signal * target) +{ +# define LARc target +# define Nc *((gsm_signal (*) [17])(target + 8)) +# define bc *((gsm_signal (*) [17])(target + 9)) +# define Mc *((gsm_signal (*) [17])(target + 10)) +# define xmaxc *((gsm_signal (*) [17])(target + 11)) + +// Wirlab + (void)s; + +#ifdef WAV49 + if (s->wav_fmt) { + + uword sr = 0; + + if (s->frame_index == 1) { + + sr = *c++; + LARc[0] = sr & 0x3f; sr >>= 6; + sr |= (uword)*c++ << 2; + LARc[1] = sr & 0x3f; sr >>= 6; + sr |= (uword)*c++ << 4; + LARc[2] = sr & 0x1f; sr >>= 5; + LARc[3] = sr & 0x1f; sr >>= 5; + sr |= (uword)*c++ << 2; + LARc[4] = sr & 0xf; sr >>= 4; + LARc[5] = sr & 0xf; sr >>= 4; + sr |= (uword)*c++ << 2; /* 5 */ + LARc[6] = sr & 0x7; sr >>= 3; + LARc[7] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[0] = sr & 0x7f; sr >>= 7; + bc[0] = sr & 0x3; sr >>= 2; + Mc[0] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[0] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (target + 12) + xmc[0] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[1] = sr & 0x7; sr >>= 3; + xmc[2] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[3] = sr & 0x7; sr >>= 3; + xmc[4] = sr & 0x7; sr >>= 3; + xmc[5] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 10 */ + xmc[6] = sr & 0x7; sr >>= 3; + xmc[7] = sr & 0x7; sr >>= 3; + xmc[8] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[9] = sr & 0x7; sr >>= 3; + xmc[10] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[11] = sr & 0x7; sr >>= 3; + xmc[12] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[1] = sr & 0x7f; sr >>= 7; + bc[1] = sr & 0x3; sr >>= 2; + Mc[1] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[1] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (target + 29 - 13) + + xmc[13] = sr & 0x7; sr >>= 3; + sr = *c++; /* 15 */ + xmc[14] = sr & 0x7; sr >>= 3; + xmc[15] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[16] = sr & 0x7; sr >>= 3; + xmc[17] = sr & 0x7; sr >>= 3; + xmc[18] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[19] = sr & 0x7; sr >>= 3; + xmc[20] = sr & 0x7; sr >>= 3; + xmc[21] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[22] = sr & 0x7; sr >>= 3; + xmc[23] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[24] = sr & 0x7; sr >>= 3; + xmc[25] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; /* 20 */ + Nc[2] = sr & 0x7f; sr >>= 7; + bc[2] = sr & 0x3; sr >>= 2; + Mc[2] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[2] = sr & 0x3f; sr >>= 6; + +#undef xmc +#define xmc (target + 46 - 26) + + xmc[26] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[27] = sr & 0x7; sr >>= 3; + xmc[28] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[29] = sr & 0x7; sr >>= 3; + xmc[30] = sr & 0x7; sr >>= 3; + xmc[31] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[32] = sr & 0x7; sr >>= 3; + xmc[33] = sr & 0x7; sr >>= 3; + xmc[34] = sr & 0x7; sr >>= 3; + sr = *c++; /* 25 */ + xmc[35] = sr & 0x7; sr >>= 3; + xmc[36] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[37] = sr & 0x7; sr >>= 3; + xmc[38] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[3] = sr & 0x7f; sr >>= 7; + bc[3] = sr & 0x3; sr >>= 2; + Mc[3] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[3] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (target + 63 - 39) + + xmc[39] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[40] = sr & 0x7; sr >>= 3; + xmc[41] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; /* 30 */ + xmc[42] = sr & 0x7; sr >>= 3; + xmc[43] = sr & 0x7; sr >>= 3; + xmc[44] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[45] = sr & 0x7; sr >>= 3; + xmc[46] = sr & 0x7; sr >>= 3; + xmc[47] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[48] = sr & 0x7; sr >>= 3; + xmc[49] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[50] = sr & 0x7; sr >>= 3; + xmc[51] = sr & 0x7; sr >>= 3; + + s->frame_chain = sr & 0xf; + } + else { + sr = s->frame_chain; + sr |= (uword)*c++ << 4; /* 1 */ + LARc[0] = sr & 0x3f; sr >>= 6; + LARc[1] = sr & 0x3f; sr >>= 6; + sr = *c++; + LARc[2] = sr & 0x1f; sr >>= 5; + sr |= (uword)*c++ << 3; + LARc[3] = sr & 0x1f; sr >>= 5; + LARc[4] = sr & 0xf; sr >>= 4; + sr |= (uword)*c++ << 2; + LARc[5] = sr & 0xf; sr >>= 4; + LARc[6] = sr & 0x7; sr >>= 3; + LARc[7] = sr & 0x7; sr >>= 3; + sr = *c++; /* 5 */ + Nc[0] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[0] = sr & 0x3; sr >>= 2; + Mc[0] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[0] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (target + 12) + xmc[0] = sr & 0x7; sr >>= 3; + xmc[1] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[2] = sr & 0x7; sr >>= 3; + xmc[3] = sr & 0x7; sr >>= 3; + xmc[4] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[5] = sr & 0x7; sr >>= 3; + xmc[6] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; /* 10 */ + xmc[7] = sr & 0x7; sr >>= 3; + xmc[8] = sr & 0x7; sr >>= 3; + xmc[9] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[10] = sr & 0x7; sr >>= 3; + xmc[11] = sr & 0x7; sr >>= 3; + xmc[12] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[1] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[1] = sr & 0x3; sr >>= 2; + Mc[1] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[1] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (target + 29 - 13) + + xmc[13] = sr & 0x7; sr >>= 3; + xmc[14] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 15 */ + xmc[15] = sr & 0x7; sr >>= 3; + xmc[16] = sr & 0x7; sr >>= 3; + xmc[17] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[18] = sr & 0x7; sr >>= 3; + xmc[19] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[20] = sr & 0x7; sr >>= 3; + xmc[21] = sr & 0x7; sr >>= 3; + xmc[22] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[23] = sr & 0x7; sr >>= 3; + xmc[24] = sr & 0x7; sr >>= 3; + xmc[25] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[2] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; /* 20 */ + bc[2] = sr & 0x3; sr >>= 2; + Mc[2] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[2] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (target + 46 - 26) + xmc[26] = sr & 0x7; sr >>= 3; + xmc[27] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[28] = sr & 0x7; sr >>= 3; + xmc[29] = sr & 0x7; sr >>= 3; + xmc[30] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[31] = sr & 0x7; sr >>= 3; + xmc[32] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[33] = sr & 0x7; sr >>= 3; + xmc[34] = sr & 0x7; sr >>= 3; + xmc[35] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 25 */ + xmc[36] = sr & 0x7; sr >>= 3; + xmc[37] = sr & 0x7; sr >>= 3; + xmc[38] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[3] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[3] = sr & 0x3; sr >>= 2; + Mc[3] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[3] = sr & 0x3f; sr >>= 6; + +#undef xmc +#define xmc (target + 63 - 39) + + xmc[39] = sr & 0x7; sr >>= 3; + xmc[40] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[41] = sr & 0x7; sr >>= 3; + xmc[42] = sr & 0x7; sr >>= 3; + xmc[43] = sr & 0x7; sr >>= 3; + sr = *c++; /* 30 */ + xmc[44] = sr & 0x7; sr >>= 3; + xmc[45] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[46] = sr & 0x7; sr >>= 3; + xmc[47] = sr & 0x7; sr >>= 3; + xmc[48] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[49] = sr & 0x7; sr >>= 3; + xmc[50] = sr & 0x7; sr >>= 3; + xmc[51] = sr & 0x7; sr >>= 3; + } + } + else +#endif + { + /* GSM_MAGIC = (*c >> 4) & 0xF; */ + + if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1; + + LARc[0] = (*c++ & 0xF) << 2; /* 1 */ + LARc[0] |= (*c >> 6) & 0x3; + LARc[1] = *c++ & 0x3F; + LARc[2] = (*c >> 3) & 0x1F; + LARc[3] = (*c++ & 0x7) << 2; + LARc[3] |= (*c >> 6) & 0x3; + LARc[4] = (*c >> 2) & 0xF; + LARc[5] = (*c++ & 0x3) << 2; + LARc[5] |= (*c >> 6) & 0x3; + LARc[6] = (*c >> 3) & 0x7; + LARc[7] = *c++ & 0x7; + + Nc[0] = (*c >> 1) & 0x7F; + + bc[0] = (*c++ & 0x1) << 1; + bc[0] |= (*c >> 7) & 0x1; + + Mc[0] = (*c >> 5) & 0x3; + + xmaxc[0] = (*c++ & 0x1F) << 1; + xmaxc[0] |= (*c >> 7) & 0x1; + +#undef xmc +#define xmc (target + 12) + + xmc[0] = (*c >> 4) & 0x7; + xmc[1] = (*c >> 1) & 0x7; + xmc[2] = (*c++ & 0x1) << 2; + xmc[2] |= (*c >> 6) & 0x3; + xmc[3] = (*c >> 3) & 0x7; + xmc[4] = *c++ & 0x7; + xmc[5] = (*c >> 5) & 0x7; + xmc[6] = (*c >> 2) & 0x7; + xmc[7] = (*c++ & 0x3) << 1; /* 10 */ + xmc[7] |= (*c >> 7) & 0x1; + xmc[8] = (*c >> 4) & 0x7; + xmc[9] = (*c >> 1) & 0x7; + xmc[10] = (*c++ & 0x1) << 2; + xmc[10] |= (*c >> 6) & 0x3; + xmc[11] = (*c >> 3) & 0x7; + xmc[12] = *c++ & 0x7; + + Nc[1] = (*c >> 1) & 0x7F; + + bc[1] = (*c++ & 0x1) << 1; + bc[1] |= (*c >> 7) & 0x1; + + Mc[1] = (*c >> 5) & 0x3; + + xmaxc[1] = (*c++ & 0x1F) << 1; + xmaxc[1] |= (*c >> 7) & 0x1; + +#undef xmc +#define xmc (target + 29 - 13) + + xmc[13] = (*c >> 4) & 0x7; + xmc[14] = (*c >> 1) & 0x7; + xmc[15] = (*c++ & 0x1) << 2; + xmc[15] |= (*c >> 6) & 0x3; + xmc[16] = (*c >> 3) & 0x7; + xmc[17] = *c++ & 0x7; + xmc[18] = (*c >> 5) & 0x7; + xmc[19] = (*c >> 2) & 0x7; + xmc[20] = (*c++ & 0x3) << 1; + xmc[20] |= (*c >> 7) & 0x1; + xmc[21] = (*c >> 4) & 0x7; + xmc[22] = (*c >> 1) & 0x7; + xmc[23] = (*c++ & 0x1) << 2; + xmc[23] |= (*c >> 6) & 0x3; + xmc[24] = (*c >> 3) & 0x7; + xmc[25] = *c++ & 0x7; + + Nc[2] = (*c >> 1) & 0x7F; + + bc[2] = (*c++ & 0x1) << 1; /* 20 */ + bc[2] |= (*c >> 7) & 0x1; + + Mc[2] = (*c >> 5) & 0x3; + + xmaxc[2] = (*c++ & 0x1F) << 1; + xmaxc[2] |= (*c >> 7) & 0x1; + +#undef xmc +#define xmc (target + 46 - 26) + + xmc[26] = (*c >> 4) & 0x7; + xmc[27] = (*c >> 1) & 0x7; + xmc[28] = (*c++ & 0x1) << 2; + xmc[28] |= (*c >> 6) & 0x3; + xmc[29] = (*c >> 3) & 0x7; + xmc[30] = *c++ & 0x7; + xmc[31] = (*c >> 5) & 0x7; + xmc[32] = (*c >> 2) & 0x7; + xmc[33] = (*c++ & 0x3) << 1; + xmc[33] |= (*c >> 7) & 0x1; + xmc[34] = (*c >> 4) & 0x7; + xmc[35] = (*c >> 1) & 0x7; + xmc[36] = (*c++ & 0x1) << 2; + xmc[36] |= (*c >> 6) & 0x3; + xmc[37] = (*c >> 3) & 0x7; + xmc[38] = *c++ & 0x7; + + Nc[3] = (*c >> 1) & 0x7F; + + bc[3] = (*c++ & 0x1) << 1; + bc[3] |= (*c >> 7) & 0x1; + + Mc[3] = (*c >> 5) & 0x3; + + xmaxc[3] = (*c++ & 0x1F) << 1; + xmaxc[3] |= (*c >> 7) & 0x1; + +#undef xmc +#define xmc (target + 63 - 39) + + xmc[39] = (*c >> 4) & 0x7; + xmc[40] = (*c >> 1) & 0x7; + xmc[41] = (*c++ & 0x1) << 2; + xmc[41] |= (*c >> 6) & 0x3; + xmc[42] = (*c >> 3) & 0x7; + xmc[43] = *c++ & 0x7; /* 30 */ + xmc[44] = (*c >> 5) & 0x7; + xmc[45] = (*c >> 2) & 0x7; + xmc[46] = (*c++ & 0x3) << 1; + xmc[46] |= (*c >> 7) & 0x1; + xmc[47] = (*c >> 4) & 0x7; + xmc[48] = (*c >> 1) & 0x7; + xmc[49] = (*c++ & 0x1) << 2; + xmc[49] |= (*c >> 6) & 0x3; + xmc[50] = (*c >> 3) & 0x7; + xmc[51] = *c & 0x7; /* 33 */ + } + + return 0; +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/gsm_implode.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/gsm_implode.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,518 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_implode.c,v 1.2 1996/07/02 14:32:43 jutta Exp jutta $ */ + +#include "private.h" + +#include "gsm.h" +#include "proto.h" + +void gsm_implode P3((s, source, c), gsm s, gsm_signal * source, gsm_byte * c) +{ + /* variable size index + + GSM_MAGIC 4 - + + LARc[0] 6 0 + LARc[1] 6 1 + LARc[2] 5 2 + LARc[3] 5 3 + LARc[4] 4 4 + LARc[5] 4 5 + LARc[6] 3 6 + LARc[7] 3 7 + + Nc[0] 7 8 + bc[0] 2 9 + Mc[0] 2 10 + xmaxc[0] 6 11 + xmc[0] 3 12 + xmc[1] 3 13 + xmc[2] 3 14 + xmc[3] 3 15 + xmc[4] 3 16 + xmc[5] 3 17 + xmc[6] 3 18 + xmc[7] 3 19 + xmc[8] 3 20 + xmc[9] 3 21 + xmc[10] 3 22 + xmc[11] 3 23 + xmc[12] 3 24 + + Nc[1] 7 25 + bc[1] 2 26 + Mc[1] 2 27 + xmaxc[1] 6 28 + xmc[13] 3 29 + xmc[14] 3 30 + xmc[15] 3 31 + xmc[16] 3 32 + xmc[17] 3 33 + xmc[18] 3 34 + xmc[19] 3 35 + xmc[20] 3 36 + xmc[21] 3 37 + xmc[22] 3 38 + xmc[23] 3 39 + xmc[24] 3 40 + xmc[25] 3 41 + + Nc[2] 7 42 + bc[2] 2 43 + Mc[2] 2 44 + xmaxc[2] 6 45 + xmc[26] 3 46 + xmc[27] 3 47 + xmc[28] 3 48 + xmc[29] 3 49 + xmc[30] 3 50 + xmc[31] 3 51 + xmc[32] 3 52 + xmc[33] 3 53 + xmc[34] 3 54 + xmc[35] 3 55 + xmc[36] 3 56 + xmc[37] 3 57 + xmc[38] 3 58 + + Nc[3] 7 59 + bc[3] 2 60 + Mc[3] 2 61 + xmaxc[3] 6 62 + xmc[39] 3 63 + xmc[40] 3 64 + xmc[41] 3 65 + xmc[42] 3 66 + xmc[43] 3 67 + xmc[44] 3 68 + xmc[45] 3 69 + xmc[46] 3 70 + xmc[47] 3 71 + xmc[48] 3 72 + xmc[49] 3 73 + xmc[50] 3 74 + xmc[51] 3 75 + */ + + /* There are 76 parameters per frame. The first eight are + * unique. The remaining 68 are four identical subframes of + * 17 parameters each. gsm_implode converts from a representation + * of these parameters as values in one array of signed words + * to the "packed" version of a GSM frame. + */ + +# define LARc source +# define Nc *((gsm_signal (*) [17])(source + 8)) +# define bc *((gsm_signal (*) [17])(source + 9)) +# define Mc *((gsm_signal (*) [17])(source + 10)) +# define xmaxc *((gsm_signal (*) [17])(source + 11)) + +// Wirlab + (void)s; + +#ifdef WAV49 + if (s->wav_fmt) { + + uword sr = 0; + if (s->frame_index == 0) { + + sr = *c++; + LARc[0] = sr & 0x3f; sr >>= 6; + sr |= (uword)*c++ << 2; + LARc[1] = sr & 0x3f; sr >>= 6; + sr |= (uword)*c++ << 4; + LARc[2] = sr & 0x1f; sr >>= 5; + LARc[3] = sr & 0x1f; sr >>= 5; + sr |= (uword)*c++ << 2; + LARc[4] = sr & 0xf; sr >>= 4; + LARc[5] = sr & 0xf; sr >>= 4; + sr |= (uword)*c++ << 2; /* 5 */ + LARc[6] = sr & 0x7; sr >>= 3; + LARc[7] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[0] = sr & 0x7f; sr >>= 7; + bc[0] = sr & 0x3; sr >>= 2; + Mc[0] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[0] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (source + 12) + xmc[0] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[1] = sr & 0x7; sr >>= 3; + xmc[2] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[3] = sr & 0x7; sr >>= 3; + xmc[4] = sr & 0x7; sr >>= 3; + xmc[5] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 10 */ + xmc[6] = sr & 0x7; sr >>= 3; + xmc[7] = sr & 0x7; sr >>= 3; + xmc[8] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[9] = sr & 0x7; sr >>= 3; + xmc[10] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[11] = sr & 0x7; sr >>= 3; + xmc[12] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[1] = sr & 0x7f; sr >>= 7; + bc[1] = sr & 0x3; sr >>= 2; + Mc[1] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[1] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (source + 29 - 13) + xmc[13] = sr & 0x7; sr >>= 3; + sr = *c++; /* 15 */ + xmc[14] = sr & 0x7; sr >>= 3; + xmc[15] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[16] = sr & 0x7; sr >>= 3; + xmc[17] = sr & 0x7; sr >>= 3; + xmc[18] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[19] = sr & 0x7; sr >>= 3; + xmc[20] = sr & 0x7; sr >>= 3; + xmc[21] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[22] = sr & 0x7; sr >>= 3; + xmc[23] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[24] = sr & 0x7; sr >>= 3; + xmc[25] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; /* 20 */ + Nc[2] = sr & 0x7f; sr >>= 7; + bc[2] = sr & 0x3; sr >>= 2; + Mc[2] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[2] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (source + 46 - 26) + xmc[26] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[27] = sr & 0x7; sr >>= 3; + xmc[28] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[29] = sr & 0x7; sr >>= 3; + xmc[30] = sr & 0x7; sr >>= 3; + xmc[31] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[32] = sr & 0x7; sr >>= 3; + xmc[33] = sr & 0x7; sr >>= 3; + xmc[34] = sr & 0x7; sr >>= 3; + sr = *c++; /* 25 */ + xmc[35] = sr & 0x7; sr >>= 3; + xmc[36] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[37] = sr & 0x7; sr >>= 3; + xmc[38] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[3] = sr & 0x7f; sr >>= 7; + bc[3] = sr & 0x3; sr >>= 2; + Mc[3] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[3] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (source + 63 - 39) + + xmc[39] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[40] = sr & 0x7; sr >>= 3; + xmc[41] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; /* 30 */ + xmc[42] = sr & 0x7; sr >>= 3; + xmc[43] = sr & 0x7; sr >>= 3; + xmc[44] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[45] = sr & 0x7; sr >>= 3; + xmc[46] = sr & 0x7; sr >>= 3; + xmc[47] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[48] = sr & 0x7; sr >>= 3; + xmc[49] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[50] = sr & 0x7; sr >>= 3; + xmc[51] = sr & 0x7; sr >>= 3; + + s->frame_chain = sr & 0xf; + } + else { + sr = s->frame_chain; + sr |= (uword)*c++ << 4; /* 1 */ + LARc[0] = sr & 0x3f; sr >>= 6; + LARc[1] = sr & 0x3f; sr >>= 6; + sr = *c++; + LARc[2] = sr & 0x1f; sr >>= 5; + sr |= (uword)*c++ << 3; + LARc[3] = sr & 0x1f; sr >>= 5; + LARc[4] = sr & 0xf; sr >>= 4; + sr |= (uword)*c++ << 2; + LARc[5] = sr & 0xf; sr >>= 4; + LARc[6] = sr & 0x7; sr >>= 3; + LARc[7] = sr & 0x7; sr >>= 3; + sr = *c++; /* 5 */ + Nc[0] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[0] = sr & 0x3; sr >>= 2; + Mc[0] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[0] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (source + 12) + xmc[0] = sr & 0x7; sr >>= 3; + xmc[1] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[2] = sr & 0x7; sr >>= 3; + xmc[3] = sr & 0x7; sr >>= 3; + xmc[4] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[5] = sr & 0x7; sr >>= 3; + xmc[6] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; /* 10 */ + xmc[7] = sr & 0x7; sr >>= 3; + xmc[8] = sr & 0x7; sr >>= 3; + xmc[9] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[10] = sr & 0x7; sr >>= 3; + xmc[11] = sr & 0x7; sr >>= 3; + xmc[12] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[1] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[1] = sr & 0x3; sr >>= 2; + Mc[1] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[1] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (source + 29 - 13) + xmc[13] = sr & 0x7; sr >>= 3; + xmc[14] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 15 */ + xmc[15] = sr & 0x7; sr >>= 3; + xmc[16] = sr & 0x7; sr >>= 3; + xmc[17] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[18] = sr & 0x7; sr >>= 3; + xmc[19] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[20] = sr & 0x7; sr >>= 3; + xmc[21] = sr & 0x7; sr >>= 3; + xmc[22] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[23] = sr & 0x7; sr >>= 3; + xmc[24] = sr & 0x7; sr >>= 3; + xmc[25] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[2] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; /* 20 */ + bc[2] = sr & 0x3; sr >>= 2; + Mc[2] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[2] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (source + 46 - 26) + xmc[26] = sr & 0x7; sr >>= 3; + xmc[27] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[28] = sr & 0x7; sr >>= 3; + xmc[29] = sr & 0x7; sr >>= 3; + xmc[30] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[31] = sr & 0x7; sr >>= 3; + xmc[32] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[33] = sr & 0x7; sr >>= 3; + xmc[34] = sr & 0x7; sr >>= 3; + xmc[35] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 25 */ + xmc[36] = sr & 0x7; sr >>= 3; + xmc[37] = sr & 0x7; sr >>= 3; + xmc[38] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[3] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[3] = sr & 0x3; sr >>= 2; + Mc[3] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[3] = sr & 0x3f; sr >>= 6; +#undef xmc +#define xmc (source + 63 - 39) + + xmc[39] = sr & 0x7; sr >>= 3; + xmc[40] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[41] = sr & 0x7; sr >>= 3; + xmc[42] = sr & 0x7; sr >>= 3; + xmc[43] = sr & 0x7; sr >>= 3; + sr = *c++; /* 30 */ + xmc[44] = sr & 0x7; sr >>= 3; + xmc[45] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[46] = sr & 0x7; sr >>= 3; + xmc[47] = sr & 0x7; sr >>= 3; + xmc[48] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[49] = sr & 0x7; sr >>= 3; + xmc[50] = sr & 0x7; sr >>= 3; + xmc[51] = sr & 0x7; sr >>= 3; + } + } + else +#endif + { + + *c++ = ((GSM_MAGIC & 0xF) << 4) /* 1 */ + | ((LARc[0] >> 2) & 0xF); + *c++ = ((LARc[0] & 0x3) << 6) + | (LARc[1] & 0x3F); + *c++ = ((LARc[2] & 0x1F) << 3) + | ((LARc[3] >> 2) & 0x7); + *c++ = ((LARc[3] & 0x3) << 6) + | ((LARc[4] & 0xF) << 2) + | ((LARc[5] >> 2) & 0x3); + *c++ = ((LARc[5] & 0x3) << 6) + | ((LARc[6] & 0x7) << 3) + | (LARc[7] & 0x7); + + + *c++ = ((Nc[0] & 0x7F) << 1) + + + | ((bc[0] >> 1) & 0x1); + *c++ = ((bc[0] & 0x1) << 7) + + + | ((Mc[0] & 0x3) << 5) + + | ((xmaxc[0] >> 1) & 0x1F); + *c++ = ((xmaxc[0] & 0x1) << 7) + +#undef xmc +#define xmc (source + 12) + + | ((xmc[0] & 0x7) << 4) + | ((xmc[1] & 0x7) << 1) + | ((xmc[2] >> 2) & 0x1); + *c++ = ((xmc[2] & 0x3) << 6) + | ((xmc[3] & 0x7) << 3) + | (xmc[4] & 0x7); + *c++ = ((xmc[5] & 0x7) << 5) /* 10 */ + | ((xmc[6] & 0x7) << 2) + | ((xmc[7] >> 1) & 0x3); + *c++ = ((xmc[7] & 0x1) << 7) + | ((xmc[8] & 0x7) << 4) + | ((xmc[9] & 0x7) << 1) + | ((xmc[10] >> 2) & 0x1); + *c++ = ((xmc[10] & 0x3) << 6) + | ((xmc[11] & 0x7) << 3) + | (xmc[12] & 0x7); + + + *c++ = ((Nc[1] & 0x7F) << 1) + + + | ((bc[1] >> 1) & 0x1); + *c++ = ((bc[1] & 0x1) << 7) + + + | ((Mc[1] & 0x3) << 5) + + + | ((xmaxc[1] >> 1) & 0x1F); + *c++ = ((xmaxc[1] & 0x1) << 7) + +#undef xmc +#define xmc (source + 29 - 13) + + | ((xmc[13] & 0x7) << 4) + | ((xmc[14] & 0x7) << 1) + | ((xmc[15] >> 2) & 0x1); + *c++ = ((xmc[15] & 0x3) << 6) + | ((xmc[16] & 0x7) << 3) + | (xmc[17] & 0x7); + *c++ = ((xmc[18] & 0x7) << 5) + | ((xmc[19] & 0x7) << 2) + | ((xmc[20] >> 1) & 0x3); + *c++ = ((xmc[20] & 0x1) << 7) + | ((xmc[21] & 0x7) << 4) + | ((xmc[22] & 0x7) << 1) + | ((xmc[23] >> 2) & 0x1); + *c++ = ((xmc[23] & 0x3) << 6) + | ((xmc[24] & 0x7) << 3) + | (xmc[25] & 0x7); + + + *c++ = ((Nc[2] & 0x7F) << 1) /* 20 */ + + + | ((bc[2] >> 1) & 0x1); + *c++ = ((bc[2] & 0x1) << 7) + + + | ((Mc[2] & 0x3) << 5) + + + | ((xmaxc[2] >> 1) & 0x1F); + *c++ = ((xmaxc[2] & 0x1) << 7) + +#undef xmc +#define xmc (source + 46 - 26) + + | ((xmc[26] & 0x7) << 4) + | ((xmc[27] & 0x7) << 1) + | ((xmc[28] >> 2) & 0x1); + *c++ = ((xmc[28] & 0x3) << 6) + | ((xmc[29] & 0x7) << 3) + | (xmc[30] & 0x7); + *c++ = ((xmc[31] & 0x7) << 5) + | ((xmc[32] & 0x7) << 2) + | ((xmc[33] >> 1) & 0x3); + *c++ = ((xmc[33] & 0x1) << 7) + | ((xmc[34] & 0x7) << 4) + | ((xmc[35] & 0x7) << 1) + | ((xmc[36] >> 2) & 0x1); + *c++ = ((xmc[36] & 0x3) << 6) + | ((xmc[37] & 0x7) << 3) + | (xmc[38] & 0x7); + + + *c++ = ((Nc[3] & 0x7F) << 1) + + + | ((bc[3] >> 1) & 0x1); + *c++ = ((bc[3] & 0x1) << 7) + + + | ((Mc[3] & 0x3) << 5) + + + | ((xmaxc[3] >> 1) & 0x1F); + *c++ = ((xmaxc[3] & 0x1) << 7) + +#undef xmc +#define xmc (source + 63 - 39) + + | ((xmc[39] & 0x7) << 4) + | ((xmc[40] & 0x7) << 1) + | ((xmc[41] >> 2) & 0x1); + *c++ = ((xmc[41] & 0x3) << 6) /* 30 */ + | ((xmc[42] & 0x7) << 3) + | (xmc[43] & 0x7); + *c++ = ((xmc[44] & 0x7) << 5) + | ((xmc[45] & 0x7) << 2) + | ((xmc[46] >> 1) & 0x3); + *c++ = ((xmc[46] & 0x1) << 7) + | ((xmc[47] & 0x7) << 4) + | ((xmc[48] & 0x7) << 1) + | ((xmc[49] >> 2) & 0x1); + *c++ = ((xmc[49] & 0x3) << 6) + | ((xmc[50] & 0x7) << 3) + | (xmc[51] & 0x7); + } +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/gsm_jni.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/gsm_jni.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,87 @@ + + +/** +extern gsm gsm_create GSMJNI_P((void)); +extern void gsm_destroy GSMJNI_P((gsm)); + +extern int gsm_print GSMJNI_P((FILE *, gsm, gsm_byte *)); +extern int gsm_option GSMJNI_P((gsm, int, int *)); + +extern void gsm_encode GSMJNI_P((gsm, gsm_signal *, gsm_byte *)); +extern int gsm_decode GSMJNI_P((gsm, gsm_byte *, gsm_signal *)); + +extern int gsm_explode GSMJNI_P((gsm, gsm_byte *, gsm_signal *)); +extern void gsm_implode GSMJNI_P((gsm, gsm_signal *, gsm_byte *)); +*/ + + +#include + + +#include "gsm.h" +#include "private.h" +#include "proto.h" + +jlong +Java_org_sipdroid_media_codecs_GSMJNI_create(JNIEnv *env) +{ + return gsm_create(); +} + +void +Java_org_sipdroid_media_codecs_GSMJNI_destroy(JNIEnv *env, jlong jgsm) +{ + gsm_destroy((void *)jgsm); +} + +void +Java_org_sipdroid_media_codecs_GSMJNI_encode(JNIEnv *env, jlong jgsm, jshortArray jgsmSignal, jlong jsrcPos, jbyteArray jgsmByte, jlong jdestPos) +{ + jshort *gsmSignal; + jbyte *gsmByte; + jboolean isCopyByte; + jboolean isCopySignal; + void *ctx = (void *) jgsm; + + gsmByte = (*env)->GetByteArrayElements( env, jgsmByte, &isCopyByte); + gsmSignal = (*env)->GetShortArrayElements( env, jgsmSignal, &isCopySignal); + + gsm_encode(ctx, gsmSignal + jsrcPos, gsmByte + jdestPos); + if (isCopyByte == JNI_TRUE) + (*env)->ReleaseByteArrayElements(env, jgsmByte, gsmByte,0); + if (isCopySignal == JNI_TRUE) + (*env)->ReleaseShortArrayElements(env, jgsmSignal, gsmSignal,0); +} + +jint +Java_org_sipdroid_media_codecs_GSMJNI_decode(JNIEnv *env, jlong jgsm, jbyteArray jgsmByte, jlong jdestPos, jshortArray jgsmSignal, jlong jsrcPos) +{ + jshort *gsmSignal; + jbyte *gsmByte; + jboolean isCopyByte; + jboolean isCopySignal; + jint res; + + gsmSignal = (*env)->GetShortArrayElements( env, jgsmSignal, &isCopySignal); + gsmByte = (*env)->GetByteArrayElements( env, jgsmByte, &isCopyByte); + res = gsm_decode((void *)jgsm, gsmByte + jdestPos, gsmSignal + jsrcPos); + if (isCopyByte == JNI_TRUE) + (*env)->ReleaseByteArrayElements(env, jgsmByte, gsmByte,0); + if (isCopySignal == JNI_TRUE) + (*env)->ReleaseShortArrayElements(env, jgsmSignal, gsmSignal,0); + return res; +} + +jint +Java_org_sipdroid_media_codecs_GSMJNI_option(JNIEnv *env, jlong jgsm, jint jopt, jintArray jval) +{ + jint *val; + jboolean isCopyVal; + jint res; + + val = (*env)->GetIntArrayElements( env, jval, &isCopyVal); + res = gsm_option((void *)jgsm, jopt, val); + if (isCopyVal == JNI_TRUE) + (*env)->ReleaseIntArrayElements(env, jval, val,0); + return res; +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/gsm_option.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/gsm_option.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,69 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_option.c,v 1.3 1996/07/02 09:59:05 jutta Exp $ */ + +#include "private.h" + +#include "gsm.h" +#include "proto.h" + +int gsm_option P3((r, opt, val), gsm r, int opt, int * val) +{ + int result = -1; + + switch (opt) { + case GSM_OPT_LTP_CUT: +#ifdef LTP_CUT + result = r->ltp_cut; + if (val) r->ltp_cut = *val; +#endif + break; + + case GSM_OPT_VERBOSE: +#ifndef NDEBUG + result = r->verbose; + if (val) r->verbose = *val; +#endif + break; + + case GSM_OPT_FAST: + +#if defined(FAST) && defined(USE_FLOAT_MUL) + result = r->fast; + if (val) r->fast = !!*val; +#endif + break; + + case GSM_OPT_FRAME_CHAIN: + +#ifdef WAV49 + result = r->frame_chain; + if (val) r->frame_chain = *val; +#endif + break; + + case GSM_OPT_FRAME_INDEX: + +#ifdef WAV49 + result = r->frame_index; + if (val) r->frame_index = *val; +#endif + break; + + case GSM_OPT_WAV49: + +#ifdef WAV49 + result = r->wav_fmt; + if (val) r->wav_fmt = !!*val; +#endif + break; + + default: + break; + } + return result; +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/gsm_print.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/gsm_print.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,170 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_print.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */ + +#include + +#include "private.h" + +#include "gsm.h" +#include "proto.h" + +int gsm_print P3((f, s, c), FILE * f, gsm s, gsm_byte * c) +{ + word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4]; +// Wirlab + (void)s; + + + /* GSM_MAGIC = (*c >> 4) & 0xF; */ + + if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1; + + LARc[0] = (*c++ & 0xF) << 2; /* 1 */ + LARc[0] |= (*c >> 6) & 0x3; + LARc[1] = *c++ & 0x3F; + LARc[2] = (*c >> 3) & 0x1F; + LARc[3] = (*c++ & 0x7) << 2; + LARc[3] |= (*c >> 6) & 0x3; + LARc[4] = (*c >> 2) & 0xF; + LARc[5] = (*c++ & 0x3) << 2; + LARc[5] |= (*c >> 6) & 0x3; + LARc[6] = (*c >> 3) & 0x7; + LARc[7] = *c++ & 0x7; + + + Nc[0] = (*c >> 1) & 0x7F; + bc[0] = (*c++ & 0x1) << 1; + bc[0] |= (*c >> 7) & 0x1; + Mc[0] = (*c >> 5) & 0x3; + xmaxc[0] = (*c++ & 0x1F) << 1; + xmaxc[0] |= (*c >> 7) & 0x1; + xmc[0] = (*c >> 4) & 0x7; + xmc[1] = (*c >> 1) & 0x7; + xmc[2] = (*c++ & 0x1) << 2; + xmc[2] |= (*c >> 6) & 0x3; + xmc[3] = (*c >> 3) & 0x7; + xmc[4] = *c++ & 0x7; + xmc[5] = (*c >> 5) & 0x7; + xmc[6] = (*c >> 2) & 0x7; + xmc[7] = (*c++ & 0x3) << 1; /* 10 */ + xmc[7] |= (*c >> 7) & 0x1; + xmc[8] = (*c >> 4) & 0x7; + xmc[9] = (*c >> 1) & 0x7; + xmc[10] = (*c++ & 0x1) << 2; + xmc[10] |= (*c >> 6) & 0x3; + xmc[11] = (*c >> 3) & 0x7; + xmc[12] = *c++ & 0x7; + + Nc[1] = (*c >> 1) & 0x7F; + bc[1] = (*c++ & 0x1) << 1; + bc[1] |= (*c >> 7) & 0x1; + Mc[1] = (*c >> 5) & 0x3; + xmaxc[1] = (*c++ & 0x1F) << 1; + xmaxc[1] |= (*c >> 7) & 0x1; + xmc[13] = (*c >> 4) & 0x7; + xmc[14] = (*c >> 1) & 0x7; + xmc[15] = (*c++ & 0x1) << 2; + xmc[15] |= (*c >> 6) & 0x3; + xmc[16] = (*c >> 3) & 0x7; + xmc[17] = *c++ & 0x7; + xmc[18] = (*c >> 5) & 0x7; + xmc[19] = (*c >> 2) & 0x7; + xmc[20] = (*c++ & 0x3) << 1; + xmc[20] |= (*c >> 7) & 0x1; + xmc[21] = (*c >> 4) & 0x7; + xmc[22] = (*c >> 1) & 0x7; + xmc[23] = (*c++ & 0x1) << 2; + xmc[23] |= (*c >> 6) & 0x3; + xmc[24] = (*c >> 3) & 0x7; + xmc[25] = *c++ & 0x7; + + + Nc[2] = (*c >> 1) & 0x7F; + bc[2] = (*c++ & 0x1) << 1; /* 20 */ + bc[2] |= (*c >> 7) & 0x1; + Mc[2] = (*c >> 5) & 0x3; + xmaxc[2] = (*c++ & 0x1F) << 1; + xmaxc[2] |= (*c >> 7) & 0x1; + xmc[26] = (*c >> 4) & 0x7; + xmc[27] = (*c >> 1) & 0x7; + xmc[28] = (*c++ & 0x1) << 2; + xmc[28] |= (*c >> 6) & 0x3; + xmc[29] = (*c >> 3) & 0x7; + xmc[30] = *c++ & 0x7; + xmc[31] = (*c >> 5) & 0x7; + xmc[32] = (*c >> 2) & 0x7; + xmc[33] = (*c++ & 0x3) << 1; + xmc[33] |= (*c >> 7) & 0x1; + xmc[34] = (*c >> 4) & 0x7; + xmc[35] = (*c >> 1) & 0x7; + xmc[36] = (*c++ & 0x1) << 2; + xmc[36] |= (*c >> 6) & 0x3; + xmc[37] = (*c >> 3) & 0x7; + xmc[38] = *c++ & 0x7; + + Nc[3] = (*c >> 1) & 0x7F; + bc[3] = (*c++ & 0x1) << 1; + bc[3] |= (*c >> 7) & 0x1; + Mc[3] = (*c >> 5) & 0x3; + xmaxc[3] = (*c++ & 0x1F) << 1; + xmaxc[3] |= (*c >> 7) & 0x1; + + xmc[39] = (*c >> 4) & 0x7; + xmc[40] = (*c >> 1) & 0x7; + xmc[41] = (*c++ & 0x1) << 2; + xmc[41] |= (*c >> 6) & 0x3; + xmc[42] = (*c >> 3) & 0x7; + xmc[43] = *c++ & 0x7; /* 30 */ + xmc[44] = (*c >> 5) & 0x7; + xmc[45] = (*c >> 2) & 0x7; + xmc[46] = (*c++ & 0x3) << 1; + xmc[46] |= (*c >> 7) & 0x1; + xmc[47] = (*c >> 4) & 0x7; + xmc[48] = (*c >> 1) & 0x7; + xmc[49] = (*c++ & 0x1) << 2; + xmc[49] |= (*c >> 6) & 0x3; + xmc[50] = (*c >> 3) & 0x7; + xmc[51] = *c & 0x7; /* 33 */ + + fprintf(f, + "LARc:\t%2.2d %2.2d %2.2d %2.2d %2.2d %2.2d %2.2d %2.2d\n", + LARc[0],LARc[1],LARc[2],LARc[3],LARc[4],LARc[5],LARc[6],LARc[7]); + + fprintf(f, "#1: Nc %4.4d bc %d Mc %d xmaxc %d\n", + Nc[0], bc[0], Mc[0], xmaxc[0]); + fprintf(f, +"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n", + xmc[0],xmc[1],xmc[2],xmc[3],xmc[4],xmc[5],xmc[6], + xmc[7],xmc[8],xmc[9],xmc[10],xmc[11],xmc[12] ); + + fprintf(f, "#2: Nc %4.4d bc %d Mc %d xmaxc %d\n", + Nc[1], bc[1], Mc[1], xmaxc[1]); + fprintf(f, +"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n", + xmc[13+0],xmc[13+1],xmc[13+2],xmc[13+3],xmc[13+4],xmc[13+5], + xmc[13+6], xmc[13+7],xmc[13+8],xmc[13+9],xmc[13+10],xmc[13+11], + xmc[13+12] ); + + fprintf(f, "#3: Nc %4.4d bc %d Mc %d xmaxc %d\n", + Nc[2], bc[2], Mc[2], xmaxc[2]); + fprintf(f, +"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n", + xmc[26+0],xmc[26+1],xmc[26+2],xmc[26+3],xmc[26+4],xmc[26+5], + xmc[26+6], xmc[26+7],xmc[26+8],xmc[26+9],xmc[26+10],xmc[26+11], + xmc[26+12] ); + + fprintf(f, "#4: Nc %4.4d bc %d Mc %d xmaxc %d\n", + Nc[3], bc[3], Mc[3], xmaxc[3]); + fprintf(f, +"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n", + xmc[39+0],xmc[39+1],xmc[39+2],xmc[39+3],xmc[39+4],xmc[39+5], + xmc[39+6], xmc[39+7],xmc[39+8],xmc[39+9],xmc[39+10],xmc[39+11], + xmc[39+12] ); + + return 0; +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/long_term.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/long_term.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,954 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/long_term.c,v 1.6 1996/07/02 12:33:19 jutta Exp $ */ + +#include +#include + +#include "private.h" + +#include "gsm.h" +#include "proto.h" + +/* + * 4.2.11 .. 4.2.12 LONG TERM PREDICTOR (LTP) SECTION + */ + + +/* + * This module computes the LTP gain (bc) and the LTP lag (Nc) + * for the long term analysis filter. This is done by calculating a + * maximum of the cross-correlation function between the current + * sub-segment short term residual signal d[0..39] (output of + * the short term analysis filter; for simplification the index + * of this array begins at 0 and ends at 39 for each sub-segment of the + * RPE-LTP analysis) and the previous reconstructed short term + * residual signal dp[ -120 .. -1 ]. A dynamic scaling must be + * performed to avoid overflow. + */ + + /* The next procedure exists in six versions. First two integer + * version (if USE_FLOAT_MUL is not defined); then four floating + * point versions, twice with proper scaling (USE_FLOAT_MUL defined), + * once without (USE_FLOAT_MUL and FAST defined, and fast run-time + * option used). Every pair has first a Cut version (see the -C + * option to toast or the LTP_CUT option to gsm_option()), then the + * uncut one. (For a detailed explanation of why this is altogether + * a bad idea, see Henry Spencer and Geoff Collyer, ``#ifdef Considered + * Harmful''.) + */ + +#ifndef USE_FLOAT_MUL + +#ifdef LTP_CUT + +static void Cut_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out), + + struct gsm_state * st, + + register word * d, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + word Nc, bc; + word wt[40]; + + longword L_result; + longword L_max, L_power; + word R, S, dmax, scal, best_k; + word ltp_cut; + + register word temp, wt_k; + + /* Search of the optimum scaling of d[0..39]. + */ + dmax = 0; + for (k = 0; k <= 39; k++) { + temp = d[k]; + temp = GSM_ABS( temp ); + if (temp > dmax) { + dmax = temp; + best_k = k; + } + } + temp = 0; + if (dmax == 0) scal = 0; + else { + assert(dmax > 0); + temp = gsm_norm( (longword)dmax << 16 ); + } + if (temp > 6) scal = 0; + else scal = 6 - temp; + assert(scal >= 0); + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + wt_k = SASR(d[best_k], scal); + + for (lambda = 40; lambda <= 120; lambda++) { + L_result = (longword)wt_k * dp[best_k - lambda]; + if (L_result > L_max) { + Nc = lambda; + L_max = L_result; + } + } + *Nc_out = Nc; + L_max <<= 1; + + /* Rescaling of L_max + */ + assert(scal <= 100 && scal >= -100); + L_max = L_max >> (6 - scal); /* sub(6, scal) */ + + assert( Nc <= 120 && Nc >= 40); + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + L_power = 0; + for (k = 0; k <= 39; k++) { + + register longword L_temp; + + L_temp = SASR( dp[k - Nc], 3 ); + L_power += L_temp * L_temp; + } + L_power <<= 1; /* from L_MULT */ + + /* Normalization of L_max and L_power + */ + + if (L_max <= 0) { + *bc_out = 0; + return; + } + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + temp = gsm_norm( L_power ); + + R = SASR( L_max << temp, 16 ); + S = SASR( L_power << temp, 16 ); + + /* Coding of the LTP gain + */ + + /* Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; + *bc_out = bc; +} + +#endif /* LTP_CUT */ + +static void Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out), + register word * d, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + word Nc, bc; + word wt[40]; + + longword L_max, L_power; + word R, S, dmax, scal; + register word temp; + + /* Search of the optimum scaling of d[0..39]. + */ + dmax = 0; + + for (k = 0; k <= 39; k++) { + temp = d[k]; + temp = GSM_ABS( temp ); + if (temp > dmax) dmax = temp; + } + + temp = 0; + if (dmax == 0) scal = 0; + else { + assert(dmax > 0); + temp = gsm_norm( (longword)dmax << 16 ); + } + + if (temp > 6) scal = 0; + else scal = 6 - temp; + + assert(scal >= 0); + + /* Initialization of a working array wt + */ + + for (k = 0; k <= 39; k++) wt[k] = SASR( d[k], scal ); + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + + for (lambda = 40; lambda <= 120; lambda++) { + +# undef STEP +# define STEP(k) (longword)wt[k] * dp[k - lambda] + + register longword L_result; + + L_result = STEP(0) ; L_result += STEP(1) ; + L_result += STEP(2) ; L_result += STEP(3) ; + L_result += STEP(4) ; L_result += STEP(5) ; + L_result += STEP(6) ; L_result += STEP(7) ; + L_result += STEP(8) ; L_result += STEP(9) ; + L_result += STEP(10) ; L_result += STEP(11) ; + L_result += STEP(12) ; L_result += STEP(13) ; + L_result += STEP(14) ; L_result += STEP(15) ; + L_result += STEP(16) ; L_result += STEP(17) ; + L_result += STEP(18) ; L_result += STEP(19) ; + L_result += STEP(20) ; L_result += STEP(21) ; + L_result += STEP(22) ; L_result += STEP(23) ; + L_result += STEP(24) ; L_result += STEP(25) ; + L_result += STEP(26) ; L_result += STEP(27) ; + L_result += STEP(28) ; L_result += STEP(29) ; + L_result += STEP(30) ; L_result += STEP(31) ; + L_result += STEP(32) ; L_result += STEP(33) ; + L_result += STEP(34) ; L_result += STEP(35) ; + L_result += STEP(36) ; L_result += STEP(37) ; + L_result += STEP(38) ; L_result += STEP(39) ; + + if (L_result > L_max) { + + Nc = lambda; + L_max = L_result; + } + } + + *Nc_out = Nc; + + L_max <<= 1; + + /* Rescaling of L_max + */ + assert(scal <= 100 && scal >= -100); + L_max = L_max >> (6 - scal); /* sub(6, scal) */ + + assert( Nc <= 120 && Nc >= 40); + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + L_power = 0; + for (k = 0; k <= 39; k++) { + + register longword L_temp; + + L_temp = SASR( dp[k - Nc], 3 ); + L_power += L_temp * L_temp; + } + L_power <<= 1; /* from L_MULT */ + + /* Normalization of L_max and L_power + */ + + if (L_max <= 0) { + *bc_out = 0; + return; + } + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + temp = gsm_norm( L_power ); + + R = SASR( L_max << temp, 16 ); + S = SASR( L_power << temp, 16 ); + + /* Coding of the LTP gain + */ + + /* Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; + *bc_out = bc; +} + +#else /* USE_FLOAT_MUL */ + +#ifdef LTP_CUT + +static void Cut_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out), + struct gsm_state * st, /* IN */ + register word * d, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + word Nc, bc; + word ltp_cut; + + float wt_float[40]; + float dp_float_base[120], * dp_float = dp_float_base + 120; + + longword L_max, L_power; + word R, S, dmax, scal; + register word temp; + + /* Search of the optimum scaling of d[0..39]. + */ + dmax = 0; + + for (k = 0; k <= 39; k++) { + temp = d[k]; + temp = GSM_ABS( temp ); + if (temp > dmax) dmax = temp; + } + + temp = 0; + if (dmax == 0) scal = 0; + else { + assert(dmax > 0); + temp = gsm_norm( (longword)dmax << 16 ); + } + + if (temp > 6) scal = 0; + else scal = 6 - temp; + + assert(scal >= 0); + ltp_cut = (longword)SASR(dmax, scal) * st->ltp_cut / 100; + + + /* Initialization of a working array wt + */ + + for (k = 0; k < 40; k++) { + register word w = SASR( d[k], scal ); + if (w < 0 ? w > -ltp_cut : w < ltp_cut) { + wt_float[k] = 0.0; + } + else { + wt_float[k] = w; + } + } + for (k = -120; k < 0; k++) dp_float[k] = dp[k]; + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + + for (lambda = 40; lambda <= 120; lambda += 9) { + + /* Calculate L_result for l = lambda .. lambda + 9. + */ + register float *lp = dp_float - lambda; + + register float W; + register float a = lp[-8], b = lp[-7], c = lp[-6], + d = lp[-5], e = lp[-4], f = lp[-3], + g = lp[-2], h = lp[-1]; + register float E; + register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0, + S5 = 0, S6 = 0, S7 = 0, S8 = 0; + +# undef STEP +# define STEP(K, a, b, c, d, e, f, g, h) \ + if ((W = wt_float[K]) != 0.0) { \ + E = W * a; S8 += E; \ + E = W * b; S7 += E; \ + E = W * c; S6 += E; \ + E = W * d; S5 += E; \ + E = W * e; S4 += E; \ + E = W * f; S3 += E; \ + E = W * g; S2 += E; \ + E = W * h; S1 += E; \ + a = lp[K]; \ + E = W * a; S0 += E; } else (a = lp[K]) + +# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h) +# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a) +# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b) +# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c) +# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d) +# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e) +# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f) +# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g) + + STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3); + STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7); + + STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11); + STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15); + + STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19); + STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23); + + STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27); + STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31); + + STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35); + STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39); + + if (S0 > L_max) { L_max = S0; Nc = lambda; } + if (S1 > L_max) { L_max = S1; Nc = lambda + 1; } + if (S2 > L_max) { L_max = S2; Nc = lambda + 2; } + if (S3 > L_max) { L_max = S3; Nc = lambda + 3; } + if (S4 > L_max) { L_max = S4; Nc = lambda + 4; } + if (S5 > L_max) { L_max = S5; Nc = lambda + 5; } + if (S6 > L_max) { L_max = S6; Nc = lambda + 6; } + if (S7 > L_max) { L_max = S7; Nc = lambda + 7; } + if (S8 > L_max) { L_max = S8; Nc = lambda + 8; } + + } + *Nc_out = Nc; + + L_max <<= 1; + + /* Rescaling of L_max + */ + assert(scal <= 100 && scal >= -100); + L_max = L_max >> (6 - scal); /* sub(6, scal) */ + + assert( Nc <= 120 && Nc >= 40); + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + L_power = 0; + for (k = 0; k <= 39; k++) { + + register longword L_temp; + + L_temp = SASR( dp[k - Nc], 3 ); + L_power += L_temp * L_temp; + } + L_power <<= 1; /* from L_MULT */ + + /* Normalization of L_max and L_power + */ + + if (L_max <= 0) { + *bc_out = 0; + return; + } + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + temp = gsm_norm( L_power ); + + R = SASR( L_max << temp, 16 ); + S = SASR( L_power << temp, 16 ); + + /* Coding of the LTP gain + */ + + /* Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; + *bc_out = bc; +} + +#endif /* LTP_CUT */ + +static void Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out), + register word * d, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + word Nc, bc; + + float wt_float[40]; + float dp_float_base[120], * dp_float = dp_float_base + 120; + + longword L_max, L_power; + word R, S, dmax, scal; + register word temp; + + /* Search of the optimum scaling of d[0..39]. + */ + dmax = 0; + + for (k = 0; k <= 39; k++) { + temp = d[k]; + temp = GSM_ABS( temp ); + if (temp > dmax) dmax = temp; + } + + temp = 0; + if (dmax == 0) scal = 0; + else { + assert(dmax > 0); + temp = gsm_norm( (longword)dmax << 16 ); + } + + if (temp > 6) scal = 0; + else scal = 6 - temp; + + assert(scal >= 0); + + /* Initialization of a working array wt + */ + + for (k = 0; k < 40; k++) wt_float[k] = SASR( d[k], scal ); + for (k = -120; k < 0; k++) dp_float[k] = dp[k]; + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + + for (lambda = 40; lambda <= 120; lambda += 9) { + + /* Calculate L_result for l = lambda .. lambda + 9. + */ + register float *lp = dp_float - lambda; + + register float W; + register float a = lp[-8], b = lp[-7], c = lp[-6], + d = lp[-5], e = lp[-4], f = lp[-3], + g = lp[-2], h = lp[-1]; + register float E; + register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0, + S5 = 0, S6 = 0, S7 = 0, S8 = 0; + +# undef STEP +# define STEP(K, a, b, c, d, e, f, g, h) \ + W = wt_float[K]; \ + E = W * a; S8 += E; \ + E = W * b; S7 += E; \ + E = W * c; S6 += E; \ + E = W * d; S5 += E; \ + E = W * e; S4 += E; \ + E = W * f; S3 += E; \ + E = W * g; S2 += E; \ + E = W * h; S1 += E; \ + a = lp[K]; \ + E = W * a; S0 += E + +# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h) +# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a) +# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b) +# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c) +# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d) +# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e) +# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f) +# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g) + + STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3); + STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7); + + STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11); + STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15); + + STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19); + STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23); + + STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27); + STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31); + + STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35); + STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39); + + if (S0 > L_max) { L_max = S0; Nc = lambda; } + if (S1 > L_max) { L_max = S1; Nc = lambda + 1; } + if (S2 > L_max) { L_max = S2; Nc = lambda + 2; } + if (S3 > L_max) { L_max = S3; Nc = lambda + 3; } + if (S4 > L_max) { L_max = S4; Nc = lambda + 4; } + if (S5 > L_max) { L_max = S5; Nc = lambda + 5; } + if (S6 > L_max) { L_max = S6; Nc = lambda + 6; } + if (S7 > L_max) { L_max = S7; Nc = lambda + 7; } + if (S8 > L_max) { L_max = S8; Nc = lambda + 8; } + } + *Nc_out = Nc; + + L_max <<= 1; + + /* Rescaling of L_max + */ + assert(scal <= 100 && scal >= -100); + L_max = L_max >> (6 - scal); /* sub(6, scal) */ + + assert( Nc <= 120 && Nc >= 40); + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + L_power = 0; + for (k = 0; k <= 39; k++) { + + register longword L_temp; + + L_temp = SASR( dp[k - Nc], 3 ); + L_power += L_temp * L_temp; + } + L_power <<= 1; /* from L_MULT */ + + /* Normalization of L_max and L_power + */ + + if (L_max <= 0) { + *bc_out = 0; + return; + } + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + temp = gsm_norm( L_power ); + + R = SASR( L_max << temp, 16 ); + S = SASR( L_power << temp, 16 ); + + /* Coding of the LTP gain + */ + + /* Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; + *bc_out = bc; +} + +#ifdef FAST +#ifdef LTP_CUT + +static void Cut_Fast_Calculation_of_the_LTP_parameters P5((st, + d,dp,bc_out,Nc_out), + struct gsm_state * st, /* IN */ + register word * d, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + register float wt_float; + word Nc, bc; + word wt_max, best_k, ltp_cut; + + float dp_float_base[120], * dp_float = dp_float_base + 120; + + register float L_result, L_max, L_power; + + wt_max = 0; + + for (k = 0; k < 40; ++k) { + if ( d[k] > wt_max) wt_max = d[best_k = k]; + else if (-d[k] > wt_max) wt_max = -d[best_k = k]; + } + + assert(wt_max >= 0); + wt_float = (float)wt_max; + + for (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k]; + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + + for (lambda = 40; lambda <= 120; lambda++) { + L_result = wt_float * dp_float[best_k - lambda]; + if (L_result > L_max) { + Nc = lambda; + L_max = L_result; + } + } + + *Nc_out = Nc; + if (L_max <= 0.) { + *bc_out = 0; + return; + } + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + dp_float -= Nc; + L_power = 0; + for (k = 0; k < 40; ++k) { + register float f = dp_float[k]; + L_power += f * f; + } + + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + /* Coding of the LTP gain + * Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + lambda = L_max / L_power * 32768.; + for (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break; + *bc_out = bc; +} + +#endif /* LTP_CUT */ + +static void Fast_Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out), + register word * d, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + word Nc, bc; + + float wt_float[40]; + float dp_float_base[120], * dp_float = dp_float_base + 120; + + register float L_max, L_power; + + for (k = 0; k < 40; ++k) wt_float[k] = (float)d[k]; + for (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k]; + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + + for (lambda = 40; lambda <= 120; lambda += 9) { + + /* Calculate L_result for l = lambda .. lambda + 9. + */ + register float *lp = dp_float - lambda; + + register float W; + register float a = lp[-8], b = lp[-7], c = lp[-6], + d = lp[-5], e = lp[-4], f = lp[-3], + g = lp[-2], h = lp[-1]; + register float E; + register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0, + S5 = 0, S6 = 0, S7 = 0, S8 = 0; + +# undef STEP +# define STEP(K, a, b, c, d, e, f, g, h) \ + W = wt_float[K]; \ + E = W * a; S8 += E; \ + E = W * b; S7 += E; \ + E = W * c; S6 += E; \ + E = W * d; S5 += E; \ + E = W * e; S4 += E; \ + E = W * f; S3 += E; \ + E = W * g; S2 += E; \ + E = W * h; S1 += E; \ + a = lp[K]; \ + E = W * a; S0 += E + +# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h) +# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a) +# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b) +# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c) +# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d) +# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e) +# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f) +# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g) + + STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3); + STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7); + + STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11); + STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15); + + STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19); + STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23); + + STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27); + STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31); + + STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35); + STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39); + + if (S0 > L_max) { L_max = S0; Nc = lambda; } + if (S1 > L_max) { L_max = S1; Nc = lambda + 1; } + if (S2 > L_max) { L_max = S2; Nc = lambda + 2; } + if (S3 > L_max) { L_max = S3; Nc = lambda + 3; } + if (S4 > L_max) { L_max = S4; Nc = lambda + 4; } + if (S5 > L_max) { L_max = S5; Nc = lambda + 5; } + if (S6 > L_max) { L_max = S6; Nc = lambda + 6; } + if (S7 > L_max) { L_max = S7; Nc = lambda + 7; } + if (S8 > L_max) { L_max = S8; Nc = lambda + 8; } + } + *Nc_out = Nc; + + if (L_max <= 0.) { + *bc_out = 0; + return; + } + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + dp_float -= Nc; + L_power = 0; + for (k = 0; k < 40; ++k) { + register float f = dp_float[k]; + L_power += f * f; + } + + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + /* Coding of the LTP gain + * Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + lambda = L_max / L_power * 32768.; + for (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break; + *bc_out = bc; +} + +#endif /* FAST */ +#endif /* USE_FLOAT_MUL */ + + +/* 4.2.12 */ + +static void Long_term_analysis_filtering P6((bc,Nc,dp,d,dpp,e), + word bc, /* IN */ + word Nc, /* IN */ + register word * dp, /* previous d [-120..-1] IN */ + register word * d, /* d [0..39] IN */ + register word * dpp, /* estimate [0..39] OUT */ + register word * e /* long term res. signal [0..39] OUT */ +) +/* + * In this part, we have to decode the bc parameter to compute + * the samples of the estimate dpp[0..39]. The decoding of bc needs the + * use of table 4.3b. The long term residual signal e[0..39] + * is then calculated to be fed to the RPE encoding section. + */ +{ + register int k; + register longword ltmp; + +# undef STEP +# define STEP(BP) \ + for (k = 0; k <= 39; k++) { \ + dpp[k] = GSM_MULT_R( BP, dp[k - Nc]); \ + e[k] = GSM_SUB( d[k], dpp[k] ); \ + } + + switch (bc) { + case 0: STEP( 3277 ); break; + case 1: STEP( 11469 ); break; + case 2: STEP( 21299 ); break; + case 3: STEP( 32767 ); break; + } +} + +void Gsm_Long_Term_Predictor P7((S,d,dp,e,dpp,Nc,bc), /* 4x for 160 samples */ + + struct gsm_state * S, + + word * d, /* [0..39] residual signal IN */ + word * dp, /* [-120..-1] d' IN */ + + word * e, /* [0..39] OUT */ + word * dpp, /* [0..39] OUT */ + word * Nc, /* correlation lag OUT */ + word * bc /* gain factor OUT */ +) +{ + +// Wirlab + (void)S; + + assert( d ); assert( dp ); assert( e ); + assert( dpp); assert( Nc ); assert( bc ); + +#if defined(FAST) && defined(USE_FLOAT_MUL) + if (S->fast) +#if defined (LTP_CUT) + if (S->ltp_cut) + Cut_Fast_Calculation_of_the_LTP_parameters(S, + d, dp, bc, Nc); + else +#endif /* LTP_CUT */ + Fast_Calculation_of_the_LTP_parameters(d, dp, bc, Nc ); + else +#endif /* FAST & USE_FLOAT_MUL */ +#ifdef LTP_CUT + if (S->ltp_cut) + Cut_Calculation_of_the_LTP_parameters(S, d, dp, bc, Nc); + else +#endif + Calculation_of_the_LTP_parameters(d, dp, bc, Nc); + + Long_term_analysis_filtering( *bc, *Nc, dp, d, dpp, e ); +} + +/* 4.3.2 */ +void Gsm_Long_Term_Synthesis_Filtering P5((S,Ncr,bcr,erp,drp), + struct gsm_state * S, + + word Ncr, + word bcr, + register word * erp, /* [0..39] IN */ + register word * drp /* [-120..-1] IN, [-120..40] OUT */ +) +/* + * This procedure uses the bcr and Ncr parameter to realize the + * long term synthesis filtering. The decoding of bcr needs + * table 4.3b. + */ +{ + + register longword ltmp; /* for ADD */ + register int k; + word brp, drpp, Nr; + + /* Check the limits of Nr. + */ + Nr = Ncr < 40 || Ncr > 120 ? S->nrp : Ncr; + S->nrp = Nr; + assert(Nr >= 40 && Nr <= 120); + + /* Decoding of the LTP gain bcr + */ + brp = gsm_QLB[ bcr ]; + + /* Computation of the reconstructed short term residual + * signal drp[0..39] + */ + assert(brp != MIN_WORD); + + for (k = 0; k <= 39; k++) { + drpp = GSM_MULT_R( brp, drp[ k - Nr ] ); + drp[k] = GSM_ADD( erp[k], drpp ); + } + + /* + * Update of the reconstructed short term residual signal + * drp[ -1..-120 ] + */ + + for (k = 0; k <= 119; k++) drp[ -120 + k ] = drp[ -80 + k ]; +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/lpc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/lpc.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,345 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/lpc.c,v 1.5 1994/12/30 23:14:54 jutta Exp $ */ + +#include +#include + +#include "private.h" + +#include "gsm.h" +#include "proto.h" + +#undef P + +/* + * 4.2.4 .. 4.2.7 LPC ANALYSIS SECTION + */ + +/* 4.2.4 */ + + +static void Autocorrelation P2((s, L_ACF), + word * s, /* [0..159] IN/OUT */ + longword * L_ACF) /* [0..8] OUT */ +/* + * The goal is to compute the array L_ACF[k]. The signal s[i] must + * be scaled in order to avoid an overflow situation. + */ +{ + register int k, i; + + word temp, smax, scalauto; + +#ifdef USE_FLOAT_MUL + float float_s[160]; +#endif + + /* Dynamic scaling of the array s[0..159] + */ + + /* Search for the maximum. + */ + smax = 0; + for (k = 0; k <= 159; k++) { + temp = GSM_ABS( s[k] ); + if (temp > smax) smax = temp; + } + + /* Computation of the scaling factor. + */ + if (smax == 0) scalauto = 0; + else { + assert(smax > 0); + scalauto = 4 - gsm_norm( (longword)smax << 16 );/* sub(4,..) */ + } + + /* Scaling of the array s[0...159] + */ + + if (scalauto > 0) { + +# ifdef USE_FLOAT_MUL +# define SCALE(n) \ + case n: for (k = 0; k <= 159; k++) \ + float_s[k] = (float) \ + (s[k] = GSM_MULT_R(s[k], 16384 >> (n-1)));\ + break; +# else +# define SCALE(n) \ + case n: for (k = 0; k <= 159; k++) \ + s[k] = GSM_MULT_R( s[k], 16384 >> (n-1) );\ + break; +# endif /* USE_FLOAT_MUL */ + + switch (scalauto) { + SCALE(1) + SCALE(2) + SCALE(3) + SCALE(4) + } +# undef SCALE + } +# ifdef USE_FLOAT_MUL + else for (k = 0; k <= 159; k++) float_s[k] = (float) s[k]; +# endif + + /* Compute the L_ACF[..]. + */ + { +# ifdef USE_FLOAT_MUL + register float * sp = float_s; + register float sl = *sp; + +# define STEP(k) L_ACF[k] += (longword)(sl * sp[ -(k) ]); +# else + word * sp = s; + word sl = *sp; + +# define STEP(k) L_ACF[k] += ((longword)sl * sp[ -(k) ]); +# endif + +# define NEXTI sl = *++sp + + + for (k = 9; k--; L_ACF[k] = 0) ; + + STEP (0); + NEXTI; + STEP(0); STEP(1); + NEXTI; + STEP(0); STEP(1); STEP(2); + NEXTI; + STEP(0); STEP(1); STEP(2); STEP(3); + NEXTI; + STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); + NEXTI; + STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); + NEXTI; + STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6); + NEXTI; + STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6); STEP(7); + + for (i = 8; i <= 159; i++) { + + NEXTI; + + STEP(0); + STEP(1); STEP(2); STEP(3); STEP(4); + STEP(5); STEP(6); STEP(7); STEP(8); + } + + for (k = 9; k--; L_ACF[k] <<= 1) ; + + } + /* Rescaling of the array s[0..159] + */ + if (scalauto > 0) { + assert(scalauto <= 4); + for (k = 160; k--; *s++ <<= scalauto) ; + } +} + +#if defined(USE_FLOAT_MUL) && defined(FAST) + +static void Fast_Autocorrelation P2((s, L_ACF), + word * s, /* [0..159] IN/OUT */ + longword * L_ACF) /* [0..8] OUT */ +{ + register int k, i; + float f_L_ACF[9]; + float scale; + + float s_f[160]; + register float *sf = s_f; + + for (i = 0; i < 160; ++i) sf[i] = s[i]; + for (k = 0; k <= 8; k++) { + register float L_temp2 = 0; + register float *sfl = sf - k; + for (i = k; i < 160; ++i) L_temp2 += sf[i] * sfl[i]; + f_L_ACF[k] = L_temp2; + } + scale = MAX_LONGWORD / f_L_ACF[0]; + + for (k = 0; k <= 8; k++) { + L_ACF[k] = f_L_ACF[k] * scale; + } +} +#endif /* defined (USE_FLOAT_MUL) && defined (FAST) */ + +/* 4.2.5 */ + +static void Reflection_coefficients P2( (L_ACF, r), + longword * L_ACF, /* 0...8 IN */ + register word * r /* 0...7 OUT */ +) +{ + register int i, m, n; + register word temp; + register longword ltmp; + word ACF[9]; /* 0..8 */ + word P[ 9]; /* 0..8 */ + word K[ 9]; /* 2..8 */ + + /* Schur recursion with 16 bits arithmetic. + */ + + if (L_ACF[0] == 0) { + for (i = 8; i--; *r++ = 0) ; + return; + } + + assert( L_ACF[0] != 0 ); + temp = gsm_norm( L_ACF[0] ); + + assert(temp >= 0 && temp < 32); + + /* ? overflow ? */ + for (i = 0; i <= 8; i++) ACF[i] = SASR( L_ACF[i] << temp, 16 ); + + /* Initialize array P[..] and K[..] for the recursion. + */ + + for (i = 1; i <= 7; i++) K[ i ] = ACF[ i ]; + for (i = 0; i <= 8; i++) P[ i ] = ACF[ i ]; + + /* Compute reflection coefficients + */ + for (n = 1; n <= 8; n++, r++) { + + temp = P[1]; + temp = GSM_ABS(temp); + if (P[0] < temp) { + for (i = n; i <= 8; i++) *r++ = 0; + return; + } + + *r = gsm_div( temp, P[0] ); + + assert(*r >= 0); + if (P[1] > 0) *r = -*r; /* r[n] = sub(0, r[n]) */ + assert (*r != MIN_WORD); + if (n == 8) return; + + /* Schur recursion + */ + temp = GSM_MULT_R( P[1], *r ); + P[0] = GSM_ADD( P[0], temp ); + + for (m = 1; m <= 8 - n; m++) { + temp = GSM_MULT_R( K[ m ], *r ); + P[m] = GSM_ADD( P[ m+1 ], temp ); + + temp = GSM_MULT_R( P[ m+1 ], *r ); + K[m] = GSM_ADD( K[ m ], temp ); + } + } +} + +/* 4.2.6 */ + +static void Transformation_to_Log_Area_Ratios P1((r), + register word * r /* 0..7 IN/OUT */ +) +/* + * The following scaling for r[..] and LAR[..] has been used: + * + * r[..] = integer( real_r[..]*32768. ); -1 <= real_r < 1. + * LAR[..] = integer( real_LAR[..] * 16384 ); + * with -1.625 <= real_LAR <= 1.625 + */ +{ + register word temp; + register int i; + + + /* Computation of the LAR[0..7] from the r[0..7] + */ + for (i = 1; i <= 8; i++, r++) { + + temp = *r; + temp = GSM_ABS(temp); + assert(temp >= 0); + + if (temp < 22118) { + temp >>= 1; + } else if (temp < 31130) { + assert( temp >= 11059 ); + temp -= 11059; + } else { + assert( temp >= 26112 ); + temp -= 26112; + temp <<= 2; + } + + *r = *r < 0 ? -temp : temp; + assert( *r != MIN_WORD ); + } +} + +/* 4.2.7 */ + +static void Quantization_and_coding P1((LAR), + register word * LAR /* [0..7] IN/OUT */ +) +{ + register word temp; + longword ltmp; + + + /* This procedure needs four tables; the following equations + * give the optimum scaling for the constants: + * + * A[0..7] = integer( real_A[0..7] * 1024 ) + * B[0..7] = integer( real_B[0..7] * 512 ) + * MAC[0..7] = maximum of the LARc[0..7] + * MIC[0..7] = minimum of the LARc[0..7] + */ + +# undef STEP +# define STEP( A, B, MAC, MIC ) \ + temp = GSM_MULT( A, *LAR ); \ + temp = GSM_ADD( temp, B ); \ + temp = GSM_ADD( temp, 256 ); \ + temp = SASR( temp, 9 ); \ + *LAR = temp>MAC ? MAC - MIC : (tempfast) Fast_Autocorrelation (s, L_ACF ); + else +#endif + Autocorrelation (s, L_ACF ); + Reflection_coefficients (L_ACF, LARc ); + Transformation_to_Log_Area_Ratios (LARc); + Quantization_and_coding (LARc); +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/preprocess.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/preprocess.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,113 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/preprocess.c,v 1.2 1994/05/10 20:18:45 jutta Exp $ */ + +#include +#include + +#include "private.h" + +#include "gsm.h" +#include "proto.h" + +/* 4.2.0 .. 4.2.3 PREPROCESSING SECTION + * + * After A-law to linear conversion (or directly from the + * Ato D converter) the following scaling is assumed for + * input to the RPE-LTP algorithm: + * + * in: 0.1.....................12 + * S.v.v.v.v.v.v.v.v.v.v.v.v.*.*.* + * + * Where S is the sign bit, v a valid bit, and * a "don't care" bit. + * The original signal is called sop[..] + * + * out: 0.1................... 12 + * S.S.v.v.v.v.v.v.v.v.v.v.v.v.0.0 + */ + + +void Gsm_Preprocess P3((S, s, so), + struct gsm_state * S, + word * s, + word * so ) /* [0..159] IN/OUT */ +{ + + word z1 = S->z1; + longword L_z2 = S->L_z2; + word mp = S->mp; + + word s1; + longword L_s2; + + longword L_temp; + + word msp, lsp; + word SO; + + longword ltmp; /* for ADD */ + ulongword utmp; /* for L_ADD */ + + register int k = 160; + + while (k--) { + + /* 4.2.1 Downscaling of the input signal + */ + SO = SASR( *s, 3 ) << 2; + s++; + + assert (SO >= -0x4000); /* downscaled by */ + assert (SO <= 0x3FFC); /* previous routine. */ + + + /* 4.2.2 Offset compensation + * + * This part implements a high-pass filter and requires extended + * arithmetic precision for the recursive part of this filter. + * The input of this procedure is the array so[0...159] and the + * output the array sof[ 0...159 ]. + */ + /* Compute the non-recursive part + */ + + s1 = SO - z1; /* s1 = gsm_sub( *so, z1 ); */ + z1 = SO; + + assert(s1 != MIN_WORD); + + /* Compute the recursive part + */ + L_s2 = s1; + L_s2 <<= 15; + + /* Execution of a 31 bv 16 bits multiplication + */ + + msp = SASR( L_z2, 15 ); + lsp = L_z2-((longword)msp<<15); /* gsm_L_sub(L_z2,(msp<<15)); */ + + L_s2 += GSM_MULT_R( lsp, 32735 ); + L_temp = (longword)msp * 32735; /* GSM_L_MULT(msp,32735) >> 1;*/ + L_z2 = GSM_L_ADD( L_temp, L_s2 ); + + /* Compute sof[k] with rounding + */ + L_temp = GSM_L_ADD( L_z2, 16384 ); + + /* 4.2.3 Preemphasis + */ + + msp = GSM_MULT_R( mp, -28180 ); + mp = SASR( L_temp, 15 ); + *so++ = GSM_ADD( mp, msp ); + } + + S->z1 = z1; + S->L_z2 = L_z2; + S->mp = mp; +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/private.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/private.h Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,268 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/private.h,v 1.6 1996/07/02 10:15:26 jutta Exp $*/ + +#ifndef PRIVATE_H +#define PRIVATE_H + +typedef short word; /* 16 bit signed int */ +typedef long longword; /* 32 bit signed int */ + +typedef unsigned short uword; /* unsigned word */ +typedef unsigned long ulongword; /* unsigned longword */ + +struct gsm_state { + + word dp0[ 280 ]; + + word z1; /* preprocessing.c, Offset_com. */ + longword L_z2; /* Offset_com. */ + int mp; /* Preemphasis */ + + word u[8]; /* short_term_aly_filter.c */ + word LARpp[2][8]; /* */ + word j; /* */ + + word ltp_cut; /* long_term.c, LTP crosscorr. */ + word nrp; /* 40 */ /* long_term.c, synthesis */ + word v[9]; /* short_term.c, synthesis */ + word msr; /* decoder.c, Postprocessing */ + + char verbose; /* only used if !NDEBUG */ + char fast; /* only used if FAST */ + + char wav_fmt; /* only used if WAV49 defined */ + unsigned char frame_index; /* odd/even chaining */ + unsigned char frame_chain; /* half-byte to carry forward */ +}; + + +#define MIN_WORD (-32767 - 1) +#define MAX_WORD 32767 + +#define MIN_LONGWORD (-2147483647 - 1) +#define MAX_LONGWORD 2147483647 + +#ifdef SASR /* flag: >> is a signed arithmetic shift right */ +#undef SASR +#define SASR(x, by) ((x) >> (by)) +#else +#define SASR(x, by) ((x) >= 0 ? (x) >> (by) : (~(-((x) + 1) >> (by)))) +#endif /* SASR */ + +#include "proto.h" + +/* + * Prototypes from add.c + */ +extern word gsm_mult P((word a, word b)); +extern longword gsm_L_mult P((word a, word b)); +extern word gsm_mult_r P((word a, word b)); + +extern word gsm_div P((word num, word denum)); + +extern word gsm_add P(( word a, word b )); +extern longword gsm_L_add P(( longword a, longword b )); + +extern word gsm_sub P((word a, word b)); +extern longword gsm_L_sub P((longword a, longword b)); + +extern word gsm_abs P((word a)); + +extern word gsm_norm P(( longword a )); + +extern longword gsm_L_asl P((longword a, int n)); +extern word gsm_asl P((word a, int n)); + +extern longword gsm_L_asr P((longword a, int n)); +extern word gsm_asr P((word a, int n)); + +/* + * Inlined functions from add.h + */ + +/* + * #define GSM_MULT_R(a, b) (* word a, word b, !(a == b == MIN_WORD) *) \ + * (0x0FFFF & SASR(((longword)(a) * (longword)(b) + 16384), 15)) + */ +#define GSM_MULT_R(a, b) /* word a, word b, !(a == b == MIN_WORD) */ \ + (SASR( ((longword)(a) * (longword)(b) + 16384), 15 )) + +# define GSM_MULT(a,b) /* word a, word b, !(a == b == MIN_WORD) */ \ + (SASR( ((longword)(a) * (longword)(b)), 15 )) + +# define GSM_L_MULT(a, b) /* word a, word b */ \ + (((longword)(a) * (longword)(b)) << 1) + +# define GSM_L_ADD(a, b) \ + ( (a) < 0 ? ( (b) >= 0 ? (a) + (b) \ + : (utmp = (ulongword)-((a) + 1) + (ulongword)-((b) + 1)) \ + >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)utmp-2 ) \ + : ((b) <= 0 ? (a) + (b) \ + : (utmp = (ulongword)(a) + (ulongword)(b)) >= MAX_LONGWORD \ + ? MAX_LONGWORD : utmp)) + +/* + * # define GSM_ADD(a, b) \ + * ((ltmp = (longword)(a) + (longword)(b)) >= MAX_WORD \ + * ? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp) + */ +/* Nonportable, but faster: */ + +#define GSM_ADD(a, b) \ + ((ulongword)((ltmp = (longword)(a) + (longword)(b)) - MIN_WORD) > \ + MAX_WORD - MIN_WORD ? (ltmp > 0 ? MAX_WORD : MIN_WORD) : ltmp) + +# define GSM_SUB(a, b) \ + ((ltmp = (longword)(a) - (longword)(b)) >= MAX_WORD \ + ? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp) + +# define GSM_ABS(a) ((a) < 0 ? ((a) == MIN_WORD ? MAX_WORD : -(a)) : (a)) + +/* Use these if necessary: + +# define GSM_MULT_R(a, b) gsm_mult_r(a, b) +# define GSM_MULT(a, b) gsm_mult(a, b) +# define GSM_L_MULT(a, b) gsm_L_mult(a, b) + +# define GSM_L_ADD(a, b) gsm_L_add(a, b) +# define GSM_ADD(a, b) gsm_add(a, b) +# define GSM_SUB(a, b) gsm_sub(a, b) + +# define GSM_ABS(a) gsm_abs(a) + +*/ + +/* + * More prototypes from implementations.. + */ +extern void Gsm_Coder P(( + struct gsm_state * S, + word * s, /* [0..159] samples IN */ + word * LARc, /* [0..7] LAR coefficients OUT */ + word * Nc, /* [0..3] LTP lag OUT */ + word * bc, /* [0..3] coded LTP gain OUT */ + word * Mc, /* [0..3] RPE grid selection OUT */ + word * xmaxc,/* [0..3] Coded maximum amplitude OUT */ + word * xMc /* [13*4] normalized RPE samples OUT */)); + +extern void Gsm_Long_Term_Predictor P(( /* 4x for 160 samples */ + struct gsm_state * S, + word * d, /* [0..39] residual signal IN */ + word * dp, /* [-120..-1] d' IN */ + word * e, /* [0..40] OUT */ + word * dpp, /* [0..40] OUT */ + word * Nc, /* correlation lag OUT */ + word * bc /* gain factor OUT */)); + +extern void Gsm_LPC_Analysis P(( + struct gsm_state * S, + word * s, /* 0..159 signals IN/OUT */ + word * LARc)); /* 0..7 LARc's OUT */ + +extern void Gsm_Preprocess P(( + struct gsm_state * S, + word * s, word * so)); + +extern void Gsm_Encoding P(( + struct gsm_state * S, + word * e, + word * ep, + word * xmaxc, + word * Mc, + word * xMc)); + +extern void Gsm_Short_Term_Analysis_Filter P(( + struct gsm_state * S, + word * LARc, /* coded log area ratio [0..7] IN */ + word * d /* st res. signal [0..159] IN/OUT */)); + +extern void Gsm_Decoder P(( + struct gsm_state * S, + word * LARcr, /* [0..7] IN */ + word * Ncr, /* [0..3] IN */ + word * bcr, /* [0..3] IN */ + word * Mcr, /* [0..3] IN */ + word * xmaxcr, /* [0..3] IN */ + word * xMcr, /* [0..13*4] IN */ + word * s)); /* [0..159] OUT */ + +extern void Gsm_Decoding P(( + struct gsm_state * S, + word xmaxcr, + word Mcr, + word * xMcr, /* [0..12] IN */ + word * erp)); /* [0..39] OUT */ + +extern void Gsm_Long_Term_Synthesis_Filtering P(( + struct gsm_state* S, + word Ncr, + word bcr, + word * erp, /* [0..39] IN */ + word * drp)); /* [-120..-1] IN, [0..40] OUT */ + +void Gsm_RPE_Decoding P(( + struct gsm_state *S, + word xmaxcr, + word Mcr, + word * xMcr, /* [0..12], 3 bits IN */ + word * erp)); /* [0..39] OUT */ + +void Gsm_RPE_Encoding P(( + struct gsm_state * S, + word * e, /* -5..-1][0..39][40..44 IN/OUT */ + word * xmaxc, /* OUT */ + word * Mc, /* OUT */ + word * xMc)); /* [0..12] OUT */ + +extern void Gsm_Short_Term_Synthesis_Filter P(( + struct gsm_state * S, + word * LARcr, /* log area ratios [0..7] IN */ + word * drp, /* received d [0...39] IN */ + word * s)); /* signal s [0..159] OUT */ + +extern void Gsm_Update_of_reconstructed_short_time_residual_signal P(( + word * dpp, /* [0...39] IN */ + word * ep, /* [0...39] IN */ + word * dp)); /* [-120...-1] IN/OUT */ + +/* + * Tables from table.c + */ +#ifndef GSM_TABLE_C + +extern word gsm_A[8], gsm_B[8], gsm_MIC[8], gsm_MAC[8]; +extern word gsm_INVA[8]; +extern word gsm_DLB[4], gsm_QLB[4]; +extern word gsm_H[11]; +extern word gsm_NRFAC[8]; +extern word gsm_FAC[8]; + +#endif /* GSM_TABLE_C */ + +/* + * Debugging + */ +#ifdef NDEBUG + +# define gsm_debug_words(a, b, c, d) /* nil */ +# define gsm_debug_longwords(a, b, c, d) /* nil */ +# define gsm_debug_word(a, b) /* nil */ +# define gsm_debug_longword(a, b) /* nil */ + +#else /* !NDEBUG => DEBUG */ + + extern void gsm_debug_words P((char * name, int, int, word *)); + extern void gsm_debug_longwords P((char * name, int, int, longword *)); + extern void gsm_debug_longword P((char * name, longword)); + extern void gsm_debug_word P((char * name, word)); + +#endif /* !NDEBUG */ + +#include "unproto.h" + +#endif /* PRIVATE_H */ diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/proto.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/proto.h Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,65 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/proto.h,v 1.1 1992/10/28 00:11:08 jutta Exp $*/ + +#ifndef PROTO_H +#define PROTO_H + +#if __cplusplus +# define NeedFunctionPrototypes 1 +#endif + +#if __STDC__ +# define NeedFunctionPrototypes 1 +#endif + +#ifdef _NO_PROTO +# undef NeedFunctionPrototypes +#endif + +#undef P /* gnu stdio.h actually defines this... */ +#undef P0 +#undef P1 +#undef P2 +#undef P3 +#undef P4 +#undef P5 +#undef P6 +#undef P7 +#undef P8 + +#if NeedFunctionPrototypes + +# define P( protos ) protos + +# define P0() (void) +# define P1(x, a) (a) +# define P2(x, a, b) (a, b) +# define P3(x, a, b, c) (a, b, c) +# define P4(x, a, b, c, d) (a, b, c, d) +# define P5(x, a, b, c, d, e) (a, b, c, d, e) +# define P6(x, a, b, c, d, e, f) (a, b, c, d, e, f) +# define P7(x, a, b, c, d, e, f, g) (a, b, c, d, e, f, g) +# define P8(x, a, b, c, d, e, f, g, h) (a, b, c, d, e, f, g, h) + +#else /* !NeedFunctionPrototypes */ + +# define P( protos ) ( /* protos */ ) + +# define P0() () +# define P1(x, a) x a; +# define P2(x, a, b) x a; b; +# define P3(x, a, b, c) x a; b; c; +# define P4(x, a, b, c, d) x a; b; c; d; +# define P5(x, a, b, c, d, e) x a; b; c; d; e; +# define P6(x, a, b, c, d, e, f) x a; b; c; d; e; f; +# define P7(x, a, b, c, d, e, f, g) x a; b; c; d; e; f; g; +# define P8(x, a, b, c, d, e, f, g, h) x a; b; c; d; e; f; g; h; + +#endif /* !NeedFunctionPrototypes */ + +#endif /* PROTO_H */ diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/rpe.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/rpe.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,496 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/rpe.c,v 1.3 1994/05/10 20:18:46 jutta Exp $ */ + +#include +#include + +#include "private.h" + +#include "gsm.h" +#include "proto.h" + +/* 4.2.13 .. 4.2.17 RPE ENCODING SECTION + */ + +/* 4.2.13 */ + +static void Weighting_filter P2((e, x), + register word * e, /* signal [-5..0.39.44] IN */ + word * x /* signal [0..39] OUT */ +) +/* + * The coefficients of the weighting filter are stored in a table + * (see table 4.4). The following scaling is used: + * + * H[0..10] = integer( real_H[ 0..10] * 8192 ); + */ +{ + /* word wt[ 50 ]; */ + + register longword L_result; + register int k /* , i */ ; + + /* Initialization of a temporary working array wt[0...49] + */ + + /* for (k = 0; k <= 4; k++) wt[k] = 0; + * for (k = 5; k <= 44; k++) wt[k] = *e++; + * for (k = 45; k <= 49; k++) wt[k] = 0; + * + * (e[-5..-1] and e[40..44] are allocated by the caller, + * are initially zero and are not written anywhere.) + */ + e -= 5; + + /* Compute the signal x[0..39] + */ + for (k = 0; k <= 39; k++) { + + L_result = 8192 >> 1; + + /* for (i = 0; i <= 10; i++) { + * L_temp = GSM_L_MULT( wt[k+i], gsm_H[i] ); + * L_result = GSM_L_ADD( L_result, L_temp ); + * } + */ + +#undef STEP +#define STEP( i, H ) (e[ k + i ] * (longword)H) + + /* Every one of these multiplications is done twice -- + * but I don't see an elegant way to optimize this. + * Do you? + */ + +#ifdef STUPID_COMPILER + L_result += STEP( 0, -134 ) ; + L_result += STEP( 1, -374 ) ; + /* + STEP( 2, 0 ) */ + L_result += STEP( 3, 2054 ) ; + L_result += STEP( 4, 5741 ) ; + L_result += STEP( 5, 8192 ) ; + L_result += STEP( 6, 5741 ) ; + L_result += STEP( 7, 2054 ) ; + /* + STEP( 8, 0 ) */ + L_result += STEP( 9, -374 ) ; + L_result += STEP( 10, -134 ) ; +#else + L_result += + STEP( 0, -134 ) + + STEP( 1, -374 ) + /* + STEP( 2, 0 ) */ + + STEP( 3, 2054 ) + + STEP( 4, 5741 ) + + STEP( 5, 8192 ) + + STEP( 6, 5741 ) + + STEP( 7, 2054 ) + /* + STEP( 8, 0 ) */ + + STEP( 9, -374 ) + + STEP(10, -134 ) + ; +#endif + + /* L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x2) *) + * L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x4) *) + * + * x[k] = SASR( L_result, 16 ); + */ + + /* 2 adds vs. >>16 => 14, minus one shift to compensate for + * those we lost when replacing L_MULT by '*'. + */ + + L_result = SASR( L_result, 13 ); + x[k] = ( L_result < MIN_WORD ? MIN_WORD + : (L_result > MAX_WORD ? MAX_WORD : L_result )); + } +} + +/* 4.2.14 */ + +static void RPE_grid_selection P3((x,xM,Mc_out), + word * x, /* [0..39] IN */ + word * xM, /* [0..12] OUT */ + word * Mc_out /* OUT */ +) +/* + * The signal x[0..39] is used to select the RPE grid which is + * represented by Mc. + */ +{ + /* register word temp1; */ + register int /* m, */ i; + register longword L_result, L_temp; + longword EM; /* xxx should be L_EM? */ + word Mc; + + longword L_common_0_3; + + EM = 0; + Mc = 0; + + /* for (m = 0; m <= 3; m++) { + * L_result = 0; + * + * + * for (i = 0; i <= 12; i++) { + * + * temp1 = SASR( x[m + 3*i], 2 ); + * + * assert(temp1 != MIN_WORD); + * + * L_temp = GSM_L_MULT( temp1, temp1 ); + * L_result = GSM_L_ADD( L_temp, L_result ); + * } + * + * if (L_result > EM) { + * Mc = m; + * EM = L_result; + * } + * } + */ + +#undef STEP +#define STEP( m, i ) L_temp = SASR( x[m + 3 * i], 2 ); \ + L_result += L_temp * L_temp; + + /* common part of 0 and 3 */ + + L_result = 0; + STEP( 0, 1 ); STEP( 0, 2 ); STEP( 0, 3 ); STEP( 0, 4 ); + STEP( 0, 5 ); STEP( 0, 6 ); STEP( 0, 7 ); STEP( 0, 8 ); + STEP( 0, 9 ); STEP( 0, 10); STEP( 0, 11); STEP( 0, 12); + L_common_0_3 = L_result; + + /* i = 0 */ + + STEP( 0, 0 ); + L_result <<= 1; /* implicit in L_MULT */ + EM = L_result; + + /* i = 1 */ + + L_result = 0; + STEP( 1, 0 ); + STEP( 1, 1 ); STEP( 1, 2 ); STEP( 1, 3 ); STEP( 1, 4 ); + STEP( 1, 5 ); STEP( 1, 6 ); STEP( 1, 7 ); STEP( 1, 8 ); + STEP( 1, 9 ); STEP( 1, 10); STEP( 1, 11); STEP( 1, 12); + L_result <<= 1; + if (L_result > EM) { + Mc = 1; + EM = L_result; + } + + /* i = 2 */ + + L_result = 0; + STEP( 2, 0 ); + STEP( 2, 1 ); STEP( 2, 2 ); STEP( 2, 3 ); STEP( 2, 4 ); + STEP( 2, 5 ); STEP( 2, 6 ); STEP( 2, 7 ); STEP( 2, 8 ); + STEP( 2, 9 ); STEP( 2, 10); STEP( 2, 11); STEP( 2, 12); + L_result <<= 1; + if (L_result > EM) { + Mc = 2; + EM = L_result; + } + + /* i = 3 */ + + L_result = L_common_0_3; + STEP( 3, 12 ); + L_result <<= 1; + if (L_result > EM) { + Mc = 3; + EM = L_result; + } + + /**/ + + /* Down-sampling by a factor 3 to get the selected xM[0..12] + * RPE sequence. + */ + for (i = 0; i <= 12; i ++) xM[i] = x[Mc + 3*i]; + *Mc_out = Mc; +} + +/* 4.12.15 */ + +static void APCM_quantization_xmaxc_to_exp_mant P3((xmaxc,exp_out,mant_out), + word xmaxc, /* IN */ + word * exp_out, /* OUT */ + word * mant_out ) /* OUT */ +{ + word exp, mant; + + /* Compute exponent and mantissa of the decoded version of xmaxc + */ + + exp = 0; + if (xmaxc > 15) exp = SASR(xmaxc, 3) - 1; + mant = xmaxc - (exp << 3); + + if (mant == 0) { + exp = -4; + mant = 7; + } + else { + while (mant <= 7) { + mant = mant << 1 | 1; + exp--; + } + mant -= 8; + } + + assert( exp >= -4 && exp <= 6 ); + assert( mant >= 0 && mant <= 7 ); + + *exp_out = exp; + *mant_out = mant; +} + +static void APCM_quantization P5((xM,xMc,mant_out,exp_out,xmaxc_out), + word * xM, /* [0..12] IN */ + + word * xMc, /* [0..12] OUT */ + word * mant_out, /* OUT */ + word * exp_out, /* OUT */ + word * xmaxc_out /* OUT */ +) +{ + int i, itest; + + word xmax, xmaxc, temp, temp1, temp2; + word exp, mant; + + + /* Find the maximum absolute value xmax of xM[0..12]. + */ + + xmax = 0; + for (i = 0; i <= 12; i++) { + temp = xM[i]; + temp = GSM_ABS(temp); + if (temp > xmax) xmax = temp; + } + + /* Qantizing and coding of xmax to get xmaxc. + */ + + exp = 0; + temp = SASR( xmax, 9 ); + itest = 0; + + for (i = 0; i <= 5; i++) { + + itest |= (temp <= 0); + temp = SASR( temp, 1 ); + + assert(exp <= 5); + if (itest == 0) exp++; /* exp = add (exp, 1) */ + } + + assert(exp <= 6 && exp >= 0); + temp = exp + 5; + + assert(temp <= 11 && temp >= 0); + xmaxc = gsm_add( SASR(xmax, temp), exp << 3 ); + + /* Quantizing and coding of the xM[0..12] RPE sequence + * to get the xMc[0..12] + */ + + APCM_quantization_xmaxc_to_exp_mant( xmaxc, &exp, &mant ); + + /* This computation uses the fact that the decoded version of xmaxc + * can be calculated by using the exponent and the mantissa part of + * xmaxc (logarithmic table). + * So, this method avoids any division and uses only a scaling + * of the RPE samples by a function of the exponent. A direct + * multiplication by the inverse of the mantissa (NRFAC[0..7] + * found in table 4.5) gives the 3 bit coded version xMc[0..12] + * of the RPE samples. + */ + + + /* Direct computation of xMc[0..12] using table 4.5 + */ + + assert( exp <= 4096 && exp >= -4096); + assert( mant >= 0 && mant <= 7 ); + + temp1 = 6 - exp; /* normalization by the exponent */ + temp2 = gsm_NRFAC[ mant ]; /* inverse mantissa */ + + for (i = 0; i <= 12; i++) { + + assert(temp1 >= 0 && temp1 < 16); + + temp = xM[i] << temp1; + temp = GSM_MULT( temp, temp2 ); + temp = SASR(temp, 12); + xMc[i] = temp + 4; /* see note below */ + } + + /* NOTE: This equation is used to make all the xMc[i] positive. + */ + + *mant_out = mant; + *exp_out = exp; + *xmaxc_out = xmaxc; +} + +/* 4.2.16 */ + +static void APCM_inverse_quantization P4((xMc,mant,exp,xMp), + register word * xMc, /* [0..12] IN */ + word mant, + word exp, + register word * xMp) /* [0..12] OUT */ +/* + * This part is for decoding the RPE sequence of coded xMc[0..12] + * samples to obtain the xMp[0..12] array. Table 4.6 is used to get + * the mantissa of xmaxc (FAC[0..7]). + */ +{ + int i; + word temp, temp1, temp2, temp3; + longword ltmp; + + assert( mant >= 0 && mant <= 7 ); + + temp1 = gsm_FAC[ mant ]; /* see 4.2-15 for mant */ + temp2 = gsm_sub( 6, exp ); /* see 4.2-15 for exp */ + temp3 = gsm_asl( 1, gsm_sub( temp2, 1 )); + + for (i = 13; i--;) { + + assert( *xMc <= 7 && *xMc >= 0 ); /* 3 bit unsigned */ + + /* temp = gsm_sub( *xMc++ << 1, 7 ); */ + temp = (*xMc++ << 1) - 7; /* restore sign */ + assert( temp <= 7 && temp >= -7 ); /* 4 bit signed */ + + temp <<= 12; /* 16 bit signed */ + temp = GSM_MULT_R( temp1, temp ); + temp = GSM_ADD( temp, temp3 ); + *xMp++ = gsm_asr( temp, temp2 ); + } +} + +/* 4.2.17 */ + +static void RPE_grid_positioning P3((Mc,xMp,ep), + word Mc, /* grid position IN */ + register word * xMp, /* [0..12] IN */ + register word * ep /* [0..39] OUT */ +) +/* + * This procedure computes the reconstructed long term residual signal + * ep[0..39] for the LTP analysis filter. The inputs are the Mc + * which is the grid position selection and the xMp[0..12] decoded + * RPE samples which are upsampled by a factor of 3 by inserting zero + * values. + */ +{ + int i = 13; + + assert(0 <= Mc && Mc <= 3); + + switch (Mc) { + case 3: *ep++ = 0; + case 2: do { + *ep++ = 0; + case 1: *ep++ = 0; + case 0: *ep++ = *xMp++; + } while (--i); + } + while (++Mc < 4) *ep++ = 0; + + /* + + int i, k; + for (k = 0; k <= 39; k++) ep[k] = 0; + for (i = 0; i <= 12; i++) { + ep[ Mc + (3*i) ] = xMp[i]; + } + */ +} + +/* 4.2.18 */ + +/* This procedure adds the reconstructed long term residual signal + * ep[0..39] to the estimated signal dpp[0..39] from the long term + * analysis filter to compute the reconstructed short term residual + * signal dp[-40..-1]; also the reconstructed short term residual + * array dp[-120..-41] is updated. + */ + +#if 0 /* Has been inlined in code.c */ +void Gsm_Update_of_reconstructed_short_time_residual_signal P3((dpp, ep, dp), + word * dpp, /* [0...39] IN */ + word * ep, /* [0...39] IN */ + word * dp) /* [-120...-1] IN/OUT */ +{ + int k; + + for (k = 0; k <= 79; k++) + dp[ -120 + k ] = dp[ -80 + k ]; + + for (k = 0; k <= 39; k++) + dp[ -40 + k ] = gsm_add( ep[k], dpp[k] ); +} +#endif /* Has been inlined in code.c */ + +void Gsm_RPE_Encoding P5((S,e,xmaxc,Mc,xMc), + + struct gsm_state * S, + + word * e, /* -5..-1][0..39][40..44 IN/OUT */ + word * xmaxc, /* OUT */ + word * Mc, /* OUT */ + word * xMc) /* [0..12] OUT */ +{ + word x[40]; + word xM[13], xMp[13]; + word mant, exp; +// Wirlab + (void)S; + + + + Weighting_filter(e, x); + RPE_grid_selection(x, xM, Mc); + + APCM_quantization( xM, xMc, &mant, &exp, xmaxc); + APCM_inverse_quantization( xMc, mant, exp, xMp); + + RPE_grid_positioning( *Mc, xMp, e ); + +} + +void Gsm_RPE_Decoding P5((S, xmaxcr, Mcr, xMcr, erp), + struct gsm_state * S, + + word xmaxcr, + word Mcr, + word * xMcr, /* [0..12], 3 bits IN */ + word * erp /* [0..39] OUT */ +) +{ + word exp, mant; + word xMp[ 13 ]; +// Wirlab + (void)S; + + + + APCM_quantization_xmaxc_to_exp_mant( xmaxcr, &exp, &mant ); + APCM_inverse_quantization( xMcr, mant, exp, xMp ); + RPE_grid_positioning( Mcr, xMp, erp ); + +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/short_term.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/short_term.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,429 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/short_term.c,v 1.2 1994/05/10 20:18:47 jutta Exp $ */ + +#include +#include + +#include "private.h" + +#include "gsm.h" +#include "proto.h" + +/* + * SHORT TERM ANALYSIS FILTERING SECTION + */ + +/* 4.2.8 */ + +static void Decoding_of_the_coded_Log_Area_Ratios P2((LARc,LARpp), + word * LARc, /* coded log area ratio [0..7] IN */ + word * LARpp) /* out: decoded .. */ +{ + register word temp1 /* , temp2 */; + register long ltmp; /* for GSM_ADD */ + + /* This procedure requires for efficient implementation + * two tables. + * + * INVA[1..8] = integer( (32768 * 8) / real_A[1..8]) + * MIC[1..8] = minimum value of the LARc[1..8] + */ + + /* Compute the LARpp[1..8] + */ + + /* for (i = 1; i <= 8; i++, B++, MIC++, INVA++, LARc++, LARpp++) { + * + * temp1 = GSM_ADD( *LARc, *MIC ) << 10; + * temp2 = *B << 1; + * temp1 = GSM_SUB( temp1, temp2 ); + * + * assert(*INVA != MIN_WORD); + * + * temp1 = GSM_MULT_R( *INVA, temp1 ); + * *LARpp = GSM_ADD( temp1, temp1 ); + * } + */ + +#undef STEP +#define STEP( B, MIC, INVA ) \ + temp1 = GSM_ADD( *LARc++, MIC ) << 10; \ + temp1 = GSM_SUB( temp1, B << 1 ); \ + temp1 = GSM_MULT_R( INVA, temp1 ); \ + *LARpp++ = GSM_ADD( temp1, temp1 ); + + STEP( 0, -32, 13107 ); + STEP( 0, -32, 13107 ); + STEP( 2048, -16, 13107 ); + STEP( -2560, -16, 13107 ); + + STEP( 94, -8, 19223 ); + STEP( -1792, -8, 17476 ); + STEP( -341, -4, 31454 ); + STEP( -1144, -4, 29708 ); + + /* NOTE: the addition of *MIC is used to restore + * the sign of *LARc. + */ +} + +/* 4.2.9 */ +/* Computation of the quantized reflection coefficients + */ + +/* 4.2.9.1 Interpolation of the LARpp[1..8] to get the LARp[1..8] + */ + +/* + * Within each frame of 160 analyzed speech samples the short term + * analysis and synthesis filters operate with four different sets of + * coefficients, derived from the previous set of decoded LARs(LARpp(j-1)) + * and the actual set of decoded LARs (LARpp(j)) + * + * (Initial value: LARpp(j-1)[1..8] = 0.) + */ + +static void Coefficients_0_12 P3((LARpp_j_1, LARpp_j, LARp), + register word * LARpp_j_1, + register word * LARpp_j, + register word * LARp) +{ + register int i; + register longword ltmp; + + for (i = 1; i <= 8; i++, LARp++, LARpp_j_1++, LARpp_j++) { + *LARp = GSM_ADD( SASR( *LARpp_j_1, 2 ), SASR( *LARpp_j, 2 )); + *LARp = GSM_ADD( *LARp, SASR( *LARpp_j_1, 1)); + } +} + +static void Coefficients_13_26 P3((LARpp_j_1, LARpp_j, LARp), + register word * LARpp_j_1, + register word * LARpp_j, + register word * LARp) +{ + register int i; + register longword ltmp; + for (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) { + *LARp = GSM_ADD( SASR( *LARpp_j_1, 1), SASR( *LARpp_j, 1 )); + } +} + +static void Coefficients_27_39 P3((LARpp_j_1, LARpp_j, LARp), + register word * LARpp_j_1, + register word * LARpp_j, + register word * LARp) +{ + register int i; + register longword ltmp; + + for (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) { + *LARp = GSM_ADD( SASR( *LARpp_j_1, 2 ), SASR( *LARpp_j, 2 )); + *LARp = GSM_ADD( *LARp, SASR( *LARpp_j, 1 )); + } +} + + +static void Coefficients_40_159 P2((LARpp_j, LARp), + register word * LARpp_j, + register word * LARp) +{ + register int i; + + for (i = 1; i <= 8; i++, LARp++, LARpp_j++) + *LARp = *LARpp_j; +} + +/* 4.2.9.2 */ + +static void LARp_to_rp P1((LARp), + register word * LARp) /* [0..7] IN/OUT */ +/* + * The input of this procedure is the interpolated LARp[0..7] array. + * The reflection coefficients, rp[i], are used in the analysis + * filter and in the synthesis filter. + */ +{ + register int i; + register word temp; + register longword ltmp; + + for (i = 1; i <= 8; i++, LARp++) { + + /* temp = GSM_ABS( *LARp ); + * + * if (temp < 11059) temp <<= 1; + * else if (temp < 20070) temp += 11059; + * else temp = GSM_ADD( temp >> 2, 26112 ); + * + * *LARp = *LARp < 0 ? -temp : temp; + */ + + if (*LARp < 0) { + temp = *LARp == MIN_WORD ? MAX_WORD : -(*LARp); + *LARp = - ((temp < 11059) ? temp << 1 + : ((temp < 20070) ? temp + 11059 + : GSM_ADD( temp >> 2, 26112 ))); + } else { + temp = *LARp; + *LARp = (temp < 11059) ? temp << 1 + : ((temp < 20070) ? temp + 11059 + : GSM_ADD( temp >> 2, 26112 )); + } + } +} + + +/* 4.2.10 */ +static void Short_term_analysis_filtering P4((S,rp,k_n,s), + struct gsm_state * S, + register word * rp, /* [0..7] IN */ + register int k_n, /* k_end - k_start */ + register word * s /* [0..n-1] IN/OUT */ +) +/* + * This procedure computes the short term residual signal d[..] to be fed + * to the RPE-LTP loop from the s[..] signal and from the local rp[..] + * array (quantized reflection coefficients). As the call of this + * procedure can be done in many ways (see the interpolation of the LAR + * coefficient), it is assumed that the computation begins with index + * k_start (for arrays d[..] and s[..]) and stops with index k_end + * (k_start and k_end are defined in 4.2.9.1). This procedure also + * needs to keep the array u[0..7] in memory for each call. + */ +{ + register word * u = S->u; + register int i; + register word di, zzz, ui, sav, rpi; + register longword ltmp; + + for (; k_n--; s++) { + + di = sav = *s; + + for (i = 0; i < 8; i++) { /* YYY */ + + ui = u[i]; + rpi = rp[i]; + u[i] = sav; + + zzz = GSM_MULT_R(rpi, di); + sav = GSM_ADD( ui, zzz); + + zzz = GSM_MULT_R(rpi, ui); + di = GSM_ADD( di, zzz ); + } + + *s = di; + } +} + +#if defined(USE_FLOAT_MUL) && defined(FAST) + +static void Fast_Short_term_analysis_filtering P4((S,rp,k_n,s), + struct gsm_state * S, + register word * rp, /* [0..7] IN */ + register int k_n, /* k_end - k_start */ + register word * s /* [0..n-1] IN/OUT */ +) +{ + register word * u = S->u; + register int i; + + float uf[8], + rpf[8]; + + register float scalef = 3.0517578125e-5; + register float sav, di, temp; + + for (i = 0; i < 8; ++i) { + uf[i] = u[i]; + rpf[i] = rp[i] * scalef; + } + for (; k_n--; s++) { + sav = di = *s; + for (i = 0; i < 8; ++i) { + register float rpfi = rpf[i]; + register float ufi = uf[i]; + + uf[i] = sav; + temp = rpfi * di + ufi; + di += rpfi * ufi; + sav = temp; + } + *s = di; + } + for (i = 0; i < 8; ++i) u[i] = uf[i]; +} +#endif /* ! (defined (USE_FLOAT_MUL) && defined (FAST)) */ + +static void Short_term_synthesis_filtering P5((S,rrp,k,wt,sr), + struct gsm_state * S, + register word * rrp, /* [0..7] IN */ + register int k, /* k_end - k_start */ + register word * wt, /* [0..k-1] IN */ + register word * sr /* [0..k-1] OUT */ +) +{ + register word * v = S->v; + register int i; + register word sri, tmp1, tmp2; + register longword ltmp; /* for GSM_ADD & GSM_SUB */ + + while (k--) { + sri = *wt++; + for (i = 8; i--;) { + + /* sri = GSM_SUB( sri, gsm_mult_r( rrp[i], v[i] ) ); + */ + tmp1 = rrp[i]; + tmp2 = v[i]; + tmp2 = ( tmp1 == MIN_WORD && tmp2 == MIN_WORD + ? MAX_WORD + : 0x0FFFF & (( (longword)tmp1 * (longword)tmp2 + + 16384) >> 15)) ; + + sri = GSM_SUB( sri, tmp2 ); + + /* v[i+1] = GSM_ADD( v[i], gsm_mult_r( rrp[i], sri ) ); + */ + tmp1 = ( tmp1 == MIN_WORD && sri == MIN_WORD + ? MAX_WORD + : 0x0FFFF & (( (longword)tmp1 * (longword)sri + + 16384) >> 15)) ; + + v[i+1] = GSM_ADD( v[i], tmp1); + } + *sr++ = v[0] = sri; + } +} + + +#if defined(FAST) && defined(USE_FLOAT_MUL) + +static void Fast_Short_term_synthesis_filtering P5((S,rrp,k,wt,sr), + struct gsm_state * S, + register word * rrp, /* [0..7] IN */ + register int k, /* k_end - k_start */ + register word * wt, /* [0..k-1] IN */ + register word * sr /* [0..k-1] OUT */ +) +{ + register word * v = S->v; + register int i; + + float va[9], rrpa[8]; + register float scalef = 3.0517578125e-5, temp; + + for (i = 0; i < 8; ++i) { + va[i] = v[i]; + rrpa[i] = (float)rrp[i] * scalef; + } + while (k--) { + register float sri = *wt++; + for (i = 8; i--;) { + sri -= rrpa[i] * va[i]; + if (sri < -32768.) sri = -32768.; + else if (sri > 32767.) sri = 32767.; + + temp = va[i] + rrpa[i] * sri; + if (temp < -32768.) temp = -32768.; + else if (temp > 32767.) temp = 32767.; + va[i+1] = temp; + } + *sr++ = va[0] = sri; + } + for (i = 0; i < 9; ++i) v[i] = va[i]; +} + +#endif /* defined(FAST) && defined(USE_FLOAT_MUL) */ + +void Gsm_Short_Term_Analysis_Filter P3((S,LARc,s), + + struct gsm_state * S, + + word * LARc, /* coded log area ratio [0..7] IN */ + word * s /* signal [0..159] IN/OUT */ +) +{ + word * LARpp_j = S->LARpp[ S->j ]; + word * LARpp_j_1 = S->LARpp[ S->j ^= 1 ]; + + word LARp[8]; + +#undef FILTER +#if defined(FAST) && defined(USE_FLOAT_MUL) +# define FILTER (* (S->fast \ + ? Fast_Short_term_analysis_filtering \ + : Short_term_analysis_filtering )) + +#else +# define FILTER Short_term_analysis_filtering +#endif + + Decoding_of_the_coded_Log_Area_Ratios( LARc, LARpp_j ); + + Coefficients_0_12( LARpp_j_1, LARpp_j, LARp ); + LARp_to_rp( LARp ); + FILTER( S, LARp, 13, s); + + Coefficients_13_26( LARpp_j_1, LARpp_j, LARp); + LARp_to_rp( LARp ); + FILTER( S, LARp, 14, s + 13); + + Coefficients_27_39( LARpp_j_1, LARpp_j, LARp); + LARp_to_rp( LARp ); + FILTER( S, LARp, 13, s + 27); + + Coefficients_40_159( LARpp_j, LARp); + LARp_to_rp( LARp ); + FILTER( S, LARp, 120, s + 40); +} + +void Gsm_Short_Term_Synthesis_Filter P4((S, LARcr, wt, s), + struct gsm_state * S, + + word * LARcr, /* received log area ratios [0..7] IN */ + word * wt, /* received d [0..159] IN */ + + word * s /* signal s [0..159] OUT */ +) +{ + word * LARpp_j = S->LARpp[ S->j ]; + word * LARpp_j_1 = S->LARpp[ S->j ^=1 ]; + + word LARp[8]; + +#undef FILTER +#if defined(FAST) && defined(USE_FLOAT_MUL) + +# define FILTER (* (S->fast \ + ? Fast_Short_term_synthesis_filtering \ + : Short_term_synthesis_filtering )) +#else +# define FILTER Short_term_synthesis_filtering +#endif + + Decoding_of_the_coded_Log_Area_Ratios( LARcr, LARpp_j ); + + Coefficients_0_12( LARpp_j_1, LARpp_j, LARp ); + LARp_to_rp( LARp ); + FILTER( S, LARp, 13, wt, s ); + + Coefficients_13_26( LARpp_j_1, LARpp_j, LARp); + LARp_to_rp( LARp ); + FILTER( S, LARp, 14, wt + 13, s + 13 ); + + Coefficients_27_39( LARpp_j_1, LARpp_j, LARp); + LARp_to_rp( LARp ); + FILTER( S, LARp, 13, wt + 27, s + 27 ); + + Coefficients_40_159( LARpp_j, LARp ); + LARp_to_rp( LARp ); + FILTER(S, LARp, 120, wt + 40, s + 40); +} diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/table.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/table.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,63 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/table.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */ + +/* Most of these tables are inlined at their point of use. + */ + +/* 4.4 TABLES USED IN THE FIXED POINT IMPLEMENTATION OF THE RPE-LTP + * CODER AND DECODER + * + * (Most of them inlined, so watch out.) + */ + +#define GSM_TABLE_C +#include "private.h" +#include "gsm.h" + +/* Table 4.1 Quantization of the Log.-Area Ratios + */ +/* i 1 2 3 4 5 6 7 8 */ +word gsm_A[8] = {20480, 20480, 20480, 20480, 13964, 15360, 8534, 9036}; +word gsm_B[8] = { 0, 0, 2048, -2560, 94, -1792, -341, -1144}; +word gsm_MIC[8] = { -32, -32, -16, -16, -8, -8, -4, -4 }; +word gsm_MAC[8] = { 31, 31, 15, 15, 7, 7, 3, 3 }; + + +/* Table 4.2 Tabulation of 1/A[1..8] + */ +word gsm_INVA[8]={ 13107, 13107, 13107, 13107, 19223, 17476, 31454, 29708 }; + + +/* Table 4.3a Decision level of the LTP gain quantizer + */ +/* bc 0 1 2 3 */ +word gsm_DLB[4] = { 6554, 16384, 26214, 32767 }; + + +/* Table 4.3b Quantization levels of the LTP gain quantizer + */ +/* bc 0 1 2 3 */ +word gsm_QLB[4] = { 3277, 11469, 21299, 32767 }; + + +/* Table 4.4 Coefficients of the weighting filter + */ +/* i 0 1 2 3 4 5 6 7 8 9 10 */ +word gsm_H[11] = {-134, -374, 0, 2054, 5741, 8192, 5741, 2054, 0, -374, -134 }; + + +/* Table 4.5 Normalized inverse mantissa used to compute xM/xmax + */ +/* i 0 1 2 3 4 5 6 7 */ +word gsm_NRFAC[8] = { 29128, 26215, 23832, 21846, 20165, 18725, 17476, 16384 }; + + +/* Table 4.6 Normalized direct mantissa used to compute xM/xmax + */ +/* i 0 1 2 3 4 5 6 7 */ +word gsm_FAC[8] = { 18431, 20479, 22527, 24575, 26623, 28671, 30719, 32767 }; diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/toast_alaw.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/toast_alaw.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,334 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /home/kbs/jutta/src/gsm/gsm-1.0/src/RCS/toast_alaw.c,v 1.2 1996/07/05 17:23:46 jutta Exp $ */ + +#include "toast.h" + +/* toast_alaw.c -- manipulate A-law encoded sound. + */ + +extern FILE * in, * out; + +#define A2S(x) (a2s[ (unsigned char )(x) ]) +#define S2A(x) (s2a[ ((unsigned short)(x)) >> 4 ]) + +static unsigned short a2s[] = { + + 60032, 60288, 59520, 59776, 61056, 61312, 60544, 60800, + 57984, 58240, 57472, 57728, 59008, 59264, 58496, 58752, + 62784, 62912, 62528, 62656, 63296, 63424, 63040, 63168, + 61760, 61888, 61504, 61632, 62272, 62400, 62016, 62144, + 43520, 44544, 41472, 42496, 47616, 48640, 45568, 46592, + 35328, 36352, 33280, 34304, 39424, 40448, 37376, 38400, + 54528, 55040, 53504, 54016, 56576, 57088, 55552, 56064, + 50432, 50944, 49408, 49920, 52480, 52992, 51456, 51968, + 65192, 65208, 65160, 65176, 65256, 65272, 65224, 65240, + 65064, 65080, 65032, 65048, 65128, 65144, 65096, 65112, + 65448, 65464, 65416, 65432, 65512, 65528, 65480, 65496, + 65320, 65336, 65288, 65304, 65384, 65400, 65352, 65368, + 64160, 64224, 64032, 64096, 64416, 64480, 64288, 64352, + 63648, 63712, 63520, 63584, 63904, 63968, 63776, 63840, + 64848, 64880, 64784, 64816, 64976, 65008, 64912, 64944, + 64592, 64624, 64528, 64560, 64720, 64752, 64656, 64688, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848 + +}; + + +static unsigned char s2a[] = { + + 213,212,215,214,209,208,211,210,221,220,223,222,217,216,219,218, + 197,196,199,198,193,192,195,194,205,204,207,206,201,200,203,202, + 245,245,244,244,247,247,246,246,241,241,240,240,243,243,242,242, + 253,253,252,252,255,255,254,254,249,249,248,248,251,251,250,250, + 229,229,229,229,228,228,228,228,231,231,231,231,230,230,230,230, + 225,225,225,225,224,224,224,224,227,227,227,227,226,226,226,226, + 237,237,237,237,236,236,236,236,239,239,239,239,238,238,238,238, + 233,233,233,233,232,232,232,232,235,235,235,235,234,234,234,234, + 149,149,149,149,149,149,149,149,148,148,148,148,148,148,148,148, + 151,151,151,151,151,151,151,151,150,150,150,150,150,150,150,150, + 145,145,145,145,145,145,145,145,144,144,144,144,144,144,144,144, + 147,147,147,147,147,147,147,147,146,146,146,146,146,146,146,146, + 157,157,157,157,157,157,157,157,156,156,156,156,156,156,156,156, + 159,159,159,159,159,159,159,159,158,158,158,158,158,158,158,158, + 153,153,153,153,153,153,153,153,152,152,152,152,152,152,152,152, + 155,155,155,155,155,155,155,155,154,154,154,154,154,154,154,154, + 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, + 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, + 135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135, + 134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131, + 130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130, + 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, + 140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, + 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, + 142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142, + 137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, + 136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, + 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, + 138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138, + 181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, + 181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, + 180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, + 180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, + 183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183, + 183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183, + 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, + 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, + 177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, + 177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, + 176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, + 176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, + 179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179, + 179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179, + 178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, + 178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, + 189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, + 189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, + 188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188, + 188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188, + 191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, + 191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, + 190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190, + 190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190, + 185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, + 185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, + 184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, + 184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, + 187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187, + 187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187, + 186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186, + 186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, + 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, + 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, + 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, + 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, + 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, + 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, + 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, + 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, + 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, + 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, + 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, + 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, + 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, + 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, + 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, + 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, + 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, + 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, + 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, + 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, + 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, + 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, + 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, + 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, + 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, + 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, + 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, + 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, + 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, + 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, + 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, + 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, + 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, + 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, + 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, + 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, + 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, + 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, + 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, + 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, + 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, + 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, + 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, + 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, + 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, + 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, + 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, + 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, + 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, + 106,106,106,106,107,107,107,107,104,104,104,104,105,105,105,105, + 110,110,110,110,111,111,111,111,108,108,108,108,109,109,109,109, + 98, 98, 98, 98, 99, 99, 99, 99, 96, 96, 96, 96, 97, 97, 97, 97, + 102,102,102,102,103,103,103,103,100,100,100,100,101,101,101,101, + 122,122,123,123,120,120,121,121,126,126,127,127,124,124,125,125, + 114,114,115,115,112,112,113,113,118,118,119,119,116,116,117,117, + 74, 75, 72, 73, 78, 79, 76, 77, 66, 67, 64, 65, 70, 71, 68, 69, + 90, 91, 88, 89, 94, 95, 92, 93, 82, 83, 80, 81, 86, 87, 84, 85 +}; + +int alaw_input P1((buf), gsm_signal * buf) +{ + int i, c; + + for (i = 0; i < 160 && (c = fgetc(in)) != EOF; i++) buf[i] = A2S( c ); + if (c == EOF && ferror(in)) return -1; + return i; +} + +int alaw_output P1((buf), gsm_signal * buf) +{ + int i; + + for (i = 0; i < 160; i++, buf++) + if (fputc( S2A( *buf ), out) == EOF) return -1; + return 0; +} + diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/toast_audio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/toast_audio.c Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,113 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/toast_audio.c,v 1.6 1995/03/07 21:21:24 jutta Exp $ */ + +#include "toast.h" + +/* toast_audio -- functions to manipulate SunOS audio files. + * + * This is reverse engineered from our present soundfiles + * and in no way portable, durable or aesthetically pleasing. + */ + +extern FILE * in, * out; +extern char * inname; +extern char * progname; + +extern int (*output) P((gsm_signal *)), + (*input ) P((gsm_signal *)); + +extern int alaw_input P((gsm_signal *)), + ulaw_input P((gsm_signal *)), + linear_input P((gsm_signal *)); + +extern int ulaw_output P((gsm_signal *)); + +static int put_u32 P2((f, u), FILE * f, unsigned long u) +{ + /* Write a 32-bit unsigned value msb first. + */ + if ( putc( (char)((u>>24) & 0x0FF), f) == EOF + || putc( (char)((u>>16) & 0x0FF), f) == EOF + || putc( (char)((u>> 8) & 0x0FF), f) == EOF + || putc( (char)( u & 0x0FF), f) == EOF) return -1; + + return 0; +} + +static int get_u32 P2((f, up), FILE * f, unsigned long * up) +{ + /* Read a 32-bit unsigned value msb first. + */ + int i; + unsigned long u; + + if ( (i = getc(f)) == EOF + || ((u = (unsigned char)i), (i = getc(f)) == EOF) + || ((u = (u<<8)|(unsigned char)i), (i = getc(f)) == EOF) + || ((u = (u<<8)|(unsigned char)i), (i = getc(f)) == EOF)) return -1; + *up = (u<<8)|(unsigned char)i; + return 0; +} + +int audio_init_input P0() +{ + unsigned long len, enc; /* unsigned 32 bits */ + + if ( fgetc(in) != '.' + || fgetc(in) != 's' + || fgetc(in) != 'n' + || fgetc(in) != 'd' + || get_u32( in, &len ) + || get_u32( in, &enc ) /* skip this */ + || get_u32( in, &enc )) { + fprintf(stderr, + "%s: bad (missing?) header in Sun audio file \"%s\";\n\ + Try one of -u, -a, -l instead (%s -h for help).\n", + progname, inname ? inname : "stdin", progname); + return -1; + } + + switch (enc) { + case 1: input = ulaw_input; break; + case 2: input = alaw_input; break; + case 3: input = linear_input; break; + default: + fprintf(stderr, +"%s: warning: file format #%lu for %s not implemented, defaulting to u-law.\n", + progname, enc, inname); + input = ulaw_input; + break; + } + + while (len > 4*4) + if (getc(in) == EOF) { + fprintf(stderr, + "%s: EOF in header of Sun audio file \"%s\";\n\ + Try one of -u, -a, -l instead (%s -h for help).\n", + progname, inname ? inname : "stdin", progname); + return -1; + } + else len--; + + return 0; +} + +int audio_init_output P0() +{ + if ( fputs(".snd", out) == EOF + || put_u32(out, 32) + || put_u32(out, ~(unsigned long)0) + || put_u32(out, 1) + || put_u32(out, 8000) + || put_u32(out, 1) + || put_u32(out, 0) + || put_u32(out, 0)) return -1; + + return 0; +} + diff -r 047b5e2b9904 -r af3a788344f9 jni/gsm/unproto.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/gsm/unproto.h Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,23 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/unproto.h,v 1.1 1992/10/28 00:11:08 jutta Exp $*/ + +#ifdef PROTO_H /* sic */ +#undef PROTO_H + +#undef P +#undef P0 +#undef P1 +#undef P2 +#undef P3 +#undef P4 +#undef P5 +#undef P6 +#undef P7 +#undef P8 + +#endif /* PROTO_H */ diff -r 047b5e2b9904 -r af3a788344f9 libs/armeabi/libOSNetworkSystem.so Binary file libs/armeabi/libOSNetworkSystem.so has changed diff -r 047b5e2b9904 -r af3a788344f9 libs/armeabi/libg722.so Binary file libs/armeabi/libg722.so has changed diff -r 047b5e2b9904 -r af3a788344f9 libs/armeabi/libgsm.so Binary file libs/armeabi/libgsm.so has changed diff -r 047b5e2b9904 -r af3a788344f9 libs/smack.jar Binary file libs/smack.jar has changed diff -r 047b5e2b9904 -r af3a788344f9 libs/smackx-debug.jar Binary file libs/smackx-debug.jar has changed diff -r 047b5e2b9904 -r af3a788344f9 libs/smackx-jingle.jar Binary file libs/smackx-jingle.jar has changed diff -r 047b5e2b9904 -r af3a788344f9 libs/smackx.jar Binary file libs/smackx.jar has changed diff -r 047b5e2b9904 -r af3a788344f9 res/layout/incall.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/res/layout/incall.xml Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 047b5e2b9904 -r af3a788344f9 res/layout/ongoing_call_notification.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/res/layout/ongoing_call_notification.xml Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + diff -r 047b5e2b9904 -r af3a788344f9 res/menu/contactlist_context.xml --- a/res/menu/contactlist_context.xml Sat Jan 16 21:32:29 2010 +0100 +++ b/res/menu/contactlist_context.xml Thu Jan 21 01:35:17 2010 +0100 @@ -2,7 +2,7 @@ + android:title="@string/CDCall" android:visible="true" /> diff -r 047b5e2b9904 -r af3a788344f9 res/values-fr/strings.xml --- a/res/values-fr/strings.xml Sat Jan 16 21:32:29 2010 +0100 +++ b/res/values-fr/strings.xml Thu Jan 21 01:35:17 2010 +0100 @@ -140,6 +140,7 @@ Beem - Contacts Beem - ContactList Activity Beem - Informations + Beem - Appel Reset diff -r 047b5e2b9904 -r af3a788344f9 src/com/beem/project/beem/BeemService.java --- a/src/com/beem/project/beem/BeemService.java Sat Jan 16 21:32:29 2010 +0100 +++ b/src/com/beem/project/beem/BeemService.java Thu Jan 21 01:35:17 2010 +0100 @@ -148,6 +148,7 @@ int pport = Integer.parseInt(mSettings.getString("settings_key_proxy_port", "1080")); ProxyInfo.ProxyType type = ProxyType.valueOf(stype); mProxyInfo = new ProxyInfo(type, phost, pport, puser, ppass); + Log.d("Proxy", "type " + type); if (mService != null) mConnectionConfiguration = new ConnectionConfiguration(mHost, mPort, mService, mProxyInfo); else @@ -219,7 +220,7 @@ mConnection = new XmppConnectionAdapter(mConnectionConfiguration, mLogin, mPassword, this); Roster.setDefaultSubscriptionMode(SubscriptionMode.manual); - mJingle = new JingleService(mConnection.getAdaptee()); + mJingle = new JingleService(mConnection.getAdaptee(), this); mBind = new XmppFacade(mConnection, mJingle); Log.d(TAG, "ONCREATE"); } diff -r 047b5e2b9904 -r af3a788344f9 src/com/beem/project/beem/jingle/JingleService.java --- a/src/com/beem/project/beem/jingle/JingleService.java Sat Jan 16 21:32:29 2010 +0100 +++ b/src/com/beem/project/beem/jingle/JingleService.java Thu Jan 21 01:35:17 2010 +0100 @@ -57,8 +57,13 @@ import org.jivesoftware.smackx.jingle.media.PayloadType; import org.jivesoftware.smackx.jingle.nat.BasicTransportManager; import org.jivesoftware.smackx.jingle.nat.TransportCandidate; +import org.sipdroid.media.codecs.CodecManager; +import android.content.Context; import android.util.Log; +import android.content.Intent; + +import com.beem.project.beem.ui.CallScreen; // TODO: Auto-generated Javadoc /** @@ -66,206 +71,185 @@ * @author nikita */ public class JingleService { - private static final String TAG = "JingleService"; - private JingleManager mJingleManager; - private final List mMediaManagers; - private JingleSession mIn; - private JingleSession mOut; - - /** - * JingleService constructor. - * @param xmppConnection a valid XMPPConnection - */ - public JingleService(final XMPPConnection xmppConnection) { - BasicTransportManager bt = new BasicTransportManager(); - // JingleTransportManager tm = new ICETransportManager(); - - mMediaManagers = new ArrayList(); - mMediaManagers.add(new MicrophoneRTPManager(bt)); - } - - /** - * finish to construct the instance. - * @param conn the xmppConnection used with constructor - */ - public void initWhenConntected(XMPPConnection conn) { - mJingleManager = new JingleManager(conn, mMediaManagers); - mJingleManager.addJingleSessionRequestListener(new BeemJingleSessionRequestListener()); - } + private static final String TAG = "JingleService"; + private JingleManager mJingleManager; + private final List mMediaManagers; + private JingleSession mIn; + private JingleSession mOut; + private Context mContext; - /** - * begin a jingle call. - * @param receiver the call receiver - */ - public void call(final String receiver) { - try { - mOut = mJingleManager.createOutgoingJingleSession(receiver); - // TODO configure out avec addMediaSession et addNegociator - mOut.addListener(new BeemJingleCallerSessionListener()); - mOut.startOutgoing(); - } catch (XMPPException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + /** + * JingleService constructor. + * @param xmppConnection a valid XMPPConnection + */ + public JingleService(final XMPPConnection xmppConnection,final Context ctx) { + BasicTransportManager bt = new BasicTransportManager(); + mMediaManagers = new ArrayList(); + mMediaManagers.add(new MicrophoneRTPManager(bt, ctx)); + CodecManager.load(); + mContext = ctx; } - } - - /* - * private void receiveData(String ip, int port) throws IOException { Socket s = null; try { s = new Socket(ip, - * port); System.out.println("Waiting data"); InputStream in = s.getInputStream(); int a; while ((a = in.read()) != - * -1) { System.out.println("Received " + a); } System.out.println("End receiving data"); } finally { if (s != null) - * s.close(); } } - */ - /** - * Listen on receiver session events. - * @author nikita - */ - private class BeemJingleReceiverSessionListener implements JingleSessionListener { /** - * constructor. + * finish to construct the instance. + * @param conn the xmppConnection used with constructor */ - public BeemJingleReceiverSessionListener() { - super(); - // TODO Auto-generated constructor stub - } - - @Override - public void sessionClosed(String reason, JingleSession jingleSession) { - // System.out.println("Session " + jingleSession.getResponder() + - // "closedd because " + reason); - } - - @Override - public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { - // TODO Auto-generated method stub - // System.out.println("Session " + jingleSession.getResponder() + - // " closed"); - + public void initWhenConntected(XMPPConnection conn) { + mJingleManager = new JingleManager(conn, mMediaManagers); + mJingleManager.addJingleSessionRequestListener(new BeemJingleSessionRequestListener()); } - @Override - public void sessionDeclined(String reason, JingleSession jingleSession) { - Log.d(TAG, "Session " + jingleSession.getResponder() + "declined because " + reason); - } - - @Override - public void sessionEstablished(PayloadType pt, TransportCandidate remoteCandidate, - TransportCandidate localCandidate, JingleSession jingleSession) { - // System.out.println("Session established"); - // System.out.println("Je recois sur " + remoteCandidate.getIp() + - // ":" + remoteCandidate.getPort()); - // TODO choose the right RTPReceiver depending on the payload type - // RTPReceiver rtpReceiver = new - // RTPReceiver(remoteCandidate.getPort()); - Log.d(TAG, "Session " + jingleSession.getResponder() + "established"); - } - - @Override - public void sessionMediaReceived(JingleSession jingleSession, String participant) { - // System.out.println("Session Media received from " + participant); + /** + * begin a jingle call. + * @param receiver the call receiver + */ + public void call(final String receiver) { + try { + mOut = mJingleManager.createOutgoingJingleSession(receiver); + // TODO configure out avec addMediaSession et addNegociator + mOut.addListener(new BeemJingleCallerSessionListener()); + mOut.startOutgoing(); + } catch (XMPPException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } - @Override - public void sessionRedirected(String redirection, JingleSession jingleSession) { - } - } + /*private void call(PayloadType pt, TransportCandidate remoteCandidate, + TransportCandidate localCandidate, JingleSession jingleSession) { - /** - * Listen on caller session events. - * @author nikita - */ - private class BeemJingleCallerSessionListener implements JingleSessionListener { + audio = new JAudioLauncher(localCandidate.getPort(), remoteCandidate.getIp(), + remoteCandidate.getPort(), pt); - private static final int SLP_DURATION = 20000; + }*/ /** - * constructor. + * Listen on receiver session events. + * @author nikita */ - public BeemJingleCallerSessionListener() { - super(); - // TODO Auto-generated constructor stub - } + private class BeemJingleReceiverSessionListener implements JingleSessionListener { - @Override - public void sessionClosed(final String reason, final JingleSession jingleSession) { - // System.out.println("Session " + jingleSession.getResponder() + - // "closed because " + reason); - } + /** + * constructor. + */ + public BeemJingleReceiverSessionListener() { + super(); + // TODO Auto-generated constructor stub + } + + @Override + public void sessionClosed(String reason, JingleSession jingleSession) { + System.out.println("Session " + jingleSession.getResponder() + "closedd because " + reason); + //audio.stopMedia(); + + } - @Override - public void sessionClosedOnError(final XMPPException e, final JingleSession jingleSession) { - // System.out.println("Session " + jingleSession.getResponder() + - // " closed on error"); + @Override + public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { + // TODO Auto-generated method stub + System.out.println("Session " + jingleSession.getResponder() + " closed"); + //audio.stopMedia(); + } + + @Override + public void sessionDeclined(String reason, JingleSession jingleSession) { + Log.d(TAG, "Session " + jingleSession.getResponder() + "declined because " + reason); + } - } + @Override + public void sessionEstablished(PayloadType pt, TransportCandidate remoteCandidate, + TransportCandidate localCandidate, JingleSession jingleSession) { + Log.d(TAG, "Session " + jingleSession.getResponder() + "established"); + jingleSession.getSession().getMediaSession(pt.getName()); + } - @Override - public void sessionDeclined(final String reason, final JingleSession jingleSession) { - // System.out.println("Session " + jingleSession.getResponder() + - // "declined because " + reason); - Log.d(TAG, "Session " + jingleSession.getResponder() + "declined because " + reason); + @Override + public void sessionMediaReceived(JingleSession jingleSession, String participant) { + Log.d(TAG, "Session Media received from " + participant); + } + + @Override + public void sessionRedirected(String redirection, JingleSession jingleSession) { + } } - @Override - public void sessionEstablished(final PayloadType pt, final TransportCandidate remoteCandidate, - final TransportCandidate localCandidate, final JingleSession jingleSession) { - Log.d(TAG, "Session " + jingleSession.getResponder() + "established"); - // String name = localCandidate.getName(); - String ip = localCandidate.getIp(); - int port = localCandidate.getPort(); - // System.out.println("Session established waiting connection on " + - // ip + ":" + port); + /** + * Listen on caller session events. + * @author nikita + */ + private class BeemJingleCallerSessionListener implements JingleSessionListener { + + private static final int SLP_DURATION = 20000; - RTPTransmitter transm = new PCMTransmitter(ip, port); + /** + * constructor. + */ + public BeemJingleCallerSessionListener() { + super(); + // TODO Auto-generated constructor stub + } + + @Override + public void sessionClosed(final String reason, final JingleSession jingleSession) { + System.out.println("Session " + jingleSession.getResponder() + "closed because " + reason); + } + + @Override + public void sessionClosedOnError(final XMPPException e, final JingleSession jingleSession) { + System.out.println("Session " + jingleSession.getResponder() + " closed on error"); + } - transm.run(); - try { - Thread.sleep(SLP_DURATION); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - transm.stop(); - // System.out.println("End of transfer"); + @Override + public void sessionDeclined(final String reason, final JingleSession jingleSession) { + Log.d(TAG, "Session " + jingleSession.getResponder() + "declined because " + reason); + } - } + @Override + public void sessionEstablished(final PayloadType pt, final TransportCandidate remoteCandidate, + final TransportCandidate localCandidate, final JingleSession jingleSession) { + Log.d(TAG, "Session " + jingleSession.getResponder() + "established"); + Intent callIntent = new Intent(mContext, CallScreen.class); + callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(callIntent); + //call(pt, remoteCandidate, localCandidate, jingleSession); + } - @Override - public void sessionMediaReceived(final JingleSession jingleSession, final String participant) { - // TODO Auto-generated method stub - System.out.println("Session Media received from " + participant); + @Override + public void sessionMediaReceived(final JingleSession jingleSession, final String participant) { + // TODO Auto-generated method stub + System.out.println("Session Media received from " + participant); + } + + @Override + public void sessionRedirected(final String redirection, final JingleSession jingleSession) { + // TODO Auto-generated method stub + } } - @Override - public void sessionRedirected(final String redirection, final JingleSession jingleSession) { - // TODO Auto-generated method stub - } - } + /** + * Listen for a Jingle session request. + * @author nikita + */ + private class BeemJingleSessionRequestListener implements JingleSessionRequestListener { - /** - * Listen for a Jingle session request. - * @author nikita - */ - private class BeemJingleSessionRequestListener implements JingleSessionRequestListener { + /** + * Constructor. + */ + public BeemJingleSessionRequestListener() { + super(); + } - /** - * Constructor. - */ - public BeemJingleSessionRequestListener() { - super(); + @Override + public void sessionRequested(JingleSessionRequest request) { + System.out.println("Jingle Session request from " + request.getFrom()); + try { + mIn = request.accept(); + mIn.addListener(new BeemJingleReceiverSessionListener()); + mIn.startIncoming(); + } catch (XMPPException e) { + e.printStackTrace(); + } + } } - @Override - public void sessionRequested(JingleSessionRequest request) { - System.out.println("Jingle Session request from " + request.getFrom()); - try { - mIn = request.accept(); - mIn.addListener(new BeemJingleReceiverSessionListener()); - mIn.startIncoming(); - } catch (XMPPException e) { - e.printStackTrace(); - } - } - } - } diff -r 047b5e2b9904 -r af3a788344f9 src/com/beem/project/beem/jingle/MicrophoneRTPManager.java --- a/src/com/beem/project/beem/jingle/MicrophoneRTPManager.java Sat Jan 16 21:32:29 2010 +0100 +++ b/src/com/beem/project/beem/jingle/MicrophoneRTPManager.java Thu Jan 21 01:35:17 2010 +0100 @@ -53,61 +53,64 @@ import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; import org.jivesoftware.smackx.jingle.nat.TransportCandidate; -// TODO: Auto-generated Javadoc +import android.content.Context; + /** * RTPMediaManager, gere les payloads et renvoie une session RTP. * @author nikita */ public class MicrophoneRTPManager extends JingleMediaManager { - - /** RTP media name. */ - public static final String MEDIA_NAME = "Microphone"; + /** + * RTP media name. + */ + public static final String MEDIA_NAME = "Microphone"; - private List mPayloads; + private List mPayloads; + + private Context mCtx; - /** - * Manage Microphone data transmission trough RTP. - * @param transportManager current jingle transport manager(basic,upnp,ice...). - */ - public MicrophoneRTPManager(final JingleTransportManager transportManager) { - super(transportManager); - // TODO Auto-generated constructor stub - setupPayloads(); - } + /** + * Manage Microphone data transmission trough RTP. + * @param transportManager current jingle transport manager(basic,upnp,ice...). + */ + public MicrophoneRTPManager(final JingleTransportManager transportManager, Context ctx) { + super(transportManager); + setupPayloads(); + mCtx = ctx; + } /* (non-Javadoc) * @see org.jivesoftware.smackx.jingle.media.JingleMediaManager#createMediaSession(org.jivesoftware.smackx.jingle.media.PayloadType, org.jivesoftware.smackx.jingle.nat.TransportCandidate, org.jivesoftware.smackx.jingle.nat.TransportCandidate, org.jivesoftware.smackx.jingle.JingleSession) */ - @Override - public JingleMediaSession createMediaSession(PayloadType payloadType, TransportCandidate remote, - TransportCandidate local, JingleSession jingleSession) { - // TODO Auto-generated method stub - return new MicrophoneRTPSession(payloadType, remote, local, null, jingleSession); - } + @Override + public JingleMediaSession createMediaSession(PayloadType payloadType, TransportCandidate remote, + TransportCandidate local, JingleSession jingleSession) { + return new RTPAudioSession(payloadType, remote, local, null, jingleSession, mCtx); + } /* (non-Javadoc) * @see org.jivesoftware.smackx.jingle.media.JingleMediaManager#getName() */ - @Override - public String getName() { - return MEDIA_NAME; - } + @Override + public String getName() { + return MEDIA_NAME; + } /* (non-Javadoc) * @see org.jivesoftware.smackx.jingle.media.JingleMediaManager#getPayloads() */ - @Override - public List getPayloads() { - // TODO Auto-generated method stub - return mPayloads; - } + @Override + public List getPayloads() { + return mPayloads; + } - /** - * Supported payload list. - */ - private void setupPayloads() { - mPayloads = new ArrayList(); - mPayloads.add(new PayloadType.Audio(0, "PCMU")); - mPayloads.add(new PayloadType.Audio(8, "PCMA")); - } + /** + * Supported payload list. + */ + private void setupPayloads() { + mPayloads = new ArrayList(); + mPayloads.add(new PayloadType.Audio(8, "PCMA")); +// mPayloads.add(new PayloadType.Audio(9, "G722")); +// mPayloads.add(new PayloadType.Audio(3, "GSM")); + } } diff -r 047b5e2b9904 -r af3a788344f9 src/com/beem/project/beem/jingle/RTPAudioSession.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/jingle/RTPAudioSession.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,107 @@ +package com.beem.project.beem.jingle; + +import jlibrtp.Participant; +import jlibrtp.RTPSession; + +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; +import org.sipdroid.media.RtpStreamReceiver; +import org.sipdroid.media.RtpStreamSender; +import org.sipdroid.media.codecs.CodecManager; +import org.sipdroid.net.SipdroidSocket; + +import android.content.Context; +import android.util.Log; + +/** + * Manage microphone RTP session. + * @author nikita + */ +public class RTPAudioSession extends JingleMediaSession { + + private RtpStreamSender mSender=null; + private RtpStreamReceiver mReceiver=null; + + private RTPSession rtpSession; + + /** + * constructor. + * @param payloadType the payload typ used + * @param remote the remote transport info + * @param local the local tranport info + * @param mediaLocator don't know + * @param jingleSession the current jingle session + * @param ctx + */ + public RTPAudioSession(final PayloadType pt, final TransportCandidate remote, + final TransportCandidate local, final String mediaLocator, final JingleSession jingleSession, Context ctx) { + super(pt, remote, local, mediaLocator, jingleSession); + + prepareRtpSession(local.getPort(), remote.getIp(), remote.getPort()); + Log.d("AUDIO", String.format("payload type : %s ipdest %s port dest %d port src %d",pt.getName(), remote.getIp(), remote.getPort(), local.getPort())); + + mSender = new RtpStreamSender(CodecManager.getCodecByRtpName(pt.getName()), rtpSession); + + mReceiver = new RtpStreamReceiver(CodecManager.getCodecByRtpName(pt.getName()), rtpSession, ctx); + } + + @Override + public void initialize() { + // TODO Auto-generated method stub + + } + + @Override + public void setTrasmit(boolean active) { + } + + @Override + public void startReceive() { + if (mReceiver != null) { + mReceiver.start(); + } + } + + @Override + public void startTrasmit() { + if (mSender != null) { + mSender.start(); + } + rtpSession.RTPSessionRegister(mReceiver, null, null); + } + + @Override + public void stopReceive() { + if (mReceiver != null) { + mReceiver.halt(); + mReceiver = null; + } + rtpSession.endSession(); + } + + @Override + public void stopTrasmit() { + if (mSender != null) { + mSender.halt(); + mSender = null; + } + } + + private void prepareRtpSession(int src_port, String dest_addr, int dest_port) { + SipdroidSocket rtpSocket = null; + SipdroidSocket rtcpSocket = null; + + try { + rtpSocket = new SipdroidSocket(src_port); + rtcpSocket = new SipdroidSocket(src_port + 1); + } catch (Exception e) { + } + rtpSession = new RTPSession(rtpSocket, rtcpSocket); + Participant p = new Participant(dest_addr,dest_port, dest_port + 1); + rtpSession.addParticipant(p); + rtpSession.naivePktReception(true); + rtpSession.packetBufferBehavior(3); + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/com/beem/project/beem/jingle/RTPReceiver.java --- a/src/com/beem/project/beem/jingle/RTPReceiver.java Sat Jan 16 21:32:29 2010 +0100 +++ b/src/com/beem/project/beem/jingle/RTPReceiver.java Thu Jan 21 01:35:17 2010 +0100 @@ -46,9 +46,9 @@ import java.net.DatagramSocket; import java.net.SocketException; -import org.jlibrtp.jlibrtp.Participant; -import org.jlibrtp.jlibrtp.RTPAppIntf; -import org.jlibrtp.jlibrtp.RTPSession; +import jlibrtp.Participant; +import jlibrtp.RTPAppIntf; +import jlibrtp.RTPSession; // TODO: Auto-generated Javadoc /** diff -r 047b5e2b9904 -r af3a788344f9 src/com/beem/project/beem/jingle/RTPTransmitter.java --- a/src/com/beem/project/beem/jingle/RTPTransmitter.java Sat Jan 16 21:32:29 2010 +0100 +++ b/src/com/beem/project/beem/jingle/RTPTransmitter.java Thu Jan 21 01:35:17 2010 +0100 @@ -46,10 +46,10 @@ import java.net.DatagramSocket; import java.net.SocketException; -import org.jlibrtp.jlibrtp.DataFrame; -import org.jlibrtp.jlibrtp.Participant; -import org.jlibrtp.jlibrtp.RTPAppIntf; -import org.jlibrtp.jlibrtp.RTPSession; +import jlibrtp.DataFrame; +import jlibrtp.Participant; +import jlibrtp.RTPAppIntf; +import jlibrtp.RTPSession; import com.beem.project.beem.utils.FreePort; diff -r 047b5e2b9904 -r af3a788344f9 src/com/beem/project/beem/jingle/demo/JingleCallActivity.java --- a/src/com/beem/project/beem/jingle/demo/JingleCallActivity.java Sat Jan 16 21:32:29 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,147 +0,0 @@ -/* - BEEM is a videoconference application on the Android Platform. - - Copyright (C) 2009 by Frederic-Charles Barthelery, - Jean-Manuel Da Silva, - Nikita Kozlov, - Philippe Lago, - Jean Baptiste Vergely, - Vincent Veronis. - - This file is part of BEEM. - - BEEM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - BEEM 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with BEEM. If not, see . - - Please send bug reports with examples or suggestions to - contact@beem-project.com or http://dev.beem-project.com/ - - Epitech, hereby disclaims all copyright interest in the program "Beem" - written by Frederic-Charles Barthelery, - Jean-Manuel Da Silva, - Nikita Kozlov, - Philippe Lago, - Jean Baptiste Vergely, - Vincent Veronis. - - Nicolas Sadirac, November 26, 2009 - President of Epitech. - - Flavien Astraud, November 26, 2009 - Head of the EIP Laboratory. - -*/ -package com.beem.project.beem.jingle.demo; -/* -import org.jivesoftware.smack.ConnectionConfiguration; -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; -import org.jivesoftware.smack.util.StringUtils; - -import android.os.Bundle; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Toast; - -import com.beem.project.beem.R; -import com.beem.project.beem.jingle.JingleService; -*/ -import android.app.Activity; - -/** - * Activity used to test Jingle call. - * @author darisk - */ -public class JingleCallActivity extends Activity { -/* - private static final int SLP_DURATION = 3000; - private static final int DEFAULT_XMPP_PORT = 5222; - - private XMPPConnection mConnection; - private ConnectionConfiguration mConf; - private JingleService mJingle; - - private Button mBtconnect; - private Button mBtcall; - private EditText mEdJID; - private EditText mEdPassword; - private EditText mEdReceiver; - - /** - * Constructor. - * - public JingleCallActivity() { - } - - /* (non-Javadoc) - * @see android.app.Activity#onCreate(android.os.Bundle) - * - @Override - protected void onCreate(Bundle savedInstanceState) { - java.security.Security.addProvider(new com.sun.security.sasl.Provider()); - super.onCreate(savedInstanceState); - setContentView(R.layout.jingle_call_activity); - mConf = new ConnectionConfiguration("10.0.2.2", DEFAULT_XMPP_PORT); - // mConf = new ConnectionConfiguration("elyzion.net", - // DEFAULT_XMPP_PORT); - mConf.setSecurityMode(SecurityMode.required); - mEdJID = (EditText) findViewById(R.id.jingledemocalljid); - mEdPassword = (EditText) findViewById(R.id.jingledemocallpassword); - mEdReceiver = (EditText) findViewById(R.id.jingledemocallreceiver); - mBtconnect = (Button) findViewById(R.id.jingledemocallconnectbutton); - mBtconnect.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View arg0) { - String jid = mEdJID.getText().toString(); - String login = StringUtils.parseName(jid); - mConnection = new XMPPConnection(mConf); - String password = mEdPassword.getText().toString(); - try { - mConnection.connect(); - Thread.sleep(SLP_DURATION); - mConnection.login(login, password); - mJingle = new JingleService(mConnection); - mJingle.initWhenConntected(mConnection); - mBtcall.setEnabled(true); - Toast.makeText(JingleCallActivity.this, "Connected", Toast.LENGTH_SHORT); - } catch (XMPPException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - }); - mBtcall = (Button) findViewById(R.id.jingledemocallbutton); - mBtcall.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View arg0) { - String jid = mEdReceiver.getText().toString(); - if (!"".equals(jid)) { - mJingle.call(jid); - Toast.makeText(JingleCallActivity.this, "Appel en cours", Toast.LENGTH_SHORT); - } else - Toast.makeText(JingleCallActivity.this, "Remplir le champ (JID complet en toto@tutu.com/truc)", - Toast.LENGTH_SHORT); - } - }); - - } -*/ -} diff -r 047b5e2b9904 -r af3a788344f9 src/com/beem/project/beem/jingle/demo/package-info.java --- a/src/com/beem/project/beem/jingle/demo/package-info.java Sat Jan 16 21:32:29 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - BEEM is a videoconference application on the Android Platform. - - Copyright (C) 2009 by Frederic-Charles Barthelery, - Jean-Manuel Da Silva, - Nikita Kozlov, - Philippe Lago, - Jean Baptiste Vergely, - Vincent Veronis. - - This file is part of BEEM. - - BEEM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - BEEM 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with BEEM. If not, see . - - Please send bug reports with examples or suggestions to - contact@beem-project.com or http://dev.beem-project.com/ - - Epitech, hereby disclaims all copyright interest in the program "Beem" - written by Frederic-Charles Barthelery, - Jean-Manuel Da Silva, - Nikita Kozlov, - Philippe Lago, - Jean Baptiste Vergely, - Vincent Veronis. - - Nicolas Sadirac, November 26, 2009 - President of Epitech. - - Flavien Astraud, November 26, 2009 - Head of the EIP Laboratory. - -*/ -package com.beem.project.beem.jingle.demo; - diff -r 047b5e2b9904 -r af3a788344f9 src/com/beem/project/beem/ui/CallScreen.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/ui/CallScreen.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,169 @@ +package com.beem.project.beem.ui; + +import org.sipdroid.media.RtpStreamReceiver; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.KeyguardManager; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.media.AudioManager; +import android.os.Bundle; +import android.os.SystemClock; +import android.text.InputType; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.EditText; + +import com.beem.project.beem.R; + +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +public class CallScreen extends Activity implements DialogInterface.OnClickListener { + public static final int FIRST_MENU_ID = Menu.FIRST; + public static final int HANG_UP_MENU_ITEM = FIRST_MENU_ID + 1; + public static final int HOLD_MENU_ITEM = FIRST_MENU_ID + 2; + public static final int MUTE_MENU_ITEM = FIRST_MENU_ID + 3; + public static final int VIDEO_MENU_ITEM = FIRST_MENU_ID + 5; + public static final int SPEAKER_MENU_ITEM = FIRST_MENU_ID + 6; + public static final int TRANSFER_MENU_ITEM = FIRST_MENU_ID + 7; + + private static EditText transferText; + + @Override + public void onCreate(Bundle savedInstance){ + super.onCreate(savedInstance); + setContentView(R.layout.incall); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + boolean result = super.onCreateOptionsMenu(menu); + + MenuItem m = menu.add(0, HOLD_MENU_ITEM, 0, "R.string.menu_hold"); + m.setIcon(android.R.drawable.stat_sys_phone_call_on_hold); + m = menu.add(0, SPEAKER_MENU_ITEM, 0, "R.string.menu_speaker"); + m.setIcon(android.R.drawable.stat_sys_speakerphone); + m = menu.add(0, MUTE_MENU_ITEM, 0, "R.string.menu_mute"); + m.setIcon(android.R.drawable.stat_notify_call_mute); + m = menu.add(0, TRANSFER_MENU_ITEM, 0, "R.string.menu_transfer"); + m.setIcon(android.R.drawable.ic_menu_call); + m = menu.add(0, VIDEO_MENU_ITEM, 0, "R.string.menu_video"); + m.setIcon(android.R.drawable.ic_menu_camera); + m = menu.add(0, HANG_UP_MENU_ITEM, 0, "R.string.menu_endCall"); +// m.setIcon(android.R.drawable.stat_notify_call_end); + + return result; + } + + public void onClick(DialogInterface dialog, int which) + { + /* if (which == DialogInterface.BUTTON_POSITIVE) + Receiver.engine(this).transfer(transferText.getText().toString());*/ + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + boolean result = super.onOptionsItemSelected(item); + Intent intent = null; + + switch (item.getItemId()) { + case HANG_UP_MENU_ITEM: + // Receiver.engine(this).rejectcall(); + break; + + case HOLD_MENU_ITEM: + //Receiver.engine(this).togglehold(); + break; + + case TRANSFER_MENU_ITEM: + //transfer(); + break; + + case MUTE_MENU_ITEM: + //Receiver.engine(this).togglemute(); + break; + + case SPEAKER_MENU_ITEM: + //Receiver.engine(this).speaker(RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL? + // AudioManager.MODE_IN_CALL:AudioManager.MODE_NORMAL); + break; + + case VIDEO_MENU_ITEM: + /*if (Receiver.call_state == UserAgent.UA_STATE_HOLD) Receiver.engine(this).togglehold(); + try { + intent = new Intent(this, org.sipdroid.sipua.ui.VideoCamera.class); + startActivity(intent); + } catch (ActivityNotFoundException e) { + }*/ + break; + } + + return result; + } + +/* long enabletime; + KeyguardManager mKeyguardManager; + KeyguardManager.KeyguardLock mKeyguardLock; + boolean enabled; + + void disableKeyguard() { + if (enabled) { + mKeyguardLock.disableKeyguard(); + enabled = false; + enabletime = SystemClock.elapsedRealtime(); + } + } + + void reenableKeyguard() { + if (!enabled) { + if (SystemClock.elapsedRealtime() < enabletime + 500) + try { + Thread.sleep(500); + } catch (InterruptedException e) { + } + mKeyguardLock.reenableKeyguard(); + enabled = true; + } + } + + @Override + public void onStart() { + super.onStart(); + if (mKeyguardManager == null) { + mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); + mKeyguardLock = mKeyguardManager.newKeyguardLock("Sipdroid"); + enabled = true; + } + disableKeyguard(); + } + + @Override + public void onStop() { + super.onStop(); + reenableKeyguard(); + } + */ + +} diff -r 047b5e2b9904 -r af3a788344f9 src/com/beem/project/beem/ui/ContactList.java --- a/src/com/beem/project/beem/ui/ContactList.java Sat Jan 16 21:32:29 2010 +0100 +++ b/src/com/beem/project/beem/ui/ContactList.java Thu Jan 21 01:35:17 2010 +0100 @@ -200,7 +200,7 @@ break; case R.id.contact_list_context_menu_call_item: try { - mXmppFacade.call(mContact.getJID() + "/psi"); + mXmppFacade.call(mContact.getJID() + "/Beem"); result = true; } catch (RemoteException e) { e.printStackTrace(); diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/AppCallerThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/AppCallerThread.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,170 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.util.Enumeration; + +/** + * The purpose of this thread is to check whether there are packets ready from + * any participants. + * + * It should sleep when not in use, and be woken up by a condition variable. + * + * Optionally, if we do jitter-control, the condition variable should have a max + * waiting period equal to how often we need to push data. + * + * @author Arne Kepp + */ +public class AppCallerThread extends Thread { + /** The parent RTP Session */ + RTPSession rtpSession; + /** The applications interface, where the callback methods are called */ + RTPAppIntf appl; + + /** + * Instatiates the AppCallerThread + * + * @param session + * the RTPSession with participants etc + * @param rtpApp + * the interface to which data is given + */ + protected AppCallerThread(RTPSession session, RTPAppIntf rtpApp) { + rtpSession = session; + appl = rtpApp; + if (RTPSession.rtpDebugLevel > 1) { + System.out.println("<-> AppCallerThread created"); + } + } + + /** + * The AppCallerThread will run in this loop until the RTPSession is + * terminated. + * + * Whenever an RTP packet is received it will loop over the participants to + * check for packet buffers that have available frame. + */ + public void run() { + if (RTPSession.rtpDebugLevel > 3) { + System.out.println("-> AppCallerThread.run()"); + } + + while (rtpSession.endSession == false) { + + rtpSession.pktBufLock.lock(); + try { + if (RTPSession.rtpDebugLevel > 4) { + System.out.println("<-> AppCallerThread going to Sleep"); + } + + try { + rtpSession.pktBufDataReady.await(); + } catch (Exception e) { + System.out.println("AppCallerThread:" + e.getMessage()); + } + + // Next loop over all participants and check whether they have + // anything for us. + Enumeration enu = rtpSession.partDb + .getParticipants(); + + while (enu.hasMoreElements()) { + Participant p = enu.nextElement(); + + boolean done = false; + // System.out.println(p.ssrc + " " + !done +" " + + // p.rtpAddress + // + " " + rtpSession.naiveReception + " " + p.pktBuffer); + // System.out.println("done: " + done + " p.unexpected: " + + // p.unexpected); + while (!done + && (!p.unexpected || rtpSession.naiveReception) + && p.pktBuffer != null && p.pktBuffer.length > 0) { + + DataFrame aFrame = p.pktBuffer.popOldestFrame(); + if (aFrame == null) { + done = true; + } else { + appl.receiveData(aFrame, p); + } + } + } + + } finally { + rtpSession.pktBufLock.unlock(); + } + } + if (RTPSession.rtpDebugLevel > 3) { + System.out.println("<- AppCallerThread.run() terminating"); + } + } + + public DataFrame getNextDataFrame() { + if (RTPSession.rtpDebugLevel > 3) { + System.out.println("-> AppCallerThread.run()"); + } + //rtpSession.pktBufLock.lock(); + try { + if (RTPSession.rtpDebugLevel > 4) { + System.out.println("<-> AppCallerThread going to Sleep"); + } + + /*try { + rtpSession.pktBufDataReady.await(); + } catch (Exception e) { + System.out.println("AppCallerThread:" + e.getMessage()); + }*/ + + // Next loop over all participants and check whether they have + // anything for us. + /*Enumeration enu = rtpSession.partDb + .getParticipants(); + + while (enu.hasMoreElements()) {*/ + Participant p = rtpSession.firstPart; + + boolean done = false; + // System.out.println(p.ssrc + " " + !done +" " + + // p.rtpAddress + // + " " + rtpSession.naiveReception + " " + p.pktBuffer); + // System.out.println("done: " + done + " p.unexpected: " + + // p.unexpected); + while (!done + && (!p.unexpected || rtpSession.naiveReception) + && p.pktBuffer != null && p.pktBuffer.length > 0) { + + DataFrame aFrame = p.pktBuffer.popOldestFrame(); + if (aFrame == null) { + done = true; + } else { + return aFrame; + } + } + //} + + } finally { + //rtpSession.pktBufLock.unlock(); + } + if (RTPSession.rtpDebugLevel > 3) { + System.out.println("<- AppCallerThread.run() terminating"); + } + return null; + } + +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/CompRtcpPkt.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/CompRtcpPkt.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,268 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.net.InetSocketAddress; +import java.util.LinkedList; +import java.util.ListIterator; + +/** + * Compound RTCP packet class. + * + * It basically holds a list of packets. This list can either be constructed by + * providing a byte[] of a compound packet, or by adding individual packets. + * + * Upon encode(), the packet will call encode on all the added packets. + * + * problem == 0 indicates the parsing succeeded. + * + * + * @author Arne Kepp + */ + +public class CompRtcpPkt { + /** Problem indicator, negative values denote packet type that cause problem */ + protected int problem = 0; + /** + * Stores the different subclasses of RtcpPkt that make up the compound + * packet + */ + protected LinkedList rtcpPkts = new LinkedList(); + + /** + * Instantiates an empty Compound RTCP packet to which you can add RTCP + * packets + */ + protected CompRtcpPkt() { + // Will have to add packets directly to rtcpPkts. + if (RTPSession.rtpDebugLevel > 7) { + System.out.println("<-> CompRtcpPkt()"); + } + } + + /** + * Add a RTCP packet to the compound packet. Pakcets are added in order, so + * you have to ensure that a Sender Report or Receiver Report is added + * first. + * + * @param aPkt + * the packet to be added + */ + protected void addPacket(RtcpPkt aPkt) { + if (RTPSession.rtpDebugLevel > 11) { + System.out.println(" <-> CompRtcpPkt.addPacket( " + + aPkt.getClass() + " )"); + } + + if (aPkt.problem == 0) { + rtcpPkts.add(aPkt); + } else { + this.problem = aPkt.problem; + } + } + + /** + * Picks a received Compound RTCP packet apart. + * + * Only SDES packets are processed directly, other packets are parsed and + * put into aComptRtcpPkt.rtcpPkts, but not + * + * Check the aComptRtcpPkt.problem , if the value is non-zero the packets + * should probably be discarded. + * + * @param rawPkt + * the byte array received from the socket + * @param packetSize + * the actual number of used bytes + * @param adr + * the socket address from which the packet was received + * @param rtpSession + * the RTPSession with the participant database + */ + + protected void init(byte[] rawPkt, int packetSize, InetSocketAddress adr, + RTPSession rtpSession) { + if (RTPSession.rtcpDebugLevel > 7) { + System.out.println("-> CompRtcpPkt(" + rawPkt.getClass() + + ", size " + packetSize + ", from " + adr.toString() + + ", " + rtpSession.getClass() + ")"); + } + // System.out.println("rawPkt.length:" + rawPkt.length + " packetSize:" + // + packetSize); + + // Chop it up + int start = 0; + + while (start < packetSize && problem == 0) { + int length = (StaticProcs.bytesToUIntInt(rawPkt, start + 2)) + 1; + + if (length * 4 + start > rawPkt.length) { + System.out.println("!!!! CompRtcpPkt.(rawPkt,..,..) length (" + + (length * 4 + start) + + ") exceeds size of raw packet (" + rawPkt.length + + ") !"); + this.problem = -3; + } + + int pktType = (int) rawPkt[start + 1]; + + if (pktType < 0) { + pktType += 256; + } + + if (start == 0) { + // Compound packets need to start with SR or RR + if (pktType != 200 && pktType != 201) { + if (RTPSession.rtcpDebugLevel > 3) { + System.out + .println("!!!! CompRtcpPkt(rawPkt...) packet did not start with SR or RR"); + } + this.problem = -1; + } + + // Padding bit should be zero for the first packet + if (((rawPkt[start] & 0x20) >>> 5) == 1) { + if (RTPSession.rtcpDebugLevel > 3) { + System.out + .println("!!!! CompRtcpPkt(rawPkt...) first packet was padded"); + } + this.problem = -2; + } + } + + // System.out.println("start: " + start + " pktType: " + pktType + + // " length:" + length ); + if (pktType == 200) { + addPacket(new RtcpPktSR(rawPkt, start, length * 4)); + } else if (pktType == 201) { + addPacket(new RtcpPktRR(rawPkt, start, -1)); + } else if (pktType == 202) { + addPacket(new RtcpPktSDES(rawPkt, start, adr, rtpSession.partDb)); + } else if (pktType == 203) { + addPacket(new RtcpPktBYE(rawPkt, start)); + } else if (pktType == 204) { + addPacket(new RtcpPktAPP(rawPkt, start)); + } else if (pktType == 205) { + addPacket(new RtcpPktRTPFB(rawPkt, start, rtpSession)); + } else if (pktType == 206) { + addPacket(new RtcpPktPSFB(rawPkt, start, rtpSession)); + } else { + System.out + .println("!!!! CompRtcpPkt(byte[] rawPkt, int packetSize...) " + + "UNKNOWN RTCP PACKET TYPE:" + pktType); + } + + // System.out.println(" start:" + start + " pktType:" + pktType + + // " length:" + length); + + start += length * 4; + + if (RTPSession.rtcpDebugLevel > 12) { + System.out.println(" start:" + start + " parsing pktType " + + pktType + " length: " + length); + } + } + if (RTPSession.rtcpDebugLevel > 7) { + System.out.println("<- CompRtcpPkt(rawPkt....)"); + } + } + + /** + * Encode combines the RTCP packets in this.rtcpPkts into a byte[] by + * calling the encode() function on each of them individually. + * + * The order of rtcpPkts is preserved, so a RR or SR packet must be first. + * + * @return the trimmed byte[] representation of the packet, ready to go into + * a UDP packet. + */ + protected byte[] encode() { + if (RTPSession.rtpDebugLevel > 9) { + System.out.println(" <- CompRtcpPkt.encode()"); + } + + ListIterator iter = rtcpPkts.listIterator(); + + byte[] rawPkt = new byte[1500]; + int index = 0; + + while (iter.hasNext()) { + RtcpPkt aPkt = (RtcpPkt) iter.next(); + + if (aPkt.packetType == 200) { + RtcpPktSR pkt = (RtcpPktSR) aPkt; + pkt.encode(); + System.arraycopy(pkt.rawPkt, 0, rawPkt, index, + pkt.rawPkt.length); + index += pkt.rawPkt.length; + } else if (aPkt.packetType == 201) { + RtcpPktRR pkt = (RtcpPktRR) aPkt; + pkt.encode(); + System.arraycopy(pkt.rawPkt, 0, rawPkt, index, + pkt.rawPkt.length); + index += pkt.rawPkt.length; + } else if (aPkt.packetType == 202) { + RtcpPktSDES pkt = (RtcpPktSDES) aPkt; + pkt.encode(); + // System.out.println(" ENCODE SIZE: " + pkt.rawPkt.length); + System.arraycopy(pkt.rawPkt, 0, rawPkt, index, + pkt.rawPkt.length); + index += pkt.rawPkt.length; + } else if (aPkt.packetType == 203) { + RtcpPktBYE pkt = (RtcpPktBYE) aPkt; + pkt.encode(); + System.arraycopy(pkt.rawPkt, 0, rawPkt, index, + pkt.rawPkt.length); + index += pkt.rawPkt.length; + } else if (aPkt.packetType == 204) { + RtcpPktAPP pkt = (RtcpPktAPP) aPkt; + pkt.encode(); + System.arraycopy(pkt.rawPkt, 0, rawPkt, index, + pkt.rawPkt.length); + index += pkt.rawPkt.length; + } else if (aPkt.packetType == 205) { + RtcpPktRTPFB pkt = (RtcpPktRTPFB) aPkt; + pkt.encode(); + System.arraycopy(pkt.rawPkt, 0, rawPkt, index, + pkt.rawPkt.length); + index += pkt.rawPkt.length; + } else if (aPkt.packetType == 206) { + RtcpPktPSFB pkt = (RtcpPktPSFB) aPkt; + pkt.encode(); + System.arraycopy(pkt.rawPkt, 0, rawPkt, index, + pkt.rawPkt.length); + index += pkt.rawPkt.length; + } else { + System.out.println("CompRtcpPkt aPkt.packetType:" + + aPkt.packetType); + } + // System.out.println(" packetType:" + aPkt.packetType + " length:" + // + aPkt.rawPkt.length + " index:" + index); + } + + byte[] output = new byte[index]; + + System.arraycopy(rawPkt, 0, output, 0, index); + + if (RTPSession.rtpDebugLevel > 9) { + System.out.println(" -> CompRtcpPkt.encode()"); + } + return output; + } +} \ No newline at end of file diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/DataFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/DataFrame.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,295 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import org.sipdroid.net.tools.DatagramPool; +import org.sipdroid.net.tools.PktBufNodePool; +import org.sipdroid.net.tools.RtpPktPool; + +/** + * Data structure to hold a complete frame if frame reconstruction is enabled, + * or the data from an individual packet if it is not + * + * It also contains most of the data from the individual packets that it is + * based on. + * + * @author Arne Kepp + */ +public class DataFrame { + /** The share RTP timestamp */ + private long rtpTimestamp; + /** The calculated UNIX timestamp, guessed after 2 Sender Reports */ + private long timestamp = -1; + /** the SSRC from which this frame originated */ + private long SSRC; + /** contributing CSRCs, only read from the first packet */ + private long[] CSRCs; + /** RTP payload type */ + private int payloadType; + /** The marks on individual packets, ordered */ + //private boolean[] marks; + /** Whether any packets were marked or not */ + private boolean anyMarked = false; + /** Whether the frame contains the expected number of packets */ + private int isComplete = 0; + // private int dataLength; + /** The data from the individual packets, ordered */ + //private byte[][] data; + /** The sequence numbers of the individual packets, ordered */ + //private int[] seqNum; + /** The total amount of data bytes in this frame */ + private int totalLength = 0; + /** The last sequence number in this frame */ + protected int lastSeqNum; + /** The first sequence number in this frame */ + protected int firstSeqNum; + /** The number of packets expected for a complete frame */ + protected int noPkts; + private RtpPkt[] pkts = new RtpPkt[5]; + + /** + * The usual way to construct a frame is by giving it a PktBufNode, which + * contains links to all the other pkts that make it up. + */ + protected DataFrame(PktBufNode aBufNode, Participant p, int noPkts) { + initDataFrame(aBufNode, p, noPkts); + } + + protected void initDataFrame(PktBufNode aBufNode, Participant p, int noPkts) { + if (RTPSession.rtpDebugLevel > 6) { + System.out.println("-> DataFrame(PktBufNode, noPkts = " + noPkts + + ")"); + } + this.noPkts = noPkts; + RtpPkt aPkt = aBufNode.pkt; + int pktCount = aBufNode.pktCount; + firstSeqNum = aBufNode.pktCount; + PktBufNode tempNode; + + // All this data should be shared, so we just get it from the first one + this.rtpTimestamp = aBufNode.timeStamp; + SSRC = aPkt.getSsrc(); + CSRCs = aPkt.getCsrcArray(); + + // Check whether we can compute an NTPish timestamp? Requires two SR + // reports + if (p.ntpGradient > 0) { + // System.out.print(Long.toString(p.ntpOffset)+" " + timestamp = p.ntpOffset + + (long) (p.ntpGradient * (double) (this.rtpTimestamp - p.lastSRRtpTs)); + } + + // Make data the right length + int payloadLength = aPkt.getPayloadLength(); + //seqNum = new int[aBufNode.pktCount]; + //marks = new boolean[aBufNode.pktCount]; + if (pktCount > 5) { + System.out.println("PKT COUNT TOO HIGH " + pktCount); + } + // Concatenate the data of the packets + int i; + for (i = 0; i < pktCount; i++) { + aPkt = aBufNode.pkt; + pkts[i] = aPkt; + // System.out.println("i " + i + " seqNum[i] " + seqNum[i] + + // " aBufNode" + aBufNode); + //seqNum[i] = aBufNode.seqNum; + if (aBufNode.pkt.isMarked()) + anyMarked = true; + + // Get next node + tempNode = aBufNode; + aBufNode = aBufNode.nextFrameNode; + PktBufNodePool.getInstance().returnBufNode(tempNode); + lastSeqNum = aPkt.getSeqNumber(); + } + + if (noPkts > 0) { + int seqDiff = firstSeqNum - lastSeqNum; + if (seqDiff < 0) + seqDiff = (Integer.MAX_VALUE - firstSeqNum) + lastSeqNum; + if (seqDiff == pktCount && pktCount == noPkts) + isComplete = 1; + } else { + isComplete = -1; + } + + if (RTPSession.rtpDebugLevel > 6) { + System.out.println("<- DataFrame(PktBufNode, noPkt), data length: " + + pkts.length); + } + } + + public DataFrame(){ + } + + /** + * Returns a two dimensial array where the first dimension represents + * individual packets, from which the frame is made up, in order of + * increasing sequence number. These indeces can be matched to the sequence + * numbers returned by sequenceNumbers(). + * + * @return 2-dim array with raw data from packets + */ + /*public byte[][] getData() { + return this.data; + }*/ + + public RtpPkt[] getPkt(){ + return this.pkts ; + } + + /** + * Returns a concatenated version of the data from getData() It ignores + * missing sequence numbers, but then isComplete() will return false + * provided that RTPAppIntf.frameSize() provides a non-negative number for + * this payload type. + * + * @return byte[] with all the data concatenated + */ + /*public byte[] getConcatenatedData() { + if (this.noPkts < 2) { + byte[] ret = new byte[this.totalLength]; + int pos = 0; + + for (int i = 0; i < data.length; i++) { + int length = data[i].length; + + // Last packet may be shorter + if (pos + length > totalLength) + length = totalLength - pos; + + System.arraycopy(data[i], 0, ret, pos, length); + pos += data[i].length; + } + return ret; + } else { + return data[0]; + } + }*/ + + /** + * If two SR packet have been received jlibrtp will attempt to calculate the + * local UNIX timestamp (in milliseconds) of all packets received. + * + * This value should ideally correspond to the local time when the SSRC sent + * the packet. Note that the source may not be reliable. + * + * Returns -1 if less than two SRs have been received + * + * @return the UNIX timestamp, similar to System.currentTimeMillis() or -1; + */ + public long timestamp() { + return this.timestamp; + + } + + /** + * Returns the RTP timestamp of all the packets in the frame. + * + * @return unmodified RTP timestamp + */ + public long rtpTimestamp() { + return this.rtpTimestamp; + } + + /** + * Returns the payload type of the packets + * + * @return the payload type of the packets + */ + public int payloadType() { + return this.payloadType; + } + + /** + * Returns an array whose values, for the same index, correpond to the + * sequence number of the packet from which the data came. + * + * This information can be valuable in conjunction with getData(), to + * identify what parts of a frame are missing. + * + * @return array with sequence numbers + */ + /*public int[] sequenceNumbers() { + return seqNum; + }*/ + + /** + * Returns an array whose values, for the same index, correpond to whether + * the data was marked or not. + * + * This information can be valuable in conjunction with getData(). + * + * @return array of booleans + */ + /*public boolean[] marks() { + return this.marks; + }*/ + + /** + * Returns true if any packet in the frame was marked. + * + * This function should be used if all your frames fit into single packets. + * + * @return true if any packet was marked, false otherwise + */ + public boolean marked() { + return this.anyMarked; + } + + /** + * The SSRC associated with this frame. + * + * @return the ssrc that created this frame + */ + public long ssrc() { + return this.SSRC; + } + + /** + * The SSRCs that contributed to this frame + * + * @return an array of contributing SSRCs, or null + */ + public long[] csrcs() { + return this.CSRCs; + } + + /** + * Checks whether the difference in sequence numbers corresponds to the + * number of packets received for the current timestamp, and whether this + * value corresponds to the expected number of packets. + * + * @return true if the right number of packets make up the frame + */ + public int complete() { + return this.isComplete; + } + + public void release() { + for(RtpPkt pkt : this.pkts) { + if (pkt != null) { + if (pkt.getDatagramPacket() != null) + DatagramPool.getInstance().returnPacket(pkt.getDatagramPacket()); + RtpPktPool.getInstance().returnPkt(pkt); + } + } + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/DebugAppIntf.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/DebugAppIntf.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,90 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.net.InetSocketAddress; + +/** + * DebugAppIntf can be registered on RTPSession to provide simple debugging + * functionality. This is particularly useful to determine whether the client is + * receing any data at all. + * + * @author Arne Kepp + * + */ + +public interface DebugAppIntf { + /** + * This function wil notify you of any packets received, valid or not. + * Useful for network debugging, and finding bugs in jlibrtp. + * + * Type is an integer describing the type of event -2 - Invalid RTCP packet + * received -1 - Invalid RTP packet received 0 - RTP packet received 1 - + * RTCP packet received + * + * Description is a string that should be meaningful to advanced users, such + * as"RTP packet received from 127.0.0.1:12312, SSRC: 1380912 , payload type 1, packet size 16 octets" + * or "Invalid RTP packet received from 127.0.0.1:12312" + * + * This function is synchonous and should return quickly. + * + * @param type + * , the type of event, see above. + * @param socket + * , taken directly from the UDP packet + * @param description + * , see above. + */ + public void packetReceived(int type, InetSocketAddress socket, + String description); + + /** + * This function will notify you of any packets sent from this instance of + * RTPSession. Useful for network debugging, and finding bugs in jlibrtp. + * + * Type is an integer describing the type of event 0 - RTP unicast packet + * sent 1 - RTP multicast packet sent 2 - RTCP unicast packet sent 3 - RTCP + * multicast packet sent + * + * Description is a string that should be meaningful to advanced users, such + * as + * + * This function is synchonous and should return quickly. + * + * @param type + * , the type of event, see above + * @param socket + * , taken directly from the UDP packet + * @param description + * , see above + */ + public void packetSent(int type, InetSocketAddress socket, + String description); + + /** + * Other important events that can occur in session -1 SSRC conflict 0 + * Session is terminating + * + * @param type + * see above + * @param description + * , see above + */ + public void importantEvent(int type, String description); +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/LICENSE.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/LICENSE.txt Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,490 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/Participant.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/Participant.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,464 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.net.InetSocketAddress; + +/** + * A participant represents a peer in an RTPSession. Based on the information + * stored on these objects, packets are processed and statistics generated for + * RTCP. + */ +public class Participant { + /** + * Whether the participant is unexpected, e.g. arrived through unicast with + * SDES + */ + protected boolean unexpected = false; + /** Where to send RTP packets (unicast) */ + protected InetSocketAddress rtpAddress = null; + /** Where to send RTCP packets (unicast) */ + protected InetSocketAddress rtcpAddress = null; + /** Where the first RTP packet was received from */ + protected InetSocketAddress rtpReceivedFromAddress = null; + /** Where the first RTCP packet was received from */ + protected InetSocketAddress rtcpReceivedFromAddress = null; + + /** SSRC of participant */ + protected long ssrc = -1; + /** SDES CNAME */ + protected String cname = null; + /** SDES The participant's real name */ + protected String name = null; + /** SDES The participant's email */ + protected String email = null; + /** SDES The participant's phone number */ + protected String phone = null; + /** SDES The participant's location */ + protected String loc = null; + /** SDES The tool the participants is using */ + protected String tool = null; + /** SDES A note */ + protected String note = null; + /** SDES A priv string, loosely defined */ + protected String priv = null; + + // Receiver Report Items + /** RR First sequence number */ + protected int firstSeqNumber = -1; + /** RR Last sequence number */ + protected int lastSeqNumber = 0; + /** RR Number of times sequence number has rolled over */ + protected long seqRollOverCount = 0; + /** RR Number of packets received */ + protected long receivedPkts = 0; + /** RR Number of octets received */ + protected long receivedOctets = 0; + /** RR Number of packets received since last SR */ + protected int receivedSinceLastSR = 0; + /** RR Sequence number associated with last SR */ + protected int lastSRRseqNumber = 0; + /** RR Interarrival jitter */ + protected double interArrivalJitter = -1.0; + /** RR Last received RTP Timestamp */ + protected long lastRtpTimestamp = 0; + + /** RR Middle 32 bits of the NTP timestamp in the last SR */ + protected long timeStampLSR = 0; + /** RR The time when we actually got the last SR */ + protected long timeReceivedLSR = 0; + + /** Gradient where UNIX timestamp = ntpGradient*RTPTimestamp * ntpOffset */ + protected double ntpGradient = -1; + /** Offset where UNIX timestamp = ntpGradient*RTPTimestamp * ntpOffset */ + protected long ntpOffset = -1; + /** Last NTP received in SR packet, MSB */ + protected long lastNtpTs1 = 0; // 32 bits + /** Last NTP received in SR packet, LSB */ + protected long lastNtpTs2 = 0; // 32 bits + /** RTP Timestamp in last SR packet */ + protected long lastSRRtpTs = 0; // 32 bits + + /** UNIX time when a BYE was received from this participant, for pruning */ + protected long timestampBYE = -1; // The user said BYE at this time + + /** Store the packets received from this participant */ + protected PktBuffer pktBuffer = null; + + /** + * UNIX time of last RTP packet, to check whether this participant has sent + * anything recently + */ + protected long lastRtpPkt = -1; // Time of last RTP packet + /** + * UNIX time of last RTCP packet, to check whether this participant has sent + * anything recently + */ + protected long lastRtcpPkt = -1; // Time of last RTCP packet + /** + * UNIX time this participant was added by application, to check whether we + * ever heard back + */ + protected long addedByApp = -1; // Time the participant was added by + // application + /** UNIX time of last time we sent an RR to this user */ + protected long lastRtcpRRPkt = -1; // Timestamp of last time we sent this + // person an RR packet + /** Unix time of second to last time we sent and RR to this user */ + protected long secondLastRtcpRRPkt = -1; // Timestamp of 2nd to last time we + // sent this person an RR Packet + + /** + * Create a basic participant. If this is a unicast session you must + * provide network address (ipv4 or ipv6) and ports for RTP and RTCP, as + * well as a cname for this contact. These things should be negotiated + * through SIP or a similar protocol. + * + * jlibrtp will listen for RTCP packets to obtain a matching SSRC for this + * participant, based on cname. + * + * @param networkAddress + * string representation of network address (ipv4 or ipv6). Use + * "127.0.0.1" for multicast session. + * @param rtpPort + * port on which peer expects RTP packets. Use 0 if this is a + * sender-only, or this is a multicast session. + * @param rtcpPort + * port on which peer expects RTCP packets. Use 0 if this is a + * sender-only, or this is a multicast session. + */ + public Participant(String networkAddress, int rtpPort, int rtcpPort) { + if (RTPSession.rtpDebugLevel > 6) { + System.out.println("Creating new participant: " + networkAddress); + } + + // RTP + if (rtpPort > 0) { + try { + rtpAddress = new InetSocketAddress(networkAddress, rtpPort); + } catch (Exception e) { + System.out.println("Couldn't resolve " + networkAddress); + } + // isReceiver = true; + } + + // RTCP + if (rtcpPort > 0) { + try { + rtcpAddress = new InetSocketAddress(networkAddress, rtcpPort); + } catch (Exception e) { + System.out.println("Couldn't resolve " + networkAddress); + } + } + + // By default this is a sender + // isSender = true; + } + + // We got a packet, but we don't know this person yet. + protected Participant(InetSocketAddress rtpAdr, InetSocketAddress rtcpAdr, + long SSRC) { + rtpReceivedFromAddress = rtpAdr; + rtcpReceivedFromAddress = rtcpAdr; + ssrc = SSRC; + unexpected = true; + } + + // Dummy constructor to ease testing + protected Participant() { + System.out.println("Don't use the Participan(void) Constructor!"); + } + + /** + * RTP Address registered with this participant. + * + * @return address of participant + */ + InetSocketAddress getRtpSocketAddress() { + return rtpAddress; + } + + /** + * RTCP Address registered with this participant. + * + * @return address of participant + */ + InetSocketAddress getRtcpSocketAddress() { + return rtcpAddress; + } + + /** + * InetSocketAddress this participant has used to send us RTP packets. + * + * @return address of participant + */ + InetSocketAddress getRtpReceivedFromAddress() { + return rtpAddress; + } + + /** + * InetSocketAddress this participant has used to send us RTCP packets. + * + * @return address of participant + */ + InetSocketAddress getRtcpReceivedFromAddress() { + return rtcpAddress; + } + + /** + * CNAME registered for this participant. + * + * @return the cname + */ + public String getCNAME() { + return cname; + } + + /** + * NAME registered for this participant. + * + * @return the name + */ + public String getNAME() { + return name; + } + + /** + * EMAIL registered for this participant. + * + * @return the email address + */ + public String getEmail() { + return email; + } + + /** + * PHONE registered for this participant. + * + * @return the phone number + */ + public String getPhone() { + return phone; + } + + /** + * LOCATION registered for this participant. + * + * @return the location + */ + public String getLocation() { + return loc; + } + + /** + * NOTE registered for this participant. + * + * @return the note + */ + public String getNote() { + return note; + } + + /** + * PRIVATE something registered for this participant. + * + * @return the private-string + */ + public String getPriv() { + return priv; + } + + /** + * TOOL something registered for this participant. + * + * @return the tool + */ + public String getTool() { + return tool; + } + + /** + * SSRC for participant, determined through RTCP SDES + * + * @return SSRC (32 bit unsigned integer as long) + */ + public long getSSRC() { + return this.ssrc; + } + + /** + * Updates the participant with information for receiver reports. + * + * @param packetLength + * to keep track of received octets + * @param pkt + * the most recently received packet + */ + protected void updateRRStats(int packetLength, RtpPkt pkt) { + int curSeqNum = pkt.getSeqNumber(); + + if (firstSeqNumber < 0) { + firstSeqNumber = curSeqNum; + } + + receivedOctets += packetLength; + receivedSinceLastSR++; + receivedPkts++; + + long curTime = System.currentTimeMillis(); + + if (this.lastSeqNumber < curSeqNum) { + // In-line packet, best thing you could hope for + this.lastSeqNumber = curSeqNum; + + } else if (this.lastSeqNumber - this.lastSeqNumber < -100) { + // Sequence counter rolled over + this.lastSeqNumber = curSeqNum; + seqRollOverCount++; + + } else { + // This was probably a duplicate or a late arrival. + } + + // Calculate jitter + if (this.lastRtpPkt > 0) { + + long D = (pkt.getTimeStamp() - curTime) + - (this.lastRtpTimestamp - this.lastRtpPkt); + if (D < 0) + D = (-1) * D; + + this.interArrivalJitter += ((double) D - this.interArrivalJitter) / 16.0; + } + + lastRtpPkt = curTime; + lastRtpTimestamp = pkt.getTimeStamp(); + } + + /** + * Calculates the extended highest sequence received by adding the last + * sequence number to 65536 times the number of times the sequence counter + * has rolled over. + * + * @return extended highest sequence + */ + protected long getExtHighSeqRecv() { + return (65536 * seqRollOverCount + lastSeqNumber); + } + + /** + * Get the fraction of lost packets, calculated as described in RFC 3550 as + * a fraction of 256. + * + * @return the fraction of lost packets since last SR received + */ + protected int getFractionLost() { + int expected = (lastSeqNumber - lastSRRseqNumber); + if (expected < 0) + expected = 65536 + expected; + + int fraction = 256 * (expected - receivedSinceLastSR); + if (expected > 0) { + fraction = (fraction / expected); + } else { + fraction = 0; + } + + // Clear counters + receivedSinceLastSR = 0; + lastSRRseqNumber = lastSeqNumber; + + return fraction; + } + + /** + * The total number of packets lost during the session. + * + * Returns zero if loss is negative, i.e. duplicates have been received. + * + * @return number of lost packets, or zero. + */ + protected long getLostPktCount() { + long lost = (this.getExtHighSeqRecv() - this.firstSeqNumber) + - receivedPkts; + + if (lost < 0) + lost = 0; + return lost; + } + + /** + * + * @return the interArrivalJitter, calculated continuously + */ + protected double getInterArrivalJitter() { + return this.interArrivalJitter; + } + + /** + * Set the timestamp for last sender report + * + * @param ntp1 + * high order bits + * @param ntp2 + * low order bits + */ + protected void setTimeStampLSR(long ntp1, long ntp2) { + // Use what we've got + byte[] high = StaticProcs.uIntLongToByteWord(ntp1); + byte[] low = StaticProcs.uIntLongToByteWord(ntp2); + low[3] = low[1]; + low[2] = low[0]; + low[1] = high[3]; + low[0] = high[2]; + + this.timeStampLSR = StaticProcs.bytesToUIntLong(low, 0); + } + + /** + * Calculate the delay between the last received sender report and now. + * + * @return the delay in units of 1/65.536ms + */ + protected long delaySinceLastSR() { + if (this.timeReceivedLSR < 1) + return 0; + + long delay = System.currentTimeMillis() - this.timeReceivedLSR; + + // Convert ms into 1/65536s = 1/65.536ms + return (long) ((double) delay * 65.536); + } + + /** + * Only for debugging purposes + */ + public void debugPrint() { + System.out.print(" Participant.debugPrint() SSRC:" + this.ssrc + + " CNAME:" + this.cname); + if (this.rtpAddress != null) + System.out.print(" RTP:" + this.rtpAddress.toString()); + if (this.rtcpAddress != null) + System.out.print(" RTCP:" + this.rtcpAddress.toString()); + System.out.println(""); + + System.out.println(" Packets received:" + + this.receivedPkts); + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/ParticipantDatabase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/ParticipantDatabase.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,277 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.concurrent.ConcurrentHashMap; + +/** + * The participant database maintains three hashtables with participants. + * + * The key issue is to be fast for operations that happen every time an RTP + * packet is sent or received. We allow linear searching in cases where we need + * to update participants with information. + * + * The keying is therefore usually the SSRC. In cases where we have the cname, + * but no SSRC is known (no SDES packet has been received), a simple hash i + * calculated based on the CNAME. The RTCP code should, when receiving SDES + * packets, check whether the participant is known and update the copy in this + * database with SSRC if needed. + * + * @author Arne Kepp + */ +public class ParticipantDatabase { + /** The parent RTP Session */ + RTPSession rtpSession = null; + /** + * A linked list to hold participants explicitly added by the application In + * unicast mode this is the list used for RTP and RTCP transmission, in + * multicast it should not be in use. + */ + LinkedList receivers = new LinkedList(); + /** + * The hashtable holds participants added through received RTP and RTCP + * packets, as well as participants that have been linked to an SSRC by ip + * address (in unicast mode). + */ + ConcurrentHashMap ssrcTable = new ConcurrentHashMap(); + + /** + * Simple constructor + * + * @param parent + * parent RTPSession + */ + protected ParticipantDatabase(RTPSession parent) { + rtpSession = parent; + } + + /** + * + * @param cameFrom + * 0: Application, 1: RTP packet, 2: RTCP + * @param p + * the participant + * @return 0 if okay, -1 if not + */ + protected int addParticipant(int cameFrom, Participant p) { + // Multicast or not? + if (this.rtpSession.mcSession) { + return this.addParticipantMulticast(cameFrom, p); + } else { + return this.addParticipantUnicast(cameFrom, p); + } + + } + + /** + * Add a multicast participant to the database + * + * @param cameFrom + * 0: Application, 1,2: discovered through RTP or RTCP + * @param p + * the participant to add + * @return 0 if okay, -2 if redundant, -1 if adding participant to multicast + */ + private int addParticipantMulticast(int cameFrom, Participant p) { + if (cameFrom == 0) { + System.out + .println("ParticipantDatabase.addParticipant() doesnt expect" + + " application to add participants to multicast session."); + return -1; + } else { + // Check this one is not redundant + if (this.ssrcTable.contains(p.ssrc)) { + System.out.println("ParticipantDatabase.addParticipant() SSRC " + + "already known " + Long.toString(p.ssrc)); + return -2; + } else { + this.ssrcTable.put(p.ssrc, p); + return 0; + } + } + } + + /** + * Add a unicast participant to the database + * + * Result will be reported back through tpSession.appIntf.userEvent + * + * @param cameFrom + * 0: Application, 1,2: discovered through RTP or RTCP + * @param p + * the participant to add + * @return 0 if new, 1 if + */ + private int addParticipantUnicast(int cameFrom, Participant p) { + if (cameFrom == 0) { + // Check whether there is a match in the ssrcTable + boolean notDone = true; + + Enumeration enu = this.ssrcTable.elements(); + while (notDone && enu.hasMoreElements()) { + Participant part = enu.nextElement(); + if (part.unexpected + && (part.rtcpReceivedFromAddress + .equals(part.rtcpAddress.getAddress()) || part.rtpReceivedFromAddress + .equals(part.rtpAddress.getAddress()))) { + + part.rtpAddress = p.rtpAddress; + part.rtcpAddress = p.rtcpAddress; + part.unexpected = false; + + // Report the match back to the application + Participant[] partArray = { part }; + this.rtpSession.appIntf.userEvent(5, partArray); + + notDone = false; + p = part; + } + } + + // Add to the table of people that we send packets to + this.receivers.add(p); + return 0; + + } else { + // Check whether there's a match in the receivers table + boolean notDone = true; + // System.out.println("GOT " + p.cname); + Iterator iter = this.receivers.iterator(); + + while (notDone && iter.hasNext()) { + Participant part = iter.next(); + + // System.out.println(part.rtpAddress.getAddress().toString() + // + " " + part.rtcpAddress.getAddress().toString() + // + " " + p.rtpReceivedFromAddress.getAddress().toString() + // + " " + p.rtcpReceivedFromAddress.getAddress().toString()); + + // System.out.println(" HUUHHHH? " + + // p.rtcpReceivedFromAddress.getAddress().equals(part.rtcpAddress.getAddress())); + if ((cameFrom == 1 && p.rtpReceivedFromAddress.getAddress() + .equals(part.rtpAddress.getAddress())) + || (cameFrom == 2 && p.rtcpReceivedFromAddress + .getAddress().equals( + part.rtcpAddress.getAddress()))) { + + part.rtpReceivedFromAddress = p.rtpReceivedFromAddress; + part.rtcpReceivedFromAddress = p.rtcpReceivedFromAddress; + + // Move information + part.ssrc = p.ssrc; + part.cname = p.cname; + part.name = p.name; + part.loc = p.loc; + part.phone = p.phone; + part.email = p.email; + part.note = p.note; + part.tool = p.tool; + part.priv = p.priv; + + this.ssrcTable.put(part.ssrc, part); + + // Report the match back to the application + Participant[] partArray = { part }; + this.rtpSession.appIntf.userEvent(5, partArray); + return 0; + } + } + + // No match? ok + this.ssrcTable.put(p.ssrc, p); + return 0; + } + } + + /** + * Remove a participant from all tables + * + * @param p + * the participant to be removed + */ + protected void removeParticipant(Participant p) { + if (!this.rtpSession.mcSession) + this.receivers.remove(p); + + this.ssrcTable.remove(p.ssrc, p); + } + + /** + * Find a participant based on the ssrc + * + * @param ssrc + * of the participant to be found + * @return the participant, null if unknonw + */ + protected Participant getParticipant(long ssrc) { + Participant p = null; + p = ssrcTable.get(ssrc); + return p; + } + + /** + * Iterator for all the unicast receivers. + * + * This one is used by both RTP for sending packets, as well as RTCP. + * + * @return iterator for unicast participants + */ + protected Iterator getUnicastReceivers() { + if (!this.rtpSession.mcSession) { + return this.receivers.iterator(); + } else { + System.out + .println("Request for ParticipantDatabase.getUnicastReceivers in multicast session"); + return null; + } + } + + /** + * Enumeration of all the participants with known ssrcs. + * + * This is primarily used for sending packets in multicast sessions. + * + * @return enumerator with all the participants with known SSRCs + */ + protected Enumeration getParticipants() { + return this.ssrcTable.elements(); + } + + protected void debugPrint() { + System.out.println(" ParticipantDatabase.debugPrint()"); + Participant p; + Enumeration enu = ssrcTable.elements(); + while (enu.hasMoreElements()) { + p = (Participant) enu.nextElement(); + System.out.println(" ssrcTable ssrc:" + p.ssrc + + " cname:" + p.cname + " loc:" + p.loc + " rtpAddress:" + + p.rtpAddress + " rtcpAddress:" + p.rtcpAddress); + } + + Iterator iter = receivers.iterator(); + while (iter.hasNext()) { + p = iter.next(); + System.out.println(" receivers: " + + p.rtpAddress.toString()); + } + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/PktBufNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/PktBufNode.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,71 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +/** + * This is a four-directional data structures used for the frame buffer, i.e. + * buffer for pkts that need to be assimilated into complete frames. + * + * All the actual work is done by PktBuffer. + * + * @author Arne Kepp + * + */ +public class PktBufNode { + /** The next node (RTP Timestamp), looking from the back -> next means older */ + protected PktBufNode nextFrameQueueNode = null; + /** + * The previous node (RTP Timestmap), looking from the back -> prev means + * newer + */ + protected PktBufNode prevFrameQueueNode = null; + /** + * The next node within the frame, i.e. higher sequence number, same RTP + * timestamp + */ + protected PktBufNode nextFrameNode = null; + /** Number of packets with the same RTP timestamp */ + protected int pktCount; + /** The RTP timeStamp associated with this node */ + protected long timeStamp; + /** The sequence number associated with this node */ + protected int seqNum; + /** The payload, a parsed RTP Packet */ + protected RtpPkt pkt = null; + + /** + * Create a new packet buffer node based on a packet + * + * @param aPkt + * the packet + */ + protected PktBufNode(RtpPkt aPkt) { + initPktBufNode(aPkt); + } + + public PktBufNode(){ + } + + public void initPktBufNode(RtpPkt aPkt) { + pkt = aPkt; + timeStamp = aPkt.getTimeStamp(); + seqNum = aPkt.getSeqNumber(); + pktCount = 1; + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/PktBuffer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/PktBuffer.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,533 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import org.sipdroid.net.tools.DataFramePool; +import org.sipdroid.net.tools.PktBufNodePool; + +/** + * A PktBuffer stores packets either for buffering purposes, or because they + * need to be assimilated to create a complete frame. + * + * This behavior can be controlled through rtpSession.pktBufBehavior() + * + * It optionally drops duplicate packets. + * + * Note that newest is the most recently received, i.e. highest timeStamp Next + * means new to old (from recently received to previously received) + * + * @author Arne Kepp + */ +public class PktBuffer { + /** + * The RTPSession holds information common to all packetBuffers, such as max + * size + */ + RTPSession rtpSession; + /** SSRC of the the participant that this buffer is for */ + long SSRC; + /** The parent participant */ + Participant p; + /** The length of the buffer */ + int length = 0; + /** The oldest, least recently received, packet */ + PktBufNode oldest = null; + /** The newest, most recently received, packet */ + PktBufNode newest = null; + + /** The last sequence number received */ + int lastSeqNumber = -1; + /** The last timestamp */ + long lastTimestamp = -1; + + /** + * Creates a new PktBuffer, a linked list of PktBufNode + * + * @param rtpSession + * the parent RTPSession + * @param p + * the participant to which this packetbuffer belongs. + * @param aPkt + * The first RTP packet, to be added to the buffer + */ + protected PktBuffer(RTPSession rtpSession, Participant p, RtpPkt aPkt) { + this.rtpSession = rtpSession; + this.p = p; + SSRC = aPkt.getSsrc(); + PktBufNode newNode = PktBufNodePool.getInstance().borrowBufNode(); + newNode.initPktBufNode(aPkt); + oldest = newNode; + newest = newNode; + // lastSeqNumber = (aPkt.getSeqNumber() - 1); + // lastTimestamp = aPkt.getTimeStamp(); + length = 1; + } + + /** + * Adds a packet, this happens in constant time if they arrive in order. + * Optimized for the case where each pkt is a complete frame. + * + * @param aPkt + * the packet to be added to the buffer. + * @return integer, negative if operation failed (see code) + */ + protected synchronized int addPkt(RtpPkt aPkt) { + if (aPkt == null) { + System.out.println("! PktBuffer.addPkt(aPkt) aPkt was null"); + return -5; + } + + long timeStamp = aPkt.getTimeStamp(); + if (RTPSession.rtpDebugLevel > 7) { + System.out.println("-> PktBuffer.addPkt() , length:" + length + + " , timeStamp of Pkt: " + Long.toString(timeStamp)); + } + + PktBufNode newNode = PktBufNodePool.getInstance().borrowBufNode(); + newNode.initPktBufNode(aPkt); + if (aPkt.getSsrc() != SSRC) { + System.out.println("PktBuffer.addPkt() SSRCs don't match!"); + } + + int retVal = 0; + if (this.rtpSession.pktBufBehavior > 0) { + retVal = bufferedAddPkt(newNode); + } else if (this.rtpSession.pktBufBehavior == 0) { + retVal = filteredAddPkt(newNode); + } else if (this.rtpSession.pktBufBehavior == -1) { + retVal = unfilteredAddPkt(newNode); + } + + if (RTPSession.rtpDebugLevel > 7) { + if (RTPSession.rtpDebugLevel > 10) { + this.debugPrint(); + } + System.out.println("<- PktBuffer.addPkt() , length:" + length + + " returning " + retVal); + } + return retVal; + } + + /** + * Adds packets in the same order that they arrive, doesn't do any filering + * or processing. + * + * @param newNode + * the node to add to the packet buffer + * @return 0 if everything is okay, -1 otherwise + */ + private int unfilteredAddPkt(PktBufNode newNode) { + if (RTPSession.rtpDebugLevel > 8) { + System.out.println("<-> PktBuffer.unfilteredAddPkt()"); + } + // No magic, just add to the end + if (oldest != null) { + oldest.nextFrameQueueNode = newNode; + newNode.prevFrameQueueNode = oldest; + oldest = newNode; + } else { + oldest = newNode; + newest = newNode; + } + return 0; + } + + /** + * Takes care of duplicate packets + * + * @param newNode + * the node to add to the packet buffer + * @return 0 if everything is okay, -1 otherwise + */ + private int filteredAddPkt(PktBufNode newNode) { + if (RTPSession.rtpDebugLevel > 8) { + System.out.println("<-> PktBuffer.filteredAddPkt()"); + } + + if (length == 0) { + // The buffer was empty, this packet is the one and only. + newest = newNode; + oldest = newNode; + length = 1; + } else { + // The packetbuffer is not empty. + if (newNode.timeStamp > newest.timeStamp + || newNode.seqNum > newest.seqNum + && (newNode.seqNum - newest.seqNum) < 10) { + // Packet came in order + newNode.nextFrameQueueNode = newest; + newest.prevFrameQueueNode = newNode; + newest = newNode; + length++; + } else { + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println("PktBuffer.filteredAddPkt Dropped a packet due to lag! " + + newNode.timeStamp + + " " + + newNode.seqNum + + " vs " + + oldest.timeStamp + + " " + + oldest.seqNum); + } + return -1; + } + } + + return 0; + } + + /** + * Does most of the packet organization for the application. Packets are put + * in order, duplicate packets or late arrivals are discarded + * + * If multiple packets make up a frame, these will also be organized by RTP + * timestamp and sequence number, and returned as a complete frame. + * + * @param newNode + * the node to add to the packet buffer + * @return 0 if everything is okay, -1 otherwise + */ + private int bufferedAddPkt(PktBufNode newNode) { + if (RTPSession.rtpDebugLevel > 8) { + System.out.println("<-> PktBuffer.bufferedAddPkt()"); + } + if (length == 0) { + // The buffer was empty, this packet is the one and only. + newest = newNode; + oldest = newNode; + } else { + // The packetbuffer is not empty. + if (newNode.timeStamp > newest.timeStamp + || newNode.seqNum > newest.seqNum) { + // Packet came in order + newNode.nextFrameQueueNode = newest; + newest.prevFrameQueueNode = newNode; + newest = newNode; + } else { + // There are packets, we need to order this one right. + if (!pktOnTime(newNode.timeStamp, newNode.seqNum) + && rtpSession.pktBufBehavior > -1) { + // We got this too late, can't put it in order anymore. + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println("PktBuffer.addPkt Dropped a packet due to lag! " + + newNode.timeStamp + + " " + + newNode.seqNum + + " vs " + + oldest.timeStamp + + " " + + oldest.seqNum); + } + return -1; + } + + // Need to do some real work, find out where it belongs (linear + // search from the back). + PktBufNode tmpNode = newest; + while (tmpNode.timeStamp > newNode.timeStamp) { + tmpNode = tmpNode.nextFrameQueueNode; + } + + if (tmpNode.timeStamp == newNode.timeStamp + && rtpSession.frameReconstruction + && newNode.seqNum != tmpNode.seqNum) { + // Packet has same timestamp, presumably belongs to frame. + // Need to order within frame. + if (RTPSession.rtpDebugLevel > 8) { + System.out + .println("Found pkt with existing timeStamp: " + + newNode.timeStamp); + } + int ret = addToFrame(tmpNode, newNode); + if (ret != 0) { + return ret; + } + } else { + + // Check that it's not a duplicate + if (tmpNode.timeStamp == newNode.timeStamp + && newNode.seqNum == tmpNode.seqNum) { + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println("PktBuffer.addPkt Dropped a duplicate packet! " + + newNode.timeStamp + + " " + + newNode.seqNum); + } + return -1; + } + + // Insert into buffer + newNode.nextFrameQueueNode = tmpNode; + newNode.prevFrameQueueNode = tmpNode.prevFrameQueueNode; + + // Update the node behind + if (newNode.prevFrameQueueNode != null) { + newNode.prevFrameQueueNode.nextFrameQueueNode = newNode; + } + tmpNode.prevFrameQueueNode = newNode; + + if (newNode.timeStamp > newest.timeStamp) { + newest = newNode; + } + } + } + } + // Update the length of this buffer + length++; + return 0; + } + + /** + * + * @param frameNode + * the node currently representing the frame in the packet buffer + * @param newNode + * the new node to be added to the frame + * @return 0 if no error, -2 if this is a duplicate packet + */ + private int addToFrame(PktBufNode frameNode, PktBufNode newNode) { + // Node has same timeStamp, assume pkt belongs to frame + + if (frameNode.seqNum < newNode.seqNum) { + // this is not the first packet in the frame + frameNode.pktCount++; + + // Find the right spot + while (frameNode.nextFrameNode != null + && frameNode.nextFrameNode.seqNum < newNode.seqNum) { + frameNode = frameNode.nextFrameNode; + } + + // Check whether packet is duplicate + if (frameNode.nextFrameNode != null + && frameNode.nextFrameNode.seqNum == newNode.seqNum) { + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println("PktBuffer.addPkt Dropped a duplicate packet!"); + } + return -2; + } + + newNode.nextFrameNode = frameNode.nextFrameNode; + frameNode.nextFrameNode = newNode; + + } else { + // newNode has the lowest sequence number + newNode.nextFrameNode = frameNode; + newNode.pktCount = frameNode.pktCount + 1; + + // Update the queue + if (frameNode.nextFrameQueueNode != null) { + frameNode.nextFrameQueueNode.prevFrameQueueNode = newNode; + newNode.nextFrameQueueNode = frameNode.nextFrameQueueNode; + frameNode.nextFrameQueueNode = null; + } + if (frameNode.prevFrameQueueNode != null) { + frameNode.prevFrameQueueNode.nextFrameQueueNode = newNode; + newNode.prevFrameQueueNode = frameNode.prevFrameQueueNode; + frameNode.prevFrameQueueNode = null; + } + if (newest.timeStamp == newNode.timeStamp) { + newest = newNode; + } + } + + return 0; + } + + /** + * Checks the oldest frame, if there is one, sees whether it is complete. + * + * @return Returns null if there are no complete frames available. + */ + protected synchronized DataFrame popOldestFrame() { + if (RTPSession.rtpDebugLevel > 7) { + System.out.println("-> PktBuffer.popOldestFrame()"); + } + if (RTPSession.rtpDebugLevel > 10) { + this.debugPrint(); + } + + if (this.rtpSession.pktBufBehavior > 0) { + return this.bufferedPopFrame(); + } else { + return this.unbufferedPopFrame(); + } + } + + /** + * Will return the oldest frame without checking whether it is in the right + * order, or whether we should wate for late arrivals. + * + * @return the first frame on the queue, null otherwise + */ + private DataFrame unbufferedPopFrame() { + if (oldest != null) { + PktBufNode retNode = oldest; + + popFrameQueueCleanup(retNode, retNode.seqNum); + DataFrame df = DataFramePool.getInstance().borrowFrame(); + df.initDataFrame(retNode, this.p, rtpSession.appIntf + .frameSize(oldest.pkt.getPayloadType())); + return df; + } else { + return null; + } + } + + /** + * Only returns if the buffer is full, i.e. length exceeds + * rtpSession.pktBufBehavior, or if the next packet directly follows the + * previous one returned to the application. + * + * @return first frame in order, null otherwise + */ + private DataFrame bufferedPopFrame() { + PktBufNode retNode = oldest; + /** + * Three scenarios: 1) There are no packets available 2) The first + * packet is vailable and in order 3) The first packet is not the next + * on in the sequence a) We have exceeded the wait buffer b) We wait + */ + // System.out.println(" Debug:" +(retNode != null) + " " + + // (retNode.seqNum == this.lastSeqNumber + 1) + // + " " + ( retNode.seqNum == 0 ) + " " + (this.length > + // this.rtpSession.maxReorderBuffer) + // + " " + (this.lastSeqNumber < 0)); + + // Pop it off, null all references. + if (retNode != null + && (retNode.seqNum == this.lastSeqNumber + 1 + || retNode.seqNum == 0 + || this.length > this.rtpSession.pktBufBehavior || this.lastSeqNumber < 0)) { + + // if(tmpNode.pktCount == compLen) { + if (RTPSession.rtpDebugLevel > 7) { + System.out + .println("<- PktBuffer.popOldestFrame() returns frame"); + } + + DataFrame df = DataFramePool.getInstance().borrowFrame(); + df.initDataFrame(retNode, this.p, rtpSession.appIntf + .frameSize(oldest.pkt.getPayloadType())); + + // DataFrame df = new DataFrame(retNode, this.p, 1); + popFrameQueueCleanup(retNode, df.lastSeqNum); + + return df; + + } else { + // If we get here we have little to show for. + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println("<- PktBuffer.popOldestFrame() returns null " + + retNode.seqNum + " " + this.lastSeqNumber); + this.debugPrint(); + } + return null; + } + } + + /** + * Cleans the packet buffer before returning the frame, i.e. making sure the + * queue has a head etc. + * + * @param retNode + * the node that is about to be popped + * @param highestSeq + * the highest sequence number returned to the application + */ + private void popFrameQueueCleanup(PktBufNode retNode, int highestSeq) { + if (1 == length) { + // There's only one frame + newest = null; + oldest = null; + } else { + // There are more frames + oldest = oldest.prevFrameQueueNode; + oldest.nextFrameQueueNode = null; + } + + // Update counters + length--; + + // Find the highest sequence number associated with this timestamp + this.lastSeqNumber = highestSeq; + this.lastTimestamp = retNode.timeStamp; + } + + /** + * Returns the length of the packetbuffer. + * + * @return number of frames (complete or not) in packetbuffer. + */ + protected int getLength() { + return length; + } + + /** + * Checks whether a packet is not too late, i.e. the next packet has already + * been returned. + * + * @param timeStamp + * the RTP timestamp of the packet under consideration + * @param seqNum + * the sequence number of the packet under consideration + * @return true if newer packets have not been handed to the application + */ + protected boolean pktOnTime(long timeStamp, int seqNum) { + if (this.lastSeqNumber == -1) { + // First packet + return true; + } else { + if (seqNum >= this.lastSeqNumber) { + if (this.lastSeqNumber < 3 && timeStamp < this.lastTimestamp) { + return false; + } + } else { + if (seqNum > 3 || timeStamp < this.lastTimestamp) { + return false; + } + } + } + return true; + } + + /** + * Prints out the packet buffer, oldest node first (on top). + */ + protected void debugPrint() { + System.out.println("PktBuffer.debugPrint() : length " + length + + " SSRC " + SSRC + " lastSeqNum:" + lastSeqNumber); + PktBufNode tmpNode = oldest; + int i = 0; + while (tmpNode != null) { + // String str = tmpNode.timeStamp.toString(); + System.out.println(" " + i + " seqNum:" + tmpNode.seqNum + + " timeStamp: " + tmpNode.timeStamp + " pktCount:" + + tmpNode.pktCount); + i++; + tmpNode = tmpNode.prevFrameQueueNode; + } + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/README.txt Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,39 @@ +jlibrtp - Java RTP Library + +Kepp, Arne +ak2618@columbia.edu + +Columbia University +New York, NY 10027 +USA + +This library was started as a term project in VoIP Security, a class taught by +Prof. Henning Schulzrinne at Columbia University. Version 0.1 (not released as such) +was written by Vaishnav Janardhan (vj2135@columbia.edu) and Arne Kepp (ak2618@columbia.edu). + +This version was rewritten by Arne Kepp, as a student project under the supervision of +Prof. Henning Schulzrinne, Columbia University. + +------Abstract +jlibrtp is a library that implements the Real-Time Transport Protocol (RTP), +a well-established standard for streaming media across IP-based networks, in Java. +The purpose of this library is to make it easy for application developers to +create applications for peer to peer streaming of audio, video and other data. +In addition, developers will need a protocol to establish contact with peers, +such as Session Initialization Protocol (SIP) and/or SDP. + +The library accepts any kind of binary data, handles packet parsing and reordering, +maintains a participant database and the control connection associated with the +protocol. The application is notified of received data through a callback-interface. +The library supports IPv4, IPv6 and multicast. It does currently not support encryption, +and should not be used in cases where confidentiality is important before this has +been remedied. + +Please refer to http://jlibrtp.org for more information and newer versions. + +The library requires Sun Microsystems Java 1.5.0 or greater, or equivalent. + +The Library is licensed under the GNU Lesser General Public License, see LICENSE.txt + +The demonstration programs can be compiled as follows: +javac ./jlibrtpDemos/SoundSenderDemo.java jlibrtp/*.java \ No newline at end of file diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RTCPAVPFIntf.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RTCPAVPFIntf.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,102 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package jlibrtp; + +/** + * This is the callback interface for the AVPF profile (RFC 4585) + * + * It is optional, you do not have to register it. + * + * If there are specific events you wish to ignore, you can simply implement + * empty functions. + * + * These are all syncrhonous, make sure to return quickly or do the handling in + * a new thread. + * + * @author Arne Kepp + */ +public interface RTCPAVPFIntf { + + /** + * This function is called when a Picture Loss Indication (PLI, FMT = 1) is + * received + * + * @param ssrcPacketSender + * the SSRC of the participant reporting loss of picture + */ + public void PSFBPktPictureLossReceived(long ssrcPacketSender); + + /** + * This function is called when a Slice Loss Indication (SLI, FMT=2) is + * received + * + * @param ssrcPacketSender + * the SSRC of the participant reporting loss of slice(s) + * @param sliceFirst + * macroblock address of first macroblock + * @param sliceNumber + * number of lost macroblocks, in scan order + * @param slicePictureId + * the six least significant bits of the picture identifier + */ + public void PSFBPktSliceLossIndic(long ssrcPacketSender, int[] sliceFirst, + int[] sliceNumber, int[] slicePictureId); + + /** + * This function is called when a Reference Picture Selection Indication + * (RPSI, FMT=3) is received + * + * @param ssrcPacketSender + * the SSRC of the participant reporting the selection + * @param rpsiPayloadType + * the RTP payload type related to the RPSI bit string + * @param rpsiBitString + * the RPSI information as natively defined by the video codec + * @param rpsiPaddingBits + * the number of padding bits at the end of the string + */ + public void PSFBPktRefPictureSelIndic(long ssrcPacketSender, + int rpsiPayloadType, byte[] rpsiBitString, int rpsiPaddingBits); + + /** + * This function is called when a Transport Layer Feedback Messages is + * received + * + * @param ssrcPacketSender + * @param alfBitString + */ + public void PSFBPktAppLayerFBReceived(long ssrcPacketSender, + byte[] alfBitString); + + /** + * This function is called when a Transport Layer Feedback Messages is + * received + * + * @param ssrcPacketSender + * @param FMT + * 1: NACK, 0,2-30: unassigned, 31: reserved + * @param packetID + * the RTP sequence number of the lost packet + * @param bitmaskLostPackets + * the bitmask of following lost packets + */ + public void RTPFBPktReceived(long ssrcPacketSender, int FMT, + int[] packetID, int[] bitmaskLostPackets); +} \ No newline at end of file diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RTCPAppIntf.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RTCPAppIntf.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,150 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package jlibrtp; + +/** + * This is the callback interface for RTCP packets. + * + * It is optional, you do not have to register it. + * + * If there are specific events you wish to ignore, you can simply implement + * empty functions. + * + * These are all syncrhonous, make sure to return quickly or do the handling in + * a new thread. + * + * @author Arne Kepp + */ +public interface RTCPAppIntf { + + /** + * This function is called whenever a Sender Report (SR) packet is received + * and returns unmodified values. + * + * A sender report may optionally include Receiver Reports (RR), which are + * returned as arrays. Index i corresponds to the same report throughout all + * of the arrays. + * + * @param ssrc + * the (SR) SSRC of the sender + * @param ntpHighOrder + * (SR) NTP high order + * @param ntpLowOrder + * (SR) NTP low order + * @param rtpTimestamp + * (SR) RTP timestamp corresponding to the NTP timestamp + * @param packetCount + * (SR) Packets sent since start of session + * @param octetCount + * (SR) Octets sent since start of session + * @param reporteeSsrc + * (RR) SSRC of sender the receiver is reporting in + * @param lossFraction + * (RR) Loss fraction, see RFC 3550 + * @param cumulPacketsLost + * (RR) Cumulative number of packets lost + * @param extHighSeq + * (RR) Extended highest sequence RTP packet received + * @param interArrivalJitter + * (RR) Interarrival jitter, see RFC 3550 + * @param lastSRTimeStamp + * (RR) RTP timestamp when last SR was received + * @param delayLastSR + * (RR) Delay, in RTP, since last SR was received + */ + public void SRPktReceived(long ssrc, long ntpHighOrder, long ntpLowOrder, + long rtpTimestamp, long packetCount, + long octetCount, + // Get the receiver reports, if any + long[] reporteeSsrc, int[] lossFraction, int[] cumulPacketsLost, + long[] extHighSeq, long[] interArrivalJitter, + long[] lastSRTimeStamp, long[] delayLastSR); + + /** + * This function is called whenever a Receiver Report (SR) packet is + * received and returns unmodified values. + * + * A receiver report may optionally include report blocks, which are + * returned as arrays. Index i corresponds to the same report throughout all + * of the arrays. + * + * @param reporterSsrc + * SSRC of the receiver reporting + * @param reporteeSsrc + * (RR) SSRC of sender the receiver is reporting in + * @param lossFraction + * (RR) Loss fraction, see RFC 3550 + * @param cumulPacketsLost + * (RR) Cumulative number of packets lost + * @param extHighSeq + * (RR) Extended highest sequence RTP packet received + * @param interArrivalJitter + * (RR) Interarrival jitter, see RFC 3550 + * @param lastSRTimeStamp + * (RR) RTP timestamp when last SR was received + * @param delayLastSR + * (RR) Delay, in RTP, since last SR was received + */ + public void RRPktReceived(long reporterSsrc, long[] reporteeSsrc, + int[] lossFraction, int[] cumulPacketsLost, long[] extHighSeq, + long[] interArrivalJitter, long[] lastSRTimeStamp, + long[] delayLastSR); + + /** + * This function is called whenever a Source Description (SDES) packet is + * received. + * + * It currently returns the updated participants AFTER they have been + * updated. + * + * @param relevantParticipants + * participants mentioned in the SDES packet + */ + public void SDESPktReceived(Participant[] relevantParticipants); + + /** + * This function is called whenever a Bye (BYE) packet is received. + * + * The participants will automatically be deleted from the participant + * database after some time, but in the mean time the application may still + * receive RTP packets from this source. + * + * @param relevantParticipants + * participants whose SSRC was in the packet + * @param reason + * the reason provided in the packet + */ + public void BYEPktReceived(Participant[] relevantParticipants, String reason); + + /** + * This function is called whenever an Application (APP) packet is received. + * + * @param part + * the participant associated with the SSRC + * @param subtype + * specified in the packet + * @param name + * ASCII description of packet + * @param data + * in the packet + */ + public void APPPktReceived(Participant part, int subtype, byte[] name, + byte[] data); +} \ No newline at end of file diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RTCPReceiverThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RTCPReceiverThread.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,446 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetSocketAddress; +import java.util.Enumeration; +import java.util.Iterator; + +import org.sipdroid.net.tools.DatagramPool; +import org.sipdroid.net.tools.GenericPool; + +/** + * This thread hangs on the RTCP socket and waits for new packets + * + * @author Arne Kepp + * + */ +public class RTCPReceiverThread extends Thread { + /** Parent RTP Session */ + private RTPSession rtpSession = null; + /** Parent RTCP Session */ + private RTCPSession rtcpSession = null; + + private GenericPool rtcpPacketPool; + + /** + * Constructor for new thread + * + * @param rtcpSession + * parent RTCP session + * @param rtpSession + * parent RTP session + */ + RTCPReceiverThread(RTCPSession rtcpSession, RTPSession rtpSession) { + this.rtpSession = rtpSession; + this.rtcpSession = rtcpSession; + + rtcpPacketPool = new GenericPool(10); + + if (RTPSession.rtpDebugLevel > 1) { + System.out.println("<-> RTCPReceiverThread created"); + } + + } + + /** + * Find out whether a participant with this SSRC is known. + * + * If the user is unknown, and the system is operating in unicast mode, try + * to match the ip-address of the sender to the ip address of a previously + * unmatched target + * + * @param ssrc + * the SSRC of the participant + * @param packet + * the packet that notified us + * @return the relevant participant, possibly newly created + */ + private Participant findParticipant(long ssrc, DatagramPacket packet) { + Participant p = rtpSession.partDb.getParticipant(ssrc); + if (p == null) { + Enumeration enu = rtpSession.partDb.getParticipants(); + while (enu.hasMoreElements()) { + Participant tmp = (Participant) enu.nextElement(); + if (tmp.ssrc < 0 + && (tmp.rtcpAddress.getAddress().equals( + packet.getAddress()) || tmp.rtpAddress + .getAddress().equals(packet.getAddress()))) { + + // Best guess + System.out + .println("RTCPReceiverThread: Got an unexpected packet from SSRC:" + + ssrc + + " @" + + packet.getAddress().toString() + + ", WAS able to match it."); + + tmp.ssrc = ssrc; + return tmp; + } + } + // Create an unknown sender + System.out + .println("RTCPReceiverThread: Got an unexpected packet from SSRC:" + + ssrc + + " @" + + packet.getAddress().toString() + + ", was NOT able to match it."); + p = new Participant((InetSocketAddress) null, + (InetSocketAddress) packet.getSocketAddress(), ssrc); + rtpSession.partDb.addParticipant(2, p); + } + return p; + } + + /** + * Parse a received UDP packet + * + * Perform the header checks and extract the RTCP packets in it + * + * @param packet + * the packet to be parsed + * @return -1 if there was a problem, 0 if successfully parsed + */ + private int parsePacket(DatagramPacket packet) { + + if (packet.getLength() % 4 != 0) { + if (RTPSession.rtcpDebugLevel > 2) { + System.out + .println("RTCPReceiverThread.parsePacket got packet that had length " + + packet.getLength()); + } + return -1; + } else { + byte[] rawPkt = packet.getData(); + + // Parse the received compound RTCP (?) packet + CompRtcpPkt compPkt = rtcpPacketPool.borrowItem(); + compPkt.init(rawPkt, packet.getLength(), + (InetSocketAddress) packet.getSocketAddress(), rtpSession); + + if (this.rtpSession.debugAppIntf != null) { + String intfStr; + + if (rtpSession.mcSession) { + intfStr = this.rtcpSession.rtcpMCSock + .getLocalSocketAddress().toString(); + } else { + intfStr = this.rtpSession.rtpSock.getLocalSocketAddress() + .toString(); + } + + if (compPkt.problem == 0) { + String str = new String( + "Received compound RTCP packet of size " + + packet.getLength() + " from " + + packet.getSocketAddress().toString() + + " via " + intfStr + " containing " + + compPkt.rtcpPkts.size() + " packets"); + + this.rtpSession.debugAppIntf.packetReceived(1, + (InetSocketAddress) packet.getSocketAddress(), str); + } else { + String str = new String( + "Received invalid RTCP packet of size " + + packet.getLength() + " from " + + packet.getSocketAddress().toString() + + " via " + intfStr + ": " + + this.debugErrorString(compPkt.problem)); + + this.rtpSession.debugAppIntf.packetReceived(-2, + (InetSocketAddress) packet.getSocketAddress(), str); + } + } + + if (RTPSession.rtcpDebugLevel > 5) { + Iterator iter = compPkt.rtcpPkts.iterator(); + String str = " "; + while (iter.hasNext()) { + RtcpPkt aPkt = iter.next(); + str += (aPkt.getClass().toString() + ":" + aPkt.itemCount + ", "); + } + System.out.println("<-> RTCPReceiverThread.parsePacket() from " + + packet.getSocketAddress().toString() + str); + } + + // Loop over the information + Iterator iter = compPkt.rtcpPkts.iterator(); + + long curTime = System.currentTimeMillis(); + + while (iter.hasNext()) { + RtcpPkt aPkt = (RtcpPkt) iter.next(); + + // Our own packets should already have been filtered out. + if (aPkt.ssrc == rtpSession.ssrc) { + System.out + .println("RTCPReceiverThread() received RTCP packet" + + " with conflicting SSRC from " + + packet.getSocketAddress().toString()); + rtpSession.resolveSsrcConflict(); + return -1; + } + + /** Receiver Reports **/ + if (aPkt.getClass() == RtcpPktRR.class) { + RtcpPktRR rrPkt = (RtcpPktRR) aPkt; + + Participant p = findParticipant(rrPkt.ssrc, packet); + p.lastRtcpPkt = curTime; + + if (rtpSession.rtcpAppIntf != null) { + rtpSession.rtcpAppIntf.RRPktReceived(rrPkt.ssrc, + rrPkt.reporteeSsrc, rrPkt.lossFraction, + rrPkt.lostPktCount, rrPkt.extHighSeqRecv, + rrPkt.interArvJitter, rrPkt.timeStampLSR, + rrPkt.delaySR); + } + + /** Sender Reports **/ + } else if (aPkt.getClass() == RtcpPktSR.class) { + RtcpPktSR srPkt = (RtcpPktSR) aPkt; + + Participant p = findParticipant(srPkt.ssrc, packet); + p.lastRtcpPkt = curTime; + + if (p != null) { + + if (p.ntpGradient < 0 && p.lastNtpTs1 > -1) { + // Calculate gradient NTP vs RTP + long newTime = StaticProcs.undoNtpMess( + srPkt.ntpTs1, srPkt.ntpTs2); + p.ntpGradient = ((double) (newTime - p.ntpOffset)) + / ((double) srPkt.rtpTs - p.lastSRRtpTs); + if (RTPSession.rtcpDebugLevel > 4) { + System.out + .println("RTCPReceiverThread calculated NTP vs RTP gradient: " + + Double + .toString(p.ntpGradient)); + } + } else { + // Calculate sum of ntpTs1 and ntpTs2 in + // milliseconds + p.ntpOffset = StaticProcs.undoNtpMess(srPkt.ntpTs1, + srPkt.ntpTs2); + p.lastNtpTs1 = srPkt.ntpTs1; + p.lastNtpTs2 = srPkt.ntpTs2; + p.lastSRRtpTs = srPkt.rtpTs; + } + + // For the next RR + p.timeReceivedLSR = curTime; + p.setTimeStampLSR(srPkt.ntpTs1, srPkt.ntpTs2); + + } + + if (rtpSession.rtcpAppIntf != null) { + if (srPkt.rReports != null) { + rtpSession.rtcpAppIntf.SRPktReceived(srPkt.ssrc, + srPkt.ntpTs1, srPkt.ntpTs2, srPkt.rtpTs, + srPkt.sendersPktCount, + srPkt.sendersPktCount, + srPkt.rReports.reporteeSsrc, + srPkt.rReports.lossFraction, + srPkt.rReports.lostPktCount, + srPkt.rReports.extHighSeqRecv, + srPkt.rReports.interArvJitter, + srPkt.rReports.timeStampLSR, + srPkt.rReports.delaySR); + } else { + rtpSession.rtcpAppIntf.SRPktReceived(srPkt.ssrc, + srPkt.ntpTs1, srPkt.ntpTs2, srPkt.rtpTs, + srPkt.sendersPktCount, + srPkt.sendersPktCount, null, null, null, + null, null, null, null); + } + } + + /** Source Descriptions **/ + } else if (aPkt.getClass() == RtcpPktSDES.class) { + RtcpPktSDES sdesPkt = (RtcpPktSDES) aPkt; + + // The the participant database is updated + // when the SDES packet is reconstructed by CompRtcpPkt + if (rtpSession.rtcpAppIntf != null) { + rtpSession.rtcpAppIntf + .SDESPktReceived(sdesPkt.participants); + } + + /** Bye Packets **/ + } else if (aPkt.getClass() == RtcpPktBYE.class) { + RtcpPktBYE byePkt = (RtcpPktBYE) aPkt; + + long time = System.currentTimeMillis(); + Participant[] partArray = new Participant[byePkt.ssrcArray.length]; + + for (int i = 0; i < byePkt.ssrcArray.length; i++) { + partArray[i] = rtpSession.partDb + .getParticipant(byePkt.ssrcArray[i]); + if (partArray[i] != null) + partArray[i].timestampBYE = time; + } + + if (rtpSession.rtcpAppIntf != null) { + rtpSession.rtcpAppIntf.BYEPktReceived(partArray, + new String(byePkt.reason)); + } + + /** Application specific Packets **/ + } else if (aPkt.getClass() == RtcpPktAPP.class) { + RtcpPktAPP appPkt = (RtcpPktAPP) aPkt; + + Participant part = findParticipant(appPkt.ssrc, packet); + + if (rtpSession.rtcpAppIntf != null) { + rtpSession.rtcpAppIntf.APPPktReceived(part, + appPkt.itemCount, appPkt.pktName, + appPkt.pktData); + } + } + + } + } + return 0; + } + + /** + * Returns a legible message when an error occurs + * + * @param errorCode + * the internal error code, commonly negative of packet type + * @return a string that is hopefully somewhat informative + */ + private String debugErrorString(int errorCode) { + String aStr = ""; + switch (errorCode) { + case -1: + aStr = "The first packet was not of type SR or RR."; + break; + case -2: + aStr = "The padding bit was set for the first packet."; + break; + case -200: + aStr = " Error parsing Sender Report packet."; + break; + case -201: + aStr = " Error parsing Receiver Report packet."; + break; + case -202: + aStr = " Error parsing SDES packet"; + break; + case -203: + aStr = " Error parsing BYE packet."; + break; + case -204: + aStr = " Error parsing Application specific packet."; + break; + case -205: + aStr = " Error parsing RTP Feedback packet."; + break; + case -206: + aStr = " Error parsing Payload-Specific Feedback packet."; + break; + default: + aStr = "Unknown error code " + errorCode + "."; + } + + return aStr; + } + + /** + * Start the RTCP receiver thread. + * + * It will 1) run when it receives a packet 2) parse the packet 3) call any + * relevant callback functions, update database 4) block until the next one + * arrives. + */ + public void run() { + if (RTPSession.rtcpDebugLevel > 1) { + if (rtpSession.mcSession) { + System.out + .println("-> RTCPReceiverThread.run() starting on MC " + + rtcpSession.rtcpMCSock.getLocalPort()); + } else { + System.out.println("-> RTCPReceiverThread.run() starting on " + + rtcpSession.rtcpSock.getLocalPort()); + } + } + + while (!rtpSession.endSession) { + + if (RTPSession.rtcpDebugLevel > 4) { + if (rtpSession.mcSession) { + System.out + .println("-> RTCPReceiverThread.run() waiting for packet on MC " + + rtcpSession.rtcpMCSock.getLocalPort()); + } else { + System.out + .println("-> RTCPReceiverThread.run() waiting for packet on " + + rtcpSession.rtcpSock.getLocalPort()); + } + } + + // Prepare a packet + DatagramPacket packet = DatagramPool.getInstance().borrowPacket(); + + // Wait for it to arrive + if (!rtpSession.mcSession) { + // Unicast + try { + rtcpSession.rtcpSock.receive(packet); + } catch (IOException e) { + if (!rtpSession.endSession) { + e.printStackTrace(); + } else { + continue; + } + } + } else { + // Multicast + try { + rtcpSession.rtcpMCSock.receive(packet); + } catch (IOException e) { + if (!rtpSession.endSession) { + e.printStackTrace(); + } else { + continue; + } + } + } + + // Check whether this is one of our own + if ((rtpSession.mcSession && !packet.getSocketAddress().equals( + rtcpSession.rtcpMCSock)) + || !packet.getSocketAddress().equals(rtcpSession.rtcpSock)) { + // System.out.println("Packet received from: " + + // packet.getSocketAddress().toString()); + parsePacket(packet); + // rtpSession.partDb.debugPrint(); + } + } + + if (RTPSession.rtcpDebugLevel > 1) { + System.out.println("<-> RTCPReceiverThread terminating"); + } + } + +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RTCPSenderThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RTCPSenderThread.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,496 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.net.DatagramPacket; +import java.net.InetSocketAddress; +import java.util.Enumeration; +import java.util.Iterator; + +/** + * This thread sends scheduled RTCP packets + * + * It also performs maintenance of various queues and the participant database. + * + * @author Arne Kepp + * + */ +public class RTCPSenderThread extends Thread { + /** Parent RTP Session */ + private RTPSession rtpSession = null; + /** Parent RTCP Session */ + private RTCPSession rtcpSession = null; + + /** Whether we have sent byes for the last conflict */ + private boolean byesSent = false; + + /** + * Constructor for new thread + * + * @param rtcpSession + * parent RTCP session + * @param rtpSession + * parent RTP session + */ + protected RTCPSenderThread(RTCPSession rtcpSession, RTPSession rtpSession) { + this.rtpSession = rtpSession; + this.rtcpSession = rtcpSession; + if (RTPSession.rtpDebugLevel > 1) { + System.out.println("<-> RTCPSenderThread created"); + } + } + + /** + * Send BYE messages to all the relevant participants + * + */ + protected void sendByes() { + // Create the packet + CompRtcpPkt compPkt = new CompRtcpPkt(); + + // Need a SR for validation + RtcpPktSR srPkt = new RtcpPktSR(this.rtpSession.ssrc, + this.rtpSession.sentPktCount, this.rtpSession.sentOctetCount, + null); + compPkt.addPacket(srPkt); + + byte[] reasonBytes; + + // Add the actualy BYE Pkt + long[] ssrcArray = { this.rtpSession.ssrc }; + if (rtpSession.conflict) { + reasonBytes = "SSRC collision".getBytes(); + } else { + reasonBytes = "jlibrtp says bye bye!".getBytes(); + } + RtcpPktBYE byePkt = new RtcpPktBYE(ssrcArray, reasonBytes); + + compPkt.addPacket(byePkt); + + // Send it off + if (rtpSession.mcSession) { + mcSendCompRtcpPkt(compPkt); + } else { + Iterator iter = rtpSession.partDb + .getUnicastReceivers(); + + while (iter.hasNext()) { + Participant part = (Participant) iter.next(); + if (part.rtcpAddress != null) + sendCompRtcpPkt(compPkt, part.rtcpAddress); + } + // System.out.println("SENT BYE PACKETS!!!!!"); + } + } + + /** + * Multicast version of sending a Compound RTCP packet + * + * @param pkt + * the packet to best + * @return 0 is successful, -1 otherwise + */ + protected int mcSendCompRtcpPkt(CompRtcpPkt pkt) { + byte[] pktBytes = pkt.encode(); + DatagramPacket packet; + + // Create datagram + try { + packet = new DatagramPacket(pktBytes, pktBytes.length, + rtpSession.mcGroup, rtcpSession.rtcpMCSock.getPort()); + } catch (Exception e) { + System.out + .println("RCTPSenderThread.MCSendCompRtcpPkt() packet creation failed."); + e.printStackTrace(); + return -1; + } + + // Send packet + if (RTPSession.rtcpDebugLevel > 5) { + System.out + .println("<-> RTCPSenderThread.SendCompRtcpPkt() multicast"); + } + try { + rtcpSession.rtcpMCSock.send(packet); + // Debug + if (this.rtpSession.debugAppIntf != null) { + this.rtpSession.debugAppIntf.packetSent(3, + (InetSocketAddress) packet.getSocketAddress(), + new String("Sent multicast RTCP packet of size " + + packet.getLength() + + " to " + + packet.getSocketAddress().toString() + + " via " + + this.rtcpSession.rtcpMCSock + .getLocalSocketAddress().toString())); + } + } catch (Exception e) { + System.out + .println("RCTPSenderThread.MCSendCompRtcpPkt() multicast failed."); + e.printStackTrace(); + return -1; + } + return packet.getLength(); + } + + /** + * Unicast version of sending a Compound RTCP packet + * + * @param pkt + * the packet to best + * @param receiver + * the socket address of the recipient + * @return 0 is successful, -1 otherwise + */ + protected int sendCompRtcpPkt(CompRtcpPkt pkt, InetSocketAddress receiver) { + byte[] pktBytes = pkt.encode(); + DatagramPacket packet; + + // Create datagram + try { + // System.out.println("receiver: " + receiver); + packet = new DatagramPacket(pktBytes, pktBytes.length, receiver); + } catch (Exception e) { + System.out + .println("RCTPSenderThread.SendCompRtcpPkt() packet creation failed."); + e.printStackTrace(); + return -1; + } + + // Send packet + if (RTPSession.rtcpDebugLevel > 5) { + Iterator iter = pkt.rtcpPkts.iterator(); + String str = " "; + while (iter.hasNext()) { + RtcpPkt aPkt = iter.next(); + str += (aPkt.getClass().toString() + ":" + aPkt.itemCount + ", "); + } + System.out + .println("<-> RTCPSenderThread.SendCompRtcpPkt() unicast to " + + receiver + str); + } + try { + rtcpSession.rtcpSock.send(packet); + // Debug + if (this.rtpSession.debugAppIntf != null) { + this.rtpSession.debugAppIntf.packetSent(2, + (InetSocketAddress) packet.getSocketAddress(), + new String("Sent unicast RTCP packet of size " + + packet.getLength() + + " to " + + packet.getSocketAddress().toString() + + " via " + + this.rtcpSession.rtcpSock + .getLocalSocketAddress().toString())); + } + } catch (Exception e) { + System.out + .println("RTCPSenderThread.SendCompRtcpPkt() unicast failed."); + e.printStackTrace(); + return -1; + } + return packet.getLength(); + } + + /** + * Check whether we can send an immediate feedback packet to this person + * + * @param ssrc + * SSRC of participant + */ + protected void reconsiderTiming(long ssrc) { + Participant part = this.rtpSession.partDb.getParticipant(ssrc); + + if (part != null && this.rtcpSession.fbSendImmediately()) { + CompRtcpPkt compPkt = preparePacket(part, false); + /*********** Send the packet ***********/ + // Keep track of sent packet length for average; + int datagramLength; + if (rtpSession.mcSession) { + datagramLength = this.mcSendCompRtcpPkt(compPkt); + } else { + // part.debugPrint(); + datagramLength = this + .sendCompRtcpPkt(compPkt, part.rtcpAddress); + } + /*********** Administrative tasks ***********/ + // Update average packet size + if (datagramLength > 0) { + rtcpSession.updateAvgPacket(datagramLength); + } + } else if (part != null && this.rtcpSession.fbAllowEarly + && this.rtcpSession.fbSendEarly()) { + + // Make sure we dont do it too often + this.rtcpSession.fbAllowEarly = false; + + CompRtcpPkt compPkt = preparePacket(part, true); + /*********** Send the packet ***********/ + // Keep track of sent packet length for average; + int datagramLength; + if (rtpSession.mcSession) { + datagramLength = this.mcSendCompRtcpPkt(compPkt); + } else { + // part.debugPrint(); + datagramLength = this + .sendCompRtcpPkt(compPkt, part.rtcpAddress); + } + /*********** Administrative tasks ***********/ + // Update average packet size + if (datagramLength > 0) { + rtcpSession.updateAvgPacket(datagramLength); + } + rtcpSession.calculateDelay(); + } + + // Out of luck, fb message will have to go with next regular packet + // Sleep for the remaining time. + this.rtcpSession.nextDelay -= System.currentTimeMillis() + - this.rtcpSession.prevTime; + if (this.rtcpSession.nextDelay < 0) + this.rtcpSession.nextDelay = 0; + + } + + /** + * Prepare a packet. The output depends on the participant and how the + * packet is scheduled. + * + * @param part + * the participant to report to + * @param regular + * whether this is a regularly, or early scheduled RTCP packet + * @return compound RTCP packet + */ + protected CompRtcpPkt preparePacket(Participant part, boolean regular) { + /*********** Figure out what we are going to send ***********/ + // Check whether this person has sent RTP packets since the last RR. + boolean incRR = false; + if (part.secondLastRtcpRRPkt > part.lastRtcpRRPkt) { + incRR = true; + part.secondLastRtcpRRPkt = part.lastRtcpRRPkt; + part.lastRtcpRRPkt = System.currentTimeMillis(); + } + + // Are we sending packets? -> add SR + boolean incSR = false; + if (rtpSession.sentPktCount > 0 && regular) { + incSR = true; + } + + /*********** Actually create the packet ***********/ + // Create compound packet + CompRtcpPkt compPkt = new CompRtcpPkt(); + + // If we're sending packets we'll use a SR for header + if (incSR) { + RtcpPktSR srPkt = new RtcpPktSR(this.rtpSession.ssrc, + this.rtpSession.sentPktCount, + this.rtpSession.sentOctetCount, null); + compPkt.addPacket(srPkt); + + if (part.ssrc > 0) { + RtcpPkt[] ar = this.rtcpSession.getFromFbQueue(part.ssrc); + if (ar != null) { + for (int i = 0; i < ar.length; i++) { + compPkt.addPacket(ar[i]); + } + } + } + + } + + // If we got anything from this participant since we sent the 2nd to + // last RtcpPkt + if (incRR || !incSR) { + Participant[] partArray = { part }; + + if (part.receivedPkts < 1) + partArray = null; + + RtcpPktRR rrPkt = new RtcpPktRR(partArray, rtpSession.ssrc); + compPkt.addPacket(rrPkt); + + if (!incSR && part.ssrc > 0) { + RtcpPkt[] ar = this.rtcpSession.getFromFbQueue(part.ssrc); + if (ar != null) { + for (int i = 0; i < ar.length; i++) { + compPkt.addPacket(ar[i]); + } + } + } + } + + // APP packets + if (regular && part.ssrc > 0) { + RtcpPkt[] ar = this.rtcpSession.getFromAppQueue(part.ssrc); + if (ar != null) { + for (int i = 0; i < ar.length; i++) { + compPkt.addPacket(ar[i]); + } + } else { + // Nope + } + } + + // For now we'll stick the SDES on every time, and only for us + // if(regular) { + RtcpPktSDES sdesPkt = new RtcpPktSDES(true, this.rtpSession, null); + compPkt.addPacket(sdesPkt); + // } + + return compPkt; + } + + /** + * Start the RTCP sender thread. + * + * RFC 4585 is more complicated, but in general it will 1) Wait a + * precalculated amount of time 2) Determine the next RTCP recipient 3) + * Construct a compound packet with all the relevant information 4) Send the + * packet 5) Calculate next delay before going to sleep + */ + public void run() { + if (RTPSession.rtcpDebugLevel > 1) { + System.out.println("<-> RTCPSenderThread running"); + } + + // Give the application a chance to register some participants + try { + Thread.sleep(10); + } catch (Exception e) { + System.out.println("RTCPSenderThread didn't get any initial rest."); + } + + // Set up an iterator for the member list + Enumeration enu = null; + Iterator iter = null; + + // TODO Change to rtcpReceivers + if (rtpSession.mcSession) { + enu = rtpSession.partDb.getParticipants(); + } else { + iter = rtpSession.partDb.getUnicastReceivers(); + } + while (!rtpSession.endSession) { + if (RTPSession.rtcpDebugLevel > 5) { + System.out.println("<-> RTCPSenderThread sleeping for " + + rtcpSession.nextDelay + " ms"); + } + + try { + Thread.sleep(rtcpSession.nextDelay); + } catch (Exception e) { + System.out.println("RTCPSenderThread Exception message:" + + e.getMessage()); + // Is the party over? + if (this.rtpSession.endSession) { + continue; + } + + if (rtcpSession.fbWaiting != -1) { + reconsiderTiming(rtcpSession.fbWaiting); + continue; + } + } + + /** Came here the regular way */ + this.rtcpSession.fbAllowEarly = true; + + if (RTPSession.rtcpDebugLevel > 5) { + System.out.println("<-> RTCPSenderThread waking up"); + } + + // Regenerate nextDelay, before anything happens. + rtcpSession.calculateDelay(); + + // We'll wait here until a conflict (if any) has been resolved, + // so that the bye packets for our current SSRC can be sent. + if (rtpSession.conflict) { + if (!this.byesSent) { + sendByes(); + this.byesSent = true; + } + continue; + } + this.byesSent = false; + + // Grab the next person + Participant part = null; + + // Multicast + if (this.rtpSession.mcSession) { + if (!enu.hasMoreElements()) + enu = rtpSession.partDb.getParticipants(); + + if (enu.hasMoreElements()) { + part = enu.nextElement(); + } else { + continue; + } + + // Unicast + } else { + if (!iter.hasNext()) { + iter = rtpSession.partDb.getUnicastReceivers(); + } + + if (iter.hasNext()) { + while (iter.hasNext() + && (part == null || part.rtcpAddress == null)) { + part = iter.next(); + } + } + + if (part == null || part.rtcpAddress == null) + continue; + } + + CompRtcpPkt compPkt = preparePacket(part, true); + + /*********** Send the packet ***********/ + // Keep track of sent packet length for average; + int datagramLength; + if (rtpSession.mcSession) { + datagramLength = this.mcSendCompRtcpPkt(compPkt); + } else { + // part.debugPrint(); + datagramLength = this + .sendCompRtcpPkt(compPkt, part.rtcpAddress); + } + + /*********** Administrative tasks ***********/ + // Update average packet size + if (datagramLength > 0) { + rtcpSession.updateAvgPacket(datagramLength); + } + } + + // Be polite, say Bye to everone + sendByes(); + try { + Thread.sleep(200); + } catch (Exception e) { + } + + if (RTPSession.rtcpDebugLevel > 0) { + System.out.println("<-> RTCPSenderThread terminating"); + } + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RTCPSession.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RTCPSession.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,565 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.MulticastSocket; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.ListIterator; + +/** + * This class acts as an organizer for most of the information and functions + * pertaining to RTCP packet generation and reception + * + * @author Arne Kepp + * + */ +public class RTCPSession { + /** Parent session */ + protected RTPSession rtpSession = null; + + /** Unicast socket */ + protected DatagramSocket rtcpSock = null; + /** Multicast socket */ + protected MulticastSocket rtcpMCSock = null; + /** Multicast group */ + protected InetAddress mcGroup = null; + + /** RTCP Receiver thread */ + protected RTCPReceiverThread recvThrd = null; + /** RTCP Sender thread */ + protected RTCPSenderThread senderThrd = null; + + /** Previous time a delay was calculated */ + protected long prevTime = System.currentTimeMillis(); + /** Delay between RTCP transmissions, in ms. Initialized in start() */ + protected int nextDelay = -1; // + /** + * The average compound RTCP packet size, in octets, including UDP and IP + * headers + */ + protected int avgPktSize = 200; // + /** Pessimistic case estimate of the current number of senders */ + protected int senderCount = 1; + /** Whether next RTCP packet can be sent early */ + protected boolean fbAllowEarly = false; + /** Feedback queue , index is SSRC of target */ + protected Hashtable> fbQueue = null; + /** APP queue , index is SSRC of target */ + protected Hashtable> appQueue = null; + /** Are we just starting up? */ + protected boolean initial = true; + /** Is there a feedback packet waiting? SSRC of destination */ + protected long fbWaiting = -1; + + /** + * Constructor for unicast sessions + * + * @param parent + * RTPSession that started this + * @param rtcpSocket + * the socket to use for listening and sending + */ + protected RTCPSession(RTPSession parent, DatagramSocket rtcpSocket) { + this.rtcpSock = rtcpSocket; + rtpSession = parent; + } + + /** + * Constructor for multicast sessions + * + * @param parent + * parent RTPSession + * @param rtcpSocket + * parent RTPSession that started this + * @param multicastGroup + * multicast group to bind the socket to + */ + protected RTCPSession(RTPSession parent, MulticastSocket rtcpSocket, + InetAddress multicastGroup) { + mcGroup = multicastGroup; + this.rtcpSock = rtcpSocket; + rtpSession = parent; + } + + /** + * Starts the session, calculates delays and fires up the threads. + * + */ + protected void start() { + // nextDelay = 2500 + rtpSession.random.nextInt(1000) - 500; + this.calculateDelay(); + recvThrd = new RTCPReceiverThread(this, this.rtpSession); + senderThrd = new RTCPSenderThread(this, this.rtpSession); + recvThrd.start(); + senderThrd.start(); + } + + /** + * Send bye packets, handled by RTCP Sender thread + * + */ + protected void sendByes() { + senderThrd.sendByes(); + } + + /** + * Calculate the delay before the next RTCP packet can be sent + * + */ + protected void calculateDelay() { + switch (rtpSession.rtcpMode) { + case 0: + calculateRegularDelay(); + break; + default: + System.out.println("RTCPSession.calculateDelay() unknown .mode"); + } + } + + /** + * Calculates a delay value in accordance with RFC 3550 + * + */ + protected void calculateRegularDelay() { + long curTime = System.currentTimeMillis(); + + if (rtpSession.bandwidth != 0 && !this.initial + && rtpSession.partDb.ssrcTable.size() > 4) { + // RTPs mechanisms for RTCP scalability + int rand = rtpSession.random.nextInt(10000) - 5000; // between -500 + // and +500 + double randDouble = ((double) 1000 + rand) / 1000.0; + + Enumeration enu = rtpSession.partDb.getParticipants(); + while (enu.hasMoreElements()) { + Participant part = enu.nextElement(); + if (part.lastRtpPkt > this.prevTime) + senderCount++; + } + + double bw; + if (rtpSession.rtcpBandwidth > -1) { + bw = rtpSession.rtcpBandwidth; + } else { + bw = rtpSession.bandwidth * 0.05; + } + if (senderCount * 2 > rtpSession.partDb.ssrcTable.size()) { + if (rtpSession.lastTimestamp > this.prevTime) { + // We're a sender + double numerator = ((double) this.avgPktSize) + * ((double) senderCount); + double denominator = 0.25 * bw; + this.nextDelay = (int) Math.round((numerator / denominator) + * randDouble); + } else { + // We're a receiver + double numerator = ((double) this.avgPktSize) + * ((double) rtpSession.partDb.ssrcTable.size()); + double denominator = 0.75 * bw; + this.nextDelay = (int) Math.round((numerator / denominator) + * randDouble); + } + } else { + double numerator = ((double) this.avgPktSize) + * ((double) rtpSession.partDb.ssrcTable.size()); + ; + double denominator = bw; + this.nextDelay = (int) Math + .round(1000.0 * (numerator / denominator)) + * (1000 + rand); + } + } else { + // Not enough data to scale, use random values + int rand = rtpSession.random.nextInt(1000) - 500; // between -500 + // and +500 + if (this.initial) { + // 2.5 to 3.5 seconds, randomly + this.nextDelay = 3000 + rand; + this.initial = false; + } else { + // 4.5 to 5.5 seconds, randomly + this.nextDelay = 5500 + rand; + } + + } + + // preflight check + if (this.nextDelay < 1000) { + int rand = rtpSession.random.nextInt(1000) - 500; // between -500 + // and +500 + System.out + .println("RTCPSession.calculateDelay() nextDelay was too short (" + + this.nextDelay + + "ms), setting to " + + (this.nextDelay = 2000 + rand)); + } + this.prevTime = curTime; + } + + /** + * Update the average packet size + * + * @param length + * of latest packet + */ + synchronized protected void updateAvgPacket(int length) { + double tempAvg = (double) this.avgPktSize; + tempAvg = (15 * tempAvg + ((double) length)) / 16; + this.avgPktSize = (int) tempAvg; + } + + /** + * Adds an RTCP APP (application) packet to the queue + * + * @param targetSsrc + * the SSRC of the recipient + * @param aPkt + */ + synchronized protected void addToAppQueue(long targetSsrc, RtcpPktAPP aPkt) { + aPkt.time = System.currentTimeMillis(); + + if (this.appQueue == null) + this.appQueue = new Hashtable>(); + + LinkedList ll = this.appQueue.get(targetSsrc); + if (ll == null) { + // No list, create and add + ll = new LinkedList(); + this.appQueue.put(targetSsrc, ll); + } + + ll.add(aPkt); + } + + /** + * Adds an RTCP APP (application) packet to the queue + * + * @param targetSsrc + * the SSRC of the recipient + * @return array of RTCP Application packets + */ + synchronized protected RtcpPktAPP[] getFromAppQueue(long targetSsrc) { + if (this.appQueue == null) + return null; + + LinkedList ll = this.appQueue.get(targetSsrc); + if (ll == null || ll.isEmpty()) { + return null; + } else { + RtcpPktAPP[] ret = new RtcpPktAPP[ll.size()]; + ListIterator li = ll.listIterator(); + int i = 0; + while (li.hasNext()) { + ret[i] = li.next(); + i++; + } + return ret; + } + } + + /** + * Cleans the TCP APP (application) packet queues of any packets that are + * too old, defined as 60 seconds since insertion. + * + * @param ssrc + * The SSRC of the user who has left, negative value -> general + * cleanup + */ + synchronized protected void cleanAppQueue(long ssrc) { + if (this.appQueue == null) + return; + + if (ssrc > 0) { + this.appQueue.remove(ssrc); + } else { + Enumeration> enu = this.appQueue.elements(); + long curTime = System.currentTimeMillis(); + + while (enu.hasMoreElements()) { + ListIterator li = enu.nextElement().listIterator(); + while (li.hasNext()) { + RtcpPkt aPkt = li.next(); + // Remove after 60 seconds + if (curTime - aPkt.time > 60000) { + li.remove(); + } + } + } + } + } + + /** + * Check the feedback queue for similar packets and adds the new packet if + * it is not redundant + * + * @param aPkt + * @return 0 if the packet was added, 1 if it was dropped + */ + synchronized protected int addToFbQueue(long targetSsrc, RtcpPkt aPkt) { + if (this.fbQueue == null) + this.fbQueue = new Hashtable>(); + + LinkedList ll = this.fbQueue.get(targetSsrc); + if (ll == null) { + // No list, create and add + ll = new LinkedList(); + ll.add(aPkt); + this.fbQueue.put(targetSsrc, ll); + } else { + // Check for matching packets, else add to end + ListIterator li = ll.listIterator(); + while (li.hasNext()) { + RtcpPkt tmp = li.next(); + if (equivalent(tmp, aPkt)) + return -1; + } + ll.addLast(aPkt); + } + return 0; + } + + /** + * Checks whether there are ny feedback packets waiting to be sent. + * + * @param ssrc + * of the participant we are notifying + * @return all relevant feedback packets, or null + */ + synchronized protected RtcpPkt[] getFromFbQueue(long ssrc) { + if (this.fbQueue == null) + return null; + + LinkedList ll = this.fbQueue.get(ssrc); + + if (ll == null) + return null; + + ListIterator li = ll.listIterator(); + if (li.hasNext()) { + long curTime = System.currentTimeMillis(); + long maxDelay = curTime - rtpSession.fbMaxDelay; + long keepDelay = curTime - 2000; + int count = 0; + + // TODO below the indeces should be collected instead of looping + // twice + + // Clean out what we dont want and count what we want + while (li.hasNext()) { + RtcpPkt aPkt = li.next(); + if (aPkt.received) { + // This is a packet received, we keep these for + // 2000ms to avoid redundant feedback + if (aPkt.time < keepDelay) + li.remove(); + } else { + // This is a packet we havent sent yet + if (aPkt.time < maxDelay) { + li.remove(); + } else { + count++; + } + } + } + + // Gather what we want to return + if (count != 0) { + li = ll.listIterator(); + RtcpPkt[] ret = new RtcpPkt[count]; + + while (count > 0) { + RtcpPkt aPkt = li.next(); + if (!aPkt.received) { + ret[ret.length - count] = aPkt; + count--; + } + } + return ret; + } + } + + return null; + } + + /** + * Cleans the feeback queue of any packets that have expired, ie feedback + * packet that are no longer relevant. + * + * @param ssrc + * The SSRC of the user who has left, negative value -> general + * cleanup + */ + synchronized protected void cleanFbQueue(long ssrc) { + if (this.fbQueue == null) + return; + + if (ssrc > 0) { + this.fbQueue.remove(ssrc); + } else { + Enumeration> enu = this.fbQueue.elements(); + long curTime = System.currentTimeMillis(); + long maxDelay = curTime - rtpSession.fbMaxDelay; + long keepDelay = curTime - 2000; + + while (enu.hasMoreElements()) { + ListIterator li = enu.nextElement().listIterator(); + while (li.hasNext()) { + RtcpPkt aPkt = li.next(); + if (aPkt.received) { + // This is a packet received, we keep these for + // 2000ms to avoid redundant feedback + if (aPkt.time < keepDelay) + li.remove(); + } else { + // This is a packet we havent sent yet + if (aPkt.time < maxDelay) + li.remove(); + } + } + } + } + } + + /** + * Check whether the conditions are satisfied to send a feedbkac packet + * immediately. + * + * @return true if they are, false otherwise + */ + protected boolean fbSendImmediately() { + if (rtpSession.partDb.ssrcTable.size() > this.rtpSession.fbEarlyThreshold + && rtpSession.partDb.receivers.size() > this.rtpSession.fbEarlyThreshold) + return false; + + return true; + } + + /** + * Check whether the conditions are satisfied to send a feedbkac packet + * immediately. + * + * @return true if they are, false otherwise + */ + protected boolean fbSendEarly() { + if (rtpSession.partDb.ssrcTable.size() > this.rtpSession.fbRegularThreshold + && rtpSession.partDb.receivers.size() > this.rtpSession.fbRegularThreshold) + return false; + + return true; + } + + /** + * Wake the sender thread because of this ssrc + * + * @param ssrc + * that has feedback waiting. + */ + protected void wakeSenderThread(long ssrc) { + this.fbWaiting = ssrc; + this.senderThrd.interrupt(); + + // Give it a chance to catch up + try { + Thread.sleep(0, 1); + } catch (Exception e) { + } + ; + } + + /** + * Compares two packets to check whether they are equivalent feedback + * messages, to avoid sending the same feedback to a host twice. + * + * Expect false negatives, but not false positives. + * + * @param one + * packet + * @param two + * packet + * @return true if they are equivalent, false otherwise + */ + private boolean equivalent(RtcpPkt one, RtcpPkt two) { + // Cheap checks + if (one.packetType != two.packetType) + return false; + + if (one.itemCount != two.itemCount) + return false; + + if (one.packetType == 205) { + // RTP Feedback, i.e. a NACK + RtcpPktRTPFB pktone = (RtcpPktRTPFB) one; + RtcpPktRTPFB pkttwo = (RtcpPktRTPFB) two; + + if (pktone.ssrcMediaSource != pkttwo.ssrcMediaSource) + return false; + + if (Arrays.equals(pktone.BLP, pkttwo.BLP) + && Arrays.equals(pktone.BLP, pkttwo.BLP)) + return true; + + return true; + } else if (one.packetType == 206) { + RtcpPktPSFB pktone = (RtcpPktPSFB) one; + RtcpPktPSFB pkttwo = (RtcpPktPSFB) two; + + if (pktone.ssrcMediaSource != pkttwo.ssrcMediaSource) + return false; + + switch (one.itemCount) { + case 1: // Picture Loss Indication + return true; + + case 2: // Slice Loss Indication + // This will not work if the slice loss indicators are in + // different order + if (pktone.sliFirst.length == pkttwo.sliFirst.length + && Arrays.equals(pktone.sliFirst, pkttwo.sliFirst) + && Arrays.equals(pktone.sliNumber, pkttwo.sliNumber) + && Arrays.equals(pktone.sliPictureId, + pkttwo.sliPictureId)) + return true; + break; + case 3: // Reference Picture Selection Indication + if (Arrays.equals(pktone.rpsiBitString, pkttwo.rpsiBitString)) + return true; + break; + case 15: // Application Layer Feedback Messages + // This will not work if the padding scheme is different + if (pktone.sliFirst.length == pkttwo.sliFirst.length + && Arrays.equals(pktone.alfBitString, + pkttwo.alfBitString)) + return true; + break; + default: + + } + return true; + } else { + System.out + .println("!!!! RTCPSession.equivalentPackets() encountered unexpected packet type!"); + } + return false; + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RTPAppIntf.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RTPAppIntf.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,74 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package jlibrtp; + +/** + * This is the callback interface for RTP packets. + * + * It is mandatory, but you can inore the data if you like. + * + * @author Arne Kepp + */ +public interface RTPAppIntf { + + /** + * The callback method through which the application will receive data from + * jlibrtp. These calls are synchronous, so you will not receive any new + * packets until this call returns. + * + * @param frame + * the frame containing the data + * @param participant + * the participant from which the data came + */ + public void receiveData(DataFrame frame, Participant participant); + + /** + * The callback method through which the application will receive + * notifications about user updates, additions and byes. Types: 1 - Bye 2 - + * New through RTP, check .getRtpSendSock() 3 - New through RTCP, check + * .getRtcpSendSock() 4 - SDES packet received, check the getCname() etc + * methods 5 - Matched SSRC to ip-address provided by application + * + * @param type + * the type of event + * @param participant + * the participants in question + */ + public void userEvent(int type, Participant[] participant); + + /** + * The callback method through which the application can specify the number + * of packets that make up a frame for a given payload type. + * + * A negative value denotes frames of variable length, so jlibrtp will + * return whatever it has at the time. + * + * In most applications, this function can simply return 1. + * + * This should be implemented as something fast, such as an integer array + * with the indeces being the payload type. + * + * @param payloadType + * the payload type specified in the RTP packet + * @return the number of packets that make up a frame + */ + public int frameSize(int payloadType); +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RTPReceiverThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RTPReceiverThread.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,369 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetSocketAddress; +import java.net.SocketException; + +import org.sipdroid.net.tools.DatagramPool; +import org.sipdroid.net.tools.RtpPktPool; + +/** + * The RTP receiver thread waits on the designated UDP socket for new packets. + * + * Once one arrives, it is parsed and tested. We also check the ip-address of + * the sender. If accepted, the packet is added onto the packet buffer of the + * participant. + * + * A separate thread moves the packet from the packet buffer to the application. + * + * @author Arne Kepp + */ +public class RTPReceiverThread extends Thread { + /** Parent RTP Session */ + RTPSession rtpSession = null; + DatagramPool datagramPool = null; + + RTPReceiverThread(RTPSession session) { + rtpSession = session; + datagramPool = DatagramPool.getInstance(); + if (RTPSession.rtpDebugLevel > 1) { + System.out.println("<-> RTPReceiverThread created"); + } + } + public void init() { + if (RTPSession.rtpDebugLevel > 1) { + if (rtpSession.mcSession) { + System.out.println("-> RTPReceiverThread.run() starting on MC " + + rtpSession.rtpMCSock.getLocalPort()); + } else { + System.out.println("-> RTPReceiverThread.run() starting on " + + rtpSession.rtpSock.getLocalPort()); + } + } + + android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO); + + DatagramPacket packet = datagramPool.borrowPacket(); + try { + rtpSession.rtpSock.setSoTimeout(1); + for (;;) + rtpSession.rtpSock.receive(packet); + } catch (SocketException e2) { + + } catch (IOException e) { + } + datagramPool.returnPacket(packet); + try { + rtpSession.rtpSock.setSoTimeout(1000); + } catch (SocketException e2) { + } + } + + public void readPacketToBuffer() { + if (RTPSession.rtpDebugLevel > 6) { + if (rtpSession.mcSession) { + System.out + .println("-> RTPReceiverThread.run() waiting for MC packet on " + + rtpSession.rtpMCSock.getLocalPort()); + } else { + System.out + .println("-> RTPReceiverThread.run() waiting for packet on " + + rtpSession.rtpSock.getLocalPort()); + } + } + + // Prepare a packet + //DatagramPacket packet = new DatagramPacket(rawPkt, rawPkt.length); + DatagramPacket packet = datagramPool.borrowPacket(); + // Wait for it to arrive + if (!rtpSession.mcSession) { + // Unicast + try { + rtpSession.rtpSock.receive(packet); + } catch (IOException e) { + if (!rtpSession.endSession) { + e.printStackTrace(); + } else { + return; + } + } + } else { + // Multicast + try { + rtpSession.rtpMCSock.receive(packet); + } catch (IOException e) { + if (!rtpSession.endSession) { + e.printStackTrace(); + } else { + return; + } + } + } + // Parse the received RTP (?) packet + RtpPkt pkt = RtpPktPool.getInstance().borrowPkt(); + pkt.initPacket(packet.getData(), packet.getLength(), packet); + + // Check whether it was valid. + if (pkt == null) { + System.out.println("Received invalid RTP packet. Ignoring"); + return; + } + + long pktSsrc = pkt.getSsrc(); + + // Check for loops and SSRC collisions + if (rtpSession.ssrc == pktSsrc) + rtpSession.resolveSsrcConflict(); + + long[] csrcArray = pkt.getCsrcArray(); + if (csrcArray != null) { + for (int i = 0; i < csrcArray.length; i++) { + if (csrcArray[i] == rtpSession.ssrc) + ; + rtpSession.resolveSsrcConflict(); + } + } + + if (RTPSession.rtpDebugLevel > 17) { + System.out + .println("-> RTPReceiverThread.run() rcvd packet, seqNum " + + pktSsrc); + if (RTPSession.rtpDebugLevel > 10) { + System.out.println("-> RTPReceiverThread.run() payload is " + + pkt.getPayloadLength()); + } + } + + // Find the participant in the database based on SSRC + //Participant part = rtpSession.partDb.getParticipant(pktSsrc); + Participant part = rtpSession.firstPart; + if (part == null) { + InetSocketAddress nullSocket = null; + part = new Participant((InetSocketAddress) packet + .getSocketAddress(), nullSocket, pkt.getSsrc()); + part.unexpected = true; + rtpSession.partDb.addParticipant(1, part); + } + + // Do checks on whether the datagram came from the expected source + // for that SSRC. + + if (part.rtpAddress == null + || packet.getAddress().equals(part.rtpAddress.getAddress())) { + PktBuffer pktBuffer = part.pktBuffer; + + if (pktBuffer != null) { + // A buffer already exists, append to it + pktBuffer.addPkt(pkt); + } else { + // Create a new packet/frame buffer + pktBuffer = new PktBuffer(this.rtpSession, part, pkt); + part.pktBuffer = pktBuffer; + } + } else { + System.out + .println("RTPReceiverThread: Got an unexpected packet from " + + pkt.getSsrc() + + " the sending ip-address was " + + packet.getAddress().toString() + + ", we expected from " + + part.rtpAddress.toString()); + } + + // Statistics for receiver report. + part.updateRRStats(packet.getLength(), pkt); + // Upate liveness + part.lastRtpPkt = System.currentTimeMillis(); + + if (RTPSession.rtpDebugLevel > 5) { + System.out + .println("<-> RTPReceiverThread signalling pktBufDataReady"); + } + + // Signal the thread that pushes data to application + /*rtpSession.pktBufLock.lock(); + try { + rtpSession.pktBufDataReady.signalAll(); + } finally { + rtpSession.pktBufLock.unlock(); + }*/ + } + + public void run() { + if (RTPSession.rtpDebugLevel > 1) { + if (rtpSession.mcSession) { + System.out.println("-> RTPReceiverThread.run() starting on MC " + + rtpSession.rtpMCSock.getLocalPort()); + } else { + System.out.println("-> RTPReceiverThread.run() starting on " + + rtpSession.rtpSock.getLocalPort()); + } + } + + android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO); + + DatagramPacket packet = datagramPool.borrowPacket(); + try { + rtpSession.rtpSock.setSoTimeout(1); + for (;;) + rtpSession.rtpSock.receive(packet); + } catch (SocketException e2) { + + } catch (IOException e) { + } + datagramPool.returnPacket(packet); + try { + rtpSession.rtpSock.setSoTimeout(0); + } catch (SocketException e2) { + } + while (!rtpSession.endSession) { + if (RTPSession.rtpDebugLevel > 6) { + if (rtpSession.mcSession) { + System.out + .println("-> RTPReceiverThread.run() waiting for MC packet on " + + rtpSession.rtpMCSock.getLocalPort()); + } else { + System.out + .println("-> RTPReceiverThread.run() waiting for packet on " + + rtpSession.rtpSock.getLocalPort()); + } + } + + // Prepare a packet + //DatagramPacket packet = new DatagramPacket(rawPkt, rawPkt.length); + packet = datagramPool.borrowPacket(); + // Wait for it to arrive + if (!rtpSession.mcSession) { + // Unicast + try { + rtpSession.rtpSock.receive(packet); + } catch (IOException e) { + if (!rtpSession.endSession) { + e.printStackTrace(); + } else { + continue; + } + } + } else { + // Multicast + try { + rtpSession.rtpMCSock.receive(packet); + } catch (IOException e) { + if (!rtpSession.endSession) { + e.printStackTrace(); + } else { + continue; + } + } + } + // Parse the received RTP (?) packet + RtpPkt pkt = RtpPktPool.getInstance().borrowPkt(); + pkt.initPacket(packet.getData(), packet.getLength(), packet); + + // Check whether it was valid. + if (pkt == null) { + System.out.println("Received invalid RTP packet. Ignoring"); + continue; + } + + long pktSsrc = pkt.getSsrc(); + + // Check for loops and SSRC collisions + if (rtpSession.ssrc == pktSsrc) + rtpSession.resolveSsrcConflict(); + + long[] csrcArray = pkt.getCsrcArray(); + if (csrcArray != null) { + for (int i = 0; i < csrcArray.length; i++) { + if (csrcArray[i] == rtpSession.ssrc) + ; + rtpSession.resolveSsrcConflict(); + } + } + + if (RTPSession.rtpDebugLevel > 17) { + System.out + .println("-> RTPReceiverThread.run() rcvd packet, seqNum " + + pktSsrc); + if (RTPSession.rtpDebugLevel > 10) { + System.out.println("-> RTPReceiverThread.run() payload is " + + pkt.getPayloadLength()); + } + } + + // Find the participant in the database based on SSRC + Participant part = rtpSession.partDb.getParticipant(pktSsrc); + + if (part == null) { + InetSocketAddress nullSocket = null; + part = new Participant((InetSocketAddress) packet + .getSocketAddress(), nullSocket, pkt.getSsrc()); + part.unexpected = true; + rtpSession.partDb.addParticipant(1, part); + } + + // Do checks on whether the datagram came from the expected source + // for that SSRC. + if (part.rtpAddress == null + || packet.getAddress().equals(part.rtpAddress.getAddress())) { + PktBuffer pktBuffer = part.pktBuffer; + + if (pktBuffer != null) { + // A buffer already exists, append to it + pktBuffer.addPkt(pkt); + } else { + // Create a new packet/frame buffer + pktBuffer = new PktBuffer(this.rtpSession, part, pkt); + part.pktBuffer = pktBuffer; + } + } else { + System.out + .println("RTPReceiverThread: Got an unexpected packet from " + + pkt.getSsrc() + + " the sending ip-address was " + + packet.getAddress().toString() + + ", we expected from " + + part.rtpAddress.toString()); + } + + // Statistics for receiver report. + part.updateRRStats(packet.getLength(), pkt); + // Upate liveness + part.lastRtpPkt = System.currentTimeMillis(); + + if (RTPSession.rtpDebugLevel > 5) { + System.out + .println("<-> RTPReceiverThread signalling pktBufDataReady"); + } + + // Signal the thread that pushes data to application + rtpSession.pktBufLock.lock(); + try { + rtpSession.pktBufDataReady.signalAll(); + } finally { + rtpSession.pktBufLock.unlock(); + } + + } + } + +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RTPSession.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RTPSession.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,1301 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.MulticastSocket; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Random; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.sipdroid.net.tools.DataFramePool; +import org.sipdroid.net.tools.DatagramPool; +import org.sipdroid.net.tools.PktBufNodePool; +import org.sipdroid.net.tools.RtpPktPool; + +import android.util.Log; + +/** + * The RTPSession object is the core of jlibrtp. + * + * One should be instantiated for every communication channel, i.e. if you send + * voice and video, you should create one for each. + * + * The instance holds a participant database, as well as other information about + * the session. When the application registers with the session, the necessary + * threads for receiving and processing RTP packets are spawned. + * + * RTP Packets are sent synchronously, all other operations are asynchronous. + * + * @author Arne Kepp + */ +public class RTPSession { + /** + * The debug level is final to avoid compilation of if-statements.
0 + * provides no debugging information, 20 provides everything
Debug + * output is written to System.out
Debug level for RTP related things. + */ + final static public int rtpDebugLevel = 1; + /** + * The debug level is final to avoid compilation of if-statements.
0 + * provides no debugging information, 20 provides everything
Debug + * output is written to System.out
Debug level for RTCP related things. + */ + final static public int rtcpDebugLevel = 1; + + /** RTP unicast socket */ + protected DatagramSocket rtpSock = null; + /** RTP multicast socket */ + protected MulticastSocket rtpMCSock = null; + /** RTP multicast group */ + protected InetAddress mcGroup = null; + + // Internal state + /** Whether this session is a multicast session or not */ + protected boolean mcSession = false; + /** Current payload type, can be changed by application */ + protected int payloadType = 0; + /** SSRC of this session */ + protected long ssrc; + /** The last timestamp when we sent something */ + protected long lastTimestamp = 0; + /** Current sequence number */ + protected int seqNum = 0; + /** Number of packets sent by this session */ + protected int sentPktCount = 0; + /** Number of octets sent by this session */ + protected int sentOctetCount = 0; + + /** The random seed */ + protected Random random = null; + + /** Session bandwidth in BYTES per second */ + protected int bandwidth = 8000; + + /** By default we do not return packets from strangers in unicast mode */ + protected boolean naiveReception = false; + + /** Should the library attempt frame reconstruction? */ + protected boolean frameReconstruction = true; + + /** Maximum number of packets used for reordering */ + protected int pktBufBehavior = 3; + + /** Participant database */ + protected ParticipantDatabase partDb = new ParticipantDatabase(this); + /** First participant */ + protected Participant firstPart; + /** Handle to application interface for RTP */ + protected RTPAppIntf appIntf = null; + /** Handle to application interface for RTCP (optional) */ + protected RTCPAppIntf rtcpAppIntf = null; + /** Handle to application interface for AVPF, RFC 4585 (optional) */ + protected RTCPAVPFIntf rtcpAVPFIntf = null; + /** Handle to application interface for debugging */ + protected DebugAppIntf debugAppIntf = null; + + /** The RTCP session associated with this RTP Session */ + protected RTCPSession rtcpSession = null; + /** The thread for receiving RTP packets */ + protected RTPReceiverThread recvThrd = null; + /** The thread for invoking callbacks for RTP packets */ + protected AppCallerThread appCallerThrd = null; + + /** Lock to protect the packet buffers */ + final protected Lock pktBufLock = new ReentrantLock(); + /** Condition variable, to tell the */ + final protected Condition pktBufDataReady = pktBufLock.newCondition(); + + /** Enough is enough, set to true when you want to quit. */ + protected boolean endSession = false; + /** Only one registered application, please */ + protected boolean registered = false; + /** We're busy resolving a SSRC conflict, please try again later */ + protected boolean conflict = false; + /** Number of conflicts observed, exessive number suggests loop in network */ + protected int conflictCount = 0; + + /** SDES CNAME */ + protected String cname = null; + /** SDES The participant's real name */ + public String name = null; + /** SDES The participant's email */ + public String email = null; + /** SDES The participant's phone number */ + public String phone = null; + /** SDES The participant's location */ + public String loc = null; + /** SDES The tool the participants is using */ + public String tool = null; + /** SDES A note */ + public String note = null; + /** SDES A priv string, loosely defined */ + public String priv = null; + + // RFC 4585 stuff. This should live on RTCPSession, but we need to have this + // infromation ready by the time the RTCP Session starts + // 0 = RFC 3550 , -1 = ACK , 1 = Immediate feedback, 2 = Early RTCP, + protected int rtcpMode = 0; + protected int fbEarlyThreshold = -1; // group size, immediate -> early + // transition point + protected int fbRegularThreshold = -1; // group size, early -> regular + // transition point + protected int minInterval = 5000; // minimum interval + protected int fbMaxDelay = 1000; // how long the information is useful + // RTCP bandwidth + protected int rtcpBandwidth = -1; + + /** + * Returns an instance of a unicast RTP session. Following this you + * should adjust any settings and then register your application. + * + * The sockets should have external ip addresses, else your CNAME + * automatically generated CNAMe will be bad. + * + * @param rtpSocket + * UDP socket to receive RTP communication on + * @param rtcpSocket + * UDP socket to receive RTCP communication on, null if none. + */ + public RTPSession(DatagramSocket rtpSocket, DatagramSocket rtcpSocket) { + mcSession = false; + rtpSock = rtpSocket; + this.generateCNAME(); + this.generateSsrc(); + this.rtcpSession = new RTCPSession(this, rtcpSocket); + // The sockets are not always imediately available? + try { + Thread.sleep(1); + } catch (InterruptedException e) { + System.out.println("RTPSession sleep failed"); + } + } + + /** + * Returns an instance of a multicast RTP session. Following this you + * should register your application. + * + * The sockets should have external ip addresses, else your CNAME + * automatically generated CNAMe will be bad. + * + * @param rtpSock + * a multicast socket to receive RTP communication on + * @param rtcpSock + * a multicast socket to receive RTP communication on + * @param multicastGroup + * the multicast group that we want to communicate with. + */ + public RTPSession(MulticastSocket rtpSock, MulticastSocket rtcpSock, + InetAddress multicastGroup) throws Exception { + mcSession = true; + rtpMCSock = rtpSock; + mcGroup = multicastGroup; + rtpMCSock.joinGroup(mcGroup); + rtcpSock.joinGroup(mcGroup); + this.generateCNAME(); + this.generateSsrc(); + this.rtcpSession = new RTCPSession(this, rtcpSock, mcGroup); + + // The sockets are not always imediately available? + try { + Thread.sleep(1); + } catch (InterruptedException e) { + System.out.println("RTPSession sleep failed"); + } + } + + /** + * Registers an application (RTPAppIntf) with the RTP session. The session + * will call receiveData() on the supplied instance whenever data has been + * received. + * + * Following this you should set the payload type and add participants to + * the session. + * + * @param rtpApp + * an object that implements the RTPAppIntf-interface + * @param rtcpApp + * an object that implements the RTCPAppIntf-interface (optional) + * @return -1 if this RTPSession-instance already has an application + * registered. + */ + public int RTPSessionRegister(RTPAppIntf rtpApp, RTCPAppIntf rtcpApp, + DebugAppIntf debugApp) { + if (registered) { + System.out + .println("RTPSessionRegister(): Can\'t register another application!"); + return -1; + } else { + + registered = true; + generateSeqNum(); + if (RTPSession.rtpDebugLevel > 0) { + System.out.println("-> RTPSessionRegister"); + } + this.appIntf = rtpApp; + this.rtcpAppIntf = rtcpApp; + this.debugAppIntf = debugApp; + + recvThrd = new RTPReceiverThread(this); + appCallerThrd = new AppCallerThread(this, rtpApp); + //recvThrd.start(); + //appCallerThrd.start(); + //rtcpSession.start(); + return 0; + } + } + + public AppCallerThread getAppCallerThrd() { + return appCallerThrd; + } + + public RTPReceiverThread getRTPRecvThrd() { + return recvThrd; + } + + /** + * Send data to all participants registered as receivers, using the current + * timeStamp, dynamic sequence number and the current payload type specified + * for the session. + * + * @param buf + * A buffer of bytes, less than 1496 bytes + * @return null if there was a problem, {RTP Timestamp, Sequence number} + * otherwise + */ + public long[] sendData(byte[] buf) { + byte[][] tmp = { buf }; + long[][] ret = this.sendData(tmp, null, null, -1, null); + + if (ret != null) + return ret[0]; + + return null; + } + + /** + * Send data to all participants registered as receivers, using the + * specified timeStamp, sequence number and the current payload type + * specified for the session. + * + * @param buf + * A buffer of bytes, less than 1496 bytes + * @param rtpTimestamp + * the RTP timestamp to be used in the packet + * @param seqNum + * the sequence number to be used in the packet + * @return null if there was a problem, {RTP Timestamp, Sequence number} + * otherwise + */ + public long[] sendData(byte[] buf, long rtpTimestamp, long seqNum) { + byte[][] tmp = { buf }; + long[][] ret = this.sendData(tmp, null, null, -1, null); + + if (ret != null) + return ret[0]; + + return null; + } + + /** + * Send data to all participants registered as receivers, using the current + * timeStamp and payload type. The RTP timestamp will be the same for all + * the packets. + * + * @param buffers + * A buffer of bytes, should not bed padded and less than 1500 + * bytes on most networks. + * @param csrcArray + * an array with the SSRCs of contributing sources + * @param markers + * An array indicating what packets should be marked. Rarely + * anything but the first one + * @param rtpTimestamp + * The RTP timestamp to be applied to all packets + * @param seqNumbers + * An array with the sequence number associated with each byte[] + * @return null if there was a problem sending the packets, 2-dim array with + * {RTP Timestamp, Sequence number} + */ + public long[][] sendData(byte[][] buffers, long[] csrcArray, + boolean[] markers, long rtpTimestamp, long[] seqNumbers) { + if (RTPSession.rtpDebugLevel > 5) { + System.out.println("-> RTPSession.sendData(byte[])"); + } + + // Same RTP timestamp for all + if (rtpTimestamp < 0) + rtpTimestamp = System.currentTimeMillis(); + + // Return values + long[][] ret = new long[buffers.length][2]; + + for (int i = 0; i < buffers.length; i++) { + byte[] buf = buffers[i]; + + boolean marker = false; + if (markers != null) + marker = markers[i]; + + if (buf.length > 1500) { + System.out + .println("RTPSession.sendData() called with buffer exceeding 1500 bytes (" + + buf.length + ")"); + } + + // Get the return values + ret[i][0] = rtpTimestamp; + if (seqNumbers == null) { + ret[i][1] = getNextSeqNum(); + } else { + ret[i][1] = seqNumbers[i]; + } + // Create a new RTP Packet + RtpPkt pkt = RtpPktPool.getInstance().borrowPkt(); + pkt.initPacket(rtpTimestamp, this.ssrc, (int) ret[i][1], + this.payloadType, buf); + + if (csrcArray != null) + pkt.setCsrcs(csrcArray); + + pkt.setMarked(marker); + + // Creates a raw packet + byte[] pktBytes = pkt.encode(); + + // System.out.println(Integer.toString(StaticProcs.bytesToUIntInt(pktBytes, + // 2))); + + // Pre-flight check, are resolving an SSRC conflict? + if (this.conflict) { + System.out + .println("RTPSession.sendData() called while trying to resolve conflict."); + return null; + } + + if (this.mcSession) { + DatagramPacket packet = null; + + try { + packet = new DatagramPacket(pktBytes, pktBytes.length, + this.mcGroup, this.rtpMCSock.getPort()); + } catch (Exception e) { + System.out + .println("RTPSession.sendData() packet creation failed."); + e.printStackTrace(); + return null; + } + + try { + rtpMCSock.send(packet); + // Debug + if (this.debugAppIntf != null) { + this.debugAppIntf.packetSent(1, + (InetSocketAddress) packet.getSocketAddress(), + new String("Sent multicast RTP packet of size " + + packet.getLength() + + " to " + + packet.getSocketAddress().toString() + + " via " + + rtpMCSock.getLocalSocketAddress() + .toString())); + } + } catch (Exception e) { + System.out + .println("RTPSession.sendData() multicast failed."); + e.printStackTrace(); + return null; + } + + } else { + // Loop over recipients + Iterator iter = partDb.getUnicastReceivers(); + while (iter.hasNext()) { + InetSocketAddress receiver = iter.next().rtpAddress; + DatagramPacket packet = null; + + if (RTPSession.rtpDebugLevel > 15) { + System.out.println(" Sending to " + + receiver.toString()); + } + + try { + packet = new DatagramPacket(pktBytes, pktBytes.length, + receiver); + } catch (Exception e) { + System.out + .println("RTPSession.sendData() packet creation failed."); + e.printStackTrace(); + return null; + } + + // Actually send the packet + try { + rtpSock.send(packet); + // Debug + if (this.debugAppIntf != null) { + this.debugAppIntf + .packetSent( + 0, + (InetSocketAddress) packet + .getSocketAddress(), + new String( + "Sent unicast RTP packet of size " + + packet + .getLength() + + " to " + + packet + .getSocketAddress() + .toString() + + " via " + + rtpSock + .getLocalSocketAddress() + .toString())); + } + } catch (Exception e) { + System.out + .println("RTPSession.sendData() unicast failed."); + e.printStackTrace(); + return null; + } + } + } + + // Update our stats + this.sentPktCount++; + this.sentOctetCount++; + + if (RTPSession.rtpDebugLevel > 5) { + System.out.println("<- RTPSession.sendData(byte[]) " + + pkt.getSeqNumber()); + } + } + + return ret; + } + + public void sendData(DatagramPacket packet, RtpPkt pkt) { + if (RTPSession.rtpDebugLevel > 5) { + System.out.println("-> RTPSession.sendData(byte[])"); + } + + pkt.setTimeStamp(System.currentTimeMillis()); + pkt.setSsrc(ssrc); + pkt.setSeqNumber(getNextSeqNum()); + + // Creates a raw packet + pkt.writeHeader(); + + // Pre-flight check, are resolving an SSRC conflict? + if (this.conflict) { + System.out + .println("RTPSession.sendData() called while trying to resolve conflict."); + return; + } + + if (this.mcSession) { + try { + packet.setPort(this.rtpMCSock.getPort()); + packet.setAddress(this.mcGroup); + } catch (Exception e) { + System.out + .println("RTPSession.sendData() packet creation failed."); + e.printStackTrace(); + return; + } + try { + rtpMCSock.send(packet); + // Debug + if (this.debugAppIntf != null) { + this.debugAppIntf.packetSent(1, + (InetSocketAddress) packet.getSocketAddress(), + new String("Sent multicast RTP packet of size " + + packet.getLength() + + " to " + + packet.getSocketAddress().toString() + + " via " + + rtpMCSock.getLocalSocketAddress() + .toString())); + } + } catch (Exception e) { + System.out + .println("RTPSession.sendData() multicast failed."); + e.printStackTrace(); + return; + } + + } else { + try { + packet.setSocketAddress(firstPart.rtpAddress); + } catch (Exception e) { + System.out + .println("RTPSession.sendData() packet creation failed."); + e.printStackTrace(); + return; + } + + // Actually send the packet + try { + + rtpSock.send(packet); + //Log.d("RTP", "packet"); + // Debug + if (this.debugAppIntf != null) { + this.debugAppIntf + .packetSent( + 0, + (InetSocketAddress) packet + .getSocketAddress(), + new String( + "Sent unicast RTP packet of size " + + packet + .getLength() + + " to " + + packet + .getSocketAddress() + .toString() + + " via " + + rtpSock + .getLocalSocketAddress() + .toString())); + } + } catch (Exception e) { + System.out + .println("RTPSession.sendData() unicast failed."); + e.printStackTrace(); + return; + } + } + + // Update our stats + this.sentPktCount++; + this.sentOctetCount++; + if (RTPSession.rtpDebugLevel > 5) { + System.out.println("<- RTPSession.sendData(byte[]) " + + pkt.getSeqNumber()); + } + } + + /** + * Send RTCP App packet to receiver specified by ssrc + * + * + * + * Return values: 0 okay -1 no RTCP session established -2 name is not + * byte[4]; -3 data is not byte[x], where x = 4*y for syme y -4 type is not + * a 5 bit unsigned integer + * + * Note that a return value of 0 does not guarantee delivery. The + * participant must also exist in the participant database, otherwise the + * message will eventually be deleted. + * + * @param ssrc + * of the participant you want to reach + * @param type + * the RTCP App packet subtype, default 0 + * @param name + * the ASCII (in byte[4]) representation + * @param data + * the data itself + * @return 0 if okay, negative value otherwise (see above) + */ + + public int sendRTCPAppPacket(long ssrc, int type, byte[] name, byte[] data) { + if (this.rtcpSession == null) + return -1; + + if (name.length != 4) + return -2; + + if (data.length % 4 != 0) + return -3; + + if (type > 63 || type < 0) + return -4; + + RtcpPktAPP pkt = new RtcpPktAPP(ssrc, type, name, data); + this.rtcpSession.addToAppQueue(ssrc, pkt); + + return 0; + } + + /** + * Add a participant object to the participant database. + * + * If packets have already been received from this user, we will try to + * update the automatically inserted participant with the information + * provided here. + * + * @param p + * A participant. + */ + public int addParticipant(Participant p) { + // For now we make all participants added this way persistent + firstPart = p; + p.unexpected = false; + return this.partDb.addParticipant(0, p); + } + + /** + * Remove a participant from the database. All buffered packets will be + * destroyed. + * + * @param p + * A participant. + */ + public void removeParticipant(Participant p) { + partDb.removeParticipant(p); + } + + public Iterator getUnicastReceivers() { + return partDb.getUnicastReceivers(); + } + + public Enumeration getParticipants() { + return partDb.getParticipants(); + } + + /** + * End the RTP Session. This will halt all threads and send bye-messages to + * other participants. + * + * RTCP related threads may require several seconds to wake up and + * terminate. + */ + public void endSession() { + this.endSession = true; + + // No more RTP packets, please + if (this.mcSession) { + this.rtpMCSock.close(); + } else { + this.rtpSock.close(); + } + + // Signal the thread that pushes data to application + this.pktBufLock.lock(); + try { + this.pktBufDataReady.signalAll(); + } finally { + this.pktBufLock.unlock(); + } + // Interrupt what may be sleeping + //this.rtcpSession.senderThrd.interrupt(); + + // Give things a chance to cool down. + try { + Thread.sleep(50); + } catch (Exception e) { + } + ; + + this.appCallerThrd.interrupt(); + + // Give things a chance to cool down. + try { + Thread.sleep(50); + } catch (Exception e) { + } + ; + + if (this.rtcpSession != null) { + // No more RTP packets, please + if (this.mcSession) { + this.rtcpSession.rtcpMCSock.close(); + } else { + this.rtcpSession.rtcpSock.close(); + } + } + DatagramPool.removeInstance(); + PktBufNodePool.removeInstance(); + DataFramePool.removeInstance(); + RtpPktPool.removeInstance(); + } + + /** + * Check whether this session is ending. + * + * @return true if session and associated threads are terminating. + */ + boolean isEnding() { + return this.endSession; + } + + /** + * Overrides CNAME, used for outgoing RTCP packets. + * + * @param cname + * a string, e.g. username@hostname. Must be unique for session. + */ + public void CNAME(String cname) { + this.cname = cname; + } + + /** + * Get the current CNAME, used for outgoing SDES packets + */ + public String CNAME() { + return this.cname; + } + + public long getSsrc() { + return this.ssrc; + } + + private void generateCNAME() { + String hostname; + + if (this.mcSession) { + hostname = this.rtpMCSock.getLocalAddress().getCanonicalHostName(); + } else { + hostname = this.rtpSock.getLocalAddress().getCanonicalHostName(); + } + + // if(hostname.equals("0.0.0.0") && System.getenv("HOSTNAME") != null) { + // hostname = System.getenv("HOSTNAME"); + // } + + cname = System.getProperty("user.name") + "@" + hostname; + } + + /** + * Change the RTP socket of the session. Peers must be notified through SIP + * or other signalling protocol. Only valid if this is a unicast session to + * begin with. + * + * @param newSock + * integer for new port number, check it is free first. + */ + public int updateRTPSock(DatagramSocket newSock) { + if (!mcSession) { + rtpSock = newSock; + return 0; + } else { + System.out.println("Can't switch from multicast to unicast."); + return -1; + } + } + + /** + * Change the RTCP socket of the session. Peers must be notified through SIP + * or other signalling protocol. Only valid if this is a unicast session to + * begin with. + * + * @param newSock + * the new unicast socket for RTP communication. + */ + public int updateRTCPSock(DatagramSocket newSock) { + if (!mcSession) { + this.rtcpSession.rtcpSock = newSock; + return 0; + } else { + System.out.println("Can't switch from multicast to unicast."); + return -1; + } + } + + /** + * Change the RTP multicast socket of the session. Peers must be notified + * through SIP or other signalling protocol. Only valid if this is a + * multicast session to begin with. + * + * @param newSock + * the new multicast socket for RTP communication. + */ + public int updateRTPSock(MulticastSocket newSock) { + if (mcSession) { + this.rtpMCSock = newSock; + return 0; + } else { + System.out.println("Can't switch from unicast to multicast."); + return -1; + } + } + + /** + * Change the RTCP multicast socket of the session. Peers must be notified + * through SIP or other signalling protocol. Only valid if this is a + * multicast session to begin with. + * + * @param newSock + * the new multicast socket for RTCP communication. + */ + public int updateRTCPSock(MulticastSocket newSock) { + if (mcSession) { + this.rtcpSession.rtcpMCSock = newSock; + return 0; + } else { + System.out.println("Can't switch from unicast to multicast."); + return -1; + } + } + + /** + * Update the payload type used for the session. It is represented as a 7 + * bit integer, whose meaning must be negotiated elsewhere (see IETF RFCs 3550 and 3551) + * + * @param payloadT + * an integer representing the payload type of any subsequent + * packets that are sent. + */ + public int payloadType(int payloadT) { + if (payloadT > 128 || payloadT < 0) { + return -1; + } else { + this.payloadType = payloadT; + return this.payloadType; + } + } + + /** + * Get the payload type that is currently used for outgoing RTP packets. + * + * @return payload type as integer + */ + public int payloadType() { + return this.payloadType; + } + + /** + * Should packets from unknown participants be returned to the application? + * This can be dangerous. + * + * @param doAccept + * packets from participants not added by the application. + */ + public void naivePktReception(boolean doAccept) { + naiveReception = doAccept; + } + + /** + * Are packets from unknown participants returned to the application? + * + * @return whether we accept packets from participants not added by the + * application. + */ + public boolean naivePktReception() { + return naiveReception; + } + + /** + * Set the number of RTP packets that should be buffered when a packet is + * missing or received out of order. Setting this number high increases the + * chance of correctly reordering packets, but increases latency when a + * packet is dropped by the network. + * + * Packets that arrive in order are not affected, they are passed straight + * to the application. + * + * The maximum delay is numberofPackets * packet rate , where the packet + * rate depends on the codec and profile used by the sender. + * + * Valid values: >0 - The maximum number of packets (based on RTP Timestamp) + * that may accumulate 0 - All valid packets received in order will be given + * to the application -1 - All valid packets will be given to the + * application + * + * @param behavior + * the be + * @return the behavior set, unchanged in the case of a erroneous value + */ + public int packetBufferBehavior(int behavior) { + if (behavior > -2) { + this.pktBufBehavior = behavior; + // Signal the thread that pushes data to application + this.pktBufLock.lock(); + try { + this.pktBufDataReady.signalAll(); + } finally { + this.pktBufLock.unlock(); + } + return this.pktBufBehavior; + } else { + return this.pktBufBehavior; + } + } + + /** + * The number of RTP packets that should be buffered when a packet is + * missing or received out of order. A high number increases the chance of + * correctly reordering packets, but increases latency when a packet is + * dropped by the network. + * + * A negative value disables the buffering, out of order packets will simply + * be dropped. + * + * @return the maximum number of packets that can accumulate before the + * first is returned + */ + public int packetBufferBehavior() { + return this.pktBufBehavior; + } + + /** + * Set whether the stack should operate in RFC 4585 mode. + * + * This will automatically call adjustPacketBufferBehavior(-1), i.e. disable + * all RTP packet buffering in jlibrtp, and disable frame reconstruction + * + * @param rtcpAVPFIntf + * the in + */ + public int registerAVPFIntf(RTCPAVPFIntf rtcpAVPFIntf, int maxDelay, + int earlyThreshold, int regularThreshold) { + if (this.rtcpSession != null) { + this.packetBufferBehavior(-1); + this.frameReconstruction = false; + this.rtcpAVPFIntf = rtcpAVPFIntf; + this.fbEarlyThreshold = earlyThreshold; + this.fbRegularThreshold = regularThreshold; + return 0; + } else { + return -1; + } + } + + /** + * Unregisters the RTCP AVPF interface, thereby going from RFC 4585 mode to + * RFC 3550 + * + * You still have to adjust packetBufferBehavior() and frameReconstruction. + * + */ + public void unregisterAVPFIntf() { + this.fbEarlyThreshold = -1; + this.fbRegularThreshold = -1; + this.rtcpAVPFIntf = null; + } + + /** + * Enable / disable frame reconstruction in the packet buffers. This is only + * relevant if getPacketBufferBehavior > 0; + * + * Default is true. + */ + public void frameReconstruction(boolean toggle) { + this.frameReconstruction = toggle; + } + + /** + * Whether the packet buffer will attempt to reconstruct packet + * automatically. + * + * @return the status + */ + public boolean frameReconstruction() { + return this.frameReconstruction; + } + + /** + * The bandwidth currently allocated to the session, in bytes per second. + * The default is 8000. + * + * This value is not enforced and currently only used to calculate the RTCP + * interval to ensure the control messages do not exceed 5% of the total + * bandwidth described here. + * + * Since the actual value may change a conservative estimate should be used + * to avoid RTCP flooding. + * + * see rtcpBandwidth(void) + * + * @return current bandwidth setting + */ + public int sessionBandwidth() { + return this.bandwidth; + } + + /** + * Set the bandwidth of the session. + * + * See sessionBandwidth(void) for details. + * + * @param bandwidth + * the new value requested, in bytes per second + * @return the actual value set + */ + public int sessionBandwidth(int bandwidth) { + if (bandwidth < 1) { + this.bandwidth = 8000; + } else { + this.bandwidth = bandwidth; + } + return this.bandwidth; + } + + /** + * RFC 3550 dictates that 5% of the total bandwidth, as set by + * sessionBandwidth, should be dedicated to RTCP traffic. This + * + * This should normally not be done, but is permissible in conjunction with + * feedback (RFC 4585) and possibly other profiles. + * + * Also see sessionBandwidth(void) + * + * @return current RTCP bandwidth setting, -1 means not in use + */ + public int rtcpBandwidth() { + return this.rtcpBandwidth; + } + + /** + * Set the RTCP bandwidth, see rtcpBandwidth(void) for details. + * + * This function must be + * + * @param bandwidth + * the new value requested, in bytes per second or -1 to disable + * @return the actual value set + */ + public int rtcpBandwidth(int bandwidth) { + if (bandwidth < -1) { + this.rtcpBandwidth = -1; + } else { + this.rtcpBandwidth = bandwidth; + } + return this.rtcpBandwidth; + } + + /********************************************* Feedback message stuff ***************************************/ + + /** + * Adds a Picture Loss Indication to the feedback queue + * + * @param ssrcMediaSource + * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant + */ + public int fbPictureLossIndication(long ssrcMediaSource) { + int ret = 0; + + if (this.rtcpAVPFIntf == null) + return -1; + + RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource); + pkt.makePictureLossIndication(); + ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt); + if (ret == 0) + this.rtcpSession.wakeSenderThread(ssrcMediaSource); + return ret; + } + + /** + * Adds a Slice Loss Indication to the feedback queue + * + * @param ssrcMediaSource + * @param sliFirst + * macroblock (MB) address of the first lost macroblock + * @param sliNumber + * number of lost macroblocks + * @param sliPictureId + * six least significant bits of the codec-specific identif + * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant + */ + public int fbSlicLossIndication(long ssrcMediaSource, int[] sliFirst, + int[] sliNumber, int[] sliPictureId) { + int ret = 0; + if (this.rtcpAVPFIntf == null) + return -1; + + RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource); + pkt.makeSliceLossIndication(sliFirst, sliNumber, sliPictureId); + + ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt); + if (ret == 0) + this.rtcpSession.wakeSenderThread(ssrcMediaSource); + return ret; + } + + /** + * Adds a Reference Picture Selection Indication to the feedback queue + * + * @param ssrcMediaSource + * @param bitPadding + * number of padded bits at end of bitString + * @param payloadType + * RTP payload type for codec + * @param bitString + * RPSI information as natively defined by the video codec + * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant + */ + public int fbRefPictureSelIndic(long ssrcMediaSource, int bitPadding, + int payloadType, byte[] bitString) { + int ret = 0; + + if (this.rtcpAVPFIntf == null) + return -1; + + RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource); + pkt.makeRefPictureSelIndic(bitPadding, payloadType, bitString); + ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt); + if (ret == 0) + this.rtcpSession.wakeSenderThread(ssrcMediaSource); + return ret; + } + + /** + * Adds a Picture Loss Indication to the feedback queue + * + * @param ssrcMediaSource + * @param bitString + * the original application message + * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant + */ + public int fbAppLayerFeedback(long ssrcMediaSource, byte[] bitString) { + int ret = 0; + + if (this.rtcpAVPFIntf == null) + return -1; + + RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource); + pkt.makeAppLayerFeedback(bitString); + ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt); + if (ret == 0) + this.rtcpSession.wakeSenderThread(ssrcMediaSource); + return ret; + } + + /** + * Adds a RTP Feedback packet to the feedback queue. + * + * These are mostly used for NACKs. + * + * @param ssrcMediaSource + * @param FMT + * the Feedback Message Subtype + * @param PID + * RTP sequence numbers of lost packets + * @param BLP + * bitmask of following lost packets, shared index with PID + * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant + */ + public int fbPictureLossIndication(long ssrcMediaSource, int FMT, + int[] PID, int[] BLP) { + int ret = 0; + + if (this.rtcpAVPFIntf == null) + return -1; + + RtcpPktRTPFB pkt = new RtcpPktRTPFB(this.ssrc, ssrcMediaSource, FMT, + PID, BLP); + ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt); + if (ret == 0) + this.rtcpSession.wakeSenderThread(ssrcMediaSource); + return ret; + } + + /** + * Fetches the next sequence number for RTP packets. + * + * @return the next sequence number + */ + private int getNextSeqNum() { + seqNum++; + // 16 bit number + if (seqNum > 65536) { + seqNum = 0; + } + return seqNum; + } + + /** + * Initializes a random variable + * + */ + private void createRandom() { + this.random = new Random(System.currentTimeMillis() + + Thread.currentThread().getId() + - Thread.currentThread().hashCode() + this.cname.hashCode()); + } + + /** + * Generates a random sequence number + */ + private void generateSeqNum() { + if (this.random == null) + createRandom(); + + seqNum = this.random.nextInt(); + if (seqNum < 0) + seqNum = -seqNum; + while (seqNum > 65535) { + seqNum = seqNum / 10; + } + } + + /** + * Generates a random SSRC + */ + private void generateSsrc() { + if (this.random == null) + createRandom(); + + // Set an SSRC + this.ssrc = this.random.nextInt(); + if (this.ssrc < 0) { + this.ssrc = this.ssrc * -1; + } + } + + /** + * Resolve an SSRC conflict. + * + * Also increments the SSRC conflict counter, after 5 conflicts it is + * assumed there is a loop somewhere and the session will terminate. + * + */ + protected void resolveSsrcConflict() { + System.out + .println("!!!!!!! Beginning SSRC conflict resolution !!!!!!!!!"); + this.conflictCount++; + + if (this.conflictCount < 5) { + // Don't send any more regular packets out until we have this sorted + // out. + this.conflict = true; + + // Send byes + rtcpSession.sendByes(); + + // Calculate the next delay + rtcpSession.calculateDelay(); + + // Generate a new Ssrc for ourselves + generateSsrc(); + + // Get the SDES packets out faster + rtcpSession.initial = true; + + this.conflict = false; + System.out.println("SSRC conflict resolution complete"); + + } else { + System.out + .println("Too many conflicts. There is probably a loop in the network."); + this.endSession(); + } + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RtcpPkt.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RtcpPkt.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,143 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.net.InetAddress; + +/** + * Common RTCP packet headers. + * + * @author Arne Kepp + */ +public class RtcpPkt { + /** Whether a problem has been encountered during parsing */ + protected int problem = 0; + /** The version, always 2, 2 bits */ + protected int version = 2; + /** Padding , 1 bit */ + protected int padding = 0; + /** Number of items, e.g. receiver report blocks. Usage may vary. 5 bits */ + protected int itemCount = 0; + /** The type of RTCP packet, 8 bits */ + protected int packetType = -1; + /** The length of the RTCP packet, in 32 bit blocks minus 1. 16 bits */ + protected int length = -1; + /** The ssrc that sent this, usually dictated by RTP Session */ + protected long ssrc = -1; + + /** Contains the actual data (eventually) */ + protected byte[] rawPkt = null; + + /** Only used for feedback messages: Time message was generated */ + protected long time = -1; + /** Only used for feedback message: Whether this packet was received */ + protected boolean received = false; + + /** + * Parses the common header of an RTCP packet + * + * @param start + * where in this.rawPkt the headers start + * @return true if parsing succeeded and header cheks + */ + protected boolean parseHeaders(int start) { + version = ((rawPkt[start + 0] & 0xC0) >>> 6); + padding = ((rawPkt[start + 0] & 0x20) >>> 5); + itemCount = (rawPkt[start + 0] & 0x1F); + packetType = (int) rawPkt[start + 1]; + if (packetType < 0) { + packetType += 256; + } + length = StaticProcs.bytesToUIntInt(rawPkt, start + 2); + + if (RTPSession.rtpDebugLevel > 9) { + System.out.println(" <-> RtcpPkt.parseHeaders() version:" + version + + " padding:" + padding + " itemCount:" + itemCount + + " packetType:" + packetType + " length:" + length); + } + + if (packetType > 207 || packetType < 200) + System.out + .println("RtcpPkt.parseHeaders problem discovered, packetType " + + packetType); + + if (version == 2 && length < 65536) { + return true; + } else { + System.out + .println("RtcpPkt.parseHeaders() failed header checks, check size and version"); + this.problem = -1; + return false; + } + } + + /** + * Writes the common header of RTCP packets. The values should be filled in + * when the packet is initiliazed and this function called at the very end + * of .encode() + */ + protected void writeHeaders() { + byte aByte = 0; + aByte |= (version << 6); + aByte |= (padding << 5); + aByte |= (itemCount); + rawPkt[0] = aByte; + aByte = 0; + aByte |= packetType; + rawPkt[1] = aByte; + if (rawPkt.length % 4 != 0) + System.out + .println("!!!! RtcpPkt.writeHeaders() rawPkt was not a multiple of 32 bits / 4 octets!"); + StaticProcs.uIntIntToByteWord((rawPkt.length / 4) - 1, rawPkt, 2); + } + + /** + * This is just a dummy to make Eclipse complain less. + */ + protected void encode() { + System.out.println("RtcpPkt.encode() should never be invoked!! " + + this.packetType); + } + + /** + * Check whether this packet came from the source we expected. + * + * Not currently used! + * + * @param adr + * address that packet came from + * @param partDb + * the participant database for the session + * @return true if this packet came from the expected source + */ + protected boolean check(InetAddress adr, ParticipantDatabase partDb) { + // Multicast -> We have to be naive + if (partDb.rtpSession.mcSession + && adr.equals(partDb.rtpSession.mcGroup)) + return true; + + // See whether this participant is known + Participant part = partDb.getParticipant(this.ssrc); + if (part != null && part.rtcpAddress.getAddress().equals(adr)) + return true; + + // If not, we should look for someone without SSRC with his ip-address? + return false; + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RtcpPktAPP.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RtcpPktAPP.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,101 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +/** + * Application specific RTCP packets + * + * @author Arne Kepp + */ +public class RtcpPktAPP extends RtcpPkt { + /** Name of packet, 4 bytes ASCII */ + protected byte[] pktName = null; + /** Data of packet */ + protected byte[] pktData = null; + + /** + * Constructor for a new Application RTCP packet + * + * @param ssrc + * the SSRC of the sender, presumably taken from RTPSession + * @param subtype + * the subtype of packet, application specific + * @param pktName + * byte[4] representing ASCII name of packet + * @param pktData + * the byte[4x] data that represents the message itself + */ + protected RtcpPktAPP(long ssrc, int subtype, byte[] pktName, byte[] pktData) { + // Fetch all the right stuff from the database + super.ssrc = ssrc; + super.packetType = 204; + super.itemCount = subtype; + this.pktName = pktName; + this.pktData = pktData; + } + + /** + * Constructor that parses a received Application RTCP packet + * + * @param aRawPkt + * the raw packet containing the date + * @param start + * where in the raw packet this packet starts + */ + protected RtcpPktAPP(byte[] aRawPkt, int start) { + super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt, 4); + super.rawPkt = aRawPkt; + + if (!super.parseHeaders(start) || packetType != 204) { + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println(" <-> RtcpPktAPP.parseHeaders() etc. problem"); + } + super.problem = -204; + } else { + // System.out.println("super.length: " + super.length); + if (super.length > 1) { + pktName = new byte[4]; + System.arraycopy(aRawPkt, 8, pktName, 0, 4); + } + if (super.length > 2) { + pktData = new byte[(super.length + 1) * 4 - 12]; + System.arraycopy(aRawPkt, 12, pktData, 0, pktData.length); + } + } + } + + /** + * Encode the packet into a byte[], saved in .rawPkt + * + * CompRtcpPkt will call this automatically + */ + protected void encode() { + super.rawPkt = new byte[12 + this.pktData.length]; + byte[] tmp = StaticProcs.uIntLongToByteWord(super.ssrc); + System.arraycopy(tmp, 0, super.rawPkt, 4, 4); + System.arraycopy(this.pktName, 0, super.rawPkt, 8, 4); + System + .arraycopy(this.pktData, 0, super.rawPkt, 12, + this.pktData.length); + writeHeaders(); + // System.out.println("ENCODE: " + super.length + " " + rawPkt.length + + // " " + pktData.length); + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RtcpPktBYE.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RtcpPktBYE.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,115 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +/** + * RTCP packets for sending Bye messages + * + * @author Arne Kepp + */ +public class RtcpPktBYE extends RtcpPkt { + /** SSRCs saying bye, 32xn bits, n<16 */ + protected long[] ssrcArray = null; + /** Optional reason */ + protected byte[] reason = null; + + protected RtcpPktBYE(long[] ssrcs, byte[] aReason) { + super.packetType = 203; + // Fetch all the right stuff from the database + reason = aReason; + ssrcArray = ssrcs; + if (ssrcs.length < 1) { + System.out + .println("RtcpBYE.RtcpPktBYE(long[] ssrcs, byte[] aReason) requires at least one SSRC!"); + } + } + + protected RtcpPktBYE(byte[] aRawPkt, int start) { + rawPkt = aRawPkt; + if (!super.parseHeaders(start) || packetType != 203) { + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println(" <-> RtcpPktBYE.parseHeaders() etc. problem"); + } + super.problem = -203; + } else { + ssrcArray = new long[super.itemCount]; + + for (int i = 0; i < super.itemCount; i++) { + ssrcArray[i] = StaticProcs.bytesToUIntLong(aRawPkt, start + + (i + 1) * 4); + } + if (super.length > (super.itemCount + 1)) { + int reasonLength = (int) aRawPkt[start + (super.itemCount + 1) + * 4]; + // System.out.println("super.itemCount:"+super.itemCount+" reasonLength:"+reasonLength+" start:"+(super.itemCount*4 + // + 4 + 1)); + reason = new byte[reasonLength]; + System.arraycopy(aRawPkt, + start + (super.itemCount + 1) * 4 + 1, reason, 0, + reasonLength); + // System.out.println("test:" + new String(reason)); + } + } + } + + protected void encode() { + itemCount = ssrcArray.length; + length = 4 * ssrcArray.length; + + if (reason != null) { + length += (reason.length + 1) / 4; + if ((reason.length + 1) % 4 != 0) { + length += 1; + } + } + rawPkt = new byte[length * 4 + 4]; + + int i; + byte[] someBytes; + + // SSRCs + for (i = 0; i < ssrcArray.length; i++) { + someBytes = StaticProcs.uIntLongToByteWord(ssrcArray[i]); + System.arraycopy(someBytes, 0, rawPkt, 4 + 4 * i, 4); + } + + // Reason for leaving + if (reason != null) { + // System.out.println("Writing to:"+(4+4*ssrcArray.length)+ + // " reason.length:"+reason.length ); + rawPkt[(4 + 4 * ssrcArray.length)] = (byte) reason.length; + System.arraycopy(reason, 0, rawPkt, 4 + 4 * i + 1, reason.length); + } + super.writeHeaders(); + } + + public void debugPrint() { + System.out.println("RtcpPktBYE.debugPrint() "); + if (ssrcArray != null) { + for (int i = 0; i < ssrcArray.length; i++) { + long anSsrc = ssrcArray[i]; + System.out.println(" ssrc: " + anSsrc); + } + } + if (reason != null) { + System.out.println(" Reason: " + new String(reason)); + } + } +} \ No newline at end of file diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RtcpPktPSFB.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RtcpPktPSFB.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,411 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +/** + * RTCP packets for Payload-Specific Feedback Messages + * + * @author Arne Kepp + */ +public class RtcpPktPSFB extends RtcpPkt { + /** If this packet was for a different SSRC */ + protected boolean notRelevant = false; + /** Parent RTP Session */ + private RTPSession rtpSession; + /** SSRC we are sending feeback to */ + protected long ssrcMediaSource = -1; + + /** SLI macroblock (MB) address of the first lost macroblock number */ + protected int[] sliFirst; + /** SLI number of lost macroblocks */ + protected int[] sliNumber; + /** SLI six least significant bits of the codec-specific identifier */ + protected int[] sliPictureId; + + // Picture loss indication + /** RPSI number of padded bits at end of bitString */ + protected int rpsiPadding = -1; + /** RPSI payloadType RTP payload type */ + protected int rpsiPayloadType = -1; + /** RPSI information as natively defined by the video codec */ + protected byte[] rpsiBitString; + + /** Application Layer Feedback Message */ + protected byte[] alfBitString; + + /** + * Generic constructor, then call make + * + * @param ssrcPacketSender + * @param ssrcMediaSource + */ + protected RtcpPktPSFB(long ssrcPacketSender, long ssrcMediaSource) { + super.ssrc = ssrcPacketSender; + this.ssrcMediaSource = ssrcMediaSource; + super.packetType = 206; // PSFB + } + + /** + * Make this packet a Picture loss indication + */ + protected void makePictureLossIndication() { + super.itemCount = 1; // FMT + } + + /** + * Make this packet a Slice Loss Indication + * + * @param sliFirst + * macroblock (MB) address of the first lost macroblock + * @param sliNumber + * number of lost macroblocks + * @param sliPictureId + * six least significant bits of the codec-specific identifier + */ + protected void makeSliceLossIndication(int[] sliFirst, int[] sliNumber, + int[] sliPictureId) { + super.itemCount = 2; // FMT + this.sliFirst = sliFirst; + this.sliNumber = sliNumber; + this.sliPictureId = sliPictureId; + } + + /** + * Make this packet a Reference Picture Selection Indication + * + * @param bitPadding + * number of padded bits at end of bitString + * @param payloadType + * RTP payload type for codec + * @param bitString + * RPSI information as natively defined by the video codec + */ + protected void makeRefPictureSelIndic(int bitPadding, int payloadType, + byte[] bitString) { + super.itemCount = 3; // FMT + this.rpsiPadding = bitPadding; + this.rpsiPayloadType = payloadType; + this.rpsiBitString = bitString; + } + + /** + * Make this packet an Application specific feedback message + * + * @param bitString + * the original application message + */ + protected void makeAppLayerFeedback(byte[] bitString) { + super.itemCount = 15; // FMT + this.alfBitString = bitString; + } + + /** + * Constructor that parses a raw packet to retrieve information + * + * @param aRawPkt + * the raw packet to be parsed + * @param start + * the start of the packet, in bytes + * @param rtpSession + * the session on which the callback interface resides + */ + protected RtcpPktPSFB(byte[] aRawPkt, int start, RTPSession rtpSession) { + if (RTPSession.rtpDebugLevel > 8) { + System.out.println(" -> RtcpPktPSFB(byte[], int start)"); + } + this.rtpSession = rtpSession; + + rawPkt = aRawPkt; + + if (!super.parseHeaders(start) || packetType != 206 || super.length < 2) { + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println(" <-> RtcpPktRTPFB.parseHeaders() etc. problem"); + } + super.problem = -206; + } else { + // FMT = super.itemCount; + ssrcMediaSource = StaticProcs.bytesToUIntLong(aRawPkt, 8 + start); + + if (ssrcMediaSource == rtpSession.ssrc) { + super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt, 4 + start); + + switch (super.itemCount) { + case 1: // Picture Loss Indication + decPictureLossIndic(); + break; + case 2: // Slice Loss Indication + decSliceLossIndic(aRawPkt, start + 12); + break; + case 3: // Reference Picture Selection Indication + decRefPictureSelIndic(aRawPkt, start + 12); + break; + case 15: // Application Layer Feedback Messages + decAppLayerFB(aRawPkt, start + 12); + break; + default: + System.out + .println("!!!! RtcpPktPSFB(byte[], int start) unexpected FMT " + + super.itemCount); + } + } else { + this.notRelevant = true; + } + } + if (RTPSession.rtpDebugLevel > 8) { + System.out.println(" <- RtcpPktPSFB()"); + } + } + + /** + * Decode Picture Loss indication + * + */ + private void decPictureLossIndic() { + if (this.rtpSession.rtcpAVPFIntf != null) { + this.rtpSession.rtcpAVPFIntf.PSFBPktPictureLossReceived(super.ssrc); + } + } + + /** + * Decode Slice Loss Indication + * + * @param aRawPkt + * @param start + */ + private void decSliceLossIndic(byte[] aRawPkt, int start) { + // 13 bit off-boundary numbers? That's rather cruel + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | First | Number | PictureID | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + int count = super.length - 2; + + sliFirst = new int[count]; + sliNumber = new int[count]; + sliPictureId = new int[count]; + + // Loop over the FCI lines + for (int i = 0; i < count; i++) { + sliFirst[i] = StaticProcs.bytesToUIntInt(aRawPkt, start) >> 3; + sliNumber[i] = (int) (StaticProcs.bytesToUIntInt(aRawPkt, start) & 0x0007FFC0) >> 6; + sliPictureId[i] = (StaticProcs.bytesToUIntInt(aRawPkt, start + 2) & 0x003F); + start += 4; + } + + if (this.rtpSession.rtcpAVPFIntf != null) { + this.rtpSession.rtcpAVPFIntf.PSFBPktSliceLossIndic(super.ssrc, + sliFirst, sliNumber, sliPictureId); + } + } + + /** + * Decode Reference Picture Selection Indication + * + * @param aRawPkt + * @param start + */ + private void decRefPictureSelIndic(byte[] aRawPkt, int start) { + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | PB |0| Payload Type| Native RPSI bit string | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | defined per codec ... | Padding (0) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + rpsiPadding = aRawPkt[start]; + + if (rpsiPadding > 32) { + System.out + .println("!!!! RtcpPktPSFB.decRefPictureSelcIndic paddingBits: " + + rpsiPadding); + } + + rpsiPayloadType = (int) rawPkt[start]; + if (rpsiPayloadType < 0) { + System.out + .println("!!!! RtcpPktPSFB.decRefPictureSelcIndic 8th bit not zero: " + + rpsiPayloadType); + } + + rpsiBitString = new byte[(super.length - 2) * 4 - 2]; + System.arraycopy(aRawPkt, start + 2, rpsiBitString, 0, + rpsiBitString.length); + + if (this.rtpSession.rtcpAVPFIntf != null) { + this.rtpSession.rtcpAVPFIntf.PSFBPktRefPictureSelIndic(super.ssrc, + rpsiPayloadType, rpsiBitString, rpsiPadding); + } + + } + + /** + * Decode Application specific feedback message + * + * @param aRawPkt + * @param start + */ + private void decAppLayerFB(byte[] aRawPkt, int start) { + // Application Message (FCI): variable length + int stringLength = (super.length - 2) * 4; + + alfBitString = new byte[stringLength]; + + System.arraycopy(aRawPkt, start, alfBitString, 0, stringLength); + + if (this.rtpSession.rtcpAVPFIntf != null) { + this.rtpSession.rtcpAVPFIntf.PSFBPktAppLayerFBReceived(super.ssrc, + alfBitString); + } + } + + /** + * Encode a Slice Loss Indication + */ + private void encSliceLossIndic() { + byte[] firstBytes; + byte[] numbBytes; + byte[] picBytes; + + int offset = 8; + // Loop over the FCI lines + for (int i = 0; i < sliFirst.length; i++) { + offset = 8 + 8 * i; + firstBytes = StaticProcs.uIntLongToByteWord(sliFirst[i] << 3); + numbBytes = StaticProcs.uIntLongToByteWord(sliNumber[i] << 2); + picBytes = StaticProcs.uIntIntToByteWord(sliPictureId[i]); + + super.rawPkt[offset] = firstBytes[2]; + super.rawPkt[offset + 1] = (byte) (firstBytes[3] | numbBytes[2]); + super.rawPkt[offset + 2] = numbBytes[3]; + super.rawPkt[offset + 3] = (byte) (numbBytes[3] | picBytes[1]); + } + } + + /** + * Encode a Reference Picture Selection Indication + * + */ + private void encRefPictureSelIndic() { + byte[] someBytes; + someBytes = StaticProcs.uIntIntToByteWord(rpsiPadding); + super.rawPkt[8] = someBytes[1]; + someBytes = StaticProcs.uIntIntToByteWord(rpsiPayloadType); + super.rawPkt[9] = someBytes[1]; + + System.arraycopy(rpsiBitString, 0, super.rawPkt, 10, + rpsiBitString.length); + } + + /** + * Encode Application Layer Feedback + * + */ + private void encAppLayerFB() { + // Application Message (FCI): variable length + System.arraycopy(alfBitString, 0, super.rawPkt, 8, alfBitString.length); + } + + /** + * Get the FMT (Feedback Message Type) + * + * @return value stored in .itemcount, same field + */ + protected int getFMT() { + return this.itemCount; + } + + /** + * Encode the packet into a byte[], saved in .rawPkt + * + * CompRtcpPkt will call this automatically + */ + protected void encode() { + switch (super.itemCount) { + case 1: // Picture Loss Indication + // Nothing to do really + super.rawPkt = new byte[24]; + break; + case 2: // Slice Loss Indication + super.rawPkt = new byte[24 + 4 * this.sliFirst.length]; + encSliceLossIndic(); + break; + case 3: // Reference Picture Selection Indication + super.rawPkt = new byte[24 + 2 + this.rpsiBitString.length / 4]; + encRefPictureSelIndic(); + break; + case 15: // Application Layer Feedback Messages + super.rawPkt = new byte[24 + this.alfBitString.length / 4]; + encAppLayerFB(); + break; + } + + byte[] someBytes = StaticProcs.uIntLongToByteWord(super.ssrc); + System.arraycopy(someBytes, 0, super.rawPkt, 4, 4); + someBytes = StaticProcs.uIntLongToByteWord(this.ssrcMediaSource); + System.arraycopy(someBytes, 0, super.rawPkt, 8, 4); + + writeHeaders(); + } + + /** + * Debug purposes only + */ + public void debugPrint() { + System.out.println("->RtcpPktPSFB.debugPrint() "); + + String str; + switch (super.itemCount) { + case 1: // Picture Loss Indication + System.out.println(" FMT: Picture Loss Indication"); + break; + case 2: // Slice Loss Indication + if (sliFirst != null) { + str = "sliFirst[].length: " + sliFirst.length; + } else { + str = "sliFirst[] is null"; + } + System.out.println(" FMT: Slice Loss Indication, " + str); + break; + case 3: // Reference Picture Selection Indication + if (rpsiBitString != null) { + str = "rpsiBitString[].length: " + rpsiBitString.length; + } else { + str = "rpsiBitString[] is null"; + } + System.out + .println(" FMT: Reference Picture Selection Indication, " + + str + " payloadType: " + this.rpsiPayloadType); + break; + case 15: // Application Layer Feedback Messages + if (alfBitString != null) { + str = "alfBitString[].length: " + alfBitString.length; + } else { + str = "alfBitString[] is null"; + } + System.out.println(" FMT: Application Layer Feedback Messages, " + + str); + break; + } + + System.out.println("<-RtcpPktPSFB.debugPrint() "); + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RtcpPktRR.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RtcpPktRR.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,235 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +/** + * RTCP packets for Receiver Reports + * + * @author Arne Kepp + */ +public class RtcpPktRR extends RtcpPkt { + /** Array of participants to send Receiver Reports to */ + protected Participant[] reportees = null; + /** SSRC of participants the reports are for */ + protected long[] reporteeSsrc = null;// -1; //32 bits + /** Fraction (over 256) of packets lost */ + protected int[] lossFraction = null;// -1; //8 bits + /** Number of lost packets */ + protected int[] lostPktCount = null;// -1; //24 bits + /** Extended highest sequence received */ + protected long[] extHighSeqRecv = null;// -1; //32 bits + /** Interarrival jitter */ + protected long[] interArvJitter = null;// -1; //32 bits + /** Middle 32 bits of NTP when last SR was received */ + protected long[] timeStampLSR = null;// -1; //32 bits + /** Delay on last SRC */ + protected long[] delaySR = null;// -1; //32 bits + + /** + * Constructor for a packet with receiver reports + * + * @param reportees + * the participants on which to generate reports + * @param ssrc + * the SSRC of the sender, from the RTPSession + */ + protected RtcpPktRR(Participant[] reportees, long ssrc) { + super.packetType = 201; + // Fetch all the right stuff from the database + super.ssrc = ssrc; + this.reportees = reportees; + } + + /** + * + * + * If rcount < 0 we assume we have to parse the entire packet, otherwise + * we'll just parse the receiver report blocks (ie. the data came from a + * Sender Report packet) + * + * @param aRawPkt + * the byte[] with the report(s) + * @param start + * where in the raw packet to start reading + * @param rrCount + * the number of receiver reports, -1 if this does not come from + * an SR + */ + protected RtcpPktRR(byte[] aRawPkt, int start, int rrCount) { + // System.out.println("RtcpPktRR: " + rrCount + " start: " + start); + super.rawPkt = aRawPkt; + + if (rrCount < 0 + && (!super.parseHeaders(start) || packetType != 201 || super.length < 1)) { + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println(" <-> RtcpPktRR.parseHeaders() etc. problem: " + + (!super.parseHeaders(start)) + " " + + packetType + " " + super.length); + } + super.problem = -201; + } + + int base; + if (rrCount > 0) { + base = start + 28; + } else { + base = start + 8; + rrCount = super.itemCount; + super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt, start + 4); + } + + if (rrCount > 0) { + reporteeSsrc = new long[rrCount]; + lossFraction = new int[rrCount]; + lostPktCount = new int[rrCount]; + extHighSeqRecv = new long[rrCount]; + interArvJitter = new long[rrCount]; + timeStampLSR = new long[rrCount]; + delaySR = new long[rrCount]; + + for (int i = 0; i < rrCount; i++) { + int pos = base + i * 24; + reporteeSsrc[i] = StaticProcs.bytesToUIntLong(aRawPkt, pos); + lossFraction[i] = (int) aRawPkt[pos + 4]; + aRawPkt[pos + 4] = (byte) 0; + lostPktCount[i] = (int) StaticProcs.bytesToUIntLong(aRawPkt, + pos + 4); + extHighSeqRecv[i] = StaticProcs.bytesToUIntLong(aRawPkt, + pos + 8); + interArvJitter[i] = StaticProcs.bytesToUIntLong(aRawPkt, + pos + 12); + timeStampLSR[i] = StaticProcs + .bytesToUIntLong(aRawPkt, pos + 16); + delaySR[i] = StaticProcs.bytesToUIntLong(aRawPkt, pos + 20); + } + } + } + + /** + * Encode the packet into a byte[], saved in .rawPkt + * + * CompRtcpPkt will call this automatically + */ + protected void encode() { + if (RTPSession.rtpDebugLevel > 9) { + System.out.println(" -> RtcpPktRR.encode()"); + } + + byte[] rRs = null; + // Gather up the actual receiver reports + if (this.reportees != null) { + rRs = this.encodeRR(); + super.rawPkt = new byte[rRs.length + 8]; + System.arraycopy(rRs, 0, super.rawPkt, 8, rRs.length); + super.itemCount = reportees.length; + } else { + super.rawPkt = new byte[8]; + super.itemCount = 0; + } + + // Write the common header + super.writeHeaders(); + + // Add our SSRC (as sender) + StaticProcs.uIntLongToByteWord(super.ssrc, super.rawPkt, 4); + + if (RTPSession.rtpDebugLevel > 9) { + System.out.println(" <- RtcpPktRR.encode()"); + } + + } + + /** + * Encodes the individual Receiver Report blocks, + * + * so they can be used either in RR packets or appended to SR + * + * @return the encoded packets + */ + protected byte[] encodeRR() { + if (RTPSession.rtpDebugLevel > 10) { + System.out.println(" -> RtcpPktRR.encodeRR()"); + } + // assuming we will always create complete reports: + byte[] ret = new byte[24 * reportees.length]; + + // Write SR stuff + for (int i = 0; i < reportees.length; i++) { + int offset = 24 * i; + StaticProcs.uIntLongToByteWord(reportees[i].ssrc, ret, offset); + + // Cumulative number of packets lost + StaticProcs.uIntLongToByteWord(reportees[i] + .getLostPktCount(), ret, 4 + offset); + + // Write Cumulative number of packets lost and loss fraction to + // packet: + System.arraycopy(reportees[i].getFractionLost(), 0, ret, 4 + offset, 1); + + // Extended highest sequence received + StaticProcs.uIntLongToByteWord(reportees[i] + .getExtHighSeqRecv(), ret, 8 + offset); + + // Interarrival jitter + if (reportees[i].interArrivalJitter >= 0) { + StaticProcs + .uIntLongToByteWord((long) reportees[i].interArrivalJitter, ret, 12 + offset); + } else { + StaticProcs.uIntLongToByteWord(0, ret, 12 + offset); + } + + // Timestamp last sender report received + StaticProcs + .uIntLongToByteWord(reportees[i].timeStampLSR, ret, 16 + offset); + + // Delay since last sender report received, in terms of 1/655536 s = + // 0.02 ms + if (reportees[i].timeReceivedLSR > 0) { + StaticProcs.uIntLongToByteWord(reportees[i] + .delaySinceLastSR(), ret, 20 + offset); + } else { + StaticProcs.uIntLongToByteWord(0,ret, 20 + offset); + } + } + if (RTPSession.rtpDebugLevel > 10) { + System.out.println(" <- RtcpPktRR.encodeRR()"); + } + return ret; + } + + /** + * Debug purposes only + */ + public void debugPrint() { + System.out.println("RtcpPktRR.debugPrint() "); + if (reportees != null) { + for (int i = 0; i < reportees.length; i++) { + Participant part = reportees[i]; + System.out.println(" part.ssrc: " + part.ssrc + + " part.cname: " + part.cname); + } + } else { + for (int i = 0; i < reporteeSsrc.length; i++) { + System.out.println(" reporteeSSRC: " + reporteeSsrc[i] + + " timeStampLSR: " + timeStampLSR[i]); + } + } + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RtcpPktRTPFB.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RtcpPktRTPFB.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,165 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +/** + * RTCP packets for RTP Feedback Messages + * + * In line with RFC 4585, this packet currently only supports NACKs + * + * @author Arne Kepp + */ +public class RtcpPktRTPFB extends RtcpPkt { + /** If this packet was for a different SSRC */ + protected boolean notRelevant = false; + /** SSRC we are sending feeback to */ + protected long ssrcMediaSource = -1; + /** RTP sequence numbers of lost packets */ + protected int PID[]; + /** bitmask of following lost packets, shared index with PID */ + protected int BLP[]; + + /** + * Constructor for RTP Feedback Message + * + * @param ssrcPacketSender + * SSRC of sender, taken from RTPSession + * @param ssrcMediaSource + * SSRC of recipient of this message + * @param FMT + * the Feedback Message Subtype + * @param PID + * RTP sequence numbers of lost packets + * @param BLP + * bitmask of following lost packets, shared index with PID + */ + protected RtcpPktRTPFB(long ssrcPacketSender, long ssrcMediaSource, + int FMT, int[] PID, int[] BLP) { + super.packetType = 205; // RTPFB + super.itemCount = FMT; + this.PID = PID; + this.BLP = BLP; + } + + /** + * Constructor that parses a raw packet to retrieve information + * + * @param aRawPkt + * the raw packet to be parsed + * @param start + * the start of the packet, in bytes + * @param rtpSession + * the session on which the callback interface resides + */ + protected RtcpPktRTPFB(byte[] aRawPkt, int start, RTPSession rtpSession) { + if (RTPSession.rtpDebugLevel > 8) { + System.out.println(" -> RtcpPktRTPFB(byte[], int start)"); + } + + rawPkt = aRawPkt; + + if (!super.parseHeaders(start) || packetType != 205 || super.length < 2) { + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println(" <-> RtcpPktRTPFB.parseHeaders() etc. problem"); + } + super.problem = -205; + } else { + // FMT = super.itemCount; + + ssrcMediaSource = StaticProcs.bytesToUIntLong(aRawPkt, 8 + start); + + if (ssrcMediaSource == rtpSession.ssrc) { + super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt, 4 + start); + int loopStop = super.length - 2; + PID = new int[loopStop]; + BLP = new int[loopStop]; + int curStart = 12; + + // Loop over Feedback Control Information (FCI) fields + for (int i = 0; i < loopStop; i++) { + PID[i] = StaticProcs.bytesToUIntInt(aRawPkt, curStart); + BLP[i] = StaticProcs.bytesToUIntInt(aRawPkt, curStart + 2); + curStart += 4; + } + + rtpSession.rtcpAVPFIntf.RTPFBPktReceived(super.ssrc, + super.itemCount, PID, BLP); + } + } + + if (RTPSession.rtpDebugLevel > 8) { + System.out.println(" <- RtcpPktRTPFB()"); + } + } + + /** + * Encode the packet into a byte[], saved in .rawPkt + * + * CompRtcpPkt will call this automatically + */ + protected void encode() { + super.rawPkt = new byte[12 + this.PID.length * 4]; + + byte[] someBytes = StaticProcs.uIntLongToByteWord(super.ssrc); + System.arraycopy(someBytes, 0, super.rawPkt, 4, 4); + someBytes = StaticProcs.uIntLongToByteWord(this.ssrcMediaSource); + System.arraycopy(someBytes, 0, super.rawPkt, 8, 4); + + // Loop over Feedback Control Information (FCI) fields + int curStart = 12; + for (int i = 0; i < this.PID.length; i++) { + someBytes = StaticProcs.uIntIntToByteWord(PID[i]); + super.rawPkt[curStart++] = someBytes[0]; + super.rawPkt[curStart++] = someBytes[1]; + someBytes = StaticProcs.uIntIntToByteWord(BLP[i]); + super.rawPkt[curStart++] = someBytes[0]; + super.rawPkt[curStart++] = someBytes[1]; + } + writeHeaders(); + } + + /** + * Get the FMT (Feedback Message Type) + * + * @return value stored in .itemcount, same field + */ + protected int getFMT() { + return this.itemCount; + } + + /** + * Debug purposes only + */ + protected void debugPrint() { + System.out.println("->RtcpPktRTPFB.debugPrint() "); + System.out.println(" ssrcPacketSender: " + super.ssrc + + " ssrcMediaSource: " + ssrcMediaSource); + + if (this.PID != null && this.PID.length < 1) { + System.out + .println(" No Feedback Control Information (FCI) fields"); + } + + for (int i = 0; i < this.PID.length; i++) { + System.out.println(" FCI -> PID: " + PID[i] + " BLP: " + BLP[i]); + } + System.out.println("<-RtcpPktRTPFB.debugPrint() "); + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RtcpPktSDES.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RtcpPktSDES.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,308 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.net.InetSocketAddress; + +/** + * RTCP packets for Source Descriptions + * + * @author Arne Kepp + */ +public class RtcpPktSDES extends RtcpPkt { + /** Whether the RTP Session object should be inclduded */ + boolean reportSelf = true; + /** The parent RTP Session object, holds participant database */ + RTPSession rtpSession = null; + /** The participants to create SDES packets for */ + protected Participant[] participants = null; + + /** + * Constructor to create a new SDES packet + * + * TODO: Currently the added participants are not actually encoded because + * the library lacks some support for acting as mixer or relay in other + * areas. + * + * @param reportThisSession + * include information from RTPSession as a participant + * @param rtpSession + * the session itself + * @param additionalParticipants + * additional participants to include + */ + protected RtcpPktSDES(boolean reportThisSession, RTPSession rtpSession, + Participant[] additionalParticipants) { + super.packetType = 202; + // Fetch all the right stuff from the database + reportSelf = reportThisSession; + participants = additionalParticipants; + this.rtpSession = rtpSession; + } + + /** + * Constructor that parses a received packet + * + * @param aRawPkt + * the byte[] containing the packet + * @param start + * where in the byte[] this packet starts + * @param socket + * the address from which the packet was received + * @param partDb + * the participant database + */ + protected RtcpPktSDES(byte[] aRawPkt, int start, InetSocketAddress socket, + ParticipantDatabase partDb) { + if (RTPSession.rtcpDebugLevel > 8) { + System.out.println(" -> RtcpPktSDES(byte[], ParticipantDabase)"); + } + rawPkt = aRawPkt; + + if (!super.parseHeaders(start) || packetType != 202) { + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println(" <-> RtcpPktSDES.parseHeaders() etc. problem"); + } + super.problem = -202; + } else { + // System.out.println(" DECODE SIZE: " + super.length + + // " itemcount " + itemCount ); + + int curPos = 4 + start; + int curLength; + int curType; + long ssrc; + boolean endReached = false; + boolean newPart; + this.participants = new Participant[itemCount]; + + // Loop over SSRC SDES chunks + for (int i = 0; i < itemCount; i++) { + ssrc = StaticProcs.bytesToUIntLong(aRawPkt, curPos); + Participant part = partDb.getParticipant(ssrc); + if (part == null) { + if (RTPSession.rtcpDebugLevel > 1) { + System.out + .println("RtcpPktSDES(byte[], ParticipantDabase) adding new participant, ssrc:" + + ssrc + " " + socket); + } + + part = new Participant(socket, socket, ssrc); + newPart = true; + } else { + newPart = false; + } + + curPos += 4; + + // System.out.println("PRE endReached " + endReached + + // " curPos: " + curPos + " length:" + this.length + + // (!endReached && (curPos/4) < this.length)); + + while (!endReached && (curPos / 4) <= this.length) { + // System.out.println("endReached " + endReached + + // " curPos: " + curPos + " length:" + this.length); + curType = (int) aRawPkt[curPos]; + + if (curType == 0) { + curPos += 4 - (curPos % 4); + endReached = true; + } else { + curLength = (int) aRawPkt[curPos + 1]; + // System.out.println("curPos:"+curPos+" curType:"+curType+" curLength:"+curLength+" read from:"+(curPos + // + 1)); + + if (curLength > 0) { + byte[] item = new byte[curLength]; + // System.out.println("curPos:"+curPos+" arawPkt.length:"+aRawPkt.length+" curLength:"+curLength); + System.arraycopy(aRawPkt, curPos + 2, item, 0, + curLength); + + switch (curType) { + case 1: + part.cname = new String(item); + break; + case 2: + part.name = new String(item); + break; + case 3: + part.email = new String(item); + break; + case 4: + part.phone = new String(item); + break; + case 5: + part.loc = new String(item); + break; + case 6: + part.tool = new String(item); + break; + case 7: + part.note = new String(item); + break; + case 8: + part.priv = new String(item); + break; + } + // System.out.println("TYPE " + curType + " value:" + // + new String(item) ); + + } else { + switch (curType) { + case 1: + part.cname = null; + break; + case 2: + part.name = null; + break; + case 3: + part.email = null; + break; + case 4: + part.phone = null; + break; + case 5: + part.loc = null; + break; + case 6: + part.tool = null; + break; + case 7: + part.note = null; + break; + case 8: + part.priv = null; + break; + } + + } + curPos = curPos + curLength + 2; + } + } + + // Save the participant + this.participants[i] = part; + if (newPart) + partDb.addParticipant(2, part); + + // System.out.println("HEPPPPPP " + participants[i].cname ); + } + } + if (RTPSession.rtcpDebugLevel > 8) { + System.out.println(" <- RtcpPktSDES()"); + } + } + + /** + * Encode the packet into a byte[], saved in .rawPkt + * + * CompRtcpPkt will call this automatically + */ + protected void encode() { + byte[] temp = new byte[1450]; + byte[] someBytes = StaticProcs.uIntLongToByteWord(this.rtpSession.ssrc); + System.arraycopy(someBytes, 0, temp, 4, 4); + int pos = 8; + + String tmpString = null; + for (int i = 1; i < 9; i++) { + switch (i) { + case 1: + tmpString = this.rtpSession.cname; + break; + case 2: + tmpString = this.rtpSession.name; + break; + case 3: + tmpString = this.rtpSession.email; + break; + case 4: + tmpString = this.rtpSession.phone; + break; + case 5: + tmpString = this.rtpSession.loc; + break; + case 6: + tmpString = this.rtpSession.tool; + break; + case 7: + tmpString = this.rtpSession.note; + break; + case 8: + tmpString = this.rtpSession.priv; + break; + } + + if (tmpString != null) { + someBytes = tmpString.getBytes(); + temp[pos] = (byte) i; + temp[pos + 1] = (byte) someBytes.length; + System.arraycopy(someBytes, 0, temp, pos + 2, someBytes.length); + // System.out.println("i: "+i+" pos:"+pos+" someBytes.length:"+someBytes.length); + pos = pos + someBytes.length + 2; + // if(i == 1 ) { + // System.out.println("trueeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + + // tmpString); + // } + } + } + int leftover = pos % 4; + if (leftover == 1) { + temp[pos] = (byte) 0; + temp[pos + 1] = (byte) 1; + pos += 3; + } else if (leftover == 2) { + temp[pos] = (byte) 0; + temp[pos + 1] = (byte) 0; + pos += 2; + } else if (leftover == 3) { + temp[pos] = (byte) 0; + temp[pos + 1] = (byte) 3; + pos += 5; + } + + // TODO Here we ought to loop over participants, if we're doing SDES for + // other participants. + + super.rawPkt = new byte[pos]; + itemCount = 1; + // This looks wrong, but appears to be fine.. + System.arraycopy(temp, 0, super.rawPkt, 0, pos); + writeHeaders(); + } + + /** + * Debug purposes only + */ + public void debugPrint() { + System.out.println("RtcpPktSDES.debugPrint() "); + if (participants != null) { + for (int i = 0; i < participants.length; i++) { + Participant part = participants[i]; + System.out.println(" part.ssrc: " + part.ssrc + + " part.cname: " + part.cname + " part.loc: " + + part.loc); + } + } else { + System.out + .println(" nothing to report (only valid for received packets)"); + } + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RtcpPktSR.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RtcpPktSR.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,190 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +/** + * RTCP packets for Sender Reports + * + * @author Arne Kepp + */ +public class RtcpPktSR extends RtcpPkt { + /** NTP timestamp, MSB */ + protected long ntpTs1 = -1; // 32 bits + /** NTP timestamp, LSB */ + protected long ntpTs2 = -1; // 32 bits + /** RTP timestamp */ + protected long rtpTs = -1; // 32 bits + /** Senders packet count */ + protected long sendersPktCount = -1; // 32 bits + /** Senders octet count */ + protected long sendersOctCount = -1; // 32 bits + /** RR packet with receiver reports that we can append */ + protected RtcpPktRR rReports = null; + + /** + * Constructor for a new Sender Report packet + * + * @param ssrc + * the senders SSRC, presumably from RTPSession + * @param pktCount + * packets sent in this session + * @param octCount + * octets sent in this session + * @param rReports + * receiver reports, as RR packets, to be included in this packet + */ + protected RtcpPktSR(long ssrc, long pktCount, long octCount, + RtcpPktRR rReports) { + // Fetch all the right stuff from the database + super.ssrc = ssrc; + super.packetType = 200; + sendersPktCount = pktCount; + sendersOctCount = octCount; + this.rReports = rReports; + } + + /** + * Constructor that parses a received packet + * + * @param aRawPkt + * the raw packet + * @param start + * the position at which SR starts + * @param length + * used to determine number of included receiver reports + */ + protected RtcpPktSR(byte[] aRawPkt, int start, int length) { + if (RTPSession.rtpDebugLevel > 9) { + System.out.println(" -> RtcpPktSR(rawPkt)"); + } + + super.rawPkt = aRawPkt; + + if (!super.parseHeaders(start) || packetType != 200) { + if (RTPSession.rtpDebugLevel > 2) { + System.out + .println(" <-> RtcpPktSR.parseHeaders() etc. problem: " + + (!super.parseHeaders(start)) + " " + + packetType + " " + super.length); + } + super.problem = -200; + } else { + super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt, 4 + start); + if (length > 11) + ntpTs1 = StaticProcs.bytesToUIntLong(aRawPkt, 8 + start); + if (length > 15) + ntpTs2 = StaticProcs.bytesToUIntLong(aRawPkt, 12 + start); + if (length > 19) + rtpTs = StaticProcs.bytesToUIntLong(aRawPkt, 16 + start); + if (length > 23) + sendersPktCount = StaticProcs.bytesToUIntLong(aRawPkt, + 20 + start); + if (length > 27) + sendersOctCount = StaticProcs.bytesToUIntLong(aRawPkt, + 24 + start); + + // RRs attached? + if (itemCount > 0) { + rReports = new RtcpPktRR(rawPkt, start, itemCount); + } + } + + if (RTPSession.rtpDebugLevel > 9) { + System.out.println(" <- RtcpPktSR(rawPkt)"); + } + } + + /** + * Encode the packet into a byte[], saved in .rawPkt + * + * CompRtcpPkt will call this automatically + */ + protected void encode() { + if (RTPSession.rtpDebugLevel > 9) { + if (this.rReports != null) { + System.out + .println(" -> RtcpPktSR.encode() receptionReports.length: " + + this.rReports.length); + } else { + System.out + .println(" -> RtcpPktSR.encode() receptionReports: null"); + } + } + + if (this.rReports != null) { + super.itemCount = this.rReports.reportees.length; + + byte[] tmp = this.rReports.encodeRR(); + super.rawPkt = new byte[tmp.length + 28]; + // super.length = (super.rawPkt.length / 4) - 1; + + System.arraycopy(tmp, 0, super.rawPkt, 28, tmp.length); + + } else { + super.itemCount = 0; + super.rawPkt = new byte[28]; + // super.length = 6; + } + // Write the common header + super.writeHeaders(); + + // Convert to NTP and chop up + long timeNow = System.currentTimeMillis(); + ntpTs1 = 2208988800L + (timeNow / 1000); + long ms = timeNow % 1000; + double tmp = ((double) ms) / 1000.0; + tmp = tmp * (double) 4294967295L; + ntpTs2 = (long) tmp; + rtpTs = System.currentTimeMillis(); + + // Write SR stuff + StaticProcs.uIntLongToByteWord(super.ssrc, super.rawPkt, 4); + StaticProcs.uIntLongToByteWord(ntpTs1, super.rawPkt, 8); + StaticProcs.uIntLongToByteWord(ntpTs2, super.rawPkt, 12); + StaticProcs.uIntLongToByteWord(rtpTs, super.rawPkt, 16); + StaticProcs.uIntLongToByteWord(sendersPktCount, super.rawPkt, 20); + StaticProcs.uIntLongToByteWord(sendersOctCount, super.rawPkt, 24); + + if (RTPSession.rtpDebugLevel > 9) { + System.out.println(" <- RtcpPktSR.encode() ntpTs1: " + + Long.toString(ntpTs1) + " ntpTs2: " + + Long.toString(ntpTs2)); + } + } + + /** + * Debug purposes only + */ + public void debugPrint() { + System.out.println("RtcpPktSR.debugPrint() "); + System.out.println(" SSRC:" + Long.toString(super.ssrc) + " ntpTs1:" + + Long.toString(ntpTs1) + " ntpTS2:" + Long.toString(ntpTs2) + + " rtpTS:" + Long.toString(rtpTs) + " senderPktCount:" + + Long.toString(sendersPktCount) + " sendersOctetCount:" + + Long.toString(sendersOctCount)); + if (this.rReports != null) { + System.out.print(" Part of Sender Report: "); + this.rReports.debugPrint(); + System.out.println(" End Sender Report"); + } else { + System.out + .println("No Receiver Reports associated with this Sender Report."); + } + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/RtpPkt.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/RtpPkt.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,465 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.net.DatagramPacket; + +/** + * RtpPkt is the basic class for creating and parsing RTP packets. + * + * There are two ways of instantiating an RtpPkt. One is for packets that you + * wish to send, which requires that you provide basic information about the + * packet and a payload. Upon calling encode() the fields of the structure are + * written into a bytebuffer, in the form that it would sent across the network, + * excluding the UDP headers. + * + * The other way is by passing a bytebuffer. The assumption is that this is a + * packet that has been received from the network, excluding UDP headers, and + * the bytebuffer will be parsed into the correct fields. + * + * The class keeps track of changes. Therefore, modifications are possible after + * calling encode(), if necessary, the raw version of the packet will be + * regenerated on subsequent requests. + * + * @author Arne Kepp + */ +public class RtpPkt { + /** Whether the packet has been changed since encode() */ + private boolean rawPktCurrent = false; + /** The version, always 2, 2 bits */ + private int version = 2; // 2 bits + /** Whether the packet is padded, 1 bit */ + private int padding; // 1 bit + /** Whether and extension is used, 1 bit */ + private int extension = 0; // 1 bit + /** Whether the packet is marked, 1 bit */ + private int marker = 0; // 1 bit + /** What payload type is used, 7 bits */ + private int payloadType; // + /** The sequence number, taken from RTP Session, 16 bits */ + private int seqNumber; // 16 bits + /** The RTP timestamp, 32bits */ + private long timeStamp; // 32 bits + /** The SSRC of the packet sender, 32 bits */ + private long ssrc; // 32 bits + /** SSRCs of contributing sources, 32xn bits, n<16 */ + private long[] csrcArray = new long[0];// + + /** Contains the actual data (eventually) */ + private byte[] rawPkt = null; + + + + /** The actual data, without any RTP stuff */ + //private byte[] payload = null; + private DatagramPacket datagramPacket; + + /** + * Construct a packet-instance. The ByteBuffer required for UDP transmission + * can afterwards be obtained from getRawPkt(). If you need to set + * additional parameters, such as the marker bit or contributing sources, + * you should do so before calling getRawPkt; + * + * @param aTimeStamp + * RTP timestamp for data + * @param syncSource + * the SSRC, usually taken from RTPSession + * @param seqNum + * Sequency number + * @param plt + * Type of payload + * @param pl + * Payload, the actual data + */ + protected void initPacket(long aTimeStamp, long syncSource, int seqNum, int plt, + byte[] pl) { + int test = 0; + test += setTimeStamp(aTimeStamp); + test += setSsrc(syncSource); + test += setSeqNumber(seqNum); + test += setPayloadType(plt); + //test += setPayload(pl); //TODO: faire d'une manière propre + datagramPacket = null; + if (test != 0) { + System.out.println("RtpPkt() failed, check with checkPkt()"); + } + rawPktCurrent = true; + if (RTPSession.rtpDebugLevel > 5) { + System.out + .println("<--> RtpPkt(aTimeStamp, syncSource, seqNum, plt, pl)"); + } + } + + /** + * Construct a packet-instance from an raw packet (believed to be RTP). The + * UDP-headers must be removed before invoking this method. Call checkPkt on + * the instance to verify that it was successfully parsed. + * + * @param aRawPkt + * The data-part of a UDP-packet believed to be RTP + * @param packetSize + * the number of valid octets in the packet, should be + * aRawPkt.length + */ + public RtpPkt(byte[] aRawPkt, int packetSize, DatagramPacket packet) { + initPacket(aRawPkt, packetSize, packet); + } + + public RtpPkt() { + } + + public RtpPkt(long aTimeStamp, long syncSource, int seqNum, int plt, + byte[] pl) { + initPacket(aTimeStamp, syncSource, seqNum, plt, pl); + } + + protected void initPacket(byte[] aRawPkt, int packetSize, DatagramPacket packet) { + if (RTPSession.rtpDebugLevel > 5) { + System.out.println("-> RtpPkt(aRawPkt)"); + } + // Check size, need to have at least a complete header + if (aRawPkt == null) { + System.out.println("RtpPkt(byte[]) Packet null"); + } + + int remOct = packetSize - 12; + if (remOct >= 0) { + rawPkt = aRawPkt; // Store it + // Interrogate the packet + datagramPacket = packet; + sliceFirstLine(); + if (version == 2) { + sliceTimeStamp(); + sliceSSRC(); + if (remOct > 4 && getCsrcCount() > 0) { + sliceCSRCs(); + remOct -= csrcArray.length * 4; // 4 octets per CSRC + } + // TODO Extension + /*if (remOct > 0) { + slicePayload(remOct); + }*/ + + // Sanity checks + checkPkt(); + + // Mark the buffer as current + rawPktCurrent = true; + } else { + System.out + .println("RtpPkt(byte[]) Packet is not version 2, giving up."); + } + } else { + System.out.println("RtpPkt(byte[]) Packet too small to be sliced"); + } + rawPktCurrent = true; + if (RTPSession.rtpDebugLevel > 5) { + System.out.println("<- RtpPkt(aRawPkt)"); + } + } + + /********************************************************************************************************* + * Reading stuff + *********************************************************************************************************/ + protected int checkPkt() { + // TODO, check for version 2 etc + return 0; + } + + protected int getHeaderLength() { + // TODO include extension + return 12 + 4 * getCsrcCount(); + } + + protected int getPayloadLength() { + return rawPkt.length - getHeaderLength(); + } + + // public int getPaddingLength() { + // return lenPadding; + // } + protected int getVersion() { + return version; + } + + // public boolean isPadded() { + // if(lenPadding > 0) { + // return true; + // }else { + // return false; + // } + // } + // public int getHeaderExtension() { + // TODO + // } + protected boolean isMarked() { + return (marker != 0); + } + + protected int getPayloadType() { + return payloadType; + } + + public int getSeqNumber() { + return seqNumber; + } + + protected long getTimeStamp() { + return timeStamp; + } + + protected long getSsrc() { + return ssrc; + } + + protected int getCsrcCount() { + if (csrcArray != null) { + return csrcArray.length; + } else { + return 0; + } + } + + protected long[] getCsrcArray() { + return csrcArray; + } + + /** + * Encodes the a + */ + protected byte[] encode() { + if (!rawPktCurrent || rawPkt == null) { + writePkt(); + android.util.Log.d("RtpPkt", "writePkt"); + } + return rawPkt; + } + + /* For debugging purposes */ + protected void printPkt() { + System.out + .print("V:" + version + " P:" + padding + " EXT:" + extension); + System.out.println(" CC:" + getCsrcCount() + " M:" + marker + " PT:" + + payloadType + " SN: " + seqNumber); + System.out.println("Timestamp:" + timeStamp + + "(long output as int, may be 2s complement)"); + System.out.println("SSRC:" + ssrc + + "(long output as int, may be 2s complement)"); + for (int i = 0; i < getCsrcCount(); i++) { + System.out.println("CSRC:" + csrcArray[i] + + "(long output as int, may be 2s complement)"); + } + + } + + /********************************************************************************************************* + * Setting stuff + *********************************************************************************************************/ + protected void setMarked(boolean mark) { + rawPktCurrent = false; + if (mark) { + marker = 1; + } else { + marker = 0; + } + } + + // public int setHeaderExtension() { + // TODO + // } + public int setPayloadType(int plType) { + int temp = (plType & 0x0000007F); // 7 bits, checks in RTPSession as + // well. + if (temp == plType) { + rawPktCurrent = false; + payloadType = temp; + return 0; + } else { + return -1; + } + } + + protected int setSeqNumber(int number) { + if (number <= 65536 && number >= 0) { + rawPktCurrent = false; + seqNumber = number; + return 0; + } else { + System.out.println("RtpPkt.setSeqNumber: invalid number"); + return -1; + } + } + + protected int setTimeStamp(long time) { + rawPktCurrent = false; + timeStamp = time; + return 0; // Naive for now + } + + protected int setSsrc(long source) { + rawPktCurrent = false; + ssrc = source; + return 0; // Naive for now + } + + protected int setCsrcs(long[] contributors) { + if (contributors.length <= 16) { + csrcArray = contributors; + return 0; + } else { + System.out + .println("RtpPkt.setCsrcs: Cannot have more than 16 CSRCs"); + return -1; + } + } + + /*protected int setPayload(byte[] data) { + // TODO Padding + if (data.length < (1500 - 12)) { + rawPktCurrent = false; + payload = data; + return 0; + } else { + System.out + .println("RtpPkt.setPayload: Cannot carry more than 1480 bytes for now."); + return -1; + } + }*/ + + public byte[] getPayload() { + return rawPkt; + } + + /********************************************************************************************************* + * Private functions + *********************************************************************************************************/ + // Generate a bytebyffer representing the packet, store it. + private void writePkt() { + int bytes = getPayloadLength(); + int headerLen = getHeaderLength(); + int csrcLen = getCsrcCount(); + rawPkt = new byte[headerLen + bytes]; + + // The first line contains, version and various bits + writeFirstLine(); + byte[] someBytes = StaticProcs.uIntLongToByteWord(timeStamp); + for (int i = 0; i < 4; i++) { + rawPkt[i + 4] = someBytes[i]; + } + // System.out.println("writePkt timeStamp:" + rawPkt[7]); + + someBytes = StaticProcs.uIntLongToByteWord(ssrc); + System.arraycopy(someBytes, 0, rawPkt, 8, 4); + // System.out.println("writePkt ssrc:" + rawPkt[11]); + + for (int i = 0; i < csrcLen; i++) { + someBytes = StaticProcs.uIntLongToByteWord(csrcArray[i]); + System.arraycopy(someBytes, 0, rawPkt, 12 + 4 * i, 4); + } + // TODO Extension + + // Payload + //System.arraycopy(payload, 0, rawPkt, headerLen, bytes); + rawPktCurrent = true; + } + + void writeHeader() { + //int bytes = rawPkt.length - 12; + //int headerLen = getHeaderLength(); + int csrcLen = getCsrcCount(); + + // The first line contains, version and various bits + writeFirstLine(); + StaticProcs.uIntLongToByteWord(timeStamp, rawPkt, 4); + // System.out.println("writePkt timeStamp:" + rawPkt[7]); + + StaticProcs.uIntLongToByteWord(ssrc, rawPkt, 8); + //System.arraycopy(someBytes, 0, rawPkt, 8, 4); + // System.out.println("writePkt ssrc:" + rawPkt[11]); + + for (int i = 0; i < csrcLen; i++) { + StaticProcs.uIntLongToByteWord(csrcArray[i], rawPkt, 12 + 4 * i); + //System.arraycopy(someBytes, 0, rawPkt, 12 + 4 * i, 4); + } + // TODO Extension + + // Payload + //System.arraycopy(payload, 0, rawPkt, headerLen, bytes); + rawPktCurrent = true; + } + + // Writes the first 4 octets of the RTP packet + protected void writeFirstLine() { + byte aByte = 0; + aByte |= (version << 6); + aByte |= (padding << 5); + aByte |= (extension << 4); + aByte |= (getCsrcCount()); + rawPkt[0] = aByte; + aByte = 0; + aByte |= (marker << 7); + aByte |= payloadType; + rawPkt[1] = aByte; + StaticProcs.uIntIntToByteWord(seqNumber, rawPkt, 2); + } + + // Picks apart the first 4 octets of an RTP packet + private void sliceFirstLine() { + version = ((rawPkt[0] & 0xC0) >>> 6); + padding = ((rawPkt[0] & 0x20) >>> 5); + extension = ((rawPkt[0] & 0x10) >>> 4); + //csrcArray = new long[(rawPkt[0] & 0x0F)]; + marker = ((rawPkt[1] & 0x80) >> 7); + payloadType = (rawPkt[1] & 0x7F); + seqNumber = StaticProcs.bytesToUIntInt(rawPkt, 2); + } + + // Takes the 4 octets representing the timestamp + private void sliceTimeStamp() { + timeStamp = StaticProcs.bytesToUIntLong(rawPkt, 4); + } + + // Takes the 4 octets representing the SSRC + private void sliceSSRC() { + ssrc = StaticProcs.bytesToUIntLong(rawPkt, 8); + } + + // Check the length of the csrcArray (set during sliceFirstLine) + private void sliceCSRCs() { + for (int i = 0; i < csrcArray.length; i++) { + ssrc = StaticProcs.bytesToUIntLong(rawPkt, i * 4 + 12); + } + } + + // Extensions //TODO + /*private void slicePayload(int bytes) { + payload = new byte[bytes]; + int headerLen = getHeaderLength(); + + System.arraycopy(rawPkt, headerLen, payload, 0, bytes); + }*/ + + public void setRawPkt(byte[] rawPkt) { + this.rawPkt = rawPkt; + } + + public DatagramPacket getDatagramPacket() { + return datagramPacket; + } +} \ No newline at end of file diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/StaticProcs.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/StaticProcs.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,298 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +/** + * Generic functions for converting between unsigned integers and byte[]s. + * + * @author Arne Kepp + */ +public class StaticProcs { + + /** + * Converts an integer into an array of bytes. Primarily used for 16 bit + * unsigned integers, ignore the first two octets. + * + * @param i + * a 16 bit unsigned integer in an int + * @return byte[2] representing the integer as unsigned, most significant + * bit first. + */ + public static byte[] uIntIntToByteWord(int i) { + byte[] byteWord = new byte[2]; + byteWord[0] = (byte) ((i >> 8) & 0x000000FF); + byteWord[1] = (byte) (i & 0x00FF); + return byteWord; + } + + public static void uIntIntToByteWord(int i, byte[] byteWord, int srcPos) { + byteWord[0 + srcPos] = (byte) ((i >> 8) & 0x000000FF); + byteWord[1 + srcPos] = (byte) (i & 0x00FF); + } + + /** + * Converts an unsigned 32 bit integer, stored in a long, into an array of + * bytes. + * + * @param j + * a long + * @return byte[4] representing the unsigned integer, most significant bit + * first. + */ + public static byte[] uIntLongToByteWord(long j) { + int i = (int) j; + byte[] byteWord = new byte[4]; + byteWord[0] = (byte) ((i >>> 24) & 0x000000FF); + byteWord[1] = (byte) ((i >> 16) & 0x000000FF); + byteWord[2] = (byte) ((i >> 8) & 0x000000FF); + byteWord[3] = (byte) (i & 0x00FF); + return byteWord; + } + + public static void uIntLongToByteWord(long j, byte[] byteword, int srcPos) { + int i = (int) j; + byteword[0 + srcPos] = (byte) ((i >>> 24) & 0x000000FF); + byteword[1 + srcPos] = (byte) ((i >> 16) & 0x000000FF); + byteword[2 + srcPos] = (byte) ((i >> 8) & 0x000000FF); + byteword[3 + srcPos] = (byte) (i & 0x00FF); + } + + /** + * Combines two bytes (most significant bit first) into a 16 bit unsigned + * integer. + * + * @param index + * of most significant byte + * @return int with the 16 bit unsigned integer + */ + public static int bytesToUIntInt(byte[] bytes, int index) { + int accum = 0; + int i = 1; + for (int shiftBy = 0; shiftBy < 16; shiftBy += 8) { + accum |= ((long) (bytes[index + i] & 0xff)) << shiftBy; + i--; + } + return accum; + } + + /** + * Combines four bytes (most significant bit first) into a 32 bit unsigned + * integer. + * + * @param bytes + * @param index + * of most significant byte + * @return long with the 32 bit unsigned integer + */ + public static long bytesToUIntLong(byte[] bytes, int index) { + long accum = 0; + int i = 3; + for (int shiftBy = 0; shiftBy < 32; shiftBy += 8) { + accum |= ((long) (bytes[index + i] & 0xff)) << shiftBy; + i--; + } + return accum; + } + + /** + * Converts an arbitrary number of bytes, assumed to represent an unsigned + * integer, to a Java long + */ + /* + * public static long bytesToUintLong(byte[] bytes, int firstByte, int + * lastByte) { long accum = 0; int i = lastByte - firstByte; if(i > 7) { + * System.out.println( + * "!!!! StaticProcs.bytesToUintLong() Can't convert more than 63 bits!"); + * return -1; } int stop = (i+1)*8; + * + * for (int shiftBy = 0; shiftBy < stop; shiftBy += 8 ) { accum |= ( (long)( + * bytes[firstByte + i] & 0xff ) ) << shiftBy; i--; } return accum; } + */ + + /** + * Converts an arbitrary number of bytes, assumed to represent an unsigned + * integer, to a Java int + */ + /* + * public static int bytesToUintInt(byte[] bytes, int firstByte, int + * lastByte) { int accum = 0; int i = lastByte - firstByte; if(i > 3) { + * System.out.println( + * "!!!! StaticProcs.bytesToUintLong() Can't convert more than 31 bits!"); + * return -1; } int stop = (i+1)*8; + * + * for (int shiftBy = 0; shiftBy < stop; shiftBy += 8 ) { accum |= ( (long)( + * bytes[firstByte + i] & 0xff ) ) << shiftBy; i--; } return accum; } + */ + + /** + * Recreates a UNIX timestamp based on the NTP representation used in RTCP + * SR packets + * + * @param ntpTs1 + * from RTCP SR packet + * @param ntpTs2 + * from RTCP SR packet + * @return the UNIX timestamp + */ + public static long undoNtpMess(long ntpTs1, long ntpTs2) { + long timeVal = (ntpTs1 - 2208988800L) * 1000; + + double tmp = (1000.0 * (double) ntpTs2) / ((double) 4294967295L); + long ms = (long) tmp; + // System.out.println(" timeVal: " +Long.toString(timeVal)+ " ms " + + // Long.toString(ms)); + timeVal += ms; + + return timeVal; + } + + /** + * Get the bits of a byte + * + * @param aByte + * the byte you wish to convert + * @return a String of 1's and 0's + */ + public static String bitsOfByte(byte aByte) { + int temp; + String out = ""; + for (int i = 7; i >= 0; i--) { + temp = (aByte >>> i); + temp &= 0x0001; + out += ("" + temp); + } + return out; + } + + /** + * Get the hex representation of a byte + * + * @param aByte + * the byte you wish to convert + * @return a String of two chars 0-1,A-F + */ + public static String hexOfByte(byte aByte) { + String out = ""; + + for (int i = 0; i < 2; i++) { + int temp = (int) aByte; + if (temp < 0) { + temp += 256; + } + if (i == 0) { + temp = temp / 16; + } else { + temp = temp % 16; + } + + if (temp > 9) { + switch (temp) { + case 10: + out += "A"; + break; + case 11: + out += "B"; + break; + case 12: + out += "C"; + break; + case 13: + out += "D"; + break; + case 14: + out += "E"; + break; + case 15: + out += "F"; + break; + } + } else { + out += Integer.toString(temp); + } + } + return out; + } + + /** + * Get the hex representation of a byte + * + * @param hex + * 4 bytes the byte you wish to convert + * @return a String of two chars 0-1,A-F + */ + public static byte byteOfHex(byte[] hex) { + byte retByte = 0; + Byte tmp; + int val = 0; + + // First 4 bits + tmp = hex[0]; + val = tmp.intValue(); + if (val > 64) { + // Letter + val -= 55; + } else { + // Number + val -= 48; + } + retByte = ((byte) (val << 4)); + + // Last 4 bits + tmp = hex[1]; + val = tmp.intValue(); + if (val > 64) { + // Letter + val -= 55; + } else { + // Number + val -= 48; + } + retByte |= ((byte) val); + + return retByte; + } + + /** + * Print the bits of a byte to standard out. For debugging. + * + * @param aByte + * the byte you wish to print out. + */ + public static void printBits(byte aByte) { + int temp; + for (int i = 7; i >= 0; i--) { + temp = (aByte >>> i); + temp &= 0x0001; + System.out.print("" + temp); + } + System.out.println(); + } + + public static String bitsOfBytes(byte[] bytes) { + String str = ""; + // Expensive, but who cares + for (int i = 0; i < bytes.length; i++) { + str += bitsOfByte(bytes[i]) + " "; + if ((i + 1) % 4 == 0) + str += "\n"; + } + + return str; + } +} \ No newline at end of file diff -r 047b5e2b9904 -r af3a788344f9 src/jlibrtp/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/package.html Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,22 @@ + + +

The jlibrtp package contains the core classes of jlibrtp. Most of these classes + are protected or private, developers looking to use jlibrtp should only concern + themselves with

+
    +
  • RTPSession - the main session object +
  • Participant - participant objects +
  • DataFrame - the containers in which data is returned +
  • RTPAppIntf - the mininum callback interface +
  • RTPCAppIntf - optional interface for receing RTCP packets +
  • RTCPAVPFIntf - optional interface for RTP with feedback +
  • DebugAppIntf - optional interface for debugging +
+

+ DebugAppIntf is great for checking network problems and keeping track of packets. + If you need extensive debugging you should statically change the debug values in RTPSession.java + and pay attention to the standard output. +

+ + + \ No newline at end of file diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/media/RtpStreamReceiver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/RtpStreamReceiver.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * Copyright (C) 2005 Luca Veltri - University of Parma - Italy + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.sipdroid.media; + +import jlibrtp.AppCallerThread; +import jlibrtp.DataFrame; +import jlibrtp.Participant; +import jlibrtp.RTPAppIntf; +import jlibrtp.RTPReceiverThread; +import jlibrtp.RTPSession; + +import org.sipdroid.media.codecs.Codec; +import org.sipdroid.net.tools.DataFramePool; +import org.sipdroid.net.tools.DatagramPool; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences.Editor; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioTrack; +import android.media.ToneGenerator; +import android.preference.PreferenceManager; +import android.provider.Settings; + +/** + * RtpStreamReceiver is a generic stream receiver. It receives packets from RTP + * and writes them into an OutputStream. + */ +public class RtpStreamReceiver extends Thread implements RTPAppIntf{ + + /** Whether working in debug mode. */ + public static boolean DEBUG = true; + + /** Size of the read buffer */ + public static final int BUFFER_SIZE = 1024; + + /** Maximum blocking time, spent waiting for reading new bytes [milliseconds] */ + public static final int SO_TIMEOUT = 200; + + /** The RtpSocket */ + //RtpSocket rtp_socket = null; + RTPSession rtpSession = null; + byte[] buffer; + + /** The codec */ + private Codec codec; + private Context mContext; + + private int frame_size; + private int codec_frame_size; + private int sampling_rate; + + /** Whether it is running */ + boolean running; + AudioManager am; + ContentResolver cr; + + private int codec_divider; + public static int speakermode; + + short lin[]; + short lin2[]; + int user, server, lserver, luser, cnt, todo, headroom, len, timeout = 1, seq = 0, cnt2 = 0, m = 1, + expseq, getseq, vm = 1, gap, oldvol; + boolean islate; + + Codec.Context codecCtx; + + AudioTrack track; + + /** + * Constructs a RtpStreamReceiver. + * @param ctx + * @param remoteAddr + * + * @param output_stream + * the stream sink + * @param socket + * the local receiver SipdroidSocket + */ + public RtpStreamReceiver(Codec ci, RTPSession rtpSession, Context ctx) { + init(ci, rtpSession, ctx); + } + + /** Inits the RtpStreamReceiver + * @param ctx + * @param remoteAddr + **/ + private void init(Codec ci, RTPSession rtpSession, Context ctx) { + this.rtpSession = rtpSession; + codec = ci; + codec_frame_size = codec.getInfo().codecFrameSize; + codec_divider = codec.getInfo().rtpSampleDivider; + frame_size = 160 * codec_divider; + sampling_rate = codec.getInfo().samplingRate; + mContext = ctx; + } + + /** Whether is running */ + public boolean isRunning() { + return running; + } + + /** Stops running */ + public void halt() { + running = false; + } + + public int speaker(int mode) { + int old = speakermode; + + saveVolume(); + speakermode = mode; + restoreVolume(); + return old; + } + + double smin = 200,s; + + private int REAL_BUFFER_SIZE; + public static int nearend; + + void calc(short[] lin,int off,int len) { + int i,j; + double sm = 30000,r; + + for (i = 0; i < len; i += 5) { + j = lin[i+off]; + s = 0.03*Math.abs(j) + 0.97*s; + if (s < sm) sm = s; + if (s > smin) nearend = 3000/5; + else if (nearend > 0) nearend--; + } + for (i = 0; i < len; i++) { + j = lin[i+off]; + if (j > 6550) + lin[i+off] = 6550*5; + else if (j < -6550) + lin[i+off] = -6550*5; + else + lin[i+off] = (short)(j*5); + } + r = (double)len/100000; + smin = sm*r + smin*(1-r); + } + + void restoreVolume() { + am.setStreamVolume(AudioManager.STREAM_MUSIC, + PreferenceManager.getDefaultSharedPreferences(mContext).getInt("volume"+speakermode, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC)* + (speakermode == AudioManager.MODE_NORMAL?4:3)/4 + ),0); + } + + void saveVolume() { + Editor edit = PreferenceManager.getDefaultSharedPreferences(mContext).edit(); + edit.putInt("volume"+speakermode,am.getStreamVolume(AudioManager.STREAM_MUSIC)); + edit.commit(); + } + + void saveSettings() { + if (!PreferenceManager.getDefaultSharedPreferences(mContext).getBoolean("oldvalid",false)) { + int oldvibrate = am.getVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); + int oldvibrate2 = am.getVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); + if (!PreferenceManager.getDefaultSharedPreferences(mContext).contains("oldvibrate2")) + oldvibrate2 = AudioManager.VIBRATE_SETTING_ON; + int oldpolicy = android.provider.Settings.System.getInt(cr, android.provider.Settings.System.WIFI_SLEEP_POLICY, + Settings.System.WIFI_SLEEP_POLICY_DEFAULT); + Editor edit = PreferenceManager.getDefaultSharedPreferences(mContext).edit(); + edit.putInt("oldvibrate", oldvibrate); + edit.putInt("oldvibrate2", oldvibrate2); + edit.putInt("oldpolicy", oldpolicy); + edit.putInt("oldring",am.getStreamVolume(AudioManager.STREAM_RING)); + edit.putBoolean("oldvalid", true); + edit.commit(); + } + } + + void restoreSettings() { + int oldvibrate = PreferenceManager.getDefaultSharedPreferences(mContext).getInt("oldvibrate",0); + int oldvibrate2 = PreferenceManager.getDefaultSharedPreferences(mContext).getInt("oldvibrate2",0); + int oldpolicy = PreferenceManager.getDefaultSharedPreferences(mContext).getInt("oldpolicy",0); + am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,oldvibrate); + am.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,oldvibrate2); + Settings.System.putInt(cr, Settings.System.WIFI_SLEEP_POLICY, oldpolicy); + am.setStreamVolume(AudioManager.STREAM_RING, PreferenceManager.getDefaultSharedPreferences(mContext).getInt("oldring",0), 0); + Editor edit = PreferenceManager.getDefaultSharedPreferences(mContext).edit(); + edit.putBoolean("oldvalid", false); + edit.commit(); + } + + public static float good, late, lost, loss; + + /** Runs it in a new Thread. */ + @Override + public void run() { + REAL_BUFFER_SIZE = BUFFER_SIZE * codec_divider; + speakermode = AudioManager.MODE_IN_CALL; + android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO); + am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + cr = mContext.getContentResolver(); + //saveSettings(); + + Settings.System.putInt(cr, Settings.System.WIFI_SLEEP_POLICY,Settings.System.WIFI_SLEEP_POLICY_NEVER); + //am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,AudioManager.VIBRATE_SETTING_OFF); + //am.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,AudioManager.VIBRATE_SETTING_OFF); + //oldvol = am.getStreamVolume(AudioManager.STREAM_MUSIC); + restoreVolume(); + + track = new AudioTrack(AudioManager.STREAM_MUSIC, sampling_rate, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, + REAL_BUFFER_SIZE*2*2, AudioTrack.MODE_STREAM); + track.play(); + lin = new short[REAL_BUFFER_SIZE]; + lin2 = new short[REAL_BUFFER_SIZE]; + user = 0; //number of samples written + server = 0; // number of samples played + lserver = 0; // last number of samples played + luser = -sampling_rate; // last number of samples written + cnt = 0; + codecCtx = codec.initDecoder(); + System.gc(); + println("DEBUG: rtpStreamReceiver session launch"); + running = true; + AppCallerThread appCall = rtpSession.getAppCallerThrd(); + RTPReceiverThread recv = rtpSession.getRTPRecvThrd(); + DataFrame frame = null; + recv.init(); + while (running) { + recv.readPacketToBuffer(); + frame = appCall.getNextDataFrame(); + if (frame == null) + continue; + buffer = (frame.getPkt()[0]).getPayload(); + if (timeout != 0) { //on ecrit du blanc sur l'audiotrack + user += track.write(lin,0,REAL_BUFFER_SIZE); + user += track.write(lin,0,REAL_BUFFER_SIZE); + } + timeout = 0; + if (running) { + + //println("seq " + seq + " frame seq " + (frame.getPkt()[0]).getSeqNumber()); + if (seq == (frame.getPkt()[0]).getSeqNumber()) { + m++; + continue; + } + + codec.decode(codecCtx, buffer, 12, codec_frame_size, lin, 0); + len = frame_size; + + if (speakermode == AudioManager.MODE_NORMAL) + calc(lin,0,len); + + server = track.getPlaybackHeadPosition(); // on récupère la position actuel de la tete de lecture + headroom = user-server; // on recalcule la différence entre la position de la tete de lecture et ce qu'on a écrit sur la piste + //println("headroom " + headroom + " user " + user + " server " + server); + if (headroom < 250 * codec_divider) { // si le headroom est trop petit, il faut rattraper le retard en écrivant du blanc/répétant ce qu'il y a à ecrire + todo = 625 * codec_divider - headroom; + //println("insert "+todo); + android.util.Log.d("RECV", "insert"); + islate = true; + if (todo < len) + user += track.write(lin,0,todo); // on écrit le packet reçu tel quel + else + user += track.write(lin2,0,todo); // ecriture de blanc de taille 625 - headroom, avant l'écriture du packet + } else + islate = false; + + if (headroom > 1000 * codec_divider) // si le headroom est trop grand, on calcule l'écart. + cnt += len; // on additione le nombre de sample ou il y a eu un headroom supérieur a 1000 + else + cnt = 0; + + if (lserver == server) // on compte le nombre de boucle que l'on a fait sans qu'aucun sample n'ai été joué. + cnt2++; + else + cnt2 = 0; + + if (cnt > 1000 * codec_divider && cnt2 < 2) { // si la position de la tete de lecture n'a pas bougé durant 2 tours et que le nombre de sample ou le headroom a été supérieur à 1000 est > 1000 + todo = headroom - 625 * codec_divider; + try { + //android.util.Log.d("RECV", "cut"); + sleep(20); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + user += track.write(lin,0,len); + m = 1; + seq = (frame.getPkt()[0]).getSeqNumber(); + DataFramePool.getInstance().returnFrame(frame); + //println("headroom " + headroom + " user " + user + " server " + server + " luser " + luser + " lserver " + lserver); + if (user >= luser + sampling_rate) { + /*if (am.getMode() != speakermode) { + am.setMode(speakermode); + switch (speakermode) { + case AudioManager.MODE_IN_CALL: + am.setStreamVolume(AudioManager.STREAM_RING,(int)( + am.getStreamMaxVolume(AudioManager.STREAM_RING)* + com.mbdsys.sfrdroid.ui.Settings.getEarGain()), 0); + track.setStereoVolume(AudioTrack.getMaxVolume()* + com.mbdsys.sfrdroid.ui.Settings.getEarGain() + ,AudioTrack.getMaxVolume()* + com.mbdsys.sfrdroid.ui.Settings.getEarGain()); + //running = false; + case AudioManager.MODE_NORMAL: + track.setStereoVolume(AudioTrack.getMaxVolume(),AudioTrack.getMaxVolume()); + //running = false; + } + }*/ + luser = user; + } + lserver = server; + System.arraycopy(lin, 0, lin2, 0, REAL_BUFFER_SIZE); + } + } + println("POOL SIZE " + DatagramPool.getInstance().getPoolSize()); + track.stop(); + //if (Receiver.pstn_state == null || Receiver.pstn_state.equals("IDLE")) + // am.setMode(AudioManager.MODE_NORMAL); + //saveVolume(); + //am.setStreamVolume(AudioManager.STREAM_MUSIC,oldvol,0); + //restoreSettings(); + ToneGenerator tg = new ToneGenerator(AudioManager.STREAM_RING,ToneGenerator.MAX_VOLUME/4*3); + tg.startTone(ToneGenerator.TONE_PROP_PROMPT); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + } + tg.stopTone(); + rtpSession = null; + track = null; + codec.cleanDecoder(codecCtx); + codec = null; + println("rtp receiver terminated"); + } + + /** Debug output */ + static int i = 0; + private static void println(String str) { + System.out.println("RtpStreamReceiver "+ i++ +": " + str); + } + + public static int byte2int(byte b) { // return (b>=0)? b : -((b^0xFF)+1); + // return (b>=0)? b : b+0x100; + return (b + 0x100) % 0x100; + } + + public static int byte2int(byte b1, byte b2) { + return (((b1 + 0x100) % 0x100) << 8) + (b2 + 0x100) % 0x100; + } + + @Override + public int frameSize(int payloadType) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void receiveData(DataFrame frame, Participant participant) { + // TODO Auto-generated method stub + + } + + @Override + public void userEvent(int type, Participant[] participant) { + // TODO Auto-generated method stub + + } + +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/media/RtpStreamSender.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/RtpStreamSender.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * Copyright (C) 2005 Luca Veltri - University of Parma - Italy + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.sipdroid.media; + +import java.io.InputStream; +import java.net.DatagramPacket; +import java.util.Random; + +import jlibrtp.RTPSession; +import jlibrtp.RtpPkt; + +import org.sipdroid.media.codecs.Codec; + +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioRecord; +import android.media.MediaRecorder; + +/** + * RtpStreamSender is a generic stream sender. It takes an InputStream and sends + * it through RTP. + */ +public class RtpStreamSender extends Thread{ + + private static final boolean DEBUG = true; + + /** The RtpSocket */ + //private RtpSocket rtp_socket = null; + private RTPSession rtpSession = null; + + /** Codec */ + private Codec codec; + + private int sampling_rate; + + /** Number of bytes per frame */ + private int frame_size; + + private int codec_frame_size; + + /** + * Whether it works synchronously with a local clock, or it it acts as slave + * of the InputStream + */ + boolean do_sync = true; + + int sync_adj = 0; + + /** Whether it is running */ + boolean running = false; + boolean muted = false; + + private int codec_divider; + + /** + * Constructs a RtpStreamSender. + * + */ + public RtpStreamSender(Codec co, RTPSession rtpSession) { + init(co, rtpSession); + } + + /** Inits the RtpStreamSender */ + private void init(Codec co, RTPSession rtpSession) { + this.rtpSession = rtpSession; + codec = co; + sampling_rate = codec.getInfo().samplingRate; + codec_frame_size = codec.getInfo().codecFrameSize; + codec_divider = codec.getInfo().rtpSampleDivider; + frame_size = 160 * codec_divider; + rtpSession.payloadType(codec.getInfo().rtpPayloadCode); + + this.do_sync = true; + } + + /** Sets the synchronization adjustment time (in milliseconds). */ + public void setSyncAdj(int millisecs) { + sync_adj = millisecs; + } + + /** Whether is running */ + public boolean isRunning() { + return running; + } + + public boolean mute() { + return muted = !muted; + } + + public static int delay = 0; + + /** Stops running */ + public void halt() { + running = false; + } + + Random random; + double smin = 200,s; + int nearend; + + void calc(short[] lin,int off,int len) { + int i,j; + double sm = 30000,r; + + for (i = 0; i < len; i += 5) { + j = lin[i+off]; + s = 0.03*Math.abs(j) + 0.97*s; + if (s < sm) sm = s; + if (s > smin) nearend = 3000/5; + else if (nearend > 0) nearend--; + } + for (i = 0; i < len; i++) { + j = lin[i+off]; + if (j > 6550) + lin[i+off] = 6550*5; + else if (j < -6550) + lin[i+off] = -6550*5; + else + lin[i+off] = (short)(j*5); + } + r = (double)len/100000; + smin = sm*r + smin*(1-r); + } + + void noise(short[] lin,int off,int len,double power) { + int i,r = (int)(power*2); + short ran; + + if (r == 0) r = 1; + for (i = 0; i < len; i += 4) { + ran = (short)(random.nextInt(r*2)-r); + lin[i+off] = ran; + lin[i+off+1] = ran; + lin[i+off+2] = ran; + lin[i+off+3] = ran; + } + } + + public static int m; + + /** Runs it in a new Thread. */ + public void run() { + if (rtpSession == null) + return; + byte[] buffer = new byte[codec_frame_size + 12]; + DatagramPacket packet = new DatagramPacket(buffer, codec_frame_size + 12); + RtpPkt pkt = new RtpPkt(); + pkt.setRawPkt(buffer); + pkt.setPayloadType(codec.getInfo().rtpPayloadCode); + int seqn = 0; + long time = 0; + double p = 0; + running = true; + m = 1; + + if (DEBUG) + println("Reading blocks of " + buffer.length + " bytes"); + + android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); + + Codec.Context codecCtx = codec.initEncoder(); + + AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, sampling_rate, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, + AudioRecord.getMinBufferSize(sampling_rate, + AudioFormat.CHANNEL_CONFIGURATION_MONO, + AudioFormat.ENCODING_PCM_16BIT)*2); + record.startRecording(); + short[] lin = new short[frame_size*11]; + int num,ring = 0; + random = new Random(); + while (running) { + num = record.read(lin,(ring+delay)%(frame_size*11),frame_size); + if (RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL) { + calc(lin,(ring+delay)%(frame_size*11),num); + if (RtpStreamReceiver.nearend != 0) + noise(lin,(ring+delay)%(frame_size*11),num,p); + else if (nearend == 0) + p = 0.9*p + 0.1*s; + } + codec.encode(codecCtx, lin, ring%(frame_size*11), frame_size, buffer, 12); + ring += frame_size; + rtpSession.sendData(packet, pkt); + if (m == 2) { + rtpSession.sendData(packet, pkt); + println("retransmit"); + } + seqn++; + time += num; + } + record.stop(); + rtpSession = null; + codec.cleanEncoder(codecCtx); + if (DEBUG) + println("rtp sender terminated"); + } + + /** Debug output */ + private static void println(String str) { + android.util.Log.d("DEBUG","RtpStreamSender: " + str); + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/media/codecs/Codec.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/codecs/Codec.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,19 @@ +package org.sipdroid.media.codecs; + +public abstract class Codec { + + public abstract class Context { + }; + + public abstract Context initEncoder(); + public abstract Context initDecoder(); + public abstract void cleanEncoder(Context ctx); + public abstract void cleanDecoder(Context ctx); + + + public abstract int encode(Context ctx, short[] insample, int inoffset, int size, byte[] outdata, int outoffset); + public abstract int decode(Context ctx, byte[] indata, int inoffset, int size, short[] outsample, int outoffset); + + public abstract CodecInfo getInfo(); + +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/media/codecs/CodecInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/codecs/CodecInfo.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,43 @@ +package org.sipdroid.media.codecs; + +/** + * @author vadim + * + */ +public class CodecInfo { + /** + * String to show in config dialog + */ + public String displayName; + /** + * codec spec string to use in SDP + */ + public String rtpPayloadName; + /** + * prefered RTP payload code + */ + public int rtpPayloadCode; + /** + * sampling rate for this codec + */ + public int samplingRate; + /** + * divider for sampling rate to use when computing rtp timestamp + */ + public int rtpSampleDivider; + /** + * minimum frameSize in milliseconds + */ + public int minFrameTimeMsecs; + /** + * codec description to display in configuration dialog + */ + public String description; + + /** + * codec frame size + */ + public int codecFrameSize; + + +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/media/codecs/CodecManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/codecs/CodecManager.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,46 @@ +/** + * + */ +package org.sipdroid.media.codecs; + +import java.util.LinkedList; + +/** + * @author vadim + * + */ +public class CodecManager { + + public static void load() { + GSM.load(); + G711.load(); + G722.load(); + } + + public static LinkedList audioCodecs = new LinkedList(); + + public static Codec getCodecByDisplayName(String cn) { + + for (Codec c : audioCodecs) { + if (c.getInfo().displayName.equals(cn)) + return c; + } + + return null; + } + + public static Codec getCodecByRtpName(String cn) { + + for (Codec c : audioCodecs) { + if (c.getInfo().rtpPayloadName.equals(cn)) + return c; + } + + return null; + } + + + public static void registerAudioCodec(Codec c) { + audioCodecs.addLast(c); + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/media/codecs/G711.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/codecs/G711.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,393 @@ +package org.sipdroid.media.codecs; + +/** + * G.711 codec. This class provides methods for u-law, A-law and linear PCM + * conversions. + */ +public class G711 extends Codec { + /* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + public static final CodecInfo mCodecInfo = new CodecInfo(); + + static { + mCodecInfo.displayName = "aLaw"; + mCodecInfo.rtpPayloadName = "PCMA"; + mCodecInfo.description = "G711 aLaw codec"; + mCodecInfo.rtpPayloadCode = 8; + mCodecInfo.samplingRate = 8000; + mCodecInfo.rtpSampleDivider = 1; + mCodecInfo.minFrameTimeMsecs = 20; + mCodecInfo.codecFrameSize = 160; + CodecManager.registerAudioCodec(new G711()); + } + + private static final int[] _a2s = { + + 60032, 60288, 59520, 59776, 61056, 61312, 60544, 60800, + 57984, 58240, 57472, 57728, 59008, 59264, 58496, 58752, + 62784, 62912, 62528, 62656, 63296, 63424, 63040, 63168, + 61760, 61888, 61504, 61632, 62272, 62400, 62016, 62144, + 43520, 44544, 41472, 42496, 47616, 48640, 45568, 46592, + 35328, 36352, 33280, 34304, 39424, 40448, 37376, 38400, + 54528, 55040, 53504, 54016, 56576, 57088, 55552, 56064, + 50432, 50944, 49408, 49920, 52480, 52992, 51456, 51968, + 65192, 65208, 65160, 65176, 65256, 65272, 65224, 65240, + 65064, 65080, 65032, 65048, 65128, 65144, 65096, 65112, + 65448, 65464, 65416, 65432, 65512, 65528, 65480, 65496, + 65320, 65336, 65288, 65304, 65384, 65400, 65352, 65368, + 64160, 64224, 64032, 64096, 64416, 64480, 64288, 64352, + 63648, 63712, 63520, 63584, 63904, 63968, 63776, 63840, + 64848, 64880, 64784, 64816, 64976, 65008, 64912, 64944, + 64592, 64624, 64528, 64560, 64720, 64752, 64656, 64688, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848 + + }; + + private static final int[] _s2a = { + + 213,212,215,214,209,208,211,210,221,220,223,222,217,216,219,218, + 197,196,199,198,193,192,195,194,205,204,207,206,201,200,203,202, + 245,245,244,244,247,247,246,246,241,241,240,240,243,243,242,242, + 253,253,252,252,255,255,254,254,249,249,248,248,251,251,250,250, + 229,229,229,229,228,228,228,228,231,231,231,231,230,230,230,230, + 225,225,225,225,224,224,224,224,227,227,227,227,226,226,226,226, + 237,237,237,237,236,236,236,236,239,239,239,239,238,238,238,238, + 233,233,233,233,232,232,232,232,235,235,235,235,234,234,234,234, + 149,149,149,149,149,149,149,149,148,148,148,148,148,148,148,148, + 151,151,151,151,151,151,151,151,150,150,150,150,150,150,150,150, + 145,145,145,145,145,145,145,145,144,144,144,144,144,144,144,144, + 147,147,147,147,147,147,147,147,146,146,146,146,146,146,146,146, + 157,157,157,157,157,157,157,157,156,156,156,156,156,156,156,156, + 159,159,159,159,159,159,159,159,158,158,158,158,158,158,158,158, + 153,153,153,153,153,153,153,153,152,152,152,152,152,152,152,152, + 155,155,155,155,155,155,155,155,154,154,154,154,154,154,154,154, + 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, + 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, + 135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135, + 134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131, + 130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130, + 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, + 140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, + 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, + 142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142, + 137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, + 136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, + 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, + 138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138, + 181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, + 181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, + 180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, + 180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, + 183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183, + 183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183, + 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, + 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, + 177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, + 177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, + 176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, + 176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, + 179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179, + 179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179, + 178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, + 178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, + 189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, + 189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, + 188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188, + 188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188, + 191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, + 191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, + 190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190, + 190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190, + 185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, + 185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, + 184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, + 184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, + 187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187, + 187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187, + 186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186, + 186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, + 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, + 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, + 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, + 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, + 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, + 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, + 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, + 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, + 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, + 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, + 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, + 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, + 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, + 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, + 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, + 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, + 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, + 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, + 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, + 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, + 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, + 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, + 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, + 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, + 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, + 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, + 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, + 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, + 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, + 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, + 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, + 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, + 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, + 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, + 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, + 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, + 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, + 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, + 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, + 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, + 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, + 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, + 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, + 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, + 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, + 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, + 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, + 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, + 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, + 106,106,106,106,107,107,107,107,104,104,104,104,105,105,105,105, + 110,110,110,110,111,111,111,111,108,108,108,108,109,109,109,109, + 98, 98, 98, 98, 99, 99, 99, 99, 96, 96, 96, 96, 97, 97, 97, 97, + 102,102,102,102,103,103,103,103,100,100,100,100,101,101,101,101, + 122,122,123,123,120,120,121,121,126,126,127,127,124,124,125,125, + 114,114,115,115,112,112,113,113,118,118,119,119,116,116,117,117, + 74, 75, 72, 73, 78, 79, 76, 77, 66, 67, 64, 65, 70, 71, 68, 69, + 90, 91, 88, 89, 94, 95, 92, 93, 82, 83, 80, 81, 86, 87, 84, 85 + }; + + + public class G711Context extends Context { + public final short[] a2s = new short[256]; + public final byte[] s2a = new byte[65536]; + } + + public void init(G711Context ctx) { + int i; + for (i = 0; i < 256; i++) + ctx.a2s[i] = (short)_a2s[i]; + for (i = 0; i < 65536; i++) + ctx.s2a[i] = (byte)_s2a[i >> 4]; + } + + private void alaw2linear(G711Context ctx, byte alaw[], int inoffset, int size, short lin[], int outoffset) { + int i; + for (i = 0; i < size; i++) + lin[i + outoffset] = ctx.a2s[alaw[i + inoffset] & 0xff]; + } + + private void linear2alaw(G711Context ctx, short lin[], int inoffset, byte alaw[], int outoffset, int size) { + int i; + for (i = 0; i < size; i++) + alaw[i + outoffset] = ctx.s2a[lin[i + inoffset] & 0xffff]; + } + + @Override + public Context initDecoder() { + G711Context ctx = new G711Context(); + init(ctx); + return ctx; + } + + @Override + public Context initEncoder() { + G711Context ctx = new G711Context(); + init(ctx); + return ctx; + } + + @Override + public void cleanDecoder(Context ctx) { + } + + @Override + public void cleanEncoder(Context ctx) { + } + + @Override + public int decode(Context ctx, byte[] indata, int inoffset, int size, + short[] outsample, int outoffset) { + alaw2linear((G711Context) ctx, indata, inoffset, size, outsample, outoffset); + return size; + } + + @Override + public int encode(Context ctx, short[] insample, int inoffset, int size, + byte[] outdata, int outoffset) { + linear2alaw((G711Context) ctx, insample, inoffset, outdata, outoffset, size); + return 0; + } + + @Override + public CodecInfo getInfo() { + return mCodecInfo; + } + + static public void load(){ + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/media/codecs/G722.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/codecs/G722.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,68 @@ +package org.sipdroid.media.codecs; + +public class G722 extends Codec { + + public static final CodecInfo mCodecInfo = new CodecInfo(); + + static { + System.loadLibrary("g722"); + mCodecInfo.displayName = "G722"; + mCodecInfo.rtpPayloadName = "G722"; + mCodecInfo.description = "G722 16kHz codec"; + mCodecInfo.rtpPayloadCode = 9; + mCodecInfo.samplingRate = 16000; + mCodecInfo.rtpSampleDivider = 2; + mCodecInfo.minFrameTimeMsecs = 20; + mCodecInfo.codecFrameSize = 160; + CodecManager.registerAudioCodec(new G722()); + } + + public class G722Context extends Context { + public long ctx; + } + + @Override + public Context initDecoder() { + G722Context decoderCtx = new G722Context(); + decoderCtx.ctx = G722JNI.decodeInit(decoderCtx.ctx, mCodecInfo.codecFrameSize, 0); + return decoderCtx; + } + + @Override + public Context initEncoder() { + G722Context encoderCtx = new G722Context(); + encoderCtx.ctx = G722JNI.encodeInit(encoderCtx.ctx, mCodecInfo.codecFrameSize, 0); + return encoderCtx; + } + + @Override + public void cleanDecoder(Context ctx) { + G722JNI.decodeRelease(((G722Context)ctx).ctx); + } + + @Override + public void cleanEncoder(Context ctx) { + G722JNI.encodeRelease(((G722Context)ctx).ctx); + } + + @Override + public int decode(Context ctx, byte[] indata, int inoffset, int size, + short[] outsample, int outoffset) { + return G722JNI.decode(((G722Context)ctx).ctx, indata, inoffset, outsample, outoffset, size); + } + + @Override + public int encode(Context ctx, short[] insample, int inoffset, int size, + byte[] outdata, int outoffset) { + G722JNI.encode(((G722Context)ctx).ctx, insample, inoffset, outdata, outoffset, size); + return size; + } + + @Override + public CodecInfo getInfo() { + return mCodecInfo; + } + + static public void load(){ + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/media/codecs/G722JNI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/codecs/G722JNI.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,10 @@ +package org.sipdroid.media.codecs; + +public class G722JNI { + public static native long encodeInit(long g722State, int rate, int options); + public static native int encodeRelease(long g722State); + public static native int encode(long g722State, short[] signal, long srcPos, byte[] g722Byte, long destPos, int len); + public static native long decodeInit(long g722State, int rate, int options); + public static native int decodeRelease(long g722State); + public static native int decode(long g722State, byte[] g722Byte, long srcPos, short[] signal, long destPos, int len); +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/media/codecs/GSM.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/codecs/GSM.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,68 @@ +package org.sipdroid.media.codecs; + +public class GSM extends Codec { + + public static final CodecInfo mCodecInfo = new CodecInfo(); + + static { + System.loadLibrary("gsm"); + mCodecInfo.displayName = "GSM"; + mCodecInfo.rtpPayloadName = "GSM"; + mCodecInfo.description = "GSM Full Rate codec"; + mCodecInfo.rtpPayloadCode = 3; + mCodecInfo.samplingRate = 8000; + mCodecInfo.rtpSampleDivider = 1; + mCodecInfo.minFrameTimeMsecs = 20; + mCodecInfo.codecFrameSize = 33; + CodecManager.registerAudioCodec(new GSM()); + } + + public class GSMContext extends Context { + public long ctx; + } + + @Override + public Context initDecoder() { + GSMContext decoderCtx = new GSMContext(); + decoderCtx.ctx = GSMJNI.create(); + return decoderCtx; + } + + @Override + public Context initEncoder() { + GSMContext encoderCtx = new GSMContext(); + encoderCtx.ctx = GSMJNI.create(); + return encoderCtx; + } + + @Override + public void cleanDecoder(Context ctx) { + GSMJNI.destroy(((GSMContext)ctx).ctx); + } + + @Override + public void cleanEncoder(Context ctx) { + GSMJNI.destroy(((GSMContext)ctx).ctx); + } + + @Override + public int decode(Context ctx, byte[] indata, int inoffset, int size, + short[] outsample, int outoffset) { + return GSMJNI.decode(((GSMContext)ctx).ctx, indata, inoffset, outsample, outoffset); + } + + @Override + public int encode(Context ctx, short[] insample, int inoffset, int size, + byte[] outdata, int outoffset) { + GSMJNI.encode(((GSMContext)ctx).ctx, insample, inoffset, outdata, outoffset); + return size; + } + + @Override + public CodecInfo getInfo() { + return mCodecInfo; + } + + static public void load(){ + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/media/codecs/GSMJNI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/codecs/GSMJNI.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,8 @@ +package org.sipdroid.media.codecs; + +public class GSMJNI { + public static native long create(); + public static native void destroy(long gsm); + public static native int decode(long gsm, byte[] gsmByte, long srcPos, short[] signal, long destPos); + public static native void encode(long gsm, short[] signal, long srcPos, byte[] gsmByte, long destPos); +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/net/SipdroidSocket.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/SipdroidSocket.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.sipdroid.net; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.SocketOptions; +import java.net.UnknownHostException; + +import org.sipdroid.net.impl.OSNetworkSystem; +import org.sipdroid.net.impl.PlainDatagramSocketImpl; + +public class SipdroidSocket extends DatagramSocket { + + PlainDatagramSocketImpl impl; + public static boolean loaded = false; + + public SipdroidSocket(int port) throws SocketException, UnknownHostException { + super(!loaded?port:0); + if (loaded) { + impl = new PlainDatagramSocketImpl(); + impl.create(); + impl.bind(port,InetAddress.getByName("0")); + android.util.Log.d("TEST","name : " + InetAddress.getByName("0")); + } + } + + public void close() { + super.close(); + if (loaded) impl.close(); + } + + public void setSoTimeout(int val) throws SocketException { + if (loaded) impl.setOption(SocketOptions.SO_TIMEOUT, val); + else super.setSoTimeout(val); + } + + public void receive(DatagramPacket pack) throws IOException { + if (loaded) impl.receive(pack); + else super.receive(pack); + } + + public void send(DatagramPacket pack) throws IOException { + if (loaded) impl.send(pack); + else super.send(pack); + } + + public boolean isConnected() { + if (loaded) return true; + else return super.isConnected(); + } + + public void disconnect() { + if (!loaded) super.disconnect(); + } + + public void connect(InetAddress addr,int port) { + if (!loaded) super.connect(addr,port); + } + + static { + System.loadLibrary("OSNetworkSystem"); + OSNetworkSystem.getOSNetworkSystem().oneTimeInitialization(true); + SipdroidSocket.loaded = true; + android.util.Log.d("OSNetworkSystem", "LOADED"); + + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/net/impl/OSNetworkSystem.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/impl/OSNetworkSystem.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,748 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// BEGIN android-note +// address length was changed from long to int for performance reasons. +// END android-note + +package org.sipdroid.net.impl; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.SocketImpl; +import java.net.UnknownHostException; +import java.nio.channels.Channel; +// BEGIN android-removed +// import java.nio.channels.SelectableChannel; +// END android-removed +/* + * + * This Class is used for native code wrap, the implement class of + * INetworkSystem. + * + */ +public final class OSNetworkSystem { + + // ---------------------------------------------------- + // Class Variables + // ---------------------------------------------------- + + private static final int ERRORCODE_SOCKET_TIMEOUT = -209; + + private static OSNetworkSystem ref = new OSNetworkSystem(); + + private static final int INETADDR_REACHABLE = 0; + + private static boolean isNetworkInited = false; + + // ---------------------------------------------------- + // Class Constructor + // ---------------------------------------------------- + + // can not be instantiated. + private OSNetworkSystem() { + super(); + } + + /* + * @return a static ref of this class + */ + public static OSNetworkSystem getOSNetworkSystem() { + return ref; + } + + // Useing when cache set/get is OK + // public static native void oneTimeInitializationDatagram( + // boolean jcl_IPv6_support); + // + // public static native void oneTimeInitializationSocket( + // boolean jcl_IPv6_support); + + // -------------------------------------------------- + // java codes that wrap native codes + // -------------------------------------------------- + + public void createSocket(FileDescriptor fd, boolean preferIPv4Stack) + throws IOException { + createSocketImpl(fd, preferIPv4Stack); + } + + public void createDatagramSocket(FileDescriptor fd, boolean preferIPv4Stack) + throws SocketException { + createDatagramSocketImpl(fd, preferIPv4Stack); + } + + public int read(FileDescriptor aFD, byte[] data, int offset, int count, + int timeout) throws IOException { + return readSocketImpl(aFD, data, offset, count, timeout); + } + + public int readDirect(FileDescriptor aFD, int address, int offset, int count, + int timeout) throws IOException { + return readSocketDirectImpl(aFD, address, offset, count, timeout); + } + + public int write(FileDescriptor aFD, byte[] data, int offset, int count) + throws IOException { + return writeSocketImpl(aFD, data, offset, count); + } + + public int writeDirect(FileDescriptor aFD, int address, int offset, + int count) throws IOException { + return writeSocketDirectImpl(aFD, address, offset, count); + } + + public void setNonBlocking(FileDescriptor aFD, boolean block) + throws IOException { + setNonBlockingImpl(aFD, block); + } + + public void connectDatagram(FileDescriptor aFD, int port, int trafficClass, + InetAddress inetAddress) throws SocketException { + connectDatagramImpl2(aFD, port, trafficClass, inetAddress); + } + + public int connect(FileDescriptor aFD, int trafficClass, + InetAddress inetAddress, int port) throws IOException{ + return connectSocketImpl(aFD, trafficClass, inetAddress, port); + } + + // BEGIN android-changed + public int connectWithTimeout(FileDescriptor aFD, int timeout, + int trafficClass, InetAddress inetAddress, int port, int step, + byte[] context) throws IOException{ + return connectWithTimeoutSocketImpl(aFD, timeout, trafficClass, + inetAddress, port, step, context); + } + // END android-changed + + public void connectStreamWithTimeoutSocket(FileDescriptor aFD, int aport, + int timeout, int trafficClass, InetAddress inetAddress) + throws IOException { + connectStreamWithTimeoutSocketImpl(aFD, aport, timeout, trafficClass, + inetAddress); + } + + public void bind(FileDescriptor aFD, int port, InetAddress inetAddress) + throws SocketException { + socketBindImpl(aFD, port, inetAddress); + } + + public boolean bind2(FileDescriptor aFD, int port, boolean bindToDevice, + InetAddress inetAddress) throws SocketException { + return socketBindImpl2(aFD, port, bindToDevice, inetAddress); + } + + public void accept(FileDescriptor fdServer, SocketImpl newSocket, + FileDescriptor fdnewSocket, int timeout) throws IOException { + acceptSocketImpl(fdServer, newSocket, fdnewSocket, timeout); + } + + public int sendDatagram(FileDescriptor fd, byte[] data, int offset, + int length, int port, boolean bindToDevice, int trafficClass, + InetAddress inetAddress) throws IOException { + return sendDatagramImpl(fd, data, offset, length, port, bindToDevice, + trafficClass, inetAddress); + } + + public int sendDatagramDirect(FileDescriptor fd, int address, int offset, + int length, int port, boolean bindToDevice, int trafficClass, + InetAddress inetAddress) throws IOException { + return sendDatagramDirectImpl(fd, address, offset, length, port, bindToDevice, + trafficClass, inetAddress); + } + + public int sendDatagram2(FileDescriptor fd, byte[] data, int offset, + int length, int port, InetAddress inetAddress) throws IOException { + return sendDatagramImpl2(fd, data, offset, length, port, inetAddress); + } + + public int receiveDatagram(FileDescriptor aFD, DatagramPacket packet, + byte[] data, int offset, int length, int receiveTimeout, + boolean peek) throws IOException { + return receiveDatagramImpl(aFD, packet, data, offset, length, + receiveTimeout, peek); + } + + public int receiveDatagramDirect(FileDescriptor aFD, DatagramPacket packet, + int address, int offset, int length, int receiveTimeout, + boolean peek) throws IOException { + return receiveDatagramDirectImpl(aFD, packet, address, offset, length, + receiveTimeout, peek); + } + + public int recvConnectedDatagram(FileDescriptor aFD, DatagramPacket packet, + byte[] data, int offset, int length, int receiveTimeout, + boolean peek) throws IOException { + return recvConnectedDatagramImpl(aFD, packet, data, offset, length, + receiveTimeout, peek); + } + + public int recvConnectedDatagramDirect(FileDescriptor aFD, DatagramPacket packet, int address, + int offset, int length, int receiveTimeout, boolean peek) + throws IOException { + return recvConnectedDatagramDirectImpl(aFD, packet, address, offset, length, receiveTimeout, peek); + } + + public int peekDatagram(FileDescriptor aFD, InetAddress sender, + int receiveTimeout) throws IOException { + return peekDatagramImpl(aFD, sender, receiveTimeout); + } + + public int sendConnectedDatagram(FileDescriptor fd, byte[] data, + int offset, int length, boolean bindToDevice) throws IOException { + return sendConnectedDatagramImpl(fd, data, offset, length, bindToDevice); + } + + public int sendConnectedDatagramDirect(FileDescriptor fd, int address, + int offset, int length, boolean bindToDevice) throws IOException { + return sendConnectedDatagramDirectImpl(fd, address, offset, length, bindToDevice); + } + + public void disconnectDatagram(FileDescriptor aFD) throws SocketException { + disconnectDatagramImpl(aFD); + } + + public void createMulticastSocket(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException { + createMulticastSocketImpl(aFD, preferIPv4Stack); + } + + public void createServerStreamSocket(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException { + createServerStreamSocketImpl(aFD, preferIPv4Stack); + } + + public int receiveStream(FileDescriptor aFD, byte[] data, int offset, + int count, int timeout) throws IOException { + return receiveStreamImpl(aFD, data, offset, count, timeout); + } + + public int sendStream(FileDescriptor fd, byte[] data, int offset, int count) + throws IOException { + return sendStreamImpl(fd, data, offset, count); + } + + public void shutdownInput(FileDescriptor descriptor) throws IOException { + shutdownInputImpl(descriptor); + } + + public void shutdownOutput(FileDescriptor descriptor) throws IOException { + shutdownOutputImpl(descriptor); + } + + public boolean supportsUrgentData(FileDescriptor fd) { + return supportsUrgentDataImpl(fd); + } + + public void sendUrgentData(FileDescriptor fd, byte value) { + sendUrgentDataImpl(fd, value); + } + + public int availableStream(FileDescriptor aFD) throws SocketException { + return availableStreamImpl(aFD); + } + + // BEGIN android-removed + // public void acceptStreamSocket(FileDescriptor fdServer, + // SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) + // throws IOException { + // acceptStreamSocketImpl(fdServer, newSocket, fdnewSocket, timeout); + // } + // + // public void createStreamSocket(FileDescriptor aFD, boolean preferIPv4Stack) + // throws SocketException { + // createStreamSocketImpl(aFD, preferIPv4Stack); + // } + // END android-removed + + public void listenStreamSocket(FileDescriptor aFD, int backlog) + throws SocketException { + listenStreamSocketImpl(aFD, backlog); + } + + // BEGIN android-removed + // public boolean isReachableByICMP(final InetAddress dest, + // InetAddress source, final int ttl, final int timeout) { + // return INETADDR_REACHABLE == isReachableByICMPImpl(dest, source, ttl, + // timeout); + // } + // END android-removed + + /* + * + * @param + * readChannels all channels interested in read and accept + * @param + * writeChannels all channels interested in write and connect + * @param timeout + * timeout in millis @return a set of channels that are ready for operation + * @throws + * SocketException @return int array, each int approve one of the * channel if OK + */ + + public int[] select(FileDescriptor[] readFDs, + FileDescriptor[] writeFDs, long timeout) + throws SocketException { + int countRead = readFDs.length; + int countWrite = writeFDs.length; + int result = 0; + if (0 == countRead + countWrite) { + return (new int[0]); + } + int[] flags = new int[countRead + countWrite]; + + // handle timeout in native + result = selectImpl(readFDs, writeFDs, countRead, countWrite, flags, + timeout); + + if (0 <= result) { + return flags; + } + if (ERRORCODE_SOCKET_TIMEOUT == result) { + return new int[0]; + } + throw new SocketException(); + + } + + public InetAddress getSocketLocalAddress(FileDescriptor aFD, + boolean preferIPv6Addresses) { + return getSocketLocalAddressImpl(aFD, preferIPv6Addresses); + } + + /* + * Query the IP stack for the local port to which this socket is bound. + * + * @param aFD the socket descriptor @param preferIPv6Addresses address + * preference for nodes that support both IPv4 and IPv6 @return int the + * local port to which the socket is bound + */ + public int getSocketLocalPort(FileDescriptor aFD, + boolean preferIPv6Addresses) { + return getSocketLocalPortImpl(aFD, preferIPv6Addresses); + } + + /* + * Query the IP stack for the nominated socket option. + * + * @param aFD the socket descriptor @param opt the socket option type + * @return the nominated socket option value + * + * @throws SocketException if the option is invalid + */ + public Object getSocketOption(FileDescriptor aFD, int opt) + throws SocketException { + return getSocketOptionImpl(aFD, opt); + } + + /* + * Set the nominated socket option in the IP stack. + * + * @param aFD the socket descriptor @param opt the option selector @param + * optVal the nominated option value + * + * @throws SocketException if the option is invalid or cannot be set + */ + public void setSocketOption(FileDescriptor aFD, int opt, Object optVal) + throws SocketException { + setSocketOptionImpl(aFD, opt, optVal); + } + + public int getSocketFlags() { + return getSocketFlagsImpl(); + } + + /* + * Close the socket in the IP stack. + * + * @param aFD the socket descriptor + */ + public void socketClose(FileDescriptor aFD) throws IOException { + socketCloseImpl(aFD); + } + + public InetAddress getHostByAddr(byte[] addr) throws UnknownHostException { + return getHostByAddrImpl(addr); + } + + public InetAddress getHostByName(String addr, boolean preferIPv6Addresses) + throws UnknownHostException { + return getHostByNameImpl(addr, preferIPv6Addresses); + } + + public void setInetAddress(InetAddress sender, byte[] address) { + setInetAddressImpl(sender, address); + } + + // --------------------------------------------------- + // Native Codes + // --------------------------------------------------- + + static native void createSocketImpl(FileDescriptor fd, + boolean preferIPv4Stack); + + /* + * Allocate a datagram socket in the IP stack. The socket is associated with + * the aFD. + * + * @param aFD the FileDescriptor to associate with the socket @param + * preferIPv4Stack IP stack preference if underlying platform is V4/V6 + * @exception SocketException upon an allocation error + */ + static native void createDatagramSocketImpl(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException; + + static native int readSocketImpl(FileDescriptor aFD, byte[] data, + int offset, int count, int timeout) throws IOException; + + static native int readSocketDirectImpl(FileDescriptor aFD, int address, + int offset, int count, int timeout) throws IOException; + + static native int writeSocketImpl(FileDescriptor fd, byte[] data, + int offset, int count) throws IOException; + + static native int writeSocketDirectImpl(FileDescriptor fd, int address, + int offset, int count) throws IOException; + + static native void setNonBlockingImpl(FileDescriptor aFD, + boolean block); + + static native int connectSocketImpl(FileDescriptor aFD, + int trafficClass, InetAddress inetAddress, int port); + + // BEGIN android-changed + static native int connectWithTimeoutSocketImpl( + FileDescriptor aFD, int timeout, int trafficClass, + InetAddress hostname, int port, int step, byte[] context); + // END android-changed + + static native void connectStreamWithTimeoutSocketImpl(FileDescriptor aFD, + int aport, int timeout, int trafficClass, InetAddress inetAddress) + throws IOException; + + static native void socketBindImpl(FileDescriptor aFD, int port, + InetAddress inetAddress) throws SocketException; + + static native void listenStreamSocketImpl(FileDescriptor aFD, int backlog) + throws SocketException; + + static native int availableStreamImpl(FileDescriptor aFD) + throws SocketException; + + static native void acceptSocketImpl(FileDescriptor fdServer, + SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) + throws IOException; + + static native boolean supportsUrgentDataImpl(FileDescriptor fd); + + static native void sendUrgentDataImpl(FileDescriptor fd, byte value); + + /* + * Connect the socket to a port and address + * + * @param aFD the FileDescriptor to associate with the socket @param port + * the port to connect to @param trafficClass the traffic Class to be used + * then the connection is made @param inetAddress address to connect to. + * + * @exception SocketException if the connect fails + */ + static native void connectDatagramImpl2(FileDescriptor aFD, + int port, int trafficClass, InetAddress inetAddress) + throws SocketException; + + /* + * Disconnect the socket to a port and address + * + * @param aFD the FileDescriptor to associate with the socket + * + * @exception SocketException if the disconnect fails + */ + static native void disconnectDatagramImpl(FileDescriptor aFD) + throws SocketException; + + /* + * Allocate a datagram socket in the IP stack. The socket is associated with + * the aFD. + * + * @param aFD the FileDescriptor to associate with the socket @param + * preferIPv4Stack IP stack preference if underlying platform is V4/V6 + * @exception SocketException upon an allocation error + */ + + /* + * Bind the socket to the port/localhost in the IP stack. + * + * @param aFD the socket descriptor @param port the option selector @param + * bindToDevice bind the socket to the specified interface @param + * inetAddress address to connect to. @return if bind successful @exception + * SocketException thrown if bind operation fails + */ + static native boolean socketBindImpl2(FileDescriptor aFD, + int port, boolean bindToDevice, InetAddress inetAddress) + throws SocketException; + + /* + * Peek on the socket, update sender address and answer the + * sender port. + * + * @param aFD the socket FileDescriptor @param sender an InetAddress, to be + * updated with the sender's address @param receiveTimeout the maximum + * length of time the socket should block, reading @return int the sender + * port + * + * @exception IOException upon an read error or timeout + */ + static native int peekDatagramImpl(FileDescriptor aFD, + InetAddress sender, int receiveTimeout) throws IOException; + + /* + * Recieve data on the socket into the specified buffer. The packet fields + * data & length are passed in addition to + * packet to eliminate the JNI field access calls. + * + * @param aFD the socket FileDescriptor @param packet the DatagramPacket to + * receive into @param data the data buffer of the packet @param offset the + * offset in the data buffer @param length the length of the data buffer in + * the packet @param receiveTimeout the maximum length of time the socket + * should block, reading @param peek indicates to peek at the data @return + * number of data received @exception IOException upon an read error or + * timeout + */ + static native int receiveDatagramImpl(FileDescriptor aFD, + DatagramPacket packet, byte[] data, int offset, int length, + int receiveTimeout, boolean peek) throws IOException; + + static native int receiveDatagramDirectImpl(FileDescriptor aFD, + DatagramPacket packet, int address, int offset, int length, + int receiveTimeout, boolean peek) throws IOException; + + /* + * Recieve data on the connected socket into the specified buffer. The + * packet fields data & length are passed in + * addition to packet to eliminate the JNI field access + * calls. + * + * @param aFD the socket FileDescriptor @param packet the DatagramPacket to + * receive into @param data the data buffer of the packet @param offset the + * offset in the data buffer @param length the length of the data buffer in + * the packet @param receiveTimeout the maximum length of time the socket + * should block, reading @param peek indicates to peek at the data @return + * number of data received @exception IOException upon an read error or + * timeout + */ + static native int recvConnectedDatagramImpl(FileDescriptor aFD, + DatagramPacket packet, byte[] data, int offset, int length, + int receiveTimeout, boolean peek) throws IOException; + + static native int recvConnectedDatagramDirectImpl(FileDescriptor aFD, + DatagramPacket packet, int address, int offset, int length, + int receiveTimeout, boolean peek) throws IOException; + + /* + * Send the data to the nominated target address + * and port. These values are derived from the + * DatagramPacket to reduce the field calls within JNI. + * + * @param fd the socket FileDescriptor @param data the data buffer of the + * packet @param offset the offset in the data buffer @param length the + * length of the data buffer in the packet @param port the target host port + * @param bindToDevice if bind to device @param trafficClass the traffic + * class to be used when the datagram is sent @param inetAddress address to + * connect to. @return number of data send + * + * @exception IOException upon an read error or timeout + */ + static native int sendDatagramImpl(FileDescriptor fd, + byte[] data, int offset, int length, int port, + boolean bindToDevice, int trafficClass, InetAddress inetAddress) + throws IOException; + + static native int sendDatagramDirectImpl(FileDescriptor fd, + int address, int offset, int length, int port, + boolean bindToDevice, int trafficClass, InetAddress inetAddress) + throws IOException; + + /* + * Send the data to the address and port to which the was + * connnected and port. + * + * @param fd the socket FileDescriptor @param data the data buffer of the + * packet @param offset the offset in the data buffer @param length the + * length of the data buffer in the packet @param bindToDevice not used, + * current kept in case needed as was the case for sendDatagramImpl @return + * number of data send @exception IOException upon an read error or timeout + */ + static native int sendConnectedDatagramImpl(FileDescriptor fd, + byte[] data, int offset, int length, boolean bindToDevice) + throws IOException; + + static native int sendConnectedDatagramDirectImpl(FileDescriptor fd, + int address, int offset, int length, boolean bindToDevice) + throws IOException; + + /* + * Answer the result of attempting to create a server stream socket in the + * IP stack. Any special options required for server sockets will be set by + * this method. + * + * @param aFD the socket FileDescriptor @param preferIPv4Stack if use IPV4 + * @exception SocketException if an error occurs while creating the socket + */ + static native void createServerStreamSocketImpl(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException; + + /* + * Answer the result of attempting to create a multicast socket in the IP + * stack. Any special options required for server sockets will be set by + * this method. + * + * @param aFD the socket FileDescriptor @param preferIPv4Stack if use IPV4 + * @exception SocketException if an error occurs while creating the socket + */ + static native void createMulticastSocketImpl(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException; + + /* + * Recieve at most count bytes into the buffer data + * at the offset on the socket. + * + * @param aFD the socket FileDescriptor @param data the receive buffer + * @param offset the offset into the buffer @param count the max number of + * bytes to receive @param timeout the max time the read operation should + * block waiting for data @return int the actual number of bytes read + * @throws IOException @exception SocketException if an error occurs while + * reading + */ + static native int receiveStreamImpl(FileDescriptor aFD, byte[] data, + int offset, int count, int timeout) throws IOException; + + /* + * Send count bytes from the buffer data at + * the offset, on the socket. + * + * @param fd + * + * @param data the send buffer @param offset the offset into the buffer + * @param count the number of bytes to receive @return int the actual number + * of bytes sent @throws IOException @exception SocketException if an error + * occurs while writing + */ + static native int sendStreamImpl(FileDescriptor fd, byte[] data, + int offset, int count) throws IOException; + + private native void shutdownInputImpl(FileDescriptor descriptor) + throws IOException; + + private native void shutdownOutputImpl(FileDescriptor descriptor) + throws IOException; + + // BEGIN android-removed + // static native void acceptStreamSocketImpl(FileDescriptor fdServer, + // SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) + // throws IOException; + // + // static native void createStreamSocketImpl(FileDescriptor aFD, + // boolean preferIPv4Stack) throws SocketException; + // END android-removed + + static native int sendDatagramImpl2(FileDescriptor fd, byte[] data, + int offset, int length, int port, InetAddress inetAddress) + throws IOException; + + static native int selectImpl(FileDescriptor[] readfd, + FileDescriptor[] writefd, int cread, int cwirte, int[] flags, + long timeout); + + static native InetAddress getSocketLocalAddressImpl(FileDescriptor aFD, + boolean preferIPv6Addresses); + + /* + * Query the IP stack for the local port to which this socket is bound. + * + * @param aFD the socket descriptor @param preferIPv6Addresses address + * preference for nodes that support both IPv4 and IPv6 @return int the + * local port to which the socket is bound + */ + static native int getSocketLocalPortImpl(FileDescriptor aFD, + boolean preferIPv6Addresses); + + /* + * Query the IP stack for the nominated socket option. + * + * @param aFD the socket descriptor @param opt the socket option type + * @return the nominated socket option value + * + * @throws SocketException if the option is invalid + */ + static native Object getSocketOptionImpl(FileDescriptor aFD, int opt) + throws SocketException; + + /* + * Set the nominated socket option in the IP stack. + * + * @param aFD the socket descriptor @param opt the option selector @param + * optVal the nominated option value + * + * @throws SocketException if the option is invalid or cannot be set + */ + static native void setSocketOptionImpl(FileDescriptor aFD, int opt, + Object optVal) throws SocketException; + + static native int getSocketFlagsImpl(); + + /* + * Close the socket in the IP stack. + * + * @param aFD the socket descriptor + */ + static native void socketCloseImpl(FileDescriptor aFD); + + static native InetAddress getHostByAddrImpl(byte[] addr) + throws UnknownHostException; + + static native InetAddress getHostByNameImpl(String addr, + boolean preferIPv6Addresses) throws UnknownHostException; + + native void setInetAddressImpl(InetAddress sender, byte[] address); + + // BEGIN android-removed + // native int isReachableByICMPImpl(InetAddress addr, InetAddress local, + // int ttl, int timeout); + // END android-removed + + native Channel inheritedChannelImpl(); + + public Channel inheritedChannel() { + return inheritedChannelImpl(); + } + + public void oneTimeInitialization(boolean jcl_supports_ipv6){ + if (!isNetworkInited){ + oneTimeInitializationImpl(jcl_supports_ipv6); + isNetworkInited = true; + } + } + + native void oneTimeInitializationImpl (boolean jcl_supports_ipv6); +} \ No newline at end of file diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/net/impl/PlainDatagramSocketImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/impl/PlainDatagramSocketImpl.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.sipdroid.net.impl; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.DatagramPacket; +import java.net.DatagramSocketImpl; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketOptions; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.security.AccessController; + +/** + * The default, concrete instance of datagram sockets. This class does not + * support security checks. Alternative types of DatagramSocketImpl's may be + * used by setting the impl.prefix system property. + */ +public class PlainDatagramSocketImpl extends DatagramSocketImpl { + + static final int MULTICAST_IF = 1; + + static final int MULTICAST_TTL = 2; + + static final int TCP_NODELAY = 4; + + static final int FLAG_SHUTDOWN = 8; + + private final static int SO_BROADCAST = 32; + + final static int IP_MULTICAST_ADD = 19; + + final static int IP_MULTICAST_DROP = 20; + + final static int IP_MULTICAST_TTL = 17; + + /** + * for datagram and multicast sockets we have to set REUSEADDR and REUSEPORT + * when REUSEADDR is set for other types of sockets we need to just set + * REUSEADDR therefore we have this other option which sets both if + * supported by the platform. this cannot be in SOCKET_OPTIONS because since + * it is a public interface it ends up being public even if it is not + * declared public + */ + static final int REUSEADDR_AND_REUSEPORT = 10001; + + private boolean bindToDevice; + + private byte[] ipaddress = { 0, 0, 0, 0 }; + + private int ttl = 1; + + private OSNetworkSystem netImpl = OSNetworkSystem.getOSNetworkSystem(); + + private volatile boolean isNativeConnected = false; + + public int receiveTimeout; + + public boolean streaming = true; + + public boolean shutdownInput; + + /** + * used to keep address to which the socket was connected to at the native + * level + */ + private InetAddress connectedAddress; + + private int connectedPort = -1; + + /** + * used to store the trafficClass value which is simply returned as the + * value that was set. We also need it to pass it to methods that specify an + * address packets are going to be sent to + */ + private int trafficClass; + + public PlainDatagramSocketImpl(FileDescriptor fd, int localPort) { + super(); + this.fd = fd; + this.localPort = localPort; + } + + public PlainDatagramSocketImpl() { + super(); + fd = new FileDescriptor(); + } + + @Override + public void bind(int port, InetAddress addr) throws SocketException { + String prop = null; //AccessController.doPrivileged(new PriviAction("bindToDevice")); //$NON-NLS-1$ + boolean useBindToDevice = prop != null && prop.toLowerCase().equals("true"); //$NON-NLS-1$ + bindToDevice = netImpl.bind2(fd, port, useBindToDevice, addr); + if (0 != port) { + localPort = port; + } else { +// localPort = netImpl.getSocketLocalPort(fd, NetUtil.preferIPv6Addresses()); + } + + try { + // Ignore failures + setOption(SO_BROADCAST, Boolean.TRUE); + } catch (IOException e) { + } + } + + @Override + public void close() { + synchronized (fd) { + if (fd.valid()) { + try { + netImpl.socketClose(fd); + } catch (IOException e) { + } + fd = new FileDescriptor(); + } + } + } + + @Override + public void create() throws SocketException { + netImpl.createDatagramSocket(fd, false); //NetUtil.preferIPv4Stack()); + } + + @Override + protected void finalize() { + close(); + } + + @Override + public Object getOption(int optID) throws SocketException { + if (optID == SocketOptions.SO_TIMEOUT) { + return Integer.valueOf(receiveTimeout); + } else if (optID == SocketOptions.IP_TOS) { + return Integer.valueOf(trafficClass); + } else { + // Call the native first so there will be + // an exception if the socket if closed. + Object result = netImpl.getSocketOption(fd, optID); + if (optID == SocketOptions.IP_MULTICAST_IF + && (netImpl.getSocketFlags() & MULTICAST_IF) != 0) { + try { + return InetAddress.getByAddress(ipaddress); + } catch (UnknownHostException e) { + return null; + } + } + return result; + } + } + + @Override + public int getTimeToLive() throws IOException { + // Call the native first so there will be an exception if the socket if + // closed. + int result = (((Byte) getOption(IP_MULTICAST_TTL)).byteValue()) & 0xFF; + if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) { + return ttl; + } + return result; + } + + @Override + public byte getTTL() throws IOException { + // Call the native first so there will be an exception if the socket if + // closed. + byte result = ((Byte) getOption(IP_MULTICAST_TTL)).byteValue(); + if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) { + return (byte) ttl; + } + return result; + } + + @Override + public void join(InetAddress addr) throws IOException { +// setOption(IP_MULTICAST_ADD, new GenericIPMreq(addr)); + } + + @Override + public void joinGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException { + if (addr instanceof InetSocketAddress) { + InetAddress groupAddr = ((InetSocketAddress) addr).getAddress(); +// setOption(IP_MULTICAST_ADD, new GenericIPMreq(groupAddr, netInterface)); + } + } + + @Override + public void leave(InetAddress addr) throws IOException { +// setOption(IP_MULTICAST_DROP, new GenericIPMreq(addr)); + } + + @Override + public void leaveGroup(SocketAddress addr, NetworkInterface netInterface) + throws IOException { + if (addr instanceof InetSocketAddress) { + InetAddress groupAddr = ((InetSocketAddress) addr).getAddress(); +// setOption(IP_MULTICAST_DROP, new GenericIPMreq(groupAddr, netInterface)); + } + } + + @Override + protected int peek(InetAddress sender) throws IOException { + if (isNativeConnected) { + /* + * in this case we know the port and address from which the data + * must have be been received as the socket is connected. However, + * we still need to do the receive in order to know that there was + * data received. We use a short buffer as we don't actually need + * the packet, only the knowledge that it is there + */ + byte[] storageArray = new byte[10]; + DatagramPacket pack = new DatagramPacket(storageArray, storageArray.length); + netImpl.recvConnectedDatagram(fd, pack, pack.getData(), pack.getOffset(), pack + .getLength(), receiveTimeout, true); // peek + // to set the sender ,we now use a native function + // sender.ipaddress = connectedAddress.getAddress(); + netImpl.setInetAddress(sender, connectedAddress.getAddress()); + return connectedPort; + } + return netImpl.peekDatagram(fd, sender, receiveTimeout); + } + + @Override + public void receive(DatagramPacket pack) throws java.io.IOException { + try { + if (isNativeConnected) { + // do not peek + netImpl.recvConnectedDatagram(fd, pack, pack.getData(), pack.getOffset(), pack + .getLength(), receiveTimeout, false); + updatePacketRecvAddress(pack); + } else { + // receiveDatagramImpl2 + netImpl.receiveDatagram(fd, pack, pack.getData(), pack.getOffset(), pack + .getLength(), receiveTimeout, false); + } + } catch (InterruptedIOException e) { + throw new SocketTimeoutException(e.getMessage()); + } + } + + @Override + public void send(DatagramPacket packet) throws IOException { + + if (isNativeConnected) { + netImpl.sendConnectedDatagram(fd, packet.getData(), packet.getOffset(), packet + .getLength(), bindToDevice); + } else { + // sendDatagramImpl2 + netImpl.sendDatagram(fd, packet.getData(), packet.getOffset(), packet.getLength(), + packet.getPort(), bindToDevice, trafficClass, packet.getAddress()); + } + } + + /** + * Set the nominated socket option. As the timeouts are not set as options + * in the IP stack, the value is stored in an instance field. + * + * @throws SocketException thrown if the option value is unsupported or + * invalid + */ + @Override + public void setOption(int optID, Object val) throws SocketException { + /* + * for datagram sockets on some platforms we have to set both the + * REUSEADDR AND REUSEPORT so for REUSEADDR set this option option which + * tells the VM to set the two values as appropriate for the platform + */ + if (optID == SocketOptions.SO_REUSEADDR) { + optID = REUSEADDR_AND_REUSEPORT; + } + + if (optID == SocketOptions.SO_TIMEOUT) { + receiveTimeout = ((Integer) val).intValue(); + } else { + int flags = netImpl.getSocketFlags(); + try { + netImpl.setSocketOption(fd, optID | (flags << 16), val); + } catch (SocketException e) { + // we don't throw an exception for IP_TOS even if the platform + // won't let us set the requested value + if (optID != SocketOptions.IP_TOS) { + throw e; + } + } + if (optID == SocketOptions.IP_MULTICAST_IF && (flags & MULTICAST_IF) != 0) { + InetAddress inet = (InetAddress) val; +// if (NetUtil.bytesToInt(inet.getAddress(), 0) == 0 || inet.isLoopbackAddress()) { +// ipaddress = ((InetAddress) val).getAddress(); +// } else + { + InetAddress local = null; + try { + local = InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + throw new SocketException("getLocalHost(): " + e.toString()); + } + if (inet.equals(local)) { + ipaddress = ((InetAddress) val).getAddress(); + } else { + throw new SocketException(val + " != getLocalHost(): " + local); + } + } + } + /* + * save this value as it is actually used differently for IPv4 and + * IPv6 so we cannot get the value using the getOption. The option + * is actually only set for IPv4 and a masked version of the value + * will be set as only a subset of the values are allowed on the + * socket. Therefore we need to retain it to return the value that + * was set. We also need the value to be passed into a number of + * natives so that it can be used properly with IPv6 + */ + if (optID == SocketOptions.IP_TOS) { + trafficClass = ((Integer) val).intValue(); + } + } + } + + @Override + public void setTimeToLive(int ttl) throws java.io.IOException { + setOption(IP_MULTICAST_TTL, Byte.valueOf((byte) (ttl & 0xFF))); + if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) { + this.ttl = ttl; + } + } + + @Override + public void setTTL(byte ttl) throws java.io.IOException { + setOption(IP_MULTICAST_TTL, Byte.valueOf(ttl)); + if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) { + this.ttl = ttl; + } + } + + @Override + public void connect(InetAddress inetAddr, int port) throws SocketException { + + // connectDatagram impl2 + netImpl.connectDatagram(fd, port, trafficClass, inetAddr); + + // if we get here then we are connected at the native level + try { + connectedAddress = InetAddress.getByAddress(inetAddr.getAddress()); + } catch (UnknownHostException e) { + // this is never expected to happen as we should not have gotten + // here if the address is not resolvable + throw new SocketException("K0317 "+inetAddr.getHostName()); //$NON-NLS-1$ + } + connectedPort = port; + isNativeConnected = true; + } + + @Override + public void disconnect() { + try { + netImpl.disconnectDatagram(fd); + } catch (Exception e) { + // there is currently no way to return an error so just eat any + // exception + } + connectedPort = -1; + connectedAddress = null; + isNativeConnected = false; + } + + @Override + public int peekData(DatagramPacket pack) throws IOException { + try { + if (isNativeConnected) { + netImpl.recvConnectedDatagram(fd, pack, pack.getData(), pack.getOffset(), pack + .getLength(), receiveTimeout, true); // peek + updatePacketRecvAddress(pack); + } else { + // receiveDatagram 2 + netImpl.receiveDatagram(fd, pack, pack.getData(), pack.getOffset(), pack + .getLength(), receiveTimeout, true); // peek + } + } catch (InterruptedIOException e) { + throw new SocketTimeoutException(e.toString()); + } + return pack.getPort(); + } + + /** + * Set the received address and port in the packet. We do this when the + * Datagram socket is connected at the native level and the + * recvConnnectedDatagramImpl does not update the packet with address from + * which the packet was received + * + * @param packet + * the packet to be updated + */ + private void updatePacketRecvAddress(DatagramPacket packet) { + packet.setAddress(connectedAddress); + packet.setPort(connectedPort); + } + +} \ No newline at end of file diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/net/tools/DataFramePool.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/tools/DataFramePool.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,44 @@ +package org.sipdroid.net.tools; + +import jlibrtp.DataFrame; + +public class DataFramePool extends ObjectPool { + + private static DataFramePool instance = null; + public static DataFramePool getInstance() { + if(instance == null) { + instance = new DataFramePool(20); + } + return instance; + } + + public static void removeInstance() { + instance = null; + } + + protected DataFramePool(int nbObject) { + super(nbObject); + for(int i = 0; i < nbObject; ++i) { + checkIn(create()); + } + } + + @Override + Object create() { + return new DataFrame(); + } + + @Override + boolean validate(Object o) { + return true; + } + + public DataFrame borrowFrame() { + return (DataFrame) super.checkOut(); + } + + public void returnFrame(DataFrame o) { + o.release(); + super.checkIn(o); + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/net/tools/DatagramPool.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/tools/DatagramPool.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,45 @@ +package org.sipdroid.net.tools; + +import java.net.DatagramPacket; + +public class DatagramPool extends ObjectPool { + + private static DatagramPool instance = null; + public static DatagramPool getInstance() { + if(instance == null) { + instance = new DatagramPool(20); + } + return instance; + } + + public static void removeInstance() { + instance = null; + } + + protected DatagramPool(int nbDatagram) { + super(nbDatagram); + for(int i = 0; i < nbDatagram; ++i) { + checkIn(create()); + } + } + + @Override + Object create() { + byte[] rawPkt = new byte[1500]; + DatagramPacket packet = new DatagramPacket(rawPkt, rawPkt.length); + return packet; + } + + @Override + boolean validate(Object o) { + return true; + } + + public DatagramPacket borrowPacket() { + return (DatagramPacket) super.checkOut(); + } + + public void returnPacket(DatagramPacket packet) { + super.checkIn(packet); + } +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/net/tools/GenericPool.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/tools/GenericPool.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,31 @@ +package org.sipdroid.net.tools; + +public class GenericPool extends ObjectPool { + + public GenericPool(int size) { + super(size); + for(int i = 0; i < size; ++i) { + checkIn(create()); + } + } + + @Override + protected E create() { + return (E)new Object(); + } + + @Override + protected boolean validate(Object o) { + // TODO Auto-generated method stub + return false; + } + + public E borrowItem() { + return (E) super.checkOut(); + } + + public void returnItem(E o) { + super.checkIn(o); + } + +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/net/tools/ObjectPool.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/tools/ObjectPool.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,37 @@ +package org.sipdroid.net.tools; + +import java.util.ArrayList; + +public abstract class ObjectPool { + private ArrayList locked, unlocked; + + ObjectPool(int size){ + locked = new ArrayList(size); + unlocked = new ArrayList(size); + } + + public int getPoolSize() { + return locked.size() + unlocked.size(); + } + + abstract Object create(); + abstract boolean validate( Object o ); + synchronized Object checkOut(){ + if(unlocked.size() > 0){ + Object cur = unlocked.get(0); + unlocked.remove(cur); + locked.add(cur); + return(cur); + } + // no objects available, create a new one + Object o = create(); + locked.add(o); + return(o); + } + + synchronized void checkIn( Object o ) { + locked.remove( o ); + unlocked.add(o); + } + +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/net/tools/PktBufNodePool.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/tools/PktBufNodePool.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,44 @@ +package org.sipdroid.net.tools; + +import jlibrtp.PktBufNode; + +public class PktBufNodePool extends ObjectPool { + + private static PktBufNodePool instance = null; + public static PktBufNodePool getInstance() { + if(instance == null) { + instance = new PktBufNodePool(20); + } + return instance; + } + + public static void removeInstance() { + instance = null; + } + + protected PktBufNodePool(int nbObject) { + super(nbObject); + for(int i = 0; i < nbObject; ++i) { + checkIn(create()); + } + } + + @Override + Object create() { + return new PktBufNode(); + } + + @Override + boolean validate(Object o) { + return true; + } + + public PktBufNode borrowBufNode() { + return (PktBufNode) super.checkOut(); + } + + public void returnBufNode(PktBufNode o) { + super.checkIn(o); + } + +} diff -r 047b5e2b9904 -r af3a788344f9 src/org/sipdroid/net/tools/RtpPktPool.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/tools/RtpPktPool.java Thu Jan 21 01:35:17 2010 +0100 @@ -0,0 +1,44 @@ +package org.sipdroid.net.tools; + +import jlibrtp.RtpPkt; + +public class RtpPktPool extends ObjectPool { + + private static RtpPktPool instance = null; + public static RtpPktPool getInstance() { + if(instance == null) { + instance = new RtpPktPool(20); + } + return instance; + } + + public static void removeInstance() { + instance = null; + } + + protected RtpPktPool(int nbObject) { + super(nbObject); + for(int i = 0; i < nbObject; ++i) { + checkIn(create()); + } + } + + @Override + Object create() { + return new RtpPkt(); + } + + @Override + boolean validate(Object o) { + return true; + } + + public RtpPkt borrowPkt() { + return (RtpPkt) super.checkOut(); + } + + public void returnPkt(RtpPkt o) { + super.checkIn(o); + } + +}