1 /*******************************************************************************
2 * conference.cc
3 *
4 * Thu Nov 26, 2015
5 * Copyright 2015 Belledonne Communications
6 * Author: Linphone's team
7 * Email info@belledonne-communications.com
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 "linphone/core.h"
27 #include "private.h"
28 #include "conference_private.h"
29 #include <mediastreamer2/msvolume.h>
30 #include <typeinfo>
31 #include <list>
32 #include <algorithm>
33
34 namespace Linphone {
35
36 template <typename _type>
toStd(const bctbx_list_t * l)37 inline std::list<_type> toStd(const bctbx_list_t *l){
38 std::list<_type> ret;
39 for(; l != NULL; l = l->next){
40 ret.push_back(static_cast<_type>(l->data));
41 }
42 return ret;
43 }
44
45 class Conference {
46 public:
47 class Participant {
48 public:
Participant(LinphoneCall * call)49 Participant(LinphoneCall *call) {
50 m_uri = linphone_address_clone(linphone_call_get_remote_address(call));
51 m_call = call;
52 }
53
~Participant()54 ~Participant() {
55 linphone_address_unref(m_uri);
56 if(m_call) m_call->conf_ref = NULL;
57
58 }
59
getUri() const60 const LinphoneAddress *getUri() const {
61 return m_uri;
62 }
63
getCall() const64 LinphoneCall *getCall() const {
65 return m_call;
66 }
67
68 private:
69 Participant(const Participant &src);
70 Participant &operator=(const Participant &src);
71
72 private:
73 LinphoneAddress *m_uri;
74 LinphoneCall *m_call;
75
76 friend class RemoteConference;
77 };
78
79
80 class Params {
81 public:
Params(const LinphoneCore * core=NULL)82 Params(const LinphoneCore *core = NULL) {
83 m_enableVideo = false;
84 if(core) {
85 const LinphoneVideoPolicy *policy = linphone_core_get_video_policy(core);
86 if(policy->automatically_initiate) m_enableVideo = true;
87 }
88 m_stateChangedCb = NULL;
89 m_userData = NULL;
90 }
enableVideo(bool enable)91 void enableVideo(bool enable) {m_enableVideo = enable;}
videoRequested() const92 bool videoRequested() const {return m_enableVideo;}
setStateChangedCallback(LinphoneConferenceStateChangedCb cb,void * userData)93 void setStateChangedCallback(LinphoneConferenceStateChangedCb cb, void *userData) {
94 m_stateChangedCb = cb;
95 m_userData = userData;
96 }
97
98 private:
99 bool m_enableVideo;
100 LinphoneConferenceStateChangedCb m_stateChangedCb;
101 void *m_userData;
102
103 friend class Conference;
104 };
105
106 Conference(LinphoneCore *core, LinphoneConference *conf, const Params *params = NULL);
~Conference()107 virtual ~Conference() {};
108
getCurrentParams() const109 const Params &getCurrentParams() const {return m_currentParams;}
110
111 virtual int inviteAddresses(const std::list<const LinphoneAddress*> &addresses, const LinphoneCallParams *params) = 0;
112 virtual int addParticipant(LinphoneCall *call) = 0;
113 virtual int removeParticipant(LinphoneCall *call) = 0;
114 virtual int removeParticipant(const LinphoneAddress *uri) = 0;
115 virtual int terminate() = 0;
116
117 virtual int enter() = 0;
118 virtual int leave() = 0;
119 virtual bool isIn() const = 0;
120
getAudioStream() const121 AudioStream *getAudioStream() const {return m_localParticipantStream;}
122 int muteMicrophone(bool val);
microphoneIsMuted() const123 bool microphoneIsMuted() const {return m_isMuted;}
124 float getInputVolume() const;
125
getSize() const126 virtual int getSize() const {return (int)m_participants.size() + (isIn()?1:0);}
getParticipants() const127 const std::list<Participant *> &getParticipants() const {return m_participants;}
128
129 virtual int startRecording(const char *path) = 0;
130 virtual int stopRecording() = 0;
131
onCallStreamStarting(LinphoneCall * call,bool isPausedByRemote)132 virtual void onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) {};
onCallStreamStopping(LinphoneCall * call)133 virtual void onCallStreamStopping(LinphoneCall *call) {};
onCallTerminating(LinphoneCall * call)134 virtual void onCallTerminating(LinphoneCall *call) {};
135
getState() const136 LinphoneConferenceState getState() const {return m_state;}
getCore() const137 LinphoneCore *getCore()const{
138 return m_core;
139 }
140 static const char *stateToString(LinphoneConferenceState state);
141
142 protected:
143 void setState(LinphoneConferenceState state);
144 Participant *findParticipant(const LinphoneCall *call) const;
145 Participant *findParticipant(const LinphoneAddress *uri) const;
146
147 protected:
148 LinphoneCore *m_core;
149 AudioStream *m_localParticipantStream;
150 bool m_isMuted;
151 std::list<Participant *> m_participants;
152 Params m_currentParams;
153 LinphoneConferenceState m_state;
154 LinphoneConference *m_conference;
155 };
156
157 class LocalConference: public Conference {
158 public:
159 LocalConference(LinphoneCore *core, LinphoneConference *conf, const Params *params = NULL);
160 virtual ~LocalConference();
161
162 virtual int inviteAddresses(const std::list<const LinphoneAddress*> &addresses, const LinphoneCallParams *params);
163 virtual int addParticipant(LinphoneCall *call);
164 virtual int removeParticipant(LinphoneCall *call);
165 virtual int removeParticipant(const LinphoneAddress *uri);
166 virtual int terminate();
167
168 virtual int enter();
169 virtual int leave();
isIn() const170 virtual bool isIn() const {return m_localParticipantStream!=NULL;}
171 virtual int getSize() const;
172
173 virtual int startRecording(const char *path);
174 virtual int stopRecording();
175
176 virtual void onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote);
177 virtual void onCallStreamStopping(LinphoneCall *call);
178 virtual void onCallTerminating(LinphoneCall *call);
179
180 private:
181 void addLocalEndpoint();
182 int remoteParticipantsCount();
183 void removeLocalEndpoint();
184 int removeFromConference(LinphoneCall *call, bool_t active);
185 int convertConferenceToCall();
186 static RtpProfile *sMakeDummyProfile(int samplerate);
187
188 MSAudioConference *m_conf;
189 MSAudioEndpoint *m_localEndpoint;
190 MSAudioEndpoint *m_recordEndpoint;
191 RtpProfile *m_localDummyProfile;
192 bool_t m_terminating;
193 };
194
195 class RemoteConference: public Conference {
196 public:
197 RemoteConference(LinphoneCore *core, LinphoneConference *conf, const Params *params = NULL);
198 virtual ~RemoteConference();
199
200 virtual int inviteAddresses(const std::list<const LinphoneAddress*> &addresses, const LinphoneCallParams *params);
201 virtual int addParticipant(LinphoneCall *call);
removeParticipant(LinphoneCall * call)202 virtual int removeParticipant(LinphoneCall *call) {return -1;}
203 virtual int removeParticipant(const LinphoneAddress *uri);
204 virtual int terminate();
205
206 virtual int enter();
207 virtual int leave();
208 virtual bool isIn() const;
209
startRecording(const char * path)210 virtual int startRecording(const char *path) {return 0;}
stopRecording()211 virtual int stopRecording() {return 0;}
212
213 private:
214 bool focusIsReady() const;
215 bool transferToFocus(LinphoneCall *call);
216 void reset();
217
218 void onFocusCallSateChanged(LinphoneCallState state);
219 void onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state);
220 void onTransferingCallStateChanged(LinphoneCall *transfered, LinphoneCallState newCallState);
221
222 static void callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message);
223 static void transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state);
224
225 const char *m_focusAddr;
226 char *m_focusContact;
227 LinphoneCall *m_focusCall;
228 LinphoneCoreCbs *m_coreCbs;
229 std::list<LinphoneCall *> m_pendingCalls;
230 std::list<LinphoneCall *> m_transferingCalls;
231 };
232
233 };
234
235
236 using namespace Linphone;
237 using namespace std;
238
239
Conference(LinphoneCore * core,LinphoneConference * conf,const Conference::Params * params)240 Conference::Conference(LinphoneCore *core, LinphoneConference *conf, const Conference::Params *params):
241 m_core(core),
242 m_localParticipantStream(NULL),
243 m_isMuted(false),
244 m_currentParams(),
245 m_state(LinphoneConferenceStopped),
246 m_conference(conf) {
247 if(params) m_currentParams = *params;
248 }
249
addParticipant(LinphoneCall * call)250 int Conference::addParticipant(LinphoneCall *call) {
251 Participant *p =new Participant(call);
252 m_participants.push_back(p);
253 call->conf_ref = m_conference;
254 return 0;
255 }
256
removeParticipant(LinphoneCall * call)257 int Conference::removeParticipant(LinphoneCall *call) {
258 Participant *p = findParticipant(call);
259 if(p == NULL) return -1;
260 delete p;
261 m_participants.remove(p);
262 return 0;
263 }
264
removeParticipant(const LinphoneAddress * uri)265 int Conference::removeParticipant(const LinphoneAddress *uri) {
266 Participant *p = findParticipant(uri);
267 if(p == NULL) return -1;
268 delete p;
269 m_participants.remove(p);
270 return 0;
271 }
272
terminate()273 int Conference::terminate() {
274 for(list<Participant *>::iterator it = m_participants.begin(); it!=m_participants.end(); it++) {
275 delete *it;
276 }
277 m_participants.clear();
278 return 0;
279 }
280
muteMicrophone(bool val)281 int Conference::muteMicrophone(bool val) {
282 if (val) {
283 audio_stream_set_mic_gain(m_localParticipantStream, 0);
284 } else {
285 audio_stream_set_mic_gain_db(m_localParticipantStream, m_core->sound_conf.soft_mic_lev);
286 }
287 if ( linphone_core_get_rtp_no_xmit_on_audio_mute(m_core) ){
288 audio_stream_mute_rtp(m_localParticipantStream, val);
289 }
290 m_isMuted=val;
291 return 0;
292 }
293
getInputVolume() const294 float Conference::getInputVolume() const {
295 AudioStream *st=m_localParticipantStream;
296 if (st && st->volsend && !m_isMuted){
297 float vol=0;
298 ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
299 return vol;
300
301 }
302 return LINPHONE_VOLUME_DB_LOWEST;
303 }
304
stateToString(LinphoneConferenceState state)305 const char *Conference::stateToString(LinphoneConferenceState state) {
306 switch(state) {
307 case LinphoneConferenceStopped: return "Stopped";
308 case LinphoneConferenceStarting: return "Starting";
309 case LinphoneConferenceRunning: return "Ready";
310 case LinphoneConferenceStartingFailed: return "Startig failed";
311 default: return "Invalid state";
312 }
313 }
314
315
316
setState(LinphoneConferenceState state)317 void Conference::setState(LinphoneConferenceState state) {
318 if(m_state != state) {
319 ms_message("Switching conference [%p] into state '%s'", this, stateToString(state));
320 m_state = state;
321 if(m_currentParams.m_stateChangedCb) {
322 m_currentParams.m_stateChangedCb(m_conference, state, m_currentParams.m_userData);
323 }
324 }
325 }
326
findParticipant(const LinphoneCall * call) const327 Conference::Participant *Conference::findParticipant(const LinphoneCall *call) const {
328 for(list<Participant *>::const_iterator it=m_participants.begin(); it!=m_participants.end(); it++) {
329 if((*it)->getCall() == call) return *it;
330 }
331 return NULL;
332 }
333
findParticipant(const LinphoneAddress * uri) const334 Conference::Participant *Conference::findParticipant(const LinphoneAddress *uri) const {
335 for(list<Participant *>::const_iterator it=m_participants.begin(); it!=m_participants.end(); it++) {
336 if(linphone_address_equal((*it)->getUri(), uri)) return *it;
337 }
338 return NULL;
339 }
340
341
342
343
LocalConference(LinphoneCore * core,LinphoneConference * conf,const Conference::Params * params)344 LocalConference::LocalConference(LinphoneCore *core, LinphoneConference *conf, const Conference::Params *params):
345 Conference(core, conf, params),
346 m_conf(NULL),
347 m_localEndpoint(NULL),
348 m_recordEndpoint(NULL),
349 m_localDummyProfile(NULL),
350 m_terminating(FALSE) {
351
352 MSAudioConferenceParams ms_conf_params;
353 ms_conf_params.samplerate = lp_config_get_int(m_core->config, "sound","conference_rate",16000);
354 m_conf=ms_audio_conference_new(&ms_conf_params, core->factory);
355 m_state= LinphoneConferenceRunning;
356 }
357
~LocalConference()358 LocalConference::~LocalConference() {
359 terminate();
360 ms_audio_conference_destroy(m_conf);
361 }
362
sMakeDummyProfile(int samplerate)363 RtpProfile *LocalConference::sMakeDummyProfile(int samplerate){
364 RtpProfile *prof=rtp_profile_new("dummy");
365 PayloadType *pt=payload_type_clone(&payload_type_l16_mono);
366 pt->clock_rate=samplerate;
367 rtp_profile_set_payload(prof,0,pt);
368 return prof;
369 }
370
addLocalEndpoint()371 void LocalConference::addLocalEndpoint() {
372 /*create a dummy audiostream in order to extract the local part of it */
373 /* network address and ports have no meaning and are not used here. */
374 AudioStream *st=audio_stream_new(m_core->factory, 65000,65001,FALSE);
375 MSSndCard *playcard=m_core->sound_conf.lsd_card ?
376 m_core->sound_conf.lsd_card : m_core->sound_conf.play_sndcard;
377 MSSndCard *captcard=m_core->sound_conf.capt_sndcard;
378 const MSAudioConferenceParams *params=ms_audio_conference_get_params(m_conf);
379 m_localDummyProfile=sMakeDummyProfile(params->samplerate);
380
381 audio_stream_start_full(st, m_localDummyProfile,
382 "127.0.0.1",
383 65000,
384 "127.0.0.1",
385 65001,
386 0,
387 40,
388 NULL,
389 NULL,
390 playcard,
391 captcard,
392 linphone_core_echo_cancellation_enabled(m_core)
393 );
394 _post_configure_audio_stream(st,m_core,FALSE);
395 m_localParticipantStream=st;
396 m_localEndpoint=ms_audio_endpoint_get_from_stream(st,FALSE);
397 ms_message("conference: adding local endpoint");
398 ms_audio_conference_add_member(m_conf,m_localEndpoint);
399 }
400
inviteAddresses(const std::list<const LinphoneAddress * > & addresses,const LinphoneCallParams * params)401 int LocalConference::inviteAddresses(const std::list<const LinphoneAddress*> &addresses, const LinphoneCallParams *params){
402
403 for (std::list<const LinphoneAddress*>::const_iterator it = addresses.begin(); it != addresses.end(); ++it){
404 const LinphoneAddress *addr = *it;
405 LinphoneCall * call = linphone_core_get_call_by_remote_address2(m_core, addr);
406 if (!call){
407 /*start a new call by indicating that it has to be put into the conference directlly*/
408 LinphoneCallParams * new_params = params ? linphone_call_params_copy(params) : linphone_core_create_call_params(m_core, NULL);
409 LinphoneCall *call;
410 /*toggle this flag so the call is immediately added to the conference upon acceptance*/
411 new_params->in_conference = TRUE;
412 linphone_call_params_enable_video(new_params, FALSE); /*turn off video as it is not supported for conferencing at this time*/
413 call = linphone_core_invite_address_with_params(m_core, addr, new_params);
414 if (!call){
415 ms_error("LocalConference::inviteAddresses(): could not invite participant");
416 }
417 linphone_call_params_unref(new_params);
418 }else{
419 /*there is already a call to this address, so simply join it to the local conference if not already done*/
420 if (!call->current_params->in_conference)
421 addParticipant(call);
422 }
423 /*if the local participant is not yet created, created it and it to the conference */
424 if (!m_localEndpoint) addLocalEndpoint();
425 }
426 return 0;
427 }
428
addParticipant(LinphoneCall * call)429 int LocalConference::addParticipant(LinphoneCall *call) {
430 if (call->current_params->in_conference){
431 ms_error("Already in conference");
432 return -1;
433 }
434
435 if (call->state==LinphoneCallPaused){
436 call->params->in_conference=TRUE;
437 call->params->has_video=FALSE;
438 linphone_call_resume(call);
439 }else if (call->state==LinphoneCallStreamsRunning){
440 LinphoneCallParams *params = linphone_core_create_call_params(m_core, call);
441 params->in_conference=TRUE;
442 params->has_video=FALSE;
443
444 if (call->audiostream || call->videostream){
445 linphone_call_stop_media_streams(call); /*free the audio & video local resources*/
446 linphone_call_init_media_streams(call);
447 }
448 if (call==m_core->current_call){
449 m_core->current_call=NULL;
450 }
451 /*this will trigger a reINVITE that will later redraw the streams */
452 /*FIXME probably a bit too much to just redraw streams !*/
453 linphone_call_update(call,params);
454 linphone_call_params_unref(params);
455 addLocalEndpoint();
456 }else{
457 ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state));
458 return -1;
459 }
460 return 0;
461 }
462
removeFromConference(LinphoneCall * call,bool_t active)463 int LocalConference::removeFromConference(LinphoneCall *call, bool_t active){
464 int err=0;
465 char *str;
466
467 if (!call->current_params->in_conference){
468 if (call->params->in_conference){
469 ms_warning("Not (yet) in conference, be patient");
470 return -1;
471 }else{
472 ms_error("Not in a conference.");
473 return -1;
474 }
475 }
476 call->params->in_conference=FALSE;
477
478 str=linphone_call_get_remote_address_as_string(call);
479 ms_message("%s will be removed from conference", str);
480 ms_free(str);
481 if (active){
482 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
483 params->in_conference=FALSE;
484 // reconnect local audio with this call
485 if (isIn()){
486 ms_message("Leaving conference for reconnecting with unique call.");
487 leave();
488 }
489 ms_message("Updating call to actually remove from conference");
490 err=linphone_call_update(call,params);
491 linphone_call_params_unref(params);
492 } else{
493 ms_message("Pausing call to actually remove from conference");
494 err=_linphone_call_pause(call);
495 }
496 return err;
497 }
498
remoteParticipantsCount()499 int LocalConference::remoteParticipantsCount() {
500 int count=getSize();
501 if (count==0) return 0;
502 if (!m_localParticipantStream) return count;
503 return count -1;
504 }
505
convertConferenceToCall()506 int LocalConference::convertConferenceToCall(){
507 int err=0;
508 bctbx_list_t *calls=m_core->calls;
509
510 if (remoteParticipantsCount()!=1){
511 ms_error("No unique call remaining in conference.");
512 return -1;
513 }
514
515 while (calls) {
516 LinphoneCall *rc=(LinphoneCall*)calls->data;
517 calls=calls->next;
518 if (rc->params->in_conference) { // not using current_param
519 bool_t active_after_removed=isIn();
520 err=removeFromConference(rc, active_after_removed);
521 break;
522 }
523 }
524 return err;
525 }
526
removeParticipant(LinphoneCall * call)527 int LocalConference::removeParticipant(LinphoneCall *call) {
528 int err;
529 char * str=linphone_call_get_remote_address_as_string(call);
530 ms_message("Removing call %s from the conference", str);
531 ms_free(str);
532 err=removeFromConference(call, FALSE);
533 if (err){
534 ms_error("Error removing participant from conference.");
535 return err;
536 }
537
538 if (remoteParticipantsCount()==1){
539 ms_message("conference size is 1: need to be converted to plain call");
540 err=convertConferenceToCall();
541 } else {
542 ms_message("the conference need not to be converted as size is %i", remoteParticipantsCount());
543 }
544 return err;
545 }
546
removeParticipant(const LinphoneAddress * uri)547 int LocalConference::removeParticipant(const LinphoneAddress *uri) {
548 const Participant *participant = findParticipant(uri);
549 if(participant == NULL) return -1;
550 LinphoneCall *call = participant->getCall();
551 if(call == NULL) return -1;
552 return removeParticipant(call);
553 }
554
terminate()555 int LocalConference::terminate() {
556 bctbx_list_t *calls=m_core->calls;
557 m_terminating =TRUE;
558
559 while (calls) {
560 LinphoneCall *call=(LinphoneCall*)calls->data;
561 calls=calls->next;
562 if (call->current_params->in_conference) {
563 linphone_call_terminate(call);
564 }
565 }
566
567 Conference::terminate();
568 m_terminating = FALSE;
569
570 return 0;
571 }
572
enter()573 int LocalConference::enter() {
574 if (linphone_core_sound_resources_locked(m_core)) {
575 return -1;
576 }
577 if (m_core->current_call != NULL) {
578 _linphone_call_pause(m_core->current_call);
579 }
580 if (m_localParticipantStream==NULL) addLocalEndpoint();
581 return 0;
582 }
583
removeLocalEndpoint()584 void LocalConference::removeLocalEndpoint(){
585 if (m_localEndpoint){
586 ms_audio_conference_remove_member(m_conf,m_localEndpoint);
587 ms_audio_endpoint_release_from_stream(m_localEndpoint);
588 m_localEndpoint=NULL;
589 audio_stream_stop(m_localParticipantStream);
590 m_localParticipantStream=NULL;
591 rtp_profile_destroy(m_localDummyProfile);
592 }
593 }
594
leave()595 int LocalConference::leave() {
596 if (isIn())
597 removeLocalEndpoint();
598 return 0;
599 }
600
getSize() const601 int LocalConference::getSize() const {
602 if (m_conf == NULL) {
603 return 0;
604 }
605 return ms_audio_conference_get_size(m_conf) - (m_recordEndpoint ? 1 : 0);
606 }
607
startRecording(const char * path)608 int LocalConference::startRecording(const char *path) {
609 if (m_conf == NULL) {
610 ms_warning("linphone_core_start_conference_recording(): no conference now.");
611 return -1;
612 }
613 if (m_recordEndpoint==NULL){
614 m_recordEndpoint=ms_audio_endpoint_new_recorder(m_core->factory);
615 ms_audio_conference_add_member(m_conf,m_recordEndpoint);
616 }
617 ms_audio_recorder_endpoint_start(m_recordEndpoint,path);
618 return 0;
619 }
620
stopRecording()621 int LocalConference::stopRecording() {
622 if (m_conf == NULL) {
623 ms_warning("linphone_core_stop_conference_recording(): no conference now.");
624 return -1;
625 }
626 if (m_recordEndpoint==NULL){
627 ms_warning("linphone_core_stop_conference_recording(): no record active.");
628 return -1;
629 }
630 ms_audio_recorder_endpoint_stop(m_recordEndpoint);
631 return 0;
632 }
633
onCallStreamStarting(LinphoneCall * call,bool isPausedByRemote)634 void LocalConference::onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) {
635 call->params->has_video = FALSE;
636 call->camera_enabled = FALSE;
637 ms_message("LocalConference::onCallStreamStarting(): joining AudioStream [%p] of call [%p] into conference.", call->audiostream, call);
638 MSAudioEndpoint *ep=ms_audio_endpoint_get_from_stream(call->audiostream,TRUE);
639 ms_audio_conference_add_member(m_conf,ep);
640 ms_audio_conference_mute_member(m_conf,ep,isPausedByRemote);
641 call->endpoint=ep;
642 setState(LinphoneConferenceRunning);
643 Conference::addParticipant(call);
644 }
645
onCallStreamStopping(LinphoneCall * call)646 void LocalConference::onCallStreamStopping(LinphoneCall *call) {
647 ms_audio_conference_remove_member(m_conf,call->endpoint);
648 ms_audio_endpoint_release_from_stream(call->endpoint);
649 call->endpoint=NULL;
650 Conference::removeParticipant(call);
651 }
652
onCallTerminating(LinphoneCall * call)653 void LocalConference::onCallTerminating(LinphoneCall *call) {
654 int remote_count=remoteParticipantsCount();
655 ms_message("conference_check_uninit(): size=%i", getSize());
656 if (remote_count==1 && !m_terminating){
657 convertConferenceToCall();
658 }
659 if (remote_count==0){
660 if (m_localParticipantStream)
661 removeLocalEndpoint();
662 if (m_recordEndpoint){
663 ms_audio_conference_remove_member(m_conf, m_recordEndpoint);
664 ms_audio_endpoint_destroy(m_recordEndpoint);
665 }
666 setState(LinphoneConferenceStopped);
667 }
668 }
669
670
671
RemoteConference(LinphoneCore * core,LinphoneConference * conf,const Conference::Params * params)672 RemoteConference::RemoteConference(LinphoneCore *core, LinphoneConference *conf, const Conference::Params *params): Conference(core, conf, params) {
673 m_focusAddr = NULL;
674 m_focusContact = NULL;
675 m_focusCall = NULL;
676 m_coreCbs = NULL;
677 m_focusAddr = lp_config_get_string(m_core->config, "misc", "conference_focus_addr", "");
678 m_coreCbs = linphone_factory_create_core_cbs(linphone_factory_get());
679 linphone_core_cbs_set_call_state_changed(m_coreCbs, callStateChangedCb);
680 linphone_core_cbs_set_transfer_state_changed(m_coreCbs, transferStateChanged);
681 linphone_core_cbs_set_user_data(m_coreCbs, this);
682 _linphone_core_add_callbacks(m_core, m_coreCbs, TRUE);
683 }
684
~RemoteConference()685 RemoteConference::~RemoteConference() {
686 terminate();
687 linphone_core_remove_callbacks(m_core, m_coreCbs);
688 linphone_core_cbs_unref(m_coreCbs);
689 }
690
inviteAddresses(const std::list<const LinphoneAddress * > & addresses,const LinphoneCallParams * params)691 int RemoteConference::inviteAddresses(const std::list<const LinphoneAddress *> &addresses, const LinphoneCallParams *params){
692 ms_error("RemoteConference::inviteAddresses() not implemented");
693 return -1;
694 }
695
addParticipant(LinphoneCall * call)696 int RemoteConference::addParticipant(LinphoneCall *call) {
697 LinphoneAddress *addr;
698 LinphoneCallParams *params;
699
700 switch(m_state) {
701 case LinphoneConferenceStopped:
702 case LinphoneConferenceStartingFailed:
703 Conference::addParticipant(call);
704 ms_message("Calling the conference focus (%s)", m_focusAddr);
705 addr = linphone_address_new(m_focusAddr);
706 if(addr) {
707 params = linphone_core_create_call_params(m_core, NULL);
708 linphone_call_params_enable_video(params, m_currentParams.videoRequested());
709 m_focusCall = linphone_core_invite_address_with_params(m_core, addr, params);
710 m_localParticipantStream = m_focusCall->audiostream;
711 m_pendingCalls.push_back(call);
712 LinphoneCallLog *callLog = linphone_call_get_call_log(m_focusCall);
713 callLog->was_conference = TRUE;
714 linphone_address_unref(addr);
715 linphone_call_params_unref(params);
716 setState(LinphoneConferenceStarting);
717 return 0;
718 } else return -1;
719
720 case LinphoneConferenceStarting:
721 Conference::addParticipant(call);
722 if(focusIsReady()) {
723 transferToFocus(call);
724 } else {
725 m_pendingCalls.push_back(call);
726 }
727 return 0;
728
729 case LinphoneConferenceRunning:
730 Conference::addParticipant(call);
731 transferToFocus(call);
732 return 0;
733
734 default:
735 ms_error("Could not add call %p to the conference. Bad conference state (%s)", call, stateToString(m_state));
736 return -1;
737 }
738 }
739
removeParticipant(const LinphoneAddress * uri)740 int RemoteConference::removeParticipant(const LinphoneAddress *uri) {
741 char *refer_to;
742 LinphoneAddress *refer_to_addr;
743 int res;
744
745 switch(m_state) {
746 case LinphoneConferenceRunning:
747 if(findParticipant(uri) == NULL) {
748 char *tmp = linphone_address_as_string(uri);
749 ms_error("Conference: could not remove participant '%s': not in the participants list", tmp);
750 ms_free(tmp);
751 return -1;
752 }
753
754 refer_to_addr = linphone_address_clone(uri);
755 linphone_address_set_method_param(refer_to_addr, "BYE");
756 refer_to = linphone_address_as_string(refer_to_addr);
757 linphone_address_unref(refer_to_addr);
758 res = sal_call_refer(m_focusCall->op, refer_to);
759 ms_free(refer_to);
760
761 if(res == 0) {
762 return Conference::removeParticipant(uri);
763 } else {
764 char *tmp = linphone_address_as_string(uri);
765 ms_error("Conference: could not remove participant '%s': REFER with BYE has failed", tmp);
766 ms_free(tmp);
767 return -1;
768 }
769
770 default:
771 ms_error("Cannot remove %s from conference: Bad conference state (%s)", linphone_address_as_string(uri), stateToString(m_state));
772 return -1;
773 }
774 }
775
terminate()776 int RemoteConference::terminate() {
777 switch(m_state) {
778 case LinphoneConferenceRunning:
779 case LinphoneConferenceStarting:
780 linphone_call_terminate(m_focusCall);
781 break;
782
783 default: break;
784 }
785 return 0;
786 }
787
enter()788 int RemoteConference::enter() {
789 if(m_state != LinphoneConferenceRunning) {
790 ms_error("Could not enter in the conference: bad conference state (%s)", stateToString(m_state));
791 return -1;
792 }
793 LinphoneCallState callState = linphone_call_get_state(m_focusCall);
794 switch(callState) {
795 case LinphoneCallStreamsRunning: break;
796 case LinphoneCallPaused:
797 linphone_call_resume(m_focusCall);
798 break;
799 default:
800 ms_error("Could not join the conference: bad focus call state (%s)", linphone_call_state_to_string(callState));
801 return -1;
802 }
803 return 0;
804 }
805
leave()806 int RemoteConference::leave() {
807 if(m_state != LinphoneConferenceRunning) {
808 ms_error("Could not leave the conference: bad conference state (%s)", stateToString(m_state));
809 return -1;
810 }
811 LinphoneCallState callState = linphone_call_get_state(m_focusCall);
812 switch(callState) {
813 case LinphoneCallPaused: break;
814 case LinphoneCallStreamsRunning:
815 linphone_call_pause(m_focusCall);
816 break;
817 default:
818 ms_error("Could not leave the conference: bad focus call state (%s)", linphone_call_state_to_string(callState));
819 return -1;
820 }
821 return 0;
822 }
823
isIn() const824 bool RemoteConference::isIn() const {
825 if(m_state != LinphoneConferenceRunning) return false;
826 LinphoneCallState callState = linphone_call_get_state(m_focusCall);
827 return callState == LinphoneCallStreamsRunning;
828 }
829
focusIsReady() const830 bool RemoteConference::focusIsReady() const {
831 LinphoneCallState focusState;
832 if(m_focusCall == NULL) return false;
833 focusState = linphone_call_get_state(m_focusCall);
834 return focusState == LinphoneCallStreamsRunning || focusState == LinphoneCallPaused;
835 }
836
transferToFocus(LinphoneCall * call)837 bool RemoteConference::transferToFocus(LinphoneCall *call) {
838 if(linphone_call_transfer(call, m_focusContact) == 0) {
839 m_transferingCalls.push_back(call);
840 return true;
841 } else {
842 ms_error("Conference: could not transfer call [%p] to %s", call, m_focusContact);
843 return false;
844 }
845 }
846
reset()847 void RemoteConference::reset() {
848 m_localParticipantStream = NULL;
849 m_focusAddr = NULL;
850 if(m_focusContact) {
851 ms_free(m_focusContact);
852 m_focusContact = NULL;
853 }
854 m_focusCall = NULL;
855 m_pendingCalls.clear();
856 m_transferingCalls.clear();
857 }
858
onFocusCallSateChanged(LinphoneCallState state)859 void RemoteConference::onFocusCallSateChanged(LinphoneCallState state) {
860 list<LinphoneCall *>::iterator it;
861
862 switch (state) {
863 case LinphoneCallConnected:
864 m_focusContact = ms_strdup(linphone_call_get_remote_contact(m_focusCall));
865 it = m_pendingCalls.begin();
866 while (it != m_pendingCalls.end()) {
867 LinphoneCall *pendingCall = *it;
868 LinphoneCallState pendingCallState = linphone_call_get_state(pendingCall);
869 if(pendingCallState == LinphoneCallStreamsRunning || pendingCallState == LinphoneCallPaused) {
870 it = m_pendingCalls.erase(it);
871 transferToFocus(pendingCall);
872 } else {
873 it++;
874 }
875 }
876 setState(LinphoneConferenceRunning);
877 break;
878
879 case LinphoneCallError:
880 reset();
881 Conference::terminate();
882 setState(LinphoneConferenceStartingFailed);
883 break;
884
885 case LinphoneCallEnd:
886 reset();
887 Conference::terminate();
888 setState(LinphoneConferenceStopped);
889 break;
890
891 default: break;
892 }
893 }
894
onPendingCallStateChanged(LinphoneCall * call,LinphoneCallState state)895 void RemoteConference::onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state) {
896 switch(state) {
897 case LinphoneCallStreamsRunning:
898 case LinphoneCallPaused:
899 if(m_state == LinphoneConferenceRunning) {
900 m_pendingCalls.remove(call);
901 m_transferingCalls.push_back(call);
902 linphone_call_transfer(call, m_focusContact);
903 }
904 break;
905
906 case LinphoneCallError:
907 case LinphoneCallEnd:
908 m_pendingCalls.remove(call);
909 Conference::removeParticipant(call);
910 if(m_participants.size() + m_pendingCalls.size() + m_transferingCalls.size() == 0) {
911 terminate();
912 }
913 break;
914
915 default: break;
916 }
917 }
918
onTransferingCallStateChanged(LinphoneCall * transfered,LinphoneCallState newCallState)919 void RemoteConference::onTransferingCallStateChanged(LinphoneCall* transfered, LinphoneCallState newCallState) {
920 switch (newCallState) {
921 case LinphoneCallConnected:
922 m_transferingCalls.push_back(transfered);
923 findParticipant(transfered)->m_call = NULL;
924 break;
925
926 case LinphoneCallError:
927 m_transferingCalls.remove(transfered);
928 Conference::removeParticipant(transfered);
929 if(m_participants.size() + m_pendingCalls.size() + m_transferingCalls.size() == 0) {
930 terminate();
931 }
932 break;
933
934 default:
935 break;
936 }
937 }
938
callStateChangedCb(LinphoneCore * lc,LinphoneCall * call,LinphoneCallState cstate,const char * message)939 void RemoteConference::callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message) {
940 LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc);
941 RemoteConference *conf = (RemoteConference *)linphone_core_v_table_get_user_data(vtable);
942 if (call == conf->m_focusCall) {
943 conf->onFocusCallSateChanged(cstate);
944 } else {
945 list<LinphoneCall *>::iterator it = find(conf->m_pendingCalls.begin(), conf->m_pendingCalls.end(), call);
946 if(it != conf->m_pendingCalls.end()) {
947 conf->onPendingCallStateChanged(call, cstate);
948 }
949 }
950 }
951
transferStateChanged(LinphoneCore * lc,LinphoneCall * transfered,LinphoneCallState new_call_state)952 void RemoteConference::transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state) {
953 LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc);
954 RemoteConference *conf = (RemoteConference *)linphone_core_v_table_get_user_data(vtable);
955 list<LinphoneCall *>::iterator it = find(conf->m_transferingCalls.begin(), conf->m_transferingCalls.end(), transfered);
956 if (it != conf->m_transferingCalls.end()) {
957 conf->onTransferingCallStateChanged(transfered, new_call_state);
958 }
959 }
960
961
962
linphone_conference_state_to_string(LinphoneConferenceState state)963 const char *linphone_conference_state_to_string(LinphoneConferenceState state) {
964 return Conference::stateToString(state);
965 }
966
967
968 struct _LinphoneConferenceParams {
969 ::belle_sip_object_t base;
970 Conference::Params *params;
971 };
972
973 static void _linphone_conference_params_uninit(LinphoneConferenceParams *params);
974 static void _linphone_conference_params_clone(LinphoneConferenceParams *params, const LinphoneConferenceParams *orig);
975
976 BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneConferenceParams);
977 BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneConferenceParams);
978 BELLE_SIP_INSTANCIATE_VPTR(LinphoneConferenceParams, belle_sip_object_t,
979 _linphone_conference_params_uninit, // uninit
980 _linphone_conference_params_clone, // clone
981 NULL, // marshal
982 FALSE // unown
983 );
984
linphone_conference_params_new(const LinphoneCore * core)985 LinphoneConferenceParams *linphone_conference_params_new(const LinphoneCore *core) {
986 LinphoneConferenceParams *obj = belle_sip_object_new(LinphoneConferenceParams);
987 obj->params = new Conference::Params(core);
988 return obj;
989 }
990
_linphone_conference_params_uninit(LinphoneConferenceParams * params)991 static void _linphone_conference_params_uninit(LinphoneConferenceParams *params) {
992 delete params->params;
993 }
994
linphone_conference_params_ref(LinphoneConferenceParams * params)995 LinphoneConferenceParams *linphone_conference_params_ref(LinphoneConferenceParams *params) {
996 return (LinphoneConferenceParams *)belle_sip_object_ref(params);
997 }
998
linphone_conference_params_unref(LinphoneConferenceParams * params)999 void linphone_conference_params_unref(LinphoneConferenceParams *params) {
1000 belle_sip_object_unref(params);
1001 }
1002
linphone_conference_params_free(LinphoneConferenceParams * params)1003 void linphone_conference_params_free(LinphoneConferenceParams *params) {
1004 linphone_conference_params_unref(params);
1005 }
1006
_linphone_conference_params_clone(LinphoneConferenceParams * params,const LinphoneConferenceParams * orig)1007 static void _linphone_conference_params_clone(LinphoneConferenceParams *params, const LinphoneConferenceParams *orig) {
1008 params->params = new Conference::Params(*orig->params);
1009 }
1010
linphone_conference_params_clone(const LinphoneConferenceParams * params)1011 LinphoneConferenceParams *linphone_conference_params_clone(const LinphoneConferenceParams *params) {
1012 return (LinphoneConferenceParams *)belle_sip_object_clone((const belle_sip_object_t *)params);
1013 }
1014
linphone_conference_params_enable_video(LinphoneConferenceParams * params,bool_t enable)1015 void linphone_conference_params_enable_video(LinphoneConferenceParams *params, bool_t enable) {
1016 params->params->enableVideo(enable ? true : false);
1017 }
1018
linphone_conference_params_video_requested(const LinphoneConferenceParams * params)1019 bool_t linphone_conference_params_video_requested(const LinphoneConferenceParams *params) {
1020 return params->params->videoRequested() ? TRUE : FALSE;
1021 }
1022
linphone_conference_params_set_state_changed_callback(LinphoneConferenceParams * params,LinphoneConferenceStateChangedCb cb,void * user_data)1023 void linphone_conference_params_set_state_changed_callback(LinphoneConferenceParams *params, LinphoneConferenceStateChangedCb cb, void *user_data) {
1024 params->params->setStateChangedCallback(cb, user_data);
1025 }
1026
1027
1028 struct _LinphoneConference {
1029 belle_sip_object_t base;
1030 Conference *conf;
1031 };
1032
1033 static void _linphone_conference_uninit(LinphoneConference *conf);
1034
1035 BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneConference);
1036 BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneConference);
1037 BELLE_SIP_INSTANCIATE_VPTR(LinphoneConference, belle_sip_object_t,
1038 _linphone_conference_uninit, // uninit
1039 NULL, // clone
1040 NULL, // marshal
1041 FALSE // unown
1042 );
1043
linphone_local_conference_new(LinphoneCore * core)1044 LinphoneConference *linphone_local_conference_new(LinphoneCore *core) {
1045 LinphoneConference *conf = belle_sip_object_new(LinphoneConference);
1046 conf->conf = new LocalConference(core, conf);
1047 return conf;
1048 }
1049
linphone_local_conference_new_with_params(LinphoneCore * core,const LinphoneConferenceParams * params)1050 LinphoneConference *linphone_local_conference_new_with_params(LinphoneCore *core, const LinphoneConferenceParams *params) {
1051 LinphoneConference *conf = belle_sip_object_new(LinphoneConference);
1052 conf->conf = new LocalConference(core, conf, params->params);
1053 return conf;
1054 }
1055
linphone_remote_conference_new(LinphoneCore * core)1056 LinphoneConference *linphone_remote_conference_new(LinphoneCore *core) {
1057 LinphoneConference *conf = belle_sip_object_new(LinphoneConference);
1058 conf->conf = new RemoteConference(core, conf);
1059 return conf;
1060 }
1061
linphone_remote_conference_new_with_params(LinphoneCore * core,const LinphoneConferenceParams * params)1062 LinphoneConference *linphone_remote_conference_new_with_params(LinphoneCore *core, const LinphoneConferenceParams *params) {
1063 LinphoneConference *conf = belle_sip_object_new(LinphoneConference);
1064 conf->conf = new RemoteConference(core, conf, params->params);
1065 return conf;
1066 }
1067
_linphone_conference_uninit(LinphoneConference * conf)1068 static void _linphone_conference_uninit(LinphoneConference *conf) {
1069 delete conf->conf;
1070 }
1071
linphone_conference_ref(LinphoneConference * conf)1072 LinphoneConference *linphone_conference_ref(LinphoneConference *conf) {
1073 return (LinphoneConference *)belle_sip_object_ref(conf);
1074 }
1075
linphone_conference_unref(LinphoneConference * conf)1076 void linphone_conference_unref(LinphoneConference *conf) {
1077 belle_sip_object_unref(conf);
1078 }
1079
linphone_conference_get_state(const LinphoneConference * obj)1080 LinphoneConferenceState linphone_conference_get_state(const LinphoneConference *obj) {
1081 return obj->conf->getState();
1082 }
1083
linphone_conference_add_participant(LinphoneConference * obj,LinphoneCall * call)1084 int linphone_conference_add_participant(LinphoneConference *obj, LinphoneCall *call) {
1085 return obj->conf->addParticipant(call);
1086 }
1087
linphone_conference_remove_participant(LinphoneConference * obj,const LinphoneAddress * uri)1088 LinphoneStatus linphone_conference_remove_participant(LinphoneConference *obj, const LinphoneAddress *uri) {
1089 return obj->conf->removeParticipant(uri);
1090 }
1091
linphone_conference_remove_participant_with_call(LinphoneConference * obj,LinphoneCall * call)1092 int linphone_conference_remove_participant_with_call(LinphoneConference *obj, LinphoneCall *call) {
1093 return obj->conf->removeParticipant(call);
1094 }
1095
linphone_conference_terminate(LinphoneConference * obj)1096 int linphone_conference_terminate(LinphoneConference *obj) {
1097 return obj->conf->terminate();
1098 }
1099
linphone_conference_enter(LinphoneConference * obj)1100 int linphone_conference_enter(LinphoneConference *obj) {
1101 return obj->conf->enter();
1102 }
1103
linphone_conference_leave(LinphoneConference * obj)1104 int linphone_conference_leave(LinphoneConference *obj) {
1105 return obj->conf->leave();
1106 }
1107
linphone_conference_is_in(const LinphoneConference * obj)1108 bool_t linphone_conference_is_in(const LinphoneConference *obj) {
1109 return obj->conf->isIn();
1110 }
1111
linphone_conference_get_audio_stream(const LinphoneConference * obj)1112 AudioStream *linphone_conference_get_audio_stream(const LinphoneConference *obj) {
1113 return obj->conf->getAudioStream();
1114 }
1115
linphone_conference_mute_microphone(LinphoneConference * obj,bool_t val)1116 int linphone_conference_mute_microphone(LinphoneConference *obj, bool_t val) {
1117 return obj->conf->muteMicrophone(val ? true : false);
1118 }
1119
linphone_conference_microphone_is_muted(const LinphoneConference * obj)1120 bool_t linphone_conference_microphone_is_muted(const LinphoneConference *obj) {
1121 return obj->conf->microphoneIsMuted() ? TRUE : FALSE;
1122 }
1123
linphone_conference_get_input_volume(const LinphoneConference * obj)1124 float linphone_conference_get_input_volume(const LinphoneConference *obj) {
1125 return obj->conf->getInputVolume();
1126 }
1127
linphone_conference_get_size(const LinphoneConference * obj)1128 int linphone_conference_get_size(const LinphoneConference *obj) {
1129 return obj->conf->getSize();
1130 }
1131
linphone_conference_get_participants(const LinphoneConference * obj)1132 bctbx_list_t *linphone_conference_get_participants(const LinphoneConference *obj) {
1133 const list<Conference::Participant *> &participants = obj->conf->getParticipants();
1134 bctbx_list_t *participants_list = NULL;
1135 for(list<Conference::Participant *>::const_iterator it=participants.begin();it!=participants.end();it++) {
1136 LinphoneAddress *uri = linphone_address_clone((*it)->getUri());
1137 participants_list = bctbx_list_append(participants_list, uri);
1138 }
1139 return participants_list;
1140 }
1141
linphone_conference_start_recording(LinphoneConference * obj,const char * path)1142 int linphone_conference_start_recording(LinphoneConference *obj, const char *path) {
1143 return obj->conf->startRecording(path);
1144 }
1145
linphone_conference_stop_recording(LinphoneConference * obj)1146 int linphone_conference_stop_recording(LinphoneConference *obj) {
1147 return obj->conf->stopRecording();
1148 }
1149
linphone_conference_on_call_stream_starting(LinphoneConference * obj,LinphoneCall * call,bool_t is_paused_by_remote)1150 void linphone_conference_on_call_stream_starting(LinphoneConference *obj, LinphoneCall *call, bool_t is_paused_by_remote) {
1151 obj->conf->onCallStreamStarting(call, is_paused_by_remote ? true : false);
1152 }
1153
linphone_conference_on_call_stream_stopping(LinphoneConference * obj,LinphoneCall * call)1154 void linphone_conference_on_call_stream_stopping(LinphoneConference *obj, LinphoneCall *call) {
1155 obj->conf->onCallStreamStopping(call);
1156 }
1157
linphone_conference_on_call_terminating(LinphoneConference * obj,LinphoneCall * call)1158 void linphone_conference_on_call_terminating(LinphoneConference *obj, LinphoneCall *call) {
1159 obj->conf->onCallTerminating(call);
1160 }
1161
linphone_conference_check_class(LinphoneConference * obj,LinphoneConferenceClass _class)1162 bool_t linphone_conference_check_class(LinphoneConference *obj, LinphoneConferenceClass _class) {
1163 switch(_class) {
1164 case LinphoneConferenceClassLocal: return typeid(obj->conf) == typeid(LocalConference);
1165 case LinphoneConferenceClassRemote: return typeid(obj->conf) == typeid(RemoteConference);
1166 default: return FALSE;
1167 }
1168 }
1169
linphone_conference_invite_participants(LinphoneConference * obj,const bctbx_list_t * addresses,const LinphoneCallParams * params)1170 LinphoneStatus linphone_conference_invite_participants(LinphoneConference *obj, const bctbx_list_t *addresses, const LinphoneCallParams *params){
1171 return obj->conf->inviteAddresses(toStd<const LinphoneAddress*>(addresses), params);
1172 }
1173