diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java index 1d86d482b..bf253b727 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java @@ -20,6 +20,7 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; @@ -97,6 +98,7 @@ public class TutorialBuddyStatus implements LinphoneCoreListener { public void globalState(LinphoneCore lc, GlobalState state, String message) {} public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg) {} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java index 91dc0f247..7514a1ddc 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java @@ -20,6 +20,7 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; @@ -75,6 +76,7 @@ public class TutorialChatRoom implements LinphoneCoreListener { public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {} public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg){} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java index 354f97501..4b63e8813 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java @@ -20,6 +20,7 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; @@ -69,6 +70,7 @@ public class TutorialHelloWorld implements LinphoneCoreListener { public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {} public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java index 48e473d61..86b300143 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java @@ -20,6 +20,7 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; @@ -80,6 +81,7 @@ public class TutorialRegistration implements LinphoneCoreListener { public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg) {} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 16d44390c..38d36ae42 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -112,6 +112,7 @@ public: vTable.message_received = message_received; vTable.new_subscription_request = new_subscription_request; vTable.notify_presence_recv = notify_presence_recv; + vTable.call_stats_updated = callStatsUpdated; listenerClass = (jclass)env->NewGlobalRef(env->GetObjectClass( alistener)); @@ -133,6 +134,9 @@ public: callStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCall$State")); callStateFromIntId = env->GetStaticMethodID(callStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCall$State;"); + /*callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats);*/ + callStatsUpdatedId = env->GetMethodID(listenerClass, "callStatsUpdated", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCallStats;)V"); + chatMessageStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatMessage$State")); chatMessageStateFromIntId = env->GetStaticMethodID(chatMessageStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneChatMessage$State;"); @@ -172,6 +176,10 @@ public: addressClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneAddressImpl")); addressCtrId =env->GetMethodID(addressClass,"", "(J)V"); + callStatsClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCallStatsImpl")); + callStatsId = env->GetMethodID(callStatsClass, "", "(JJ)V"); + callSetAudioStatsId = env->GetMethodID(callClass, "setAudioStats", "(Lorg/linphone/core/LinphoneCallStats;)V"); + callSetVideoStatsId = env->GetMethodID(callClass, "setVideoStats", "(Lorg/linphone/core/LinphoneCallStats;)V"); } ~LinphoneCoreData() { @@ -184,6 +192,7 @@ public: env->DeleteGlobalRef(globalStateClass); env->DeleteGlobalRef(registrationStateClass); env->DeleteGlobalRef(callStateClass); + env->DeleteGlobalRef(callStatsClass); env->DeleteGlobalRef(chatMessageStateClass); env->DeleteGlobalRef(proxyClass); env->DeleteGlobalRef(callClass); @@ -202,6 +211,7 @@ public: jmethodID notifyPresenceReceivedId; jmethodID textReceivedId; jmethodID messageReceivedId; + jmethodID callStatsUpdatedId; jclass globalStateClass; jmethodID globalStateId; @@ -215,6 +225,11 @@ public: jmethodID callStateId; jmethodID callStateFromIntId; + jclass callStatsClass; + jmethodID callStatsId; + jmethodID callSetAudioStatsId; + jmethodID callSetVideoStatsId; + jclass chatMessageStateClass; jmethodID chatMessageStateFromIntId; @@ -424,6 +439,24 @@ public: } } + static void callStatsUpdated(LinphoneCore *lc, LinphoneCall* call, const LinphoneCallStats *stats) { + JNIEnv *env = 0; + jobject statsobj; + jobject callobj; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM\n"); + return; + } + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + statsobj = env->NewObject(lcData->callStatsClass, lcData->callStatsId, (jlong)call, (jlong)stats); + callobj = lcData->getCall(env, call); + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + env->CallVoidMethod(callobj, lcData->callSetAudioStatsId, statsobj); + else + env->CallVoidMethod(callobj, lcData->callSetVideoStatsId, statsobj); + env->CallVoidMethod(lcData->listener, lcData->callStatsUpdatedId, lcData->core, callobj, statsobj); + } }; @@ -1211,6 +1244,113 @@ extern "C" jint Java_org_linphone_core_LinphoneCallLogImpl_getCallDuration(JNIEn return ((LinphoneCallLog*)ptr)->duration; } +/* CallStats */ +extern "C" int Java_org_linphone_core_LinphoneCallStatsImpl_getMediaType(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (int)((LinphoneCallStats *)stats_ptr)->type; +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getSenderLossRate(JNIEnv *env, jobject thiz, jlong stats_ptr) { + const LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const report_block_t *srb = NULL; + + if (!stats->sent_rtcp) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->sent_rtcp->b_cont != NULL) + msgpullup(stats->sent_rtcp, -1); + if (rtcp_is_SR(stats->sent_rtcp)) + srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0); + else if (rtcp_is_RR(stats->sent_rtcp)) + srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0); + if (!srb) + return (jfloat)0.0; + return (jfloat)(100.0 * report_block_get_fraction_lost(srb) / 256.0); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getReceiverLossRate(JNIEnv *env, jobject thiz, jlong stats_ptr) { + const LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const report_block_t *rrb = NULL; + + if (!stats->received_rtcp) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->received_rtcp->b_cont != NULL) + msgpullup(stats->received_rtcp, -1); + if (rtcp_is_RR(stats->received_rtcp)) + rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0); + else if (rtcp_is_SR(stats->received_rtcp)) + rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0); + if (!rrb) + return (jfloat)0.0; + return (jfloat)(100.0 * report_block_get_fraction_lost(rrb) / 256.0); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getSenderInterarrivalJitter(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) { + LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const LinphoneCall *call = (LinphoneCall *)call_ptr; + const LinphoneCallParams *params = linphone_call_get_current_params(call); + const PayloadType *pt; + const report_block_t *srb = NULL; + + if (!stats->sent_rtcp) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->sent_rtcp->b_cont != NULL) + msgpullup(stats->sent_rtcp, -1); + if (rtcp_is_SR(stats->sent_rtcp)) + srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0); + else if (rtcp_is_RR(stats->sent_rtcp)) + srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0); + if (!srb) + return (jfloat)0.0; + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + pt = linphone_call_params_get_used_audio_codec(params); + else + pt = linphone_call_params_get_used_video_codec(params); + return (jfloat)((float)report_block_get_interarrival_jitter(srb) / (float)pt->clock_rate); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getReceiverInterarrivalJitter(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) { + LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const LinphoneCall *call = (LinphoneCall *)call_ptr; + const LinphoneCallParams *params = linphone_call_get_current_params(call); + const PayloadType *pt; + const report_block_t *rrb = NULL; + + if (!stats->received_rtcp) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->received_rtcp->b_cont != NULL) + msgpullup(stats->received_rtcp, -1); + if (rtcp_is_SR(stats->received_rtcp)) + rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0); + else if (rtcp_is_RR(stats->received_rtcp)) + rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0); + if (!rrb) + return (jfloat)0.0; + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + pt = linphone_call_params_get_used_audio_codec(params); + else + pt = linphone_call_params_get_used_video_codec(params); + return (jfloat)((float)report_block_get_interarrival_jitter(rrb) / (float)pt->clock_rate); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getRoundTripDelay(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jfloat)((LinphoneCallStats *)stats_ptr)->round_trip_delay; +} +extern "C" jlong Java_org_linphone_core_LinphoneCallStatsImpl_getLatePacketsCumulativeNumber(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) { + LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + LinphoneCall *call = (LinphoneCall *)call_ptr; + rtp_stats_t rtp_stats; + + memset(&rtp_stats, 0, sizeof(rtp_stats)); + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + audio_stream_get_local_rtp_stats(call->audiostream, &rtp_stats); +#ifdef VIDEO_ENABLED + else + video_stream_get_local_rtp_stats(call->videostream, &rtp_stats); +#endif + return (jlong)rtp_stats.outoftime; +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getJitterBufferSize(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jfloat)((LinphoneCallStats *)stats_ptr)->jitter_stats.jitter_buffer_size_ms; +} + /*payloadType*/ extern "C" jstring Java_org_linphone_core_PayloadTypeImpl_toString(JNIEnv* env,jobject thiz,jlong ptr) { PayloadType* pt = (PayloadType*)ptr; diff --git a/java/common/org/linphone/core/LinphoneCall.java b/java/common/org/linphone/core/LinphoneCall.java index 6fe6704de..35e173d5a 100644 --- a/java/common/org/linphone/core/LinphoneCall.java +++ b/java/common/org/linphone/core/LinphoneCall.java @@ -159,6 +159,30 @@ public interface LinphoneCall { * @Return LinphoneCallLog **/ LinphoneCallLog getCallLog(); + + /** + * Set the audio statistics associated with this call. + * @return LinphoneCallStats + */ + void setAudioStats(LinphoneCallStats stats); + + /** + * Set the video statistics associated with this call. + * @return LinphoneCallStats + */ + void setVideoStats(LinphoneCallStats stats); + + /** + * Get the audio statistics associated with this call. + * @return LinphoneCallStats + */ + LinphoneCallStats getAudioStats(); + + /** + * Get the video statistics associated with this call. + * @return LinphoneCallStats + */ + LinphoneCallStats getVideoStats(); LinphoneCallParams getRemoteParams(); diff --git a/java/common/org/linphone/core/LinphoneCallStats.java b/java/common/org/linphone/core/LinphoneCallStats.java new file mode 100644 index 000000000..f391b2a91 --- /dev/null +++ b/java/common/org/linphone/core/LinphoneCallStats.java @@ -0,0 +1,102 @@ +/* +LinPhoneCallStats.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +This program 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 2 +of the License, or (at your option) any later version. + +This program 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 program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +package org.linphone.core; + +import java.util.Vector; + + +public interface LinphoneCallStats { + static public class MediaType { + static private Vector values = new Vector(); + /** + * Audio + */ + static public MediaType Audio = new MediaType(0, "Audio"); + /** + * Video + */ + static public MediaType Video = new MediaType(1, "Video"); + protected final int mValue; + private final String mStringValue; + + private MediaType(int value, String stringValue) { + mValue = value; + values.addElement(this); + mStringValue = stringValue; + } + public static MediaType fromInt(int value) { + for (int i = 0; i < values.size(); i++) { + MediaType mtype = (MediaType) values.elementAt(i); + if (mtype.mValue == value) return mtype; + } + throw new RuntimeException("MediaType not found [" + value + "]"); + } + public String toString() { + return mStringValue; + } + } + + /** + * Get the stats media type + * @return MediaType + */ + public MediaType getMediaType(); + + /** + * Get the sender loss rate since last report + * @return The sender loss rate + */ + public float getSenderLossRate(); + + /** + * Get the receiver loss rate since last report + * @return The receiver loss rate + */ + public float getReceiverLossRate(); + + /** + * Get the sender interarrival jitter + * @return The interarrival jitter at last emitted sender report + */ + public float getSenderInterarrivalJitter(); + + /** + * Get the receiver interarrival jitter + * @return The interarrival jitter at last received receiver report + */ + public float getReceiverInterarrivalJitter(); + + /** + * Get the round trip delay + * @return The round trip delay in seconds, -1 if the information is not available + */ + public float getRoundTripDelay(); + + /** + * Get the cumulative number of late packets + * @return The cumulative number of late packets + */ + public long getLatePacketsCumulativeNumber(); + + /** + * Get the jitter buffer size + * @return The jitter buffer size in milliseconds + */ + public float getJitterBufferSize(); +} diff --git a/java/common/org/linphone/core/LinphoneCoreListener.java b/java/common/org/linphone/core/LinphoneCoreListener.java index a84f736d2..c81dafec4 100644 --- a/java/common/org/linphone/core/LinphoneCoreListener.java +++ b/java/common/org/linphone/core/LinphoneCoreListener.java @@ -40,6 +40,11 @@ public interface LinphoneCoreListener { * */ void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State cstate,String message); + /** + * Call stats notification + */ + void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats); + /** * Callback to display change in encryption state. * @param encrypted true if all streams of the call are encrypted