]> sjero.net Git - linphone/blob - coreapi/conference.c
ICE was broken due to uPNP support, now ICE is back !
[linphone] / coreapi / conference.c
1 /***************************************************************************
2  *            conference.c
3  *
4  *  Mon Sep 12, 2011
5  *  Copyright  2011  Belledonne Communications
6  *  Author: Simon Morlat
7  *  Email simon dot morlat at linphone dot org
8  ****************************************************************************/
9
10 /*
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.
15  *
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.
20  *
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.
24  */
25  
26 #include "private.h"
27 #include "lpconfig.h"
28
29 #include "mediastreamer2/msvolume.h"
30
31 /**
32  * @addtogroup conferencing
33  * @{
34 **/
35
36
37 static int convert_conference_to_call(LinphoneCore *lc);
38
39 static void conference_check_init(LinphoneConference *ctx, int samplerate){
40         if (ctx->conf==NULL){
41                 MSAudioConferenceParams params;
42                 params.samplerate=samplerate;
43                 ctx->conf=ms_audio_conference_new(&params);
44         }
45 }
46
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);
55         }
56 }
57
58 static int remote_participants_count(LinphoneConference *ctx) {
59         if (!ctx->conf || ms_audio_conference_get_size(ctx->conf)==0) return 0;
60         if (!ctx->local_participant) return ms_audio_conference_get_size(ctx->conf);
61         return ms_audio_conference_get_size(ctx->conf) -1;
62 }
63
64 void linphone_core_conference_check_uninit(LinphoneCore *lc){
65         LinphoneConference *ctx=&lc->conf_ctx;
66         if (ctx->conf){
67                 ms_message("conference_check_uninit(): nmembers=%i",ms_audio_conference_get_size(ctx->conf));
68                 if (remote_participants_count(ctx)==1){
69                         convert_conference_to_call(lc);
70                 }
71                 if (ms_audio_conference_get_size(ctx->conf)==1 && ctx->local_participant!=NULL){
72                         remove_local_endpoint(ctx);
73                 }
74                 if (ms_audio_conference_get_size(ctx->conf)==0){
75                         ms_audio_conference_destroy(ctx->conf);
76                         ctx->conf=NULL;
77                 }
78         }
79 }
80
81 void linphone_call_add_to_conf(LinphoneCall *call, bool_t muted){
82         LinphoneCore *lc=call->core;
83         LinphoneConference *conf=&lc->conf_ctx;
84         MSAudioEndpoint *ep;
85         call->params.has_video = FALSE;
86         call->camera_active = FALSE;
87         ep=ms_audio_endpoint_get_from_stream(call->audiostream,TRUE);
88         ms_audio_conference_add_member(conf->conf,ep);
89         ms_audio_conference_mute_member(conf->conf,ep,muted);
90         call->endpoint=ep;
91 }
92
93 void linphone_call_remove_from_conf(LinphoneCall *call){
94         LinphoneCore *lc=call->core;
95         LinphoneConference *conf=&lc->conf_ctx;
96         
97         ms_audio_conference_remove_member(conf->conf,call->endpoint);
98         ms_audio_endpoint_release_from_stream(call->endpoint);
99         call->endpoint=NULL;
100 }
101
102 static RtpProfile *make_dummy_profile(int samplerate){
103         RtpProfile *prof=rtp_profile_new("dummy");
104         PayloadType *pt=payload_type_clone(&payload_type_l16_mono);
105         pt->clock_rate=samplerate;
106         rtp_profile_set_payload(prof,0,pt);
107         return prof;
108 }
109
110 static void add_local_endpoint(LinphoneConference *conf,LinphoneCore *lc){
111         /*create a dummy audiostream in order to extract the local part of it */
112         /* network address and ports have no meaning and are not used here. */
113         AudioStream *st=audio_stream_new(65000,65001,FALSE);
114         MSSndCard *playcard=lc->sound_conf.lsd_card ? 
115                         lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
116         MSSndCard *captcard=lc->sound_conf.capt_sndcard;
117         const MSAudioConferenceParams *params=ms_audio_conference_get_params(conf->conf);
118         conf->local_dummy_profile=make_dummy_profile(params->samplerate);
119         
120         audio_stream_start_full(st, conf->local_dummy_profile,
121                                 "127.0.0.1",
122                                 65000,
123                                 "127.0.0.1",
124                                 65001,
125                                 0,
126                                 40,
127                                 NULL,
128                                 NULL,
129                                 playcard,
130                                 captcard,
131                                 linphone_core_echo_cancellation_enabled(lc)
132                                 );
133         _post_configure_audio_stream(st,lc,FALSE);
134         conf->local_participant=st;
135         conf->local_endpoint=ms_audio_endpoint_get_from_stream(st,FALSE);
136         ms_audio_conference_add_member(conf->conf,conf->local_endpoint);
137         
138 }
139
140 /**
141  * Returns the sound volume (mic input) of the local participant of the conference.
142  * @param lc the linphone core
143  * @returns the measured input volume expressed in dbm0.
144  **/
145 float linphone_core_get_conference_local_input_volume(LinphoneCore *lc){
146         LinphoneConference *conf=&lc->conf_ctx;
147         AudioStream *st=conf->local_participant;
148         if (st && st->volsend && !conf->local_muted){
149                 float vol=0;
150                 ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
151                 return vol;
152                 
153         }
154         return LINPHONE_VOLUME_DB_LOWEST;
155 }
156
157 /**
158  * Merge a call into a conference.
159  * @param lc the linphone core
160  * @param call an established call, either in LinphoneCallStreamsRunning or LinphoneCallPaused state.
161  * 
162  * If this is the first call that enters the conference, the virtual conference will be created automatically.
163  * 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.
164  * If the call was in paused state, then it is automatically resumed when entering into the conference.
165  * 
166  * @returns 0 if successful, -1 otherwise.
167 **/
168 int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call){
169         LinphoneCallParams params;
170         LinphoneConference *conf=&lc->conf_ctx;
171         
172         if (call->current_params.in_conference){
173                 ms_error("Already in conference");
174                 return -1;
175         }
176         conference_check_init(&lc->conf_ctx, lp_config_get_int(lc->config, "sound","conference_rate",16000));
177         call->params.in_conference=TRUE;
178         call->params.has_video=FALSE;
179         call->params.media_encryption=LinphoneMediaEncryptionNone;
180         params=call->params;
181         if (call->state==LinphoneCallPaused)
182                 linphone_core_resume_call(lc,call);
183         else if (call->state==LinphoneCallStreamsRunning){
184                 /*this will trigger a reINVITE that will later redraw the streams */
185                 if (call->audiostream || call->videostream){
186                         linphone_call_stop_media_streams (call); /*free the audio & video local resources*/
187                 }
188                 if (call==lc->current_call){
189                         lc->current_call=NULL;
190                 }
191                 linphone_core_update_call(lc,call,&params);
192                 add_local_endpoint(conf,lc);
193         }else{
194                 ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state));
195                 return -1;
196         }
197         return 0;
198 }
199
200 static int remove_from_conference(LinphoneCore *lc, LinphoneCall *call, bool_t active){
201         int err=0;
202
203         if (!call->current_params.in_conference){
204                 if (call->params.in_conference){
205                         ms_warning("Not (yet) in conference, be patient");
206                         return -1;
207                 }else{
208                         ms_error("Not in a conference.");
209                         return -1;
210                 }
211         }
212         call->params.in_conference=FALSE;
213
214         char *str=linphone_call_get_remote_address_as_string(call);
215         ms_message("%s will be removed from conference", str);
216         ms_free(str);
217         if (active){
218                 // reconnect local audio with this call
219                 if (linphone_core_is_in_conference(lc)){
220                         ms_message("Leaving conference for reconnecting with unique call.");
221                         linphone_core_leave_conference(lc);
222                 }
223                 ms_message("Updating call to actually remove from conference");
224                 err=linphone_core_update_call(lc,call,&call->params);
225         } else{
226                 ms_message("Pausing call to actually remove from conference");
227                 err=linphone_core_pause_call(lc,call);
228         }
229
230         return err;
231 }
232
233 static int convert_conference_to_call(LinphoneCore *lc){
234         int err=0;
235         MSList *calls=lc->calls;
236
237         if (remote_participants_count(&lc->conf_ctx)!=1){
238                 ms_error("No unique call remaining in conference.");
239                 return -1;
240         }
241
242         while (calls) {
243                 LinphoneCall *rc=(LinphoneCall*)calls->data;
244                 calls=calls->next;
245                 if (rc->params.in_conference) { // not using current_param
246                         bool_t active_after_removed=linphone_core_is_in_conference(lc);
247                         err=remove_from_conference(lc, rc, active_after_removed);
248                         break;
249                 }
250         }
251         return err;
252 }
253
254 /**
255  * Remove a call from the conference.
256  * @param lc the linphone core
257  * @param call a call that has been previously merged into the conference.
258  * 
259  * After removing the remote participant belonging to the supplied call, the call becomes a normal call in paused state.
260  * If one single remote participant is left alone together with the local user in the conference after the removal, then the conference is
261  * automatically transformed into a simple call in StreamsRunning state.
262  * The conference's resources are then automatically destroyed.
263  * 
264  * In other words, unless linphone_core_leave_conference() is explicitely called, the last remote participant of a conference is automatically
265  * put in a simple call in running state.
266  * 
267  * @returns 0 if successful, -1 otherwise.
268  **/
269 int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call){
270         char * str=linphone_call_get_remote_address_as_string(call);
271         ms_message("Removing call %s from the conference", str);
272         ms_free(str);
273         int err=remove_from_conference(lc,call, FALSE);
274         if (err){
275                 ms_error("Error removing participant from conference.");
276                 return err;
277         }
278
279         if (remote_participants_count(&lc->conf_ctx)==1){
280                 ms_message("conference size is 1: need to be converted to plain call");
281                 err=convert_conference_to_call(lc);
282         } else {
283                 ms_message("the conference need not to be converted as size is %i", remote_participants_count(&lc->conf_ctx));
284         }
285         return err;
286 }
287
288 /**
289  * Indicates whether the local participant is part of the conference.
290  * @param lc the linphone core
291  * @returns TRUE if the local participant is in the conference, FALSE otherwise.
292 **/
293 bool_t linphone_core_is_in_conference(const LinphoneCore *lc){
294         return lc->conf_ctx.local_participant!=NULL;
295 }
296
297 /**
298  * Moves the local participant out of the conference.
299  * @param lc the linphone core
300  * When the local participant is out of the conference, the remote participants can continue to talk normally.
301  * @returns 0 if successful, -1 otherwise.
302 **/
303 int linphone_core_leave_conference(LinphoneCore *lc){
304         LinphoneConference *conf=&lc->conf_ctx;
305         if (linphone_core_is_in_conference(lc))
306                 remove_local_endpoint(conf);
307         return 0;
308 }
309
310 /**
311  * Moves the local participant inside the conference.
312  * @param lc the linphone core
313  * 
314  * Makes the local participant to join the conference. 
315  * Typically, the local participant is by default always part of the conference when joining an active call into a conference.
316  * However, by calling linphone_core_leave_conference() and linphone_core_enter_conference() the application can decide to temporarily
317  * move out and in the local participant from the conference.
318  * 
319  * @returns 0 if successful, -1 otherwise
320 **/
321 int linphone_core_enter_conference(LinphoneCore *lc){
322         if (linphone_core_sound_resources_locked(lc)) {
323                 return -1;
324         }
325         if (lc->current_call != NULL) {
326                 linphone_core_pause_call(lc, lc->current_call);
327         }
328         LinphoneConference *conf=&lc->conf_ctx;
329         if (conf->local_participant==NULL) add_local_endpoint(conf,lc);
330         return 0;
331 }
332
333 /**
334  * Add all calls into a conference.
335  * @param lc the linphone core
336  * 
337  * Merge all established calls (either in LinphoneCallStreamsRunning or LinphoneCallPaused) into a conference.
338  * 
339  * @returns 0 if successful, -1 otherwise
340 **/
341 int linphone_core_add_all_to_conference(LinphoneCore *lc) {
342         MSList *calls=lc->calls;
343         while (calls) {
344                 LinphoneCall *call=(LinphoneCall*)calls->data;
345                 calls=calls->next;
346                 if (!call->current_params.in_conference) {
347                         linphone_core_add_to_conference(lc, call);
348                 }
349         }
350         linphone_core_enter_conference(lc);
351         return 0;
352 }
353
354 /**
355  * Terminates the conference and the calls associated with it.
356  * @param lc the linphone core
357  * 
358  * All the calls that were merged to the conference are terminated, and the conference resources are destroyed.
359  * 
360  * @returns 0 if successful, -1 otherwise
361 **/
362 int linphone_core_terminate_conference(LinphoneCore *lc) {
363         MSList *calls=lc->calls;
364         while (calls) {
365                 LinphoneCall *call=(LinphoneCall*)calls->data;
366                 calls=calls->next;
367                 if (call->current_params.in_conference) {
368                         linphone_core_terminate_call(lc, call);
369                 }
370         }
371         return 0;
372 }
373
374 /**
375  * Returns the number of participants to the conference, including the local participant.
376  * @param lc the linphone core
377  * 
378  * Typically, after merging two calls into the conference, there is total of 3 participants:
379  * the local participant (or local user), and two remote participants that were the destinations of the two previously establised calls.
380  * 
381  * @returns the number of participants to the conference
382 **/
383 int linphone_core_get_conference_size(LinphoneCore *lc) {
384         if (lc->conf_ctx.conf == NULL) {
385                 return 0;
386         }
387         return ms_audio_conference_get_size(lc->conf_ctx.conf);
388 }
389
390 /**
391  * @}
392 **/
393