diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 36e1ac520d5976bc8d98f9611b1dff41301773ae..617c2baa9e4713b6d5ff1dbce69400e18e053619 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1319,7 +1319,7 @@ static void sound_config_read(LinphoneCore *lc) { /*just parse requested stream feature once at start to print out eventual errors*/ linphone_core_get_audio_features(lc); - _linphone_core_set_tone(lc,LinphoneReasonBusy,LinphoneToneBusy,NULL); + L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->setTone(LinphoneReasonBusy, LinphoneToneBusy, NULL); } static int _linphone_core_tls_postcheck_callback(void *data, const bctbx_x509_certificate_t *peer_cert){ @@ -2438,7 +2438,6 @@ static void linphone_core_init(LinphoneCore * lc, LinphoneCoreCbs *cbs, LpConfig lc->is_unreffing = FALSE; lc->config=lp_config_ref(config); lc->data=userdata; - lc->ringstream_autorelease=TRUE; // We need the Sal on the Android platform helper init lc->sal=new Sal(NULL); @@ -3580,19 +3579,6 @@ void linphone_core_iterate(LinphoneCore *lc){ lc_callback_obj_invoke(&lc->preview_finished_cb,lc); } - if (lc->ringstream && lc->ringstream_autorelease && lc->dmfs_playing_start_time!=0 - && (curtime_ms/1000 - (uint64_t)lc->dmfs_playing_start_time)>5){ - MSPlayerState state; - bool_t stop=TRUE; - if (lc->ringstream->source && ms_filter_call_method(lc->ringstream->source,MS_PLAYER_GET_STATE,&state)==0){ - if (state==MSPlayerPlaying) stop=FALSE; - } - if (stop) { - ms_message("Releasing inactive tone player."); - linphone_core_stop_dtmf_stream(lc); - } - } - lc->sal->iterate(); if (lc->msevq) ms_event_queue_pump(lc->msevq); if (linphone_core_get_global_state(lc) == LinphoneGlobalConfiguring) @@ -3983,25 +3969,6 @@ bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription return linphone_core_is_media_encryption_mandatory(lc) && linphone_core_get_media_encryption(lc)==LinphoneMediaEncryptionSRTP && !sal_media_description_has_srtp(md); } -void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){ - /* Play the ring if this is the only call*/ - if (linphone_core_get_calls_nb(lc)==1){ - MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; - L_GET_PRIVATE_FROM_C_OBJECT(lc)->setCurrentCall(L_GET_CPP_PTR_FROM_C_OBJECT(call)); - if (lc->ringstream && lc->dmfs_playing_start_time!=0){ - linphone_core_stop_dtmf_stream(lc); - } - if (ringcard) { - ms_snd_card_set_stream_type(ringcard, MS_SND_CARD_STREAM_RING); - linphone_ringtoneplayer_start(lc->factory, lc->ringtoneplayer, ringcard, lc->sound_conf.local_ring, 2000); - } - }else{ - /* else play a tone within the context of the current call */ - L_GET_PRIVATE_FROM_C_OBJECT(call)->setRingingBeep(true); - linphone_core_play_named_tone(lc,LinphoneToneCallWaiting); - } -} - LinphoneStatus linphone_core_accept_early_media_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params) { return linphone_call_accept_early_media_with_params(call, params); } @@ -4112,9 +4079,6 @@ int linphone_core_preempt_sound_resources(LinphoneCore *lc){ ms_message("Pausing automatically the current call."); err = L_GET_CPP_PTR_FROM_C_OBJECT(current_call)->pause(); } - if (lc->ringstream){ - linphone_core_stop_ringing(lc); - } return err; } @@ -6029,152 +5993,16 @@ void linphone_core_set_record_file(LinphoneCore *lc, const char *file){ } } -typedef enum{ - LinphoneToneGenerator, - LinphoneLocalPlayer -}LinphoneAudioResourceType; - -static MSFilter *get_audio_resource(LinphoneCore *lc, LinphoneAudioResourceType rtype, MSSndCard *card, bool_t create) { - LinphoneCall *call=linphone_core_get_current_call(lc); - AudioStream *stream=NULL; - RingStream *ringstream; - if (call){ - stream=reinterpret_cast<AudioStream *>(linphone_call_get_stream(call, LinphoneStreamTypeAudio)); - }else if (linphone_core_is_in_conference(lc)){ - stream=linphone_conference_get_audio_stream(lc->conf_ctx); - } - if (stream){ - if (rtype==LinphoneToneGenerator) return stream->dtmfgen; - if (rtype==LinphoneLocalPlayer) return stream->local_player; - return NULL; - } - if (card && lc->ringstream && card != lc->ringstream->card){ - ring_stop(lc->ringstream); - lc->ringstream = NULL; - } - if (lc->ringstream == NULL) { - float amp=lp_config_get_float(lc->config,"sound","dtmf_player_amp",0.1f); - MSSndCard *ringcard=lc->sound_conf.lsd_card - ? lc->sound_conf.lsd_card - : card - ? card - : lc->sound_conf.ring_sndcard; - - if (ringcard == NULL) - return NULL; - - if (!create) return NULL; - - ringstream=lc->ringstream=ring_start(lc->factory, NULL,0,ringcard); - ms_filter_call_method(lc->ringstream->gendtmf,MS_DTMF_GEN_SET_DEFAULT_AMPLITUDE,&); - lc->dmfs_playing_start_time = (time_t)ms_get_cur_time_ms()/1000; - }else{ - ringstream=lc->ringstream; - if (lc->dmfs_playing_start_time!=0) - lc->dmfs_playing_start_time = (time_t)ms_get_cur_time_ms()/1000; - } - if (rtype==LinphoneToneGenerator) return ringstream->gendtmf; - if (rtype==LinphoneLocalPlayer) return ringstream->source; - return NULL; -} - -static MSFilter *get_dtmf_gen(LinphoneCore *lc, MSSndCard *card) { - return get_audio_resource(lc,LinphoneToneGenerator, card, TRUE); -} - void linphone_core_play_dtmf(LinphoneCore *lc, char dtmf, int duration_ms){ - MSSndCard *card = linphone_core_in_call(lc) - ? lc->sound_conf.play_sndcard - : lc->sound_conf.ring_sndcard; - MSFilter *f=get_dtmf_gen(lc, card); - if (f==NULL){ - ms_error("No dtmf generator at this time !"); - return; - } - - if (duration_ms > 0) - ms_filter_call_method(f, MS_DTMF_GEN_PLAY, &dtmf); - else ms_filter_call_method(f, MS_DTMF_GEN_START, &dtmf); -} - -LinphoneStatus _linphone_core_play_local(LinphoneCore *lc, const char *audiofile, MSSndCard *card) { - MSFilter *f=get_audio_resource(lc,LinphoneLocalPlayer, card, TRUE); - int loopms=-1; - if (!f) return -1; - ms_filter_call_method(f,MS_PLAYER_SET_LOOP,&loopms); - if (ms_filter_call_method(f,MS_PLAYER_OPEN,(void*)audiofile)!=0){ - return -1; - } - ms_filter_call_method_noarg(f,MS_PLAYER_START); - return 0; + L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCorePlayDtmf(dtmf, duration_ms); } LinphoneStatus linphone_core_play_local(LinphoneCore *lc, const char *audiofile) { - return _linphone_core_play_local(lc, audiofile, NULL); -} - -void linphone_core_play_named_tone(LinphoneCore *lc, LinphoneToneID toneid){ - if (linphone_core_tone_indications_enabled(lc)){ - const char *audiofile=linphone_core_get_tone_file(lc,toneid); - if (!audiofile){ - MSFilter *f=get_dtmf_gen(lc, lc->sound_conf.play_sndcard); - MSDtmfGenCustomTone def; - if (f==NULL){ - ms_error("No dtmf generator at this time !"); - return; - } - memset(&def,0,sizeof(def)); - def.amplitude=1; - /*these are french tones, excepted the failed one, which is USA congestion tone (does not exist in France)*/ - switch(toneid){ - case LinphoneToneCallOnHold: - case LinphoneToneCallWaiting: - def.duration=300; - def.frequencies[0]=440; - def.interval=2000; - break; - case LinphoneToneBusy: - def.duration=500; - def.frequencies[0]=440; - def.interval=500; - def.repeat_count=3; - break; - case LinphoneToneCallLost: - def.duration=250; - def.frequencies[0]=480; - def.frequencies[0]=620; - def.interval=250; - def.repeat_count=3; - - break; - default: - ms_warning("Unhandled tone id."); - } - if (def.duration>0) - ms_filter_call_method(f, MS_DTMF_GEN_PLAY_CUSTOM,&def); - }else{ - _linphone_core_play_local(lc,audiofile, lc->sound_conf.play_sndcard); - } - } -} - -void linphone_core_play_call_error_tone(LinphoneCore *lc, LinphoneReason reason){ - if (linphone_core_tone_indications_enabled(lc)){ - LinphoneToneDescription *tone=linphone_core_get_call_error_tone(lc,reason); - if (tone){ - if (tone->audiofile){ - _linphone_core_play_local(lc, tone->audiofile, lc->sound_conf.play_sndcard); - }else if (tone->toneid != LinphoneToneUndefined){ - linphone_core_play_named_tone(lc, tone->toneid); - } - } - } + return L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCorePlayLocal(audiofile); } void linphone_core_stop_dtmf(LinphoneCore *lc){ - MSFilter *f=get_audio_resource(lc, LinphoneToneGenerator, NULL, FALSE); - if (f!=NULL) - ms_filter_call_method_noarg (f, MS_DTMF_GEN_STOP); + L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCoreStopDtmf(); } void *linphone_core_get_user_data(const LinphoneCore *lc){ @@ -6892,31 +6720,15 @@ bool_t linphone_core_keep_alive_enabled(LinphoneCore* lc) { } void linphone_core_start_dtmf_stream(LinphoneCore* lc) { - get_dtmf_gen(lc, lc->sound_conf.ring_sndcard); /*make sure ring stream is started*/ - lc->ringstream_autorelease=FALSE; /*disable autorelease mode*/ + L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCoreStartDtmfStream(); } void linphone_core_stop_ringing(LinphoneCore* lc) { - LinphoneCall *call=linphone_core_get_current_call(lc); - if (linphone_ringtoneplayer_is_started(lc->ringtoneplayer)) { - linphone_ringtoneplayer_stop(lc->ringtoneplayer); - } - if (lc->ringstream) { - ring_stop(lc->ringstream); - lc->ringstream=NULL; - lc->dmfs_playing_start_time=0; - lc->ringstream_autorelease=TRUE; - } - if (call && L_GET_PRIVATE_FROM_C_OBJECT(call)->getRingingBeep()) { - linphone_core_stop_dtmf(lc); - L_GET_PRIVATE_FROM_C_OBJECT(call)->setRingingBeep(false); - } + L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCoreStopRinging(); } void linphone_core_stop_dtmf_stream(LinphoneCore* lc) { - if (lc->dmfs_playing_start_time!=0) { - linphone_core_stop_ringing(lc); - } + L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCoreStopDtmfStream(); } int linphone_core_get_max_calls(LinphoneCore *lc) { diff --git a/coreapi/misc.c b/coreapi/misc.c index 0b3bb54146e613135174d4b789b701b58fea2a17..bcf3082b4df51b69e66c5b7cf3f56f5147fe128e 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -21,6 +21,7 @@ #include "linphone/lpconfig.h" #include "linphone/wrapper_utils.h" #include "mediastreamer2/mediastream.h" +#include "core/core-p.h" #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -574,44 +575,12 @@ void linphone_tone_description_destroy(LinphoneToneDescription *obj){ ms_free(obj); } -static LinphoneToneDescription *linphone_core_lookup_tone(const LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id){ - const bctbx_list_t *elem; - for (elem=lc->tones;elem!=NULL;elem=elem->next){ - LinphoneToneDescription *tone=(LinphoneToneDescription*)elem->data; - if (reason == LinphoneReasonNone){ - if (tone->toneid == id && tone->reason == LinphoneReasonNone) return tone; - }else{ - if (tone->reason==reason) return tone; - } - } - return NULL; -} - -LinphoneToneDescription *linphone_core_get_call_error_tone(const LinphoneCore *lc, LinphoneReason reason){ - return linphone_core_lookup_tone(lc, reason, LinphoneToneUndefined); -} - -const char *linphone_core_get_tone_file(const LinphoneCore *lc, LinphoneToneID id){ - LinphoneToneDescription *tone = linphone_core_lookup_tone(lc, LinphoneReasonNone, id); - return tone ? tone->audiofile : NULL; -} - -void _linphone_core_set_tone(LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id, const char *audiofile){ - LinphoneToneDescription *tone = linphone_core_lookup_tone(lc,reason, id); - if (tone){ - lc->tones=bctbx_list_remove(lc->tones,tone); - linphone_tone_description_destroy(tone); - } - tone=linphone_tone_description_new(reason,id,audiofile); - lc->tones=bctbx_list_append(lc->tones,tone); -} - void linphone_core_set_call_error_tone(LinphoneCore *lc, LinphoneReason reason, const char *audiofile){ - _linphone_core_set_tone(lc,reason,LinphoneToneUndefined, audiofile); + L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->setTone(reason, LinphoneToneUndefined, audiofile); } void linphone_core_set_tone(LinphoneCore *lc, LinphoneToneID id, const char *audiofile){ - _linphone_core_set_tone(lc, LinphoneReasonNone, id, audiofile); + L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->setTone(LinphoneReasonNone, id, audiofile); } const MSCryptoSuite * linphone_core_get_srtp_crypto_suites(LinphoneCore *lc){ diff --git a/coreapi/private_functions.h b/coreapi/private_functions.h index 0ef576f7d34fe0667a793522cf78c6541ac6a1fa..0f0dee401434d96464012fea6d4301f6b2353486 100644 --- a/coreapi/private_functions.h +++ b/coreapi/private_functions.h @@ -287,7 +287,6 @@ void linphone_core_start_waiting(LinphoneCore *lc, const char *purpose); void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float progresses); void linphone_core_stop_waiting(LinphoneCore *lc); -void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call); bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription *md); extern LinphonePrivate::Sal::Callbacks linphone_sal_callbacks; LINPHONE_PUBLIC bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc); @@ -345,10 +344,6 @@ const LinphoneParticipantImdnState *_linphone_participant_imdn_state_from_cpp_ob LinphoneToneDescription * linphone_tone_description_new(LinphoneReason reason, LinphoneToneID id, const char *audiofile); void linphone_tone_description_destroy(LinphoneToneDescription *obj); -LinphoneToneDescription *linphone_core_get_call_error_tone(const LinphoneCore *lc, LinphoneReason reason); -void linphone_core_play_call_error_tone(LinphoneCore *lc, LinphoneReason reason); -void _linphone_core_set_tone(LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id, const char *audiofile); -LINPHONE_PUBLIC const char *linphone_core_get_tone_file(const LinphoneCore *lc, LinphoneToneID id); void linphone_task_list_init(LinphoneTaskList *t); void linphone_task_list_add(LinphoneTaskList *t, LinphoneCoreIterateHook hook, void *hook_data); @@ -419,7 +414,6 @@ void linphone_chat_message_set_message_state_changed_cb_user_data(LinphoneChatMe void * linphone_chat_message_get_message_state_changed_cb_user_data(LinphoneChatMessage* msg); LinphoneChatRoom *_linphone_core_create_chat_room_from_call(LinphoneCall *call); -void linphone_core_play_named_tone(LinphoneCore *lc, LinphoneToneID id); bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc); const char *linphone_core_create_uuid(LinphoneCore *lc); void linphone_configure_op(LinphoneCore *lc, LinphonePrivate::SalOp *op, const LinphoneAddress *dest, SalCustomHeader *headers, bool_t with_contact); diff --git a/coreapi/private_structs.h b/coreapi/private_structs.h index 47c3498d4ee614316daee766468ab912bdd35311..b527ae6548116dc0089d68300947f536b8be122e 100644 --- a/coreapi/private_structs.h +++ b/coreapi/private_structs.h @@ -762,7 +762,6 @@ namespace LinphonePrivate { MSList *friends_lists; \ MSList *auth_info; \ struct _RingStream *ringstream; \ - time_t dmfs_playing_start_time; \ LCCallbackObj preview_finished_cb; \ MSList *queued_calls; \ MSList *call_logs; \ @@ -805,7 +804,6 @@ namespace LinphonePrivate { bool_t network_reachable_to_be_notified; \ bool_t use_preview_window; \ bool_t network_last_status; \ - bool_t ringstream_autorelease; \ bool_t vtables_running; \ bool_t send_call_stats_periodical_updates; \ bool_t forced_ice_relay; \ diff --git a/coreapi/tester_utils.cpp b/coreapi/tester_utils.cpp index 259352e5e3052ce0966cc859cf530ac54f97e847..afb6ab0f5c5dafbcfff6e26184d724bc5fcd0791 100644 --- a/coreapi/tester_utils.cpp +++ b/coreapi/tester_utils.cpp @@ -199,4 +199,15 @@ char * linphone_core_get_device_identity(LinphoneCore *lc) { return identity; } +LinphoneCoreToneManagerStats *linphone_core_get_tone_manager_stats(LinphoneCore *lc) { + return L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->getStats(); +} + +void linphone_core_reset_tone_manager_stats(LinphoneCore *lc) { + L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->resetStats(); +} +const char *linphone_core_get_tone_file(LinphoneCore *lc, LinphoneToneID id){ + LinphoneToneDescription *tone = L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->getToneFromId(id); + return tone ? tone->audiofile : NULL; +} diff --git a/coreapi/tester_utils.h b/coreapi/tester_utils.h index b1cd952942eed3130bee4a92d63b68a7605e42d2..c8037577dad1599b45d2be141b3e7dd8e7d9c909 100644 --- a/coreapi/tester_utils.h +++ b/coreapi/tester_utils.h @@ -42,6 +42,16 @@ typedef enum _LinphoneProxyConfigAddressComparisonResult{ LinphoneProxyConfigAddressWeakEqual } LinphoneProxyConfigAddressComparisonResult; +typedef struct _LinphoneCoreToneManagerStats { + int number_of_startRingbackTone; + int number_of_startRingtone; + int number_of_startErrorTone; + int number_of_startNamedTone; + int number_of_stopRingbackTone; + int number_of_stopRingtone; + int number_of_stopTone; +} LinphoneCoreToneManagerStats; + #ifdef __cplusplus extern "C" { #endif @@ -53,7 +63,6 @@ LINPHONE_PUBLIC int linphone_core_get_local_ip_for(int type, const char *dest, c LINPHONE_PUBLIC void linphone_core_enable_forced_ice_relay(LinphoneCore *lc, bool_t enable); LINPHONE_PUBLIC void linphone_core_set_zrtp_not_available_simulation(LinphoneCore *lc, bool_t enabled); LINPHONE_PUBLIC belle_http_provider_t *linphone_core_get_http_provider(const LinphoneCore *lc); -LINPHONE_PUBLIC const char *linphone_core_get_tone_file(const LinphoneCore *lc, LinphoneToneID id); LINPHONE_PUBLIC IceSession * linphone_call_get_ice_session(const LinphoneCall *call); LINPHONE_PUBLIC const struct addrinfo *linphone_core_get_stun_server_addrinfo(LinphoneCore *lc); LINPHONE_PUBLIC void linphone_core_enable_send_call_stats_periodical_updates(LinphoneCore *lc, bool_t enabled); @@ -129,6 +138,10 @@ LINPHONE_PUBLIC int linphone_remote_provisioning_load_file( LinphoneCore* lc, co LINPHONE_PUBLIC char *linphone_core_get_device_identity(LinphoneCore *lc); +LINPHONE_PUBLIC LinphoneCoreToneManagerStats *linphone_core_get_tone_manager_stats(LinphoneCore *lc); +LINPHONE_PUBLIC void linphone_core_reset_tone_manager_stats(LinphoneCore *lc); +LINPHONE_PUBLIC const char *linphone_core_get_tone_file(LinphoneCore *lc, LinphoneToneID id); + /** * Send an XML-RPC request to delete a Linphone account. * @param[in] creator LinphoneAccountCreator object diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0a6fe8d3131767b655185890641ba1d7958acff..be4b480f6354c2c63f87c9e56a7da57672c3eba2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -165,6 +165,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES conference/session/call-session.h conference/session/media-session.h conference/session/port-config.h + conference/session/tone-manager.h containers/lru-cache.h content/content-disposition.h content/content-manager.h @@ -330,6 +331,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES conference/remote-conference.cpp conference/session/call-session.cpp conference/session/media-session.cpp + conference/session/tone-manager.cpp content/content-disposition.cpp content/content-manager.cpp content/content-type.cpp diff --git a/src/call/call-p.h b/src/call/call-p.h index 2b53c3a3b9e538cd42e828c988c10661e4bff5b8..c2f52fba1f320d177d2a86bb2245617a11c1251d 100644 --- a/src/call/call-p.h +++ b/src/call/call-p.h @@ -58,9 +58,6 @@ public: MediaStream *getMediaStream (LinphoneStreamType type) const; SalCallOp *getOp () const; - bool getRingingBeep () const { return ringingBeep; } - void setRingingBeep (bool value) { ringingBeep = value; } - bool getSpeakerMuted () const; void setSpeakerMuted (bool muted); @@ -109,12 +106,7 @@ private: void onSetCurrentSession (const std::shared_ptr<CallSession> &session) override; void onFirstVideoFrameDecoded (const std::shared_ptr<CallSession> &session) override; void onResetFirstVideoFrameDecoded (const std::shared_ptr<CallSession> &session) override; - void onPlayErrorTone (const std::shared_ptr<CallSession> &session, LinphoneReason reason) override; void onRingbackToneRequested (const std::shared_ptr<CallSession> &session, bool requested) override; - void onStartRinging (const std::shared_ptr<CallSession> &session) override; - void onStopRinging (const std::shared_ptr<CallSession> &session) override; - void onStopRingingIfInCall (const std::shared_ptr<CallSession> &session) override; - void onStopRingingIfNeeded (const std::shared_ptr<CallSession> &session) override; bool areSoundResourcesAvailable (const std::shared_ptr<CallSession> &session) override; bool isPlayingRingbackTone (const std::shared_ptr<CallSession> &session) override; void onRealTimeTextCharacterReceived (const std::shared_ptr<CallSession> &session, RealtimeTextReceivedCharacter *character) override; @@ -125,7 +117,6 @@ private: CallCallbackObj nextVideoFrameDecoded; - bool ringingBeep = false; bool playingRingbackTone = false; BackgroundTask bgTask; diff --git a/src/call/call.cpp b/src/call/call.cpp index b801cf47dbf5d229a35cb70f2fb0eb51e0b05283..a5925302e3d648bc49153ff5b7cc11601f4a258f 100644 --- a/src/call/call.cpp +++ b/src/call/call.cpp @@ -190,7 +190,7 @@ void CallPrivate::terminateBecauseOfLostMedia () { lInfo() << "Call [" << q << "]: Media connectivity with " << q->getRemoteAddress().asString() << " is lost, call is going to be terminated"; static_pointer_cast<MediaSession>(getActiveSession())->terminateBecauseOfLostMedia(); - linphone_core_play_named_tone(q->getCore()->getCCore(), LinphoneToneCallLost); + q->getCore()->getPrivate()->getToneManager()->startNamedTone(getActiveSession(), LinphoneToneCallLost); } // ----------------------------------------------------------------------------- @@ -222,16 +222,6 @@ bool CallPrivate::onCallSessionAccepted (const shared_ptr<CallSession> &session) if (q->getCore()->getCurrentCall() != q->getSharedFromThis()) linphone_core_preempt_sound_resources(lc); - // Stop ringing - if (linphone_ringtoneplayer_is_started(lc->ringtoneplayer)) { - lInfo() << "Stop ringing"; - linphone_core_stop_ringing(lc); - wasRinging = true; - } - if (ringingBeep) { - linphone_core_stop_dtmf(lc); - ringingBeep = false; - } return wasRinging; } @@ -276,10 +266,6 @@ void CallPrivate::onCallSessionSetTerminated (const shared_ptr<CallSession> &ses lError() << "Could not remove the call from the list!!!"; if (core->conf_ctx) linphone_conference_on_call_terminating(core->conf_ctx, L_GET_C_BACK_PTR(q)); - if (ringingBeep) { - linphone_core_stop_dtmf(core); - ringingBeep = false; - } #if 0 if (lcall->chat_room) linphone_chat_room_set_call(lcall->chat_room, nullptr); @@ -294,6 +280,8 @@ void CallPrivate::onCallSessionStartReferred (const shared_ptr<CallSession> &ses void CallPrivate::onCallSessionStateChanged (const shared_ptr<CallSession> &session, CallSession::State state, const string &message) { L_Q(); + q->getCore()->getPrivate()->getToneManager()->update(session); + switch(state){ case CallSession::State::OutgoingInit: case CallSession::State::IncomingReceived: @@ -352,7 +340,10 @@ void CallPrivate::onIncomingCallSessionNotified (const shared_ptr<CallSession> & void CallPrivate::onIncomingCallSessionStarted (const shared_ptr<CallSession> &session) { L_Q(); - linphone_core_notify_incoming_call(q->getCore()->getCCore(), L_GET_C_BACK_PTR(q)); + if (linphone_core_get_calls_nb(q->getCore()->getCCore())==1) { + L_GET_PRIVATE_FROM_C_OBJECT(q->getCore()->getCCore())->setCurrentCall(q->getSharedFromThis()); + } + q->getCore()->getPrivate()->getToneManager()->startRingtone(session); } void CallPrivate::onIncomingCallSessionTimeoutCheck (const shared_ptr<CallSession> &session, int elapsed, bool oneSecondElapsed) { @@ -442,11 +433,6 @@ void CallPrivate::requestNotifyNextVideoFrameDecoded(){ static_pointer_cast<MediaSession>(getActiveSession())->requestNotifyNextVideoFrameDecoded(); } -void CallPrivate::onPlayErrorTone (const shared_ptr<CallSession> &session, LinphoneReason reason) { - L_Q(); - linphone_core_play_call_error_tone(q->getCore()->getCCore(), reason); -} - void CallPrivate::onRingbackToneRequested (const shared_ptr<CallSession> &session, bool requested) { L_Q(); if (requested && linphone_core_get_remote_ringback_tone(q->getCore()->getCCore())) @@ -455,44 +441,6 @@ void CallPrivate::onRingbackToneRequested (const shared_ptr<CallSession> &sessio playingRingbackTone = false; } -void CallPrivate::onStartRinging (const shared_ptr<CallSession> &session) { - L_Q(); - LinphoneCore *lc = q->getCore()->getCCore(); - if (lc->ringstream) - return; // Already ringing! - startRemoteRing(); -} - -void CallPrivate::onStopRinging (const shared_ptr<CallSession> &session) { - L_Q(); - linphone_core_stop_ringing(q->getCore()->getCCore()); -} - -void CallPrivate::onStopRingingIfInCall (const shared_ptr<CallSession> &session) { - L_Q(); - LinphoneCore *lc = q->getCore()->getCCore(); - // We stop the ring only if we have this current call or if we are in call - if ((q->getCore()->getCallCount() == 1) || linphone_core_in_call(lc)) { - linphone_core_stop_ringing(lc); - } -} - -void CallPrivate::onStopRingingIfNeeded (const shared_ptr<CallSession> &session) { - L_Q(); - LinphoneCore *lc = q->getCore()->getCCore(); - bool stopRinging = true; - bool ringDuringEarlyMedia = !!linphone_core_get_ring_during_incoming_early_media(lc); - for (const auto &call : q->getCore()->getCalls()) { - if ((call->getState() == CallSession::State::IncomingReceived) - || (ringDuringEarlyMedia && call->getState() == CallSession::State::IncomingEarlyMedia)) { - stopRinging = false; - break; - } - } - if (stopRinging) - linphone_core_stop_ringing(lc); -} - bool CallPrivate::areSoundResourcesAvailable (const shared_ptr<CallSession> &session) { L_Q(); LinphoneCore *lc = q->getCore()->getCCore(); diff --git a/src/conference/session/call-session-listener.h b/src/conference/session/call-session-listener.h index 8d25ae3e83361abe524952ed3fe31b3ec9d5c84a..a1233e800a067ac601ec1110bf9283a78594a703 100644 --- a/src/conference/session/call-session-listener.h +++ b/src/conference/session/call-session-listener.h @@ -70,12 +70,7 @@ public: virtual void onFirstVideoFrameDecoded (const std::shared_ptr<CallSession> &session) {} virtual void onResetFirstVideoFrameDecoded (const std::shared_ptr<CallSession> &session) {} - virtual void onPlayErrorTone (const std::shared_ptr<CallSession> &session, LinphoneReason reason) {} virtual void onRingbackToneRequested (const std::shared_ptr<CallSession> &session, bool requested) {} - virtual void onStartRinging (const std::shared_ptr<CallSession> &session) {} - virtual void onStopRinging (const std::shared_ptr<CallSession> &session) {} - virtual void onStopRingingIfInCall (const std::shared_ptr<CallSession> &session) {} - virtual void onStopRingingIfNeeded (const std::shared_ptr<CallSession> &session) {} virtual bool areSoundResourcesAvailable (const std::shared_ptr<CallSession> &session) { return true; } virtual bool isPlayingRingbackTone (const std::shared_ptr<CallSession> &session) { return false; } diff --git a/src/conference/session/call-session.cpp b/src/conference/session/call-session.cpp index d0656241dd90bd89425dd77102d5dfd9b04d6511..e68a04f26cc4dad33f3766101a9ab0c932c69239 100644 --- a/src/conference/session/call-session.cpp +++ b/src/conference/session/call-session.cpp @@ -301,8 +301,6 @@ bool CallSessionPrivate::failure () { else setState(CallSession::State::End, ei->full_string ? ei->full_string : ""); } - if ((ei->reason != SalReasonNone) && listener) - listener->onPlayErrorTone(q->getSharedFromThis(), linphone_reason_from_sal(ei->reason)); } if (referer) { // Notify referer of the failure @@ -344,12 +342,8 @@ void CallSessionPrivate::referred (const Address &referToAddr) { } void CallSessionPrivate::remoteRinging () { - L_Q(); /* Set privacy */ currentParams->setPrivacy((LinphonePrivacyMask)op->getPrivacy()); - if (listener) - listener->onStartRinging(q->getSharedFromThis()); - lInfo() << "Remote ringing..."; setState(CallSession::State::OutgoingRinging, "Remote ringing"); } @@ -416,8 +410,6 @@ void CallSessionPrivate::terminated () { } if (referPending && listener) listener->onCallSessionStartReferred(q->getSharedFromThis()); - if (listener) - listener->onStopRingingIfInCall(q->getSharedFromThis()); setState(CallSession::State::End, "Call ended"); } @@ -637,6 +629,8 @@ void CallSessionPrivate::setReleased () { referer = nullptr; transferTarget = nullptr; + q->getCore()->getPrivate()->getToneManager()->removeSession(q->getSharedFromThis()); + if (listener) listener->onCallSessionSetReleased(q->getSharedFromThis()); } diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp index 399e1faebd327d0ada38dc0f16875eeb002d77b8..dc37985a03627c19b336f763d933378013d19ed9 100644 --- a/src/conference/session/media-session.cpp +++ b/src/conference/session/media-session.cpp @@ -264,8 +264,11 @@ bool MediaSessionPrivate::failure () { "Automatic CallSession resuming after failed transfer"); } - if (listener) - listener->onStopRingingIfNeeded(q->getSharedFromThis()); + q->getCore()->getPrivate()->getToneManager()->stop(q->getSharedFromThis()); + + if (ei->reason != SalReasonNone) + q->getCore()->getPrivate()->getToneManager()->startErrorTone(q->getSharedFromThis(), linphone_reason_from_sal(ei->reason)); + stopStreams(); return false; } @@ -310,25 +313,21 @@ void MediaSessionPrivate::remoteRinging () { } setState(CallSession::State::OutgoingEarlyMedia, "Early media"); - if (listener) - listener->onStopRinging(q->getSharedFromThis()); + q->getCore()->getPrivate()->getToneManager()->stop(q->getSharedFromThis()); lInfo() << "Doing early media..."; iceAgent->updateFromRemoteMediaDescription(localDesc, rmd, !op->isOfferer()); updateStreams(md, state); if ((q->getCurrentParams()->getAudioDirection() == LinphoneMediaDirectionInactive) && audioStream) { - if (listener) - listener->onStartRinging(q->getSharedFromThis()); + q->getCore()->getPrivate()->getToneManager()->startRingbackTone(q->getSharedFromThis()); } } else { - linphone_core_stop_dtmf_stream(q->getCore()->getCCore()); if (state == CallSession::State::OutgoingEarlyMedia) { /* Already doing early media */ return; } - if (listener) - listener->onStartRinging(q->getSharedFromThis()); - lInfo() << "Remote ringing..."; + setState(CallSession::State::OutgoingRinging, "Remote ringing"); + q->getCore()->getPrivate()->getToneManager()->startRingbackTone(q->getSharedFromThis()); } } @@ -372,8 +371,10 @@ void MediaSessionPrivate::telephoneEventReceived (int event) { } void MediaSessionPrivate::terminated () { + L_Q(); stopStreams(); CallSessionPrivate::terminated(); + q->getCore()->getPrivate()->getToneManager()->stop(q->getSharedFromThis()); } /* This callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session */ @@ -782,11 +783,6 @@ void MediaSessionPrivate::restartStream (SalStreamDescription *streamDesc, int s startStream(streamDesc, streamIndex, targetState); - if (streamDesc->type == SalAudio && audioStream) { - if ((state == CallSession::State::Pausing) && pausedByApp && (q->getCore()->getCallCount() == 1)) - linphone_core_play_named_tone(q->getCore()->getCCore(), LinphoneToneCallOnHold); - } - updateStreamFrozenPayloads(streamDesc, &localDesc->streams[streamIndex]); } @@ -3659,8 +3655,10 @@ void MediaSessionPrivate::updateFrozenPayloads (SalMediaDescription *result) { void MediaSessionPrivate::updateStreams (SalMediaDescription *newMd, CallSession::State targetState) { L_Q(); - if (!((state == CallSession::State::IncomingEarlyMedia) && linphone_core_get_ring_during_incoming_early_media(q->getCore()->getCCore()))) - linphone_core_stop_ringing(q->getCore()->getCCore()); + if (state == CallSession::State::Connected || state == CallSession::State::Resuming || + (state == CallSession::State::IncomingEarlyMedia && !linphone_core_get_ring_during_incoming_early_media(q->getCore()->getCCore()))) { + q->getCore()->getPrivate()->getToneManager()->goToCall(q->getSharedFromThis()); + } if (!newMd) { lError() << "updateStreams() called with null media description"; @@ -3719,7 +3717,7 @@ void MediaSessionPrivate::updateStreams (SalMediaDescription *newMd, CallSession if (newMd->streams[i].type == SalAudio && audioStream) { if ((state == CallSession::State::Pausing) && pausedByApp && (q->getCore()->getCallCount() == 1)) - linphone_core_play_named_tone(q->getCore()->getCCore(), LinphoneToneCallOnHold); + q->getCore()->getPrivate()->getToneManager()->startNamedTone(q->getSharedFromThis(), LinphoneToneCallOnHold); } updateStreamFrozenPayloads(&newMd->streams[i], &localDesc->streams[i]); @@ -3820,8 +3818,9 @@ void MediaSessionPrivate::updateStreams (SalMediaDescription *newMd, CallSession startStreams(targetState); - if ((state == CallSession::State::Pausing) && pausedByApp && (q->getCore()->getCallCount() == 1)) - linphone_core_play_named_tone(q->getCore()->getCCore(), LinphoneToneCallOnHold); + if ((state == CallSession::State::Pausing) && pausedByApp && (q->getCore()->getCallCount() == 1)) { + q->getCore()->getPrivate()->getToneManager()->startNamedTone(q->getSharedFromThis(), LinphoneToneCallOnHold); + } updateFrozenPayloads(newMd); @@ -4143,8 +4142,7 @@ void MediaSessionPrivate::reportBandwidthForStream (MediaStream *ms, LinphoneStr void MediaSessionPrivate::abort (const string &errorMsg) { L_Q(); - if (listener) - listener->onStopRinging(q->getSharedFromThis()); + q->getCore()->getPrivate()->getToneManager()->stop(q->getSharedFromThis()); stopStreams(); CallSessionPrivate::abort(errorMsg); } @@ -4250,9 +4248,7 @@ LinphoneStatus MediaSessionPrivate::startUpdate (const string &subject) { void MediaSessionPrivate::terminate () { L_Q(); - if (listener) - listener->onStopRingingIfNeeded(q->getSharedFromThis()); - + q->getCore()->getPrivate()->getToneManager()->stop(q->getSharedFromThis()); stopStreams(); CallSessionPrivate::terminate(); } diff --git a/src/conference/session/media-session.h b/src/conference/session/media-session.h index a02d2759be5497197582702872f85f6b285d2846..ff39c48c1ec89e13cfa4929cc88d415984102301 100644 --- a/src/conference/session/media-session.h +++ b/src/conference/session/media-session.h @@ -37,6 +37,7 @@ class LINPHONE_PUBLIC MediaSession : public CallSession { friend class Call; friend class CallPrivate; friend class IceAgent; + friend class ToneManager; public: MediaSession (const std::shared_ptr<Core> &core, std::shared_ptr<Participant> me, const CallSessionParams *params, CallSessionListener *listener); diff --git a/src/conference/session/tone-manager.cpp b/src/conference/session/tone-manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f5195a894dcd988f24bc560f179faa4a025e69f --- /dev/null +++ b/src/conference/session/tone-manager.cpp @@ -0,0 +1,623 @@ +/* + * Copyright (c) 2010-2019 Belledonne Communications SARL. + * + * This file is part of Liblinphone. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "tone-manager.h" +#include "linphone/utils/utils.h" +#include "logger/logger.h" +#include "call/call-p.h" +#include "conference/session/media-session.h" +#include "conference/session/media-session-p.h" +#include "linphone/utils/general.h" +#include "core/core-p.h" +#include "conference_private.h" + +LINPHONE_BEGIN_NAMESPACE + +ToneManager::ToneManager(std::shared_ptr<Core> core) : CoreAccessor(core) { + lInfo() << "[ToneManager] create ToneManager()"; + mStats = new LinphoneCoreToneManagerStats; + *mStats = {0, 0, 0, 0, 0, 0, 0}; +} + +ToneManager::~ToneManager() { + lInfo() << "[ToneManager] destroy ToneManager()"; + delete mStats; +} + +std::string ToneManager::stateToString(ToneManager::State state) { + switch(state) { + case State::None: + return "None"; + case State::Call: + return "Call"; + case State::Ringback: + return "Ringback"; + case State::Ringtone: + return "Ringtone"; + case State::Tone: + return "Tone"; + default: + return ""; + } +} + +void ToneManager::printDebugInfo(const std::shared_ptr<CallSession> &session) { + auto callState = session->getState(); + auto toneState = getState(session); + lInfo() << "[ToneManager] [" << session << "] state changed : [" << stateToString(toneState) << ", " << Utils::toString(callState) << "]"; +} + +// --------------------------------------------------- +// public entrypoints for tones +// --------------------------------------------------- + +void ToneManager::startRingbackTone(const std::shared_ptr<CallSession> &session) { + printDebugInfo(session); + if (getState(session) == State::Ringback) { + return; + } + + setState(session, State::Ringback); + mStats->number_of_startRingbackTone++; + + if (!isAnotherSessionInState(session, State::Ringback)) { + doStopToneToPlaySomethingElse(session); + doStartRingbackTone(session); + } +} + +void ToneManager::startRingtone(const std::shared_ptr<CallSession> &session) { + printDebugInfo(session); + setState(session, State::Ringtone); + if (!isAnotherSessionInState(session, State::Ringtone) && !isAnotherSessionInState(session, State::Ringback)) { + doStopToneToPlaySomethingElse(session); + doStartRingtone(session); + mStats->number_of_startRingtone++; + } +} + +void ToneManager::startErrorTone(const std::shared_ptr<CallSession> &session, LinphoneReason reason) { + LinphoneCore *lc = getCore()->getCCore(); + if (linphone_core_tone_indications_enabled(lc)) { + printDebugInfo(session); + doStopToneToPlaySomethingElse(session); + doStartErrorTone(session, reason); + mStats->number_of_startErrorTone++; + } +} + +void ToneManager::startNamedTone(const std::shared_ptr<CallSession> &session, LinphoneToneID toneId) { + LinphoneCore *lc = getCore()->getCCore(); + if (linphone_core_tone_indications_enabled(lc)) { + printDebugInfo(session); + setState(session, State::Tone); + doStopToneToPlaySomethingElse(session); + doStartNamedTone(toneId); + mStats->number_of_startNamedTone++; + } +} + +void ToneManager::goToCall(const std::shared_ptr<CallSession> &session) { + printDebugInfo(session); + lInfo() << "[ToneManager] " << __func__; + doStop(session, State::Call); +} + +void ToneManager::stop(const std::shared_ptr<CallSession> &session) { + printDebugInfo(session); + lInfo() << "[ToneManager] " << __func__; + doStop(session, State::None); +} + +void ToneManager::removeSession(const std::shared_ptr<CallSession> &session) { + printDebugInfo(session); + mSessions.erase(session); + lInfo() << "[ToneManager] removeSession mSession size : " << mSessions.size(); +} + +/** + * This start again ringtone when one call is not anymore in Ringtone state but the second call is still in this state + * This code can't be called juste after the doStopRingtone because the first call needs to change its context (deletion or start a call) + */ +void ToneManager::update(const std::shared_ptr<CallSession> &session) { + switch(session->getState()) { + case CallSession::State::UpdatedByRemote: + case CallSession::State::Updating: + case CallSession::State::Released: + printDebugInfo(session); + if (isAnotherSessionInState(session, State::Ringtone)) { + lInfo() << "[ToneManager] start again ringtone"; + doStartRingtone(nullptr); + mStats->number_of_startRingtone++; + } + return; + break; + default: + break; + } +} + +// --------------------------------------------------- +// linphone core public API entrypoints +// --------------------------------------------------- + +void ToneManager::linphoneCorePlayDtmf(char dtmf, int duration) { + lInfo() << "[ToneManager] " << __func__; + LinphoneCore *lc = getCore()->getCCore(); + + std::shared_ptr<CallSession> session = nullptr; + bool hasSession = getSessionInState(State::Tone, session); + if (hasSession) { + doStop(session, State::None); + } + + MSSndCard *card = linphone_core_in_call(lc) + ? lc->sound_conf.play_sndcard + : lc->sound_conf.ring_sndcard; + MSFilter *f = getAudioResource(ToneGenerator, card, true); + + if (f == NULL) { + lError() << "[ToneManager] No dtmf generator at this time !"; + return; + } + + if (duration > 0) { + ms_filter_call_method(f, MS_DTMF_GEN_PLAY, &dtmf); + } else { + ms_filter_call_method(f, MS_DTMF_GEN_START, &dtmf); + } +} + +void ToneManager::linphoneCoreStopDtmf() { + lInfo() << "[ToneManager] " << __func__; + MSFilter *f = getAudioResource(ToneGenerator, NULL, false); + if (f != NULL) { + ms_filter_call_method_noarg (f, MS_DTMF_GEN_STOP); + } +} + +LinphoneStatus ToneManager::linphoneCorePlayLocal(const char *audiofile) { + lInfo() << "[ToneManager] " << __func__; + return playFile(audiofile); +} + +void ToneManager::linphoneCoreStartDtmfStream() { + lInfo() << "[ToneManager] " << __func__; + LinphoneCore *lc = getCore()->getCCore(); + + /*make sure ring stream is started*/ + getAudioResource(ToneGenerator, lc->sound_conf.ring_sndcard, true); +} + +void ToneManager::linphoneCoreStopRinging() { + lInfo() << "[ToneManager] " << __func__; + doStopRingtone(nullptr); +} + + +void ToneManager::linphoneCoreStopDtmfStream() { + lInfo() << "[ToneManager] " << __func__; + doStopRingtone(nullptr); +} + +// --------------------------------------------------- +// timer +// --------------------------------------------------- + +void ToneManager::createTimerToCleanTonePlayer(unsigned int delay) { + lInfo() << "[ToneManager] " << __func__; + if (!mTimer) { + auto callback = [this] () -> bool { + lInfo() << "[ToneManager] callback"; + auto source = getCore()->getCCore()->ringstream->source; + MSPlayerState state; + if (source && ms_filter_call_method(source, MS_PLAYER_GET_STATE, &state) == 0) { + if (state != MSPlayerPlaying) { + deleteTimer(); + return false; + } + } + return true; + }; + + mTimer = getCore()->createTimer(callback, delay); + } +} + +void ToneManager::deleteTimer() { + if (mTimer) { + lInfo() << "[ToneManager] " << __func__; + doStopTone(); + mStats->number_of_stopTone++; + getCore()->destroyTimer(mTimer); + mTimer = nullptr; + } +} + +// --------------------------------------------------- +// setup tones +// --------------------------------------------------- + +LinphoneToneDescription *ToneManager::getToneFromReason(LinphoneReason reason) { + const bctbx_list_t *elem; + for (elem = getCore()->getCCore()->tones; elem != NULL; elem = elem->next) { + LinphoneToneDescription *tone = (LinphoneToneDescription *) elem->data; + if (tone->reason==reason) return tone; + } + return NULL; +} + +LinphoneToneDescription *ToneManager::getToneFromId(LinphoneToneID id) { + const bctbx_list_t *elem; + for (elem = getCore()->getCCore()->tones; elem != NULL; elem = elem->next) { + LinphoneToneDescription *tone = (LinphoneToneDescription *) elem->data; + if (tone->toneid == id) return tone; + } + return NULL; +} + +void ToneManager::setTone(LinphoneReason reason, LinphoneToneID id, const char *audiofile) { + LinphoneCore *lc = getCore()->getCCore(); + LinphoneToneDescription *tone = getToneFromId(id); + if (!tone) tone = getToneFromReason(reason); + + if (tone) { + lc->tones = bctbx_list_remove(lc->tones, tone); + linphone_tone_description_destroy(tone); + } + tone = linphone_tone_description_new(reason, id, audiofile); + lc->tones = bctbx_list_append(lc->tones, tone); +} + +// --------------------------------------------------- +// callbacks file player +// --------------------------------------------------- + +static void on_file_player_end(void *userData, MSFilter *f, unsigned int eventId, void *arg) { + ToneManager *toneManager = (ToneManager *) userData; + toneManager->onFilePlayerEnd(eventId); +} + +void ToneManager::onFilePlayerEnd(unsigned int eventId) { + switch (eventId) { + case MS_PLAYER_EOF: + lInfo() << "[ToneManager] " << __func__; + doStopTone(); + break; + default: + break; + } +} + +// --------------------------------------------------- +// tester +// --------------------------------------------------- + +LinphoneCoreToneManagerStats *ToneManager::getStats() { + return mStats; +} + +void ToneManager::resetStats() { + *mStats = {0, 0, 0, 0, 0, 0, 0}; +} + +// --------------------------------------------------- +// sessions +// --------------------------------------------------- + +/* set State for the session. Insert session if not present */ +void ToneManager::setState(const std::shared_ptr<CallSession> &session, ToneManager::State newState) { + if (mSessions.count(session) == 0) { + lInfo() << "[ToneManager] add new session [" << session << "]"; + } + mSessions[session] = newState; +} + +ToneManager::State ToneManager::getState(const std::shared_ptr<CallSession> &session) { + auto search = mSessions.find(session); + if (search != mSessions.end()) { + return search->second; + } else { + return State::None; + } +} + +bool ToneManager::isAnotherSessionInState(const std::shared_ptr<CallSession> &me, ToneManager::State state) { + for (auto it = mSessions.begin(); it != mSessions.end(); it++) { + if (it->second == state && it->first != me) { + return true; + } + } + return false; +} + +bool ToneManager::getSessionInState(ToneManager::State state, std::shared_ptr<CallSession> &session) { + for (auto it = mSessions.begin(); it != mSessions.end(); it++) { + if (it->second == state) { + session = it->first; + return true; + } + } + return false; +} + +bool ToneManager::isThereACall() { + for (auto it = mSessions.begin(); it != mSessions.end(); it++) { + if (it->second == State::Call) { + return true; + } + } + return false; +} + +// --------------------------------------------------- +// start +// --------------------------------------------------- + +void ToneManager::doStartErrorTone(const std::shared_ptr<CallSession> &session, LinphoneReason reason) { + lInfo() << "[ToneManager] " << __func__ << " [" << Utils::toString(reason) << "]"; + LinphoneToneDescription *tone = getToneFromReason(reason); + + if (tone) { + if (tone->audiofile) { + setState(session, State::Tone); + playFile(tone->audiofile); + } else if (tone->toneid != LinphoneToneUndefined) { + setState(session, State::Tone); + MSDtmfGenCustomTone dtmfTone = generateToneFromId(tone->toneid); + playTone(dtmfTone); + } + } +} + +void ToneManager::doStartNamedTone(LinphoneToneID toneId) { + lInfo() << "[ToneManager] " << __func__ << " [" << Utils::toString(toneId) << "]"; + LinphoneToneDescription *tone = getToneFromId(toneId); + + if (tone && tone->audiofile) { + playFile(tone->audiofile); + } else { + MSDtmfGenCustomTone dtmfTone = generateToneFromId(toneId); + playTone(dtmfTone); + } +} + +void ToneManager::doStartRingbackTone(const std::shared_ptr<CallSession> &session) { + lInfo() << "[ToneManager] " << __func__; + LinphoneCore *lc = getCore()->getCCore(); + + if (!lc->sound_conf.play_sndcard) + return; + + MSSndCard *ringCard = lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; + int maxRate = std::static_pointer_cast<MediaSession>(session)->getPrivate()->getLocalDesc()->streams[0].max_rate; + if (maxRate > 0) ms_snd_card_set_preferred_sample_rate(ringCard, maxRate); + + /* We release sound before playing ringback tone */ + AudioStream *as = reinterpret_cast<AudioStream *>(std::static_pointer_cast<MediaSession>(session)->getPrivate()->getMediaStream(LinphoneStreamTypeAudio)); + if (as) audio_stream_unprepare_sound(as); + + if (lc->sound_conf.remote_ring) { + ms_snd_card_set_stream_type(ringCard, MS_SND_CARD_STREAM_VOICE); + lc->ringstream = ring_start(lc->factory, lc->sound_conf.remote_ring, 2000, ringCard); + } +} + +void ToneManager::doStartRingtone(const std::shared_ptr<CallSession> &session) { + lInfo() << "[ToneManager] " << __func__; + LinphoneCore *lc = getCore()->getCCore(); + if (isAnotherSessionInState(session, State::Call)) { + /* play a tone within the context of the current call */ + doStartNamedTone(LinphoneToneCallWaiting); + } else { + MSSndCard *ringcard = lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; + if (ringcard) { + ms_snd_card_set_stream_type(ringcard, MS_SND_CARD_STREAM_RING); + linphone_ringtoneplayer_start(lc->factory, lc->ringtoneplayer, ringcard, lc->sound_conf.local_ring, 2000); + } + } +} + +// --------------------------------------------------- +// stop +// --------------------------------------------------- + +void ToneManager::doStop(const std::shared_ptr<CallSession> &session, ToneManager::State newState) { + switch (getState(session)) { + case ToneManager::Ringback: + setState(session, newState); + doStopRingbackTone(); + mStats->number_of_stopRingbackTone++; + break; + case ToneManager::Ringtone: + setState(session, newState); + doStopRingtone(session); + /* start again ringtone in update() in case another call is in Ringtone state */ + mStats->number_of_stopRingtone++; + break; + case ToneManager::Tone: + setState(session, newState); + doStopTone(); + mStats->number_of_stopTone++; + break; + case ToneManager::Call: + setState(session, newState); + if (isAnotherSessionInState(session, ToneManager::Ringtone)) { + doStopTone(); + } + break; + default: + lInfo() << "[ToneManager] nothing to stop"; + break; + } +} + +void ToneManager::doStopRingbackTone() { + lInfo() << "[ToneManager] " << __func__; + LinphoneCore *lc = getCore()->getCCore(); + if (lc->ringstream) { + ring_stop(lc->ringstream); + lc->ringstream = NULL; + } +} + +void ToneManager::doStopTone() { + lInfo() << "[ToneManager] " << __func__; + LinphoneCore *lc = getCore()->getCCore(); + if (lc->ringstream) { + ring_stop(lc->ringstream); + lc->ringstream = NULL; + } + + if (isThereACall()) { + MSFilter *f = getAudioResource(ToneGenerator, NULL, FALSE); + if (f != NULL) ms_filter_call_method_noarg(f, MS_DTMF_GEN_STOP); + } +} + +void ToneManager::doStopToneToPlaySomethingElse(const std::shared_ptr<CallSession> &session) { + lInfo() << "[ToneManager] " << __func__; + if (isAnotherSessionInState(session, State::Tone)) { + doStopTone(); + } +} + + +void ToneManager::doStopRingtone(const std::shared_ptr<CallSession> &session) { + lInfo() << "[ToneManager] " << __func__; + if (isAnotherSessionInState(session, State::Call)) { + /* stop the tone within the context of the current call */ + doStopTone(); + } else { + LinphoneCore *lc = getCore()->getCCore(); + if (linphone_ringtoneplayer_is_started(lc->ringtoneplayer)) { + linphone_ringtoneplayer_stop(lc->ringtoneplayer); + } + } +} + +// --------------------------------------------------- +// sound +// --------------------------------------------------- + +LinphoneStatus ToneManager::playFile(const char *audiofile) { + LinphoneCore *lc = getCore()->getCCore(); + MSFilter *f = getAudioResource(LocalPlayer, lc->sound_conf.play_sndcard, true); + int loopms = -1; + if (!f) return -1; + ms_filter_call_method(f, MS_PLAYER_SET_LOOP, &loopms); + if (ms_filter_call_method(f, MS_PLAYER_OPEN, (void*) audiofile) != 0) { + return -1; + } + ms_filter_call_method_noarg(f, MS_PLAYER_START); + + if (lc->ringstream && lc->ringstream->source) { + ms_filter_add_notify_callback(lc->ringstream->source, &on_file_player_end, this, FALSE); + } + return 0; +} + +MSDtmfGenCustomTone ToneManager::generateToneFromId(LinphoneToneID toneId) { + MSDtmfGenCustomTone def; + memset(&def, 0, sizeof(def)); + def.amplitude = 1; + /*these are french tones, excepted the failed one, which is USA congestion tone (does not exist in France)*/ + switch(toneId) { + case LinphoneToneCallOnHold: + case LinphoneToneCallWaiting: + def.duration=300; + def.frequencies[0]=440; + def.interval=2000; + break; + case LinphoneToneBusy: + def.duration=500; + def.frequencies[0]=440; + def.interval=500; + def.repeat_count=3; + break; + case LinphoneToneCallLost: + def.duration=250; + def.frequencies[0]=480; + def.frequencies[0]=620; + def.interval=250; + def.repeat_count=3; + break; + default: + lWarning() << "[ToneManager] Unhandled tone id."; + } + return def; +} + +void ToneManager::playTone(MSDtmfGenCustomTone tone) { + LinphoneCore *lc = getCore()->getCCore(); + MSFilter *f = getAudioResource(ToneGenerator, lc->sound_conf.play_sndcard, true); + if (f == NULL) { + lError() << "[ToneManager] No tone generator at this time !"; + return; + } + if (tone.duration > 0) { + ms_filter_call_method(f, MS_DTMF_GEN_PLAY_CUSTOM, &tone); + if (tone.repeat_count > 0) { + int delay = (tone.duration + tone.interval) * tone.repeat_count + 1000; + createTimerToCleanTonePlayer((unsigned int) delay); + } + } +} + +MSFilter *ToneManager::getAudioResource(AudioResourceType rtype, MSSndCard *card, bool create) { + LinphoneCore *lc = getCore()->getCCore(); + LinphoneCall *call = linphone_core_get_current_call(lc); + AudioStream *stream = NULL; + RingStream *ringstream; + if (call) { + stream = reinterpret_cast<AudioStream *>(linphone_call_get_stream(call, LinphoneStreamTypeAudio)); + } else if (linphone_core_is_in_conference(lc)) { + stream = linphone_conference_get_audio_stream(lc->conf_ctx); + } + if (stream) { + if (rtype == ToneGenerator) return stream->dtmfgen; + if (rtype == LocalPlayer) return stream->local_player; + return NULL; + } + if (card && lc->ringstream && card != lc->ringstream->card) { + ring_stop(lc->ringstream); + lc->ringstream = NULL; + } + if (lc->ringstream == NULL) { + float amp = lp_config_get_float(lc->config, "sound", "dtmf_player_amp", 0.1f); + MSSndCard *ringcard = lc->sound_conf.lsd_card + ? lc->sound_conf.lsd_card + : card + ? card + : lc->sound_conf.ring_sndcard; + + if (ringcard == NULL) return NULL; + if (!create) return NULL; + + ringstream = lc->ringstream = ring_start(lc->factory, NULL, 0, ringcard); + ms_filter_call_method(lc->ringstream->gendtmf, MS_DTMF_GEN_SET_DEFAULT_AMPLITUDE, &); + } else { + ringstream = lc->ringstream; + } + if (rtype == ToneGenerator) return ringstream->gendtmf; + if (rtype == LocalPlayer) return ringstream->source; + return NULL; +} + +LINPHONE_END_NAMESPACE diff --git a/src/conference/session/tone-manager.h b/src/conference/session/tone-manager.h new file mode 100644 index 0000000000000000000000000000000000000000..b862a7dafa92f598553f21c8e499b1bf9b049499 --- /dev/null +++ b/src/conference/session/tone-manager.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2010-2019 Belledonne Communications SARL. + * + * This file is part of Liblinphone. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "conference/session/call-session.h" +#include "core/core-accessor.h" +#include "private.h" +#include "mediastreamer2/dtmfgen.h" +#include "core/core.h" +#include "tester_utils.h" +#include <map> + +LINPHONE_BEGIN_NAMESPACE + +class ToneManager : public CoreAccessor { + public: + ToneManager(std::shared_ptr<Core> core); + ~ToneManager(); + + // public entrypoints for tones + void startRingbackTone(const std::shared_ptr<CallSession> &session); + void startRingtone(const std::shared_ptr<CallSession> &session); + void startErrorTone(const std::shared_ptr<CallSession> &session, LinphoneReason reason); + void startNamedTone(const std::shared_ptr<CallSession> &session, LinphoneToneID toneId); + void goToCall(const std::shared_ptr<CallSession> &session); + void stop(const std::shared_ptr<CallSession> &session); + void removeSession(const std::shared_ptr<CallSession> &session); + void update(const std::shared_ptr<CallSession> &session); + + // linphone core public API entrypoints + void linphoneCorePlayDtmf(char dtmf, int duration); + void linphoneCoreStopDtmf(); + LinphoneStatus linphoneCorePlayLocal(const char *audiofile); + void linphoneCoreStartDtmfStream(); + void linphoneCoreStopRinging(); + void linphoneCoreStopDtmfStream(); + + // callback file player + void onFilePlayerEnd(unsigned int eventId); + + // tester + LinphoneCoreToneManagerStats *getStats(); + void resetStats(); + + // timer + void deleteTimer(); + + //tones setup + LinphoneToneDescription *getToneFromReason(LinphoneReason reason); + LinphoneToneDescription *getToneFromId(LinphoneToneID id); + void setTone(LinphoneReason reason, LinphoneToneID id, const char *audiofile); + + private: + using AudioResourceType = enum { + ToneGenerator, + LocalPlayer + }; + + using State = enum { + None, // No tone played, not in call (the session just started or will end soon) + Call, // Running call + Ringback, // Play Ringback tone + Ringtone, // Play Ringtone or play a tone over the current call + Tone // Play a DTMF tone or a tone file + }; + std::string stateToString(ToneManager::State state); + void printDebugInfo(const std::shared_ptr<CallSession> &session); + + std::map<std::shared_ptr<CallSession>, ToneManager::State> mSessions; + belle_sip_source_t *mTimer = nullptr; + LinphoneCoreToneManagerStats *mStats; + + // timer + void createTimerToCleanTonePlayer(unsigned int delay); + + //sessions + void setState(const std::shared_ptr<CallSession> &session, ToneManager::State newState); + ToneManager::State getState(const std::shared_ptr<CallSession> &session); + bool isAnotherSessionInState(const std::shared_ptr<CallSession> &me, ToneManager::State state); + bool getSessionInState(ToneManager::State state, std::shared_ptr<CallSession> &session); + bool isThereACall(); + + // start + void doStartRingbackTone(const std::shared_ptr<CallSession> &session); + void doStartRingtone(const std::shared_ptr<CallSession> &session); + void doStartErrorTone(const std::shared_ptr<CallSession> &session, LinphoneReason reason); + void doStartNamedTone(LinphoneToneID toneId); + + // stop + void doStopRingbackTone(); + void doStopTone(); + void doStopToneToPlaySomethingElse(const std::shared_ptr<CallSession> &session); + void doStopRingtone(const std::shared_ptr<CallSession> &session); + void doStop(const std::shared_ptr<CallSession> &session, ToneManager::State newState); + + // sound + MSFilter *getAudioResource(AudioResourceType rtype, MSSndCard *card, bool create); + LinphoneStatus playFile(const char *audiofile); + void playTone(MSDtmfGenCustomTone dtmf); + MSDtmfGenCustomTone generateToneFromId(LinphoneToneID toneId); +}; + +LINPHONE_END_NAMESPACE diff --git a/src/core/core-p.h b/src/core/core-p.h index 245d891d33e2c89ad0202b6f4d3d1b8ef05211ff..9820ccb748cce731c6c060a94e2b1bef08ca9ec1 100644 --- a/src/core/core-p.h +++ b/src/core/core-p.h @@ -28,6 +28,7 @@ #include "object/object-p.h" #include "sal/call-op.h" #include "auth-info/auth-stack.h" +#include "conference/session/tone-manager.h" // ============================================================================= @@ -80,6 +81,8 @@ public: void insertChatRoomWithDb (const std::shared_ptr<AbstractChatRoom> &chatRoom, unsigned int notifyId = 0); std::shared_ptr<AbstractChatRoom> createBasicChatRoom (const ConferenceId &conferenceId, AbstractChatRoom::CapabilitiesMask capabilities, const std::shared_ptr<ChatRoomParams> ¶ms); + std::shared_ptr<ToneManager> getToneManager(); + //Base std::shared_ptr<AbstractChatRoom> createClientGroupChatRoom ( const std::string &subject, @@ -144,6 +147,8 @@ private: std::list<std::string> specs; + std::shared_ptr<ToneManager> toneManager; + // This is to keep a ref on a clientGroupChatRoom while it is being created // Otherwise the chatRoom will be freed() before it is inserted std::unordered_map<const AbstractChatRoom *, std::shared_ptr<const AbstractChatRoom>> noCreatedClientGroupChatRooms; diff --git a/src/core/core.cpp b/src/core/core.cpp index 71fd3c678ca9fc38deb0ea064fcf0781773826f5..81a228ef1c1fc575361bf39c9a3ae6db759337ef 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -116,6 +116,8 @@ void CorePrivate::uninit () { ms_usleep(10000); } + if (toneManager) toneManager->deleteTimer(); + chatRoomsById.clear(); noCreatedClientGroupChatRooms.clear(); listeners.clear(); @@ -225,6 +227,13 @@ bool CorePrivate::basicToFlexisipChatroomMigrationEnabled()const{ CorePrivate::CorePrivate() : authStack(*this){ } +std::shared_ptr<ToneManager> CorePrivate::getToneManager() { + L_Q(); + if (!toneManager) { + toneManager = make_shared<ToneManager>(q->getSharedFromThis()); + } + return toneManager; +} // ============================================================================= @@ -570,5 +579,14 @@ void Core::doLater(const std::function<void ()> &something){ getPrivate()->doLater(something); } +belle_sip_source_t *Core::createTimer(const std::function<bool ()> &something, unsigned int milliseconds){ + return belle_sip_main_loop_create_cpp_timeout_2(getPrivate()->getMainLoop(), something, milliseconds, ""); +} +/* Stop and destroy a timer created by createTimer()*/ +void Core::destroyTimer(belle_sip_source_t *timer){ + belle_sip_main_loop_remove_source(getPrivate()->getMainLoop(), timer); + belle_sip_object_unref(timer); +} + LINPHONE_END_NAMESPACE diff --git a/src/core/core.h b/src/core/core.h index e0cd8c0d46a81e9899ea50a2471f5b4823540373..4274e305f7d88d8c202dddf02bec65f7bf16e966 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -31,6 +31,8 @@ L_DECL_C_STRUCT(LinphoneCore); +typedef struct belle_sip_source belle_sip_source_t; + LINPHONE_BEGIN_NAMESPACE class AbstractChatRoom; @@ -64,6 +66,8 @@ class LINPHONE_PUBLIC Core : public Object { friend class RemoteConferenceListEventHandler; friend class ServerGroupChatRoom; friend class ServerGroupChatRoomPrivate; + friend class CallSessionPrivate; + friend class ToneManager; public: L_OVERRIDE_SHARED_FROM_THIS(Core); @@ -186,6 +190,13 @@ public: // Execute specified lambda later in main loop. This method can be used from any thread to execute something later on main thread. void doLater(const std::function<void ()> &something); + /* + * Run supplied std::function as a timer. It should return true if repeated, false otherwise. + * It may be unrefed with (with belle_sip_object_unref()) before expiration, if this timer never needs to be cancelled. + */ + belle_sip_source_t *createTimer(const std::function<bool ()> &something, unsigned int milliseconds); + /* Stop (ie cancel) and destroy a timer created by createTimer() */ + void destroyTimer(belle_sip_source_t *timer); private: Core (); diff --git a/tester/call_multi_tester.c b/tester/call_multi_tester.c index eed7ec2050f845c7f3ae02781fd3fffa9a9bae9f..12d14eca9b208c948a17777d6e02ecb5870a7956 100644 --- a/tester/call_multi_tester.c +++ b/tester/call_multi_tester.c @@ -228,6 +228,7 @@ static void incoming_call_accepted_when_outgoing_call_in_state(LinphoneCallState ,pauline->lc ,state==LinphoneCallOutgoingEarlyMedia?&marie->stat.number_of_LinphoneCallOutgoingEarlyMedia:&marie->stat.number_of_LinphoneCallOutgoingRinging ,1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingbackTone, 1)); } else if (state==LinphoneCallOutgoingProgress) { BC_ASSERT_PTR_NOT_NULL(linphone_core_invite_address(marie->lc,pauline->identity)); } else { @@ -237,6 +238,11 @@ static void incoming_call_accepted_when_outgoing_call_in_state(LinphoneCallState BC_ASSERT_TRUE(call_with_caller_params(laure,marie,laure_params)); + if (state==LinphoneCallOutgoingRinging) { + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingtone, 0, int, "%d"); + } else if (state==LinphoneCallOutgoingProgress || state==LinphoneCallOutgoingEarlyMedia) { + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingtone, 1, int, "%d"); + } BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,10000)); @@ -1162,6 +1168,7 @@ void do_not_stop_ringing_when_declining_one_of_two_incoming_calls(void) { ,&pauline->stat.number_of_LinphoneCallIncomingReceived ,1)); pauline_called_by_laure=linphone_core_get_current_call(pauline->lc); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingtone, 1, int, "%d"); BC_ASSERT_PTR_NOT_NULL(linphone_core_invite_address_with_params(marie->lc,pauline->identity,marie_params)); linphone_call_params_unref(marie_params); @@ -1171,16 +1178,23 @@ void do_not_stop_ringing_when_declining_one_of_two_incoming_calls(void) { ,&pauline->stat.number_of_LinphoneCallIncomingReceived ,2)); pauline_called_by_marie=linphone_core_get_current_call(marie->lc); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingtone, 1, int, "%d"); + linphone_call_decline(pauline_called_by_laure, LinphoneReasonDeclined); BC_ASSERT_TRUE(wait_for(laure->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallEnd,1)); BC_ASSERT_TRUE(wait_for(laure->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallReleased,1)); + // check that rigntone player restart + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingtone, 1, int, "%d"); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingtone, 2, int, "%d"); + BC_ASSERT_TRUE(linphone_ringtoneplayer_is_started(linphone_core_get_ringtoneplayer(pauline->lc))); linphone_call_terminate(pauline_called_by_marie); BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallEnd,1)); BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallReleased,1)); BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallEnd,2)); BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallReleased,2)); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingtone, 2, int, "%d"); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); diff --git a/tester/call_single_tester.c b/tester/call_single_tester.c index 2f8bbf5cc90377bf672cb7748569b960d1ae14f5..d5c35602b36ed3805db9eaeae9848c73c243f9e0 100644 --- a/tester/call_single_tester.c +++ b/tester/call_single_tester.c @@ -970,6 +970,10 @@ static void cancel_other_device_after_decline(void) { BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallOutgoingInit,1)); BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallIncomingReceived, 1)); BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallOutgoingProgress, 1)); + + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr->lc)->number_of_startRingtone, 1, int, "%d"); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_startRingbackTone, 1)); + call_callee = linphone_core_get_current_call(callee_mgr->lc); if (BC_ASSERT_PTR_NOT_NULL(call_callee)) { linphone_call_ref(call_callee); @@ -978,13 +982,20 @@ static void cancel_other_device_after_decline(void) { call_callee_2 = linphone_core_get_current_call(callee_mgr_2->lc); linphone_call_ref(call_callee_2); BC_ASSERT_PTR_NOT_NULL(call_callee_2); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr_2->lc)->number_of_startRingtone, 1, int, "%d"); BC_ASSERT_EQUAL(linphone_call_decline(call_callee, LinphoneReasonDeclined), 0 , int, "%d"); BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallEnd,1)); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_stopRingbackTone, 1, int, "%d"); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_startErrorTone, 1, int, "%d"); BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallReleased, 1)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallEnd,1)); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr->lc)->number_of_stopRingtone, 1, int, "%d"); BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallReleased, 1)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr_2->lc, &callee_mgr_2->stat.number_of_LinphoneCallEnd,1)); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr_2->lc)->number_of_stopRingtone, 1, int, "%d"); BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr_2->lc, &callee_mgr_2->stat.number_of_LinphoneCallReleased,1)); rei = linphone_call_get_error_info(call_callee_2); @@ -996,6 +1007,9 @@ static void cancel_other_device_after_decline(void) { BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(rei), "SIP"); } BC_ASSERT_EQUAL(linphone_call_log_get_status(linphone_call_get_call_log(call_callee_2)), LinphoneCallDeclinedElsewhere, int, "%d"); + + // There is currently no tone for LinphoneReasonDeclined, so there is no call to stopTone + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_stopTone, 0, int, "%d"); } if (out_call) linphone_call_unref(out_call); if (call_callee) linphone_call_unref(call_callee); @@ -1208,10 +1222,7 @@ static void early_declined_call(void) { /*wait until flexisip transfers the busy...*/ BC_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallError,1,33000)); BC_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallError,1, int, "%d"); - /* FIXME http://git.linphone.org/mantis/view.php?id=757 - BC_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonBusy, int, "%d"); - */ if (bctbx_list_size(linphone_core_get_call_logs(pauline->lc))>0) { BC_ASSERT_PTR_NOT_NULL(out_call_log=(LinphoneCallLog*)(linphone_core_get_call_logs(pauline->lc)->data)); BC_ASSERT_EQUAL(linphone_call_log_get_status(out_call_log),LinphoneCallAborted, int, "%d"); @@ -1366,10 +1377,15 @@ static void call_declined_base(bool_t use_timeout) { BC_ASSERT_PTR_NOT_NULL(in_call=linphone_core_get_current_call(marie->lc)); if (in_call) { linphone_call_ref(in_call); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingtone, 1, int, "%d"); + BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingbackTone,1)); if (!use_timeout) linphone_call_terminate(in_call); BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallReleased,1)); BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallReleased,1)); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_stopRingtone, 1, int, "%d"); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingbackTone, 1, int, "%d"); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startErrorTone, 1, int, "%d"); BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallEnd,1, int, "%d"); BC_ASSERT_EQUAL(use_timeout ? pauline->stat.number_of_LinphoneCallError : pauline->stat.number_of_LinphoneCallEnd,1, int, "%d"); BC_ASSERT_EQUAL(linphone_call_get_reason(in_call),LinphoneReasonDeclined, int, "%d"); @@ -1379,6 +1395,13 @@ static void call_declined_base(bool_t use_timeout) { linphone_call_unref(in_call); } linphone_call_unref(out_call); + + // Make sure the error tone ends by calling linphone_core_stop + linphone_core_stop(pauline->lc); + // In case of LinphoneReasonDeclined no tone is played, so there is no tone to stop + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopTone, use_timeout? 1 : 0, int, "%d"); + linphone_core_start(pauline->lc); + linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } @@ -1973,6 +1996,7 @@ void call_paused_resumed_base(bool_t multicast, bool_t with_losses) { BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallPausedByRemote,1)); BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallPaused,1)); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startNamedTone, 1, int, "%d"); /*stay in pause a little while in order to generate traffic*/ wait_for_until(pauline->lc, marie->lc, NULL, 5, 2000); @@ -1980,6 +2004,7 @@ void call_paused_resumed_base(bool_t multicast, bool_t with_losses) { BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,2)); BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,2)); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopTone, 1, int, "%d"); /*same here: wait a while for a bit of a traffic, we need to receive a RTCP packet*/ wait_for_until(pauline->lc, marie->lc, NULL, 5, 5000); @@ -2420,6 +2445,9 @@ static void call_with_file_player(void) { for (attempts=0; attempts<3; attempts++){ reset_counters(&marie->stat); reset_counters(&pauline->stat); + linphone_core_reset_tone_manager_stats(marie->lc); + linphone_core_reset_tone_manager_stats(pauline->lc); + /*make sure the record file doesn't already exists, otherwise this test will append new samples to it*/ unlink(recordpath); /*caller uses files instead of soundcard in order to avoid mixing soundcard input with file played using call's player*/ @@ -2735,6 +2763,8 @@ static void early_media_call_with_ringing_base(bool_t network_change){ BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,3000)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingRinging,1,1000)); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingtone, 1, int, "%d"); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingbackTone, 1, int, "%d"); if (linphone_core_inc_invite_pending(pauline->lc)) { /* send a 183 to initiate the early media */ @@ -2744,6 +2774,10 @@ static void early_media_call_with_ringing_base(bool_t network_change){ BC_ASSERT_TRUE( wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,2000) ); BC_ASSERT_TRUE(linphone_call_get_all_muted(marie_call)); + bool_t ringWithEarlyMedia = linphone_core_get_ring_during_incoming_early_media(pauline->lc); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingtone, ringWithEarlyMedia ? 0 : 1, int, "%d"); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_stopRingbackTone, 1, int, "%d"); + liblinphone_tester_check_rtcp(marie, pauline); /* this is a hack to simulate an incoming OK with a different IP address @@ -2759,6 +2793,7 @@ static void early_media_call_with_ringing_base(bool_t network_change){ BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 1,1000)); connected_time=ms_get_cur_time_ms(); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 1,1000)); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingtone, 1, int, "%d"); BC_ASSERT_PTR_EQUAL(marie_call, linphone_core_get_current_call(marie->lc)); BC_ASSERT_FALSE(linphone_call_get_all_muted(marie_call)); @@ -2807,6 +2842,8 @@ static void early_media_call_with_update_base(bool_t media_change){ BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingRinging,1,5000)); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingtone, 1, int, "%d"); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingbackTone, 1, int, "%d"); pauline_call = linphone_core_get_current_call(pauline->lc); if (!pauline_call) goto end; @@ -2816,6 +2853,10 @@ static void early_media_call_with_update_base(bool_t media_change){ BC_ASSERT_TRUE( wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,5000) ); BC_ASSERT_TRUE(linphone_call_get_all_muted(marie_call)); + bool_t ringWithEarlyMedia = linphone_core_get_ring_during_incoming_early_media(pauline->lc); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingtone, ringWithEarlyMedia ? 0 : 1, int, "%d"); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_stopRingbackTone, 1, int, "%d"); + pauline_params = linphone_call_params_copy(linphone_call_get_current_params(pauline_call)); if (media_change) { @@ -4131,6 +4172,8 @@ static void call_with_rtp_io_mode(void) { unlink(recordpath); reset_counters(&marie->stat); reset_counters(&pauline->stat); + linphone_core_reset_tone_manager_stats(marie->lc); + linphone_core_reset_tone_manager_stats(pauline->lc); /* The caller uses files instead of soundcard in order to avoid mixing soundcard input with file played using call's player. */ linphone_core_use_files(marie->lc, TRUE); @@ -4701,11 +4744,15 @@ static void call_logs_sqlite_storage(void) { reset_counters(&marie->stat); reset_counters(&pauline->stat); + linphone_core_reset_tone_manager_stats(marie->lc); + linphone_core_reset_tone_manager_stats(pauline->lc); BC_ASSERT_TRUE(call(marie, pauline)); end_call(marie, pauline); reset_counters(&marie->stat); reset_counters(&pauline->stat); + linphone_core_reset_tone_manager_stats(marie->lc); + linphone_core_reset_tone_manager_stats(pauline->lc); BC_ASSERT_TRUE(call(marie, pauline)); end_call(marie, pauline); BC_ASSERT_TRUE(linphone_core_get_call_history_size(marie->lc) == 2); diff --git a/tester/tester.c b/tester/tester.c index f9722196899308b773d71a6c802fffc93b70ee02..b44708a443221c0d598a279bbc4116058e71ad5b 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -1744,6 +1744,15 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr linphone_address_unref(callee_from); } + LinphoneCoreToneManagerStats *callee_stats = linphone_core_get_tone_manager_stats(callee_mgr->lc); + if (callee_stats->number_of_startRingbackTone == callee_stats->number_of_stopRingbackTone) { + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr->lc)->number_of_startRingtone, callee_mgr->stat.number_of_LinphoneCallIncomingReceived, int, "%d"); + } else { + // in this case, the call is currently in RingbackTone so the Ringtone should not start + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr->lc)->number_of_startRingtone, callee_mgr->stat.number_of_LinphoneCallIncomingReceived-1, int, "%d"); + } + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_startRingbackTone, caller_mgr->stat.number_of_LinphoneCallOutgoingRinging, int, "%d"); + if (callee_params){ linphone_call_accept_with_params(callee_call,callee_params); @@ -1765,6 +1774,9 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr && wait_for_until(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallStreamsRunning,initial_callee.number_of_LinphoneCallStreamsRunning+1, 2000); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr->lc)->number_of_stopRingtone, callee_mgr->stat.number_of_LinphoneCallIncomingReceived, int, "%d"); + BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_stopRingbackTone, caller_mgr->stat.number_of_LinphoneCallOutgoingRinging, int, "%d"); + if (linphone_core_get_media_encryption(caller_mgr->lc) != LinphoneMediaEncryptionNone || linphone_core_get_media_encryption(callee_mgr->lc) != LinphoneMediaEncryptionNone) { /*wait for encryption to be on, in case of zrtp or dtls, it can take a few seconds*/