1 /***************************************************************************
5 * Copyright 2011 Belledonne Communications
7 * Email simon dot morlat at linphone dot org
8 ****************************************************************************/
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include "mediastreamer2/msvolume.h"
32 * @addtogroup conferencing
37 static int convert_conference_to_call(LinphoneCore *lc);
39 static void conference_check_init(LinphoneConference *ctx, int samplerate){
41 MSAudioConferenceParams params;
42 params.samplerate=samplerate;
43 ctx->conf=ms_audio_conference_new(¶ms);
47 static void remove_local_endpoint(LinphoneConference *ctx){
48 if (ctx->local_endpoint){
49 ms_audio_conference_remove_member(ctx->conf,ctx->local_endpoint);
50 ms_audio_endpoint_release_from_stream(ctx->local_endpoint);
51 ctx->local_endpoint=NULL;
52 audio_stream_stop(ctx->local_participant);
53 ctx->local_participant=NULL;
54 rtp_profile_destroy(ctx->local_dummy_profile);
58 static int linphone_conference_get_size(LinphoneConference *conf){
59 if (conf->conf == NULL) {
62 return ms_audio_conference_get_size(conf->conf) - (conf->record_endpoint ? 1 : 0);
65 static int remote_participants_count(LinphoneConference *ctx) {
66 int count=linphone_conference_get_size(ctx);
67 if (count==0) return 0;
68 if (!ctx->local_participant) return count;
72 void linphone_core_conference_check_uninit(LinphoneCore *lc){
73 LinphoneConference *ctx=&lc->conf_ctx;
75 int remote_count=remote_participants_count(ctx);
76 ms_message("conference_check_uninit(): size=%i",linphone_conference_get_size(ctx));
78 convert_conference_to_call(lc);
81 if (ctx->local_participant!=NULL)
82 remove_local_endpoint(ctx);
83 if (ctx->record_endpoint){
84 ms_audio_conference_remove_member(ctx->conf,ctx->record_endpoint);
85 ms_audio_endpoint_destroy(ctx->record_endpoint);
86 ctx->record_endpoint=NULL;
90 if (ms_audio_conference_get_size(ctx->conf)==0){
91 ms_audio_conference_destroy(ctx->conf);
97 void linphone_call_add_to_conf(LinphoneCall *call, bool_t muted){
98 LinphoneCore *lc=call->core;
99 LinphoneConference *conf=&lc->conf_ctx;
101 call->params.has_video = FALSE;
102 call->camera_active = FALSE;
103 ep=ms_audio_endpoint_get_from_stream(call->audiostream,TRUE);
104 ms_audio_conference_add_member(conf->conf,ep);
105 ms_audio_conference_mute_member(conf->conf,ep,muted);
109 void linphone_call_remove_from_conf(LinphoneCall *call){
110 LinphoneCore *lc=call->core;
111 LinphoneConference *conf=&lc->conf_ctx;
113 ms_audio_conference_remove_member(conf->conf,call->endpoint);
114 ms_audio_endpoint_release_from_stream(call->endpoint);
118 static RtpProfile *make_dummy_profile(int samplerate){
119 RtpProfile *prof=rtp_profile_new("dummy");
120 PayloadType *pt=payload_type_clone(&payload_type_l16_mono);
121 pt->clock_rate=samplerate;
122 rtp_profile_set_payload(prof,0,pt);
126 static void add_local_endpoint(LinphoneConference *conf,LinphoneCore *lc){
127 /*create a dummy audiostream in order to extract the local part of it */
128 /* network address and ports have no meaning and are not used here. */
129 AudioStream *st=audio_stream_new(65000,65001,FALSE);
130 MSSndCard *playcard=lc->sound_conf.lsd_card ?
131 lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
132 MSSndCard *captcard=lc->sound_conf.capt_sndcard;
133 const MSAudioConferenceParams *params=ms_audio_conference_get_params(conf->conf);
134 conf->local_dummy_profile=make_dummy_profile(params->samplerate);
136 audio_stream_start_full(st, conf->local_dummy_profile,
147 linphone_core_echo_cancellation_enabled(lc)
149 _post_configure_audio_stream(st,lc,FALSE);
150 conf->local_participant=st;
151 conf->local_endpoint=ms_audio_endpoint_get_from_stream(st,FALSE);
152 ms_audio_conference_add_member(conf->conf,conf->local_endpoint);
157 * Returns the sound volume (mic input) of the local participant of the conference.
158 * @param lc the linphone core
159 * @returns the measured input volume expressed in dbm0.
161 float linphone_core_get_conference_local_input_volume(LinphoneCore *lc){
162 LinphoneConference *conf=&lc->conf_ctx;
163 AudioStream *st=conf->local_participant;
164 if (st && st->volsend && !conf->local_muted){
166 ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
170 return LINPHONE_VOLUME_DB_LOWEST;
174 * Merge a call into a conference.
175 * @param lc the linphone core
176 * @param call an established call, either in LinphoneCallStreamsRunning or LinphoneCallPaused state.
178 * If this is the first call that enters the conference, the virtual conference will be created automatically.
179 * If the local user was actively part of the call (ie not in paused state), then the local user is automatically entered into the conference.
180 * If the call was in paused state, then it is automatically resumed when entering into the conference.
182 * @returns 0 if successful, -1 otherwise.
184 int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call){
185 LinphoneConference *conf=&lc->conf_ctx;
187 if (call->current_params.in_conference){
188 ms_error("Already in conference");
191 conference_check_init(&lc->conf_ctx, lp_config_get_int(lc->config, "sound","conference_rate",16000));
193 if (call->state==LinphoneCallPaused){
194 call->params.in_conference=TRUE;
195 call->params.has_video=FALSE;
196 linphone_core_resume_call(lc,call);
197 }else if (call->state==LinphoneCallStreamsRunning){
198 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
199 params->in_conference=TRUE;
200 params->has_video=FALSE;
202 if (call->audiostream || call->videostream){
203 linphone_call_stop_media_streams (call); /*free the audio & video local resources*/
205 if (call==lc->current_call){
206 lc->current_call=NULL;
208 /*this will trigger a reINVITE that will later redraw the streams */
209 linphone_core_update_call(lc,call,params);
210 linphone_call_params_destroy(params);
211 add_local_endpoint(conf,lc);
213 ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state));
219 static int remove_from_conference(LinphoneCore *lc, LinphoneCall *call, bool_t active){
222 if (!call->current_params.in_conference){
223 if (call->params.in_conference){
224 ms_warning("Not (yet) in conference, be patient");
227 ms_error("Not in a conference.");
231 call->params.in_conference=FALSE;
233 char *str=linphone_call_get_remote_address_as_string(call);
234 ms_message("%s will be removed from conference", str);
237 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
238 params->in_conference=FALSE;
239 // reconnect local audio with this call
240 if (linphone_core_is_in_conference(lc)){
241 ms_message("Leaving conference for reconnecting with unique call.");
242 linphone_core_leave_conference(lc);
244 ms_message("Updating call to actually remove from conference");
245 err=linphone_core_update_call(lc,call,params);
246 linphone_call_params_destroy(params);
248 ms_message("Pausing call to actually remove from conference");
249 err=_linphone_core_pause_call(lc,call);
255 static int convert_conference_to_call(LinphoneCore *lc){
257 MSList *calls=lc->calls;
259 if (remote_participants_count(&lc->conf_ctx)!=1){
260 ms_error("No unique call remaining in conference.");
265 LinphoneCall *rc=(LinphoneCall*)calls->data;
267 if (rc->params.in_conference) { // not using current_param
268 bool_t active_after_removed=linphone_core_is_in_conference(lc);
269 err=remove_from_conference(lc, rc, active_after_removed);
277 * Remove a call from the conference.
278 * @param lc the linphone core
279 * @param call a call that has been previously merged into the conference.
281 * After removing the remote participant belonging to the supplied call, the call becomes a normal call in paused state.
282 * If one single remote participant is left alone together with the local user in the conference after the removal, then the conference is
283 * automatically transformed into a simple call in StreamsRunning state.
284 * The conference's resources are then automatically destroyed.
286 * In other words, unless linphone_core_leave_conference() is explicitely called, the last remote participant of a conference is automatically
287 * put in a simple call in running state.
289 * @returns 0 if successful, -1 otherwise.
291 int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call){
292 char * str=linphone_call_get_remote_address_as_string(call);
293 ms_message("Removing call %s from the conference", str);
295 int err=remove_from_conference(lc,call, FALSE);
297 ms_error("Error removing participant from conference.");
301 if (remote_participants_count(&lc->conf_ctx)==1){
302 ms_message("conference size is 1: need to be converted to plain call");
303 err=convert_conference_to_call(lc);
305 ms_message("the conference need not to be converted as size is %i", remote_participants_count(&lc->conf_ctx));
311 * Indicates whether the local participant is part of the conference.
312 * @param lc the linphone core
313 * @returns TRUE if the local participant is in the conference, FALSE otherwise.
315 bool_t linphone_core_is_in_conference(const LinphoneCore *lc){
316 return lc->conf_ctx.local_participant!=NULL;
320 * Moves the local participant out of the conference.
321 * @param lc the linphone core
322 * When the local participant is out of the conference, the remote participants can continue to talk normally.
323 * @returns 0 if successful, -1 otherwise.
325 int linphone_core_leave_conference(LinphoneCore *lc){
326 LinphoneConference *conf=&lc->conf_ctx;
327 if (linphone_core_is_in_conference(lc))
328 remove_local_endpoint(conf);
333 * Moves the local participant inside the conference.
334 * @param lc the linphone core
336 * Makes the local participant to join the conference.
337 * Typically, the local participant is by default always part of the conference when joining an active call into a conference.
338 * However, by calling linphone_core_leave_conference() and linphone_core_enter_conference() the application can decide to temporarily
339 * move out and in the local participant from the conference.
341 * @returns 0 if successful, -1 otherwise
343 int linphone_core_enter_conference(LinphoneCore *lc){
344 if (linphone_core_sound_resources_locked(lc)) {
347 if (lc->current_call != NULL) {
348 _linphone_core_pause_call(lc, lc->current_call);
350 LinphoneConference *conf=&lc->conf_ctx;
351 if (conf->local_participant==NULL) add_local_endpoint(conf,lc);
356 * Add all calls into a conference.
357 * @param lc the linphone core
359 * Merge all established calls (either in LinphoneCallStreamsRunning or LinphoneCallPaused) into a conference.
361 * @returns 0 if successful, -1 otherwise
363 int linphone_core_add_all_to_conference(LinphoneCore *lc) {
364 MSList *calls=lc->calls;
366 LinphoneCall *call=(LinphoneCall*)calls->data;
368 if (!call->current_params.in_conference) {
369 linphone_core_add_to_conference(lc, call);
372 linphone_core_enter_conference(lc);
377 * Terminates the conference and the calls associated with it.
378 * @param lc the linphone core
380 * All the calls that were merged to the conference are terminated, and the conference resources are destroyed.
382 * @returns 0 if successful, -1 otherwise
384 int linphone_core_terminate_conference(LinphoneCore *lc) {
385 MSList *calls=lc->calls;
387 LinphoneCall *call=(LinphoneCall*)calls->data;
389 if (call->current_params.in_conference) {
390 linphone_core_terminate_call(lc, call);
397 * Returns the number of participants to the conference, including the local participant.
398 * @param lc the linphone core
400 * Typically, after merging two calls into the conference, there is total of 3 participants:
401 * the local participant (or local user), and two remote participants that were the destinations of the two previously establised calls.
403 * @returns the number of participants to the conference
405 int linphone_core_get_conference_size(LinphoneCore *lc) {
406 LinphoneConference *conf=&lc->conf_ctx;
407 return linphone_conference_get_size(conf);
411 int linphone_core_start_conference_recording(LinphoneCore *lc, const char *path){
412 LinphoneConference *conf=&lc->conf_ctx;
413 if (conf->conf == NULL) {
414 ms_warning("linphone_core_start_conference_recording(): no conference now.");
417 if (conf->record_endpoint==NULL){
418 conf->record_endpoint=ms_audio_endpoint_new_recorder();
419 ms_audio_conference_add_member(conf->conf,conf->record_endpoint);
421 ms_audio_recorder_endpoint_start(conf->record_endpoint,path);
425 int linphone_core_stop_conference_recording(LinphoneCore *lc){
426 LinphoneConference *conf=&lc->conf_ctx;
427 if (conf->conf == NULL) {
428 ms_warning("linphone_core_stop_conference_recording(): no conference now.");
431 if (conf->record_endpoint==NULL){
432 ms_warning("linphone_core_stop_conference_recording(): no record active.");
435 ms_audio_recorder_endpoint_stop(conf->record_endpoint);