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