1 /*
2 * Copyright (C) 2009 Barracuda Networks, Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19 #include "avcall.h"
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <QCoreApplication>
24 #include <QLibrary>
25 #include <QDir>
26 #include <QtCrypto>
27 #include "xmpp_jid.h"
28 #include "jinglertp.h"
29 #include "../psimedia/psimedia.h"
30 #include "applicationinfo.h"
31 #include "psiaccount.h"
32 #include "psioptions.h"
33
34 #define USE_THREAD
35
36 class Configuration
37 {
38 public:
39 bool liveInput;
40 QString audioOutDeviceId, audioInDeviceId, videoInDeviceId;
41 QString file;
42 bool loopFile;
43 PsiMedia::AudioParams audioParams;
44 PsiMedia::VideoParams videoParams;
45
46 int basePort;
47 QString extHost;
48
Configuration()49 Configuration() :
50 liveInput(false),
51 loopFile(false),
52 basePort(-1)
53 {
54 }
55 };
56
57 // get default settings
getDefaultConfiguration()58 static Configuration getDefaultConfiguration()
59 {
60 Configuration config;
61 config.liveInput = true;
62 config.loopFile = true;
63 return config;
64 }
65
66 static Configuration *g_config = 0;
67
ensureConfig()68 static void ensureConfig()
69 {
70 if(!g_config)
71 {
72 g_config = new Configuration;
73 *g_config = getDefaultConfiguration();
74 }
75 }
76
77 #ifdef GSTPROVIDER_STATIC
Q_IMPORT_PLUGIN(gstprovider)78 Q_IMPORT_PLUGIN(gstprovider)
79 #endif
80
81 #ifndef GSTPROVIDER_STATIC
82 static QString findPlugin(const QString &relpath, const QString &basename)
83 {
84 QDir dir(QCoreApplication::applicationDirPath());
85 if(!dir.cd(relpath))
86 return QString();
87 foreach(const QString &fileName, dir.entryList())
88 {
89 if(fileName.contains(basename))
90 {
91 QString filePath = dir.filePath(fileName);
92 if(QLibrary::isLibrary(filePath))
93 return filePath;
94 }
95 }
96 return QString();
97 }
98 #endif
99
100 static bool g_loaded = false;
101
ensureLoaded()102 static void ensureLoaded()
103 {
104 if(!g_loaded)
105 {
106 #ifndef GSTPROVIDER_STATIC
107 QString pluginFile;
108 QString resourcePath;
109
110 pluginFile = qgetenv("PSI_MEDIA_PLUGIN");
111 if(pluginFile.isEmpty())
112 {
113 #if defined(Q_OS_WIN)
114 pluginFile = findPlugin(".", "gstprovider" DEBUG_POSTFIX);
115 resourcePath = QCoreApplication::applicationDirPath() + "/gstreamer-0.10";
116 #elif defined(Q_OS_MAC)
117 pluginFile = findPlugin("../Plugins", "gstprovider" DEBUG_POSTFIX);
118 resourcePath = QCoreApplication::applicationDirPath() + "/../Frameworks/gstreamer-0.10";
119 #else
120 pluginFile = findPlugin(ApplicationInfo::libDir() + "/plugins", "gstprovider" DEBUG_POSTFIX);
121 #endif
122 }
123
124 PsiMedia::PluginResult r = PsiMedia::loadPlugin(pluginFile, resourcePath);
125 if(r == PsiMedia::PluginSuccess)
126 g_loaded = true;
127 #else
128 g_loaded = true;
129 #endif
130 if(g_loaded)
131 ensureConfig();
132 }
133 }
134
payloadInfoToPayloadType(const PsiMedia::PayloadInfo & pi)135 static JingleRtpPayloadType payloadInfoToPayloadType(const PsiMedia::PayloadInfo &pi)
136 {
137 JingleRtpPayloadType out;
138 out.id = pi.id();
139 out.name = pi.name();
140 out.clockrate = pi.clockrate();
141 out.channels = pi.channels();
142 out.ptime = pi.ptime();
143 out.maxptime = pi.maxptime();
144 foreach(const PsiMedia::PayloadInfo::Parameter &pip, pi.parameters())
145 {
146 JingleRtpPayloadType::Parameter ptp;
147 ptp.name = pip.name;
148 ptp.value = pip.value;
149 out.parameters += ptp;
150 }
151 return out;
152 }
153
payloadTypeToPayloadInfo(const JingleRtpPayloadType & pt)154 static PsiMedia::PayloadInfo payloadTypeToPayloadInfo(const JingleRtpPayloadType &pt)
155 {
156 PsiMedia::PayloadInfo out;
157 out.setId(pt.id);
158 out.setName(pt.name);
159 out.setClockrate(pt.clockrate);
160 out.setChannels(pt.channels);
161 out.setPtime(pt.ptime);
162 out.setMaxptime(pt.maxptime);
163 QList<PsiMedia::PayloadInfo::Parameter> list;
164 foreach(const JingleRtpPayloadType::Parameter &ptp, pt.parameters)
165 {
166 PsiMedia::PayloadInfo::Parameter pip;
167 pip.name = ptp.name;
168 pip.value = ptp.value;
169 list += pip;
170 }
171 out.setParameters(list);
172 return out;
173 }
174
175 class AvTransmit : public QObject
176 {
177 Q_OBJECT
178
179 public:
180 PsiMedia::RtpChannel *audio, *video;
181 JingleRtpChannel *transport;
182
AvTransmit(PsiMedia::RtpChannel * _audio,PsiMedia::RtpChannel * _video,JingleRtpChannel * _transport,QObject * parent=0)183 AvTransmit(PsiMedia::RtpChannel *_audio, PsiMedia::RtpChannel *_video, JingleRtpChannel *_transport, QObject *parent = 0) :
184 QObject(parent),
185 audio(_audio),
186 video(_video),
187 transport(_transport)
188 {
189 if(audio)
190 {
191 audio->setParent(this);
192 connect(audio, SIGNAL(readyRead()), SLOT(audio_readyRead()));
193 }
194
195 if(video)
196 {
197 video->setParent(this);
198 connect(video, SIGNAL(readyRead()), SLOT(video_readyRead()));
199 }
200
201 transport->setParent(this);
202 connect(transport, SIGNAL(readyRead()), SLOT(transport_readyRead()));
203 connect(transport, SIGNAL(packetsWritten(int)), SLOT(transport_packetsWritten(int)));
204 }
205
~AvTransmit()206 ~AvTransmit()
207 {
208 if(audio)
209 audio->setParent(0);
210 if(video)
211 video->setParent(0);
212 transport->setParent(0);
213 }
214
215 private slots:
audio_readyRead()216 void audio_readyRead()
217 {
218 while(audio->packetsAvailable() > 0)
219 {
220 PsiMedia::RtpPacket packet = audio->read();
221
222 JingleRtp::RtpPacket jpacket;
223 jpacket.type = JingleRtp::Audio;
224 jpacket.portOffset = packet.portOffset();
225 jpacket.value = packet.rawValue();
226
227 transport->write(jpacket);
228 }
229 }
230
video_readyRead()231 void video_readyRead()
232 {
233 while(video->packetsAvailable() > 0)
234 {
235 PsiMedia::RtpPacket packet = video->read();
236
237 JingleRtp::RtpPacket jpacket;
238 jpacket.type = JingleRtp::Video;
239 jpacket.portOffset = packet.portOffset();
240 jpacket.value = packet.rawValue();
241
242 transport->write(jpacket);
243 }
244 }
245
transport_readyRead()246 void transport_readyRead()
247 {
248 while(transport->packetsAvailable())
249 {
250 JingleRtp::RtpPacket jpacket = transport->read();
251
252 if(jpacket.type == JingleRtp::Audio)
253 audio->write(PsiMedia::RtpPacket(jpacket.value, jpacket.portOffset));
254 else if(jpacket.type == JingleRtp::Video)
255 video->write(PsiMedia::RtpPacket(jpacket.value, jpacket.portOffset));
256 }
257 }
258
transport_packetsWritten(int count)259 void transport_packetsWritten(int count)
260 {
261 Q_UNUSED(count);
262
263 // nothing
264 }
265 };
266
267 class AvTransmitHandler : public QObject
268 {
269 Q_OBJECT
270
271 public:
272 AvTransmit *avTransmit;
273 QThread *previousThread;
274
AvTransmitHandler(QObject * parent=0)275 AvTransmitHandler(QObject *parent = 0) :
276 QObject(parent),
277 avTransmit(0)
278 {
279 }
280
~AvTransmitHandler()281 ~AvTransmitHandler()
282 {
283 if(avTransmit)
284 releaseAvTransmit();
285 }
286
287 // NOTE: the handler never touches these variables except here
288 // and on destruction, so it's safe to call this function from
289 // another thread if you know what you're doing.
setAvTransmit(AvTransmit * _avTransmit)290 void setAvTransmit(AvTransmit *_avTransmit)
291 {
292 avTransmit = _avTransmit;
293 previousThread = avTransmit->thread();
294 avTransmit->moveToThread(thread());
295 }
296
releaseAvTransmit()297 void releaseAvTransmit()
298 {
299 Q_ASSERT(avTransmit);
300 avTransmit->moveToThread(previousThread);
301 avTransmit = 0;
302 }
303 };
304
305 class AvTransmitThread : public QCA::SyncThread
306 {
307 Q_OBJECT
308
309 public:
310 AvTransmitHandler *handler;
311
AvTransmitThread(QObject * parent=0)312 AvTransmitThread(QObject *parent = 0) :
313 QCA::SyncThread(parent),
314 handler(0)
315 {
316 }
317
~AvTransmitThread()318 ~AvTransmitThread()
319 {
320 stop();
321 }
322
323 protected:
atStart()324 virtual void atStart()
325 {
326 handler = new AvTransmitHandler;
327 }
328
atEnd()329 virtual void atEnd()
330 {
331 delete handler;
332 }
333 };
334
335 //----------------------------------------------------------------------------
336 // AvCall
337 //----------------------------------------------------------------------------
338 class AvCallManagerPrivate : public QObject
339 {
340 Q_OBJECT
341
342 public:
343 AvCallManager *q;
344 PsiAccount *pa;
345 JingleRtpManager *rtpManager;
346 QList<AvCall*> sessions;
347 QList<AvCall*> pending;
348
349 AvCallManagerPrivate(PsiAccount *_pa, AvCallManager *_q);
350 ~AvCallManagerPrivate();
351
352 void unlink(AvCall *call);
353
354 private slots:
355 void rtp_incomingReady();
356 };
357
358 class AvCallPrivate : public QObject
359 {
360 Q_OBJECT
361
362 public:
363 AvCall *q;
364 AvCallManagerPrivate *manager;
365 bool incoming;
366 JingleRtp *sess;
367 PsiMedia::RtpSession rtp;
368 XMPP::Jid peer;
369 AvCall::Mode mode;
370 int bitrate;
371 bool allowVideo;
372 QString errorString;
373 bool transmitAudio;
374 bool transmitVideo;
375 bool transmitting;
376 AvTransmit *avTransmit;
377 AvTransmitThread *avTransmitThread;
378
AvCallPrivate(AvCall * _q)379 AvCallPrivate(AvCall *_q) :
380 QObject(_q),
381 q(_q),
382 manager(0),
383 sess(0),
384 transmitAudio(false),
385 transmitVideo(false),
386 transmitting(false),
387 avTransmit(0),
388 avTransmitThread(0)
389 {
390 allowVideo = AvCallManager::isVideoSupported();
391
392 connect(&rtp, SIGNAL(started()), SLOT(rtp_started()));
393 connect(&rtp, SIGNAL(preferencesUpdated()), SLOT(rtp_preferencesUpdated()));
394 connect(&rtp, SIGNAL(stopped()), SLOT(rtp_stopped()));
395 connect(&rtp, SIGNAL(error()), SLOT(rtp_error()));
396 }
397
~AvCallPrivate()398 ~AvCallPrivate()
399 {
400 rtp.disconnect(this);
401 cleanup();
402 unlink();
403 }
404
unlink()405 void unlink()
406 {
407 if(manager)
408 {
409 // note that the object remains active, just
410 // dissociated from the manager
411 manager->unlink(q);
412 manager = 0;
413 }
414 }
415
startOutgoing()416 void startOutgoing()
417 {
418 if(!manager)
419 return;
420
421 manager->rtpManager->setBasePort(g_config->basePort);
422 manager->rtpManager->setExternalAddress(g_config->extHost);
423
424 start_rtp();
425 }
426
initIncoming()427 bool initIncoming()
428 {
429 setup_sess();
430
431 // JingleRtp guarantees there will be at least one of audio or video
432 bool offeredAudio = false;
433 bool offeredVideo = false;
434 if(!sess->remoteAudioPayloadTypes().isEmpty())
435 offeredAudio = true;
436 if(allowVideo && !sess->remoteVideoPayloadTypes().isEmpty())
437 offeredVideo = true;
438
439 if(offeredAudio && offeredVideo)
440 mode = AvCall::Both;
441 else if(offeredAudio)
442 mode = AvCall::Audio;
443 else if(offeredVideo)
444 mode = AvCall::Video;
445 else
446 {
447 // this could happen if only video is offered but
448 // we don't allow it
449 return false;
450 }
451
452 return true;
453 }
454
accept()455 void accept()
456 {
457 if(!manager)
458 return;
459
460 manager->rtpManager->setBasePort(g_config->basePort);
461 manager->rtpManager->setExternalAddress(g_config->extHost);
462
463 // kick off the acceptance negotiation while simultaneously
464 // initializing the rtp engine. note that session-accept
465 // won't actually get sent to the peer until we call
466 // localMediaUpdated()
467 int types;
468 if(mode == AvCall::Both)
469 types = JingleRtp::Audio | JingleRtp::Video;
470 else if(mode == AvCall::Audio)
471 types = JingleRtp::Audio;
472 else // Video
473 types = JingleRtp::Video;
474
475 sess->accept(types);
476 start_rtp();
477 }
478
reject()479 void reject()
480 {
481 if(sess)
482 sess->reject();
483 cleanup();
484 }
485
486 private:
rtpSessionErrorToString(PsiMedia::RtpSession::Error e)487 static QString rtpSessionErrorToString(PsiMedia::RtpSession::Error e)
488 {
489 QString str;
490 switch(e)
491 {
492 case PsiMedia::RtpSession::ErrorSystem:
493 str = tr("System error"); break;
494 case PsiMedia::RtpSession::ErrorCodec:
495 str = tr("Codec error"); break;
496 default: // generic
497 str = tr("Generic error"); break;
498 }
499 return str;
500 }
501
cleanup()502 void cleanup()
503 {
504 // if we had a thread, this will move the object back
505 delete avTransmitThread;
506 avTransmitThread = 0;
507
508 delete avTransmit;
509 avTransmit = 0;
510
511 rtp.reset();
512
513 delete sess;
514 sess = 0;
515 }
516
start_rtp()517 void start_rtp()
518 {
519 Configuration &config = *g_config;
520
521 transmitAudio = false;
522 transmitVideo = false;
523
524 if(config.liveInput)
525 {
526 if(config.audioInDeviceId.isEmpty() && config.videoInDeviceId.isEmpty())
527 {
528 errorString = tr("Cannot call without selecting a device. Do you have a microphone? Check the Psi options.");
529 cleanup();
530 emit q->error();
531 return;
532 }
533
534 if((mode == AvCall::Audio || mode == AvCall::Both) && !config.audioInDeviceId.isEmpty())
535 {
536 rtp.setAudioInputDevice(config.audioInDeviceId);
537 transmitAudio = true;
538 }
539 else
540 rtp.setAudioInputDevice(QString());
541
542 if((mode == AvCall::Video || mode == AvCall::Both) && !config.videoInDeviceId.isEmpty() && allowVideo)
543 {
544 rtp.setVideoInputDevice(config.videoInDeviceId);
545 transmitVideo = true;
546 }
547 else
548 rtp.setVideoInputDevice(QString());
549 }
550 else // non-live (file) input
551 {
552 rtp.setFileInput(config.file);
553 rtp.setFileLoopEnabled(config.loopFile);
554
555 // we just assume the file has both audio and video.
556 // if it doesn't, no big deal, it'll still work.
557 // update: after starting, we can correct these
558 // variables.
559 transmitAudio = true;
560 transmitVideo = true;
561 }
562
563 if(!config.audioOutDeviceId.isEmpty())
564 rtp.setAudioOutputDevice(config.audioOutDeviceId);
565
566 // media types are flagged by params, even if empty
567 QList<PsiMedia::AudioParams> audioParamsList;
568 if(transmitAudio)
569 audioParamsList += PsiMedia::AudioParams();
570 rtp.setLocalAudioPreferences(audioParamsList);
571
572 QList<PsiMedia::VideoParams> videoParamsList;
573 if(transmitVideo)
574 videoParamsList += PsiMedia::VideoParams();
575 rtp.setLocalVideoPreferences(videoParamsList);
576
577 // for incoming sessions, we have the remote media info at
578 // the start, so use it
579 if(incoming)
580 setup_remote_media();
581
582 if(bitrate != -1)
583 rtp.setMaximumSendingBitrate(bitrate);
584
585 transmitting = false;
586 rtp.start();
587 }
588
setup_sess()589 void setup_sess()
590 {
591 connect(sess, SIGNAL(rejected()), SLOT(sess_rejected()));
592 connect(sess, SIGNAL(error()), SLOT(sess_error()));
593 connect(sess, SIGNAL(activated()), SLOT(sess_activated()));
594 connect(sess, SIGNAL(remoteMediaUpdated()), SLOT(sess_remoteMediaUpdated()));
595 }
596
setup_remote_media()597 void setup_remote_media()
598 {
599 if(transmitAudio)
600 {
601 QList<JingleRtpPayloadType> payloadTypes = sess->remoteAudioPayloadTypes();
602 QList<PsiMedia::PayloadInfo> list;
603 foreach(const JingleRtpPayloadType &pt, payloadTypes)
604 list += payloadTypeToPayloadInfo(pt);
605 rtp.setRemoteAudioPreferences(list);
606 }
607
608 if(transmitVideo)
609 {
610 QList<JingleRtpPayloadType> payloadTypes = sess->remoteVideoPayloadTypes();
611 QList<PsiMedia::PayloadInfo> list;
612 foreach(const JingleRtpPayloadType &pt, payloadTypes)
613 list += payloadTypeToPayloadInfo(pt);
614 rtp.setRemoteVideoPreferences(list);
615 }
616
617 // FIXME: if the remote side doesn't support a media type,
618 // then we need to downgrade locally
619 }
620
621 private slots:
rtp_started()622 void rtp_started()
623 {
624 if(!manager)
625 return;
626
627 printf("rtp_started\n");
628
629 if(!incoming)
630 {
631 sess = manager->rtpManager->createOutgoing();
632 setup_sess();
633 }
634
635 if(transmitAudio && !rtp.localAudioPayloadInfo().isEmpty())
636 {
637 QList<JingleRtpPayloadType> pis;
638 foreach(PsiMedia::PayloadInfo pi, rtp.localAudioPayloadInfo())
639 {
640 JingleRtpPayloadType pt = payloadInfoToPayloadType(pi);
641 pis << pt;
642 }
643 sess->setLocalAudioPayloadTypes(pis);
644 }
645 else
646 transmitAudio = false;
647
648 if(transmitVideo && !rtp.localVideoPayloadInfo().isEmpty())
649 {
650 QList<JingleRtpPayloadType> pis;
651 foreach(PsiMedia::PayloadInfo pi, rtp.localVideoPayloadInfo())
652 {
653 JingleRtpPayloadType pt = payloadInfoToPayloadType(pi);
654 pis << pt;
655 }
656 sess->setLocalVideoPayloadTypes(pis);
657 }
658 else
659 transmitVideo = false;
660
661 if(transmitAudio && transmitVideo)
662 mode = AvCall::Both;
663 else if(transmitAudio && !transmitVideo)
664 mode = AvCall::Audio;
665 else if(transmitVideo && !transmitAudio)
666 mode = AvCall::Video;
667 else
668 {
669 // can't happen?
670 Q_ASSERT(0);
671 }
672
673 if(!incoming)
674 sess->connectToJid(peer);
675 else
676 sess->localMediaUpdate();
677 }
678
rtp_preferencesUpdated()679 void rtp_preferencesUpdated()
680 {
681 // nothing?
682 }
683
rtp_stopped()684 void rtp_stopped()
685 {
686 // nothing for now, until we do async shutdown
687 }
688
rtp_error()689 void rtp_error()
690 {
691 errorString = tr("An error occurred while trying to send:\n%1.").arg(rtpSessionErrorToString(rtp.errorCode()));
692 reject();
693 emit q->error();
694 }
695
sess_rejected()696 void sess_rejected()
697 {
698 errorString = tr("Call was rejected or terminated.");
699 cleanup();
700 emit q->error();
701 }
702
sess_error()703 void sess_error()
704 {
705 JingleRtp::Error e = sess->errorCode();
706 if(e == JingleRtp::ErrorTimeout)
707 {
708 errorString = tr("Call negotiation timed out.");
709 cleanup();
710 }
711 else if(e == JingleRtp::ErrorICE)
712 {
713 errorString = tr("Unable to establish peer-to-peer connection.");
714 reject();
715 }
716 else
717 {
718 errorString = tr("Call negotiation failed.");
719 cleanup();
720 }
721
722 emit q->error();
723 }
724
sess_activated()725 void sess_activated()
726 {
727 PsiMedia::RtpChannel *audio = 0;
728 PsiMedia::RtpChannel *video = 0;
729
730 if(transmitAudio)
731 audio = rtp.audioRtpChannel();
732 if(transmitVideo)
733 video = rtp.videoRtpChannel();
734
735 avTransmit = new AvTransmit(audio, video, sess->rtpChannel());
736 #ifdef USE_THREAD
737 avTransmitThread = new AvTransmitThread(this);
738 avTransmitThread->start();
739 avTransmitThread->handler->setAvTransmit(avTransmit);
740 #endif
741
742 if(transmitAudio)
743 rtp.transmitAudio();
744 if(transmitVideo)
745 rtp.transmitVideo();
746
747 transmitting = true;
748 emit q->activated();
749 }
750
sess_remoteMediaUpdated()751 void sess_remoteMediaUpdated()
752 {
753 setup_remote_media();
754 rtp.updatePreferences();
755 }
756 };
757
AvCall()758 AvCall::AvCall()
759 {
760 d = new AvCallPrivate(this);
761 }
762
AvCall(const AvCall & from)763 AvCall::AvCall(const AvCall &from) :
764 QObject(0)
765 {
766 Q_UNUSED(from);
767 fprintf(stderr, "AvCall copy not supported\n");
768 abort();
769 }
770
~AvCall()771 AvCall::~AvCall()
772 {
773 delete d;
774 }
775
jid() const776 XMPP::Jid AvCall::jid() const
777 {
778 if(d->sess)
779 return d->sess->jid();
780 else
781 return XMPP::Jid();
782 }
783
mode() const784 AvCall::Mode AvCall::mode() const
785 {
786 return d->mode;
787 }
788
connectToJid(const XMPP::Jid & jid,Mode mode,int kbps)789 void AvCall::connectToJid(const XMPP::Jid &jid, Mode mode, int kbps)
790 {
791 d->peer = jid;
792 d->mode = mode;
793 d->bitrate = kbps;
794 d->startOutgoing();
795 }
796
accept(Mode mode,int kbps)797 void AvCall::accept(Mode mode, int kbps)
798 {
799 d->mode = mode;
800 d->bitrate = kbps;
801 d->accept();
802 }
803
reject()804 void AvCall::reject()
805 {
806 d->reject();
807 }
808
setIncomingVideo(PsiMedia::VideoWidget * widget)809 void AvCall::setIncomingVideo(PsiMedia::VideoWidget *widget)
810 {
811 d->rtp.setVideoOutputWidget(widget);
812 }
813
errorString() const814 QString AvCall::errorString() const
815 {
816 return d->errorString;
817 }
818
unlink()819 void AvCall::unlink()
820 {
821 d->unlink();
822 }
823
824 //----------------------------------------------------------------------------
825 // AvCallManager
826 //----------------------------------------------------------------------------
AvCallManagerPrivate(PsiAccount * _pa,AvCallManager * _q)827 AvCallManagerPrivate::AvCallManagerPrivate(PsiAccount *_pa, AvCallManager *_q) :
828 QObject(_q),
829 q(_q),
830 pa(_pa)
831 {
832 rtpManager = new JingleRtpManager(pa->client());
833 connect(rtpManager, SIGNAL(incomingReady()), SLOT(rtp_incomingReady()));
834 }
835
~AvCallManagerPrivate()836 AvCallManagerPrivate::~AvCallManagerPrivate()
837 {
838 delete rtpManager;
839 }
840
unlink(AvCall * call)841 void AvCallManagerPrivate::unlink(AvCall *call)
842 {
843 sessions.removeAll(call);
844 }
845
rtp_incomingReady()846 void AvCallManagerPrivate::rtp_incomingReady()
847 {
848 AvCall *call = new AvCall;
849 call->d->manager = this;
850 call->d->incoming = true;
851 call->d->sess = rtpManager->takeIncoming();
852 sessions += call;
853 if(!call->d->initIncoming())
854 {
855 call->d->sess->reject();
856 delete call->d->sess;
857 call->d->sess = 0;
858 delete call;
859 return;
860 }
861
862 pending += call;
863 emit q->incomingReady();
864 }
865
AvCallManager(PsiAccount * pa)866 AvCallManager::AvCallManager(PsiAccount *pa) :
867 QObject(0)
868 {
869 d = new AvCallManagerPrivate(pa, this);
870 }
871
~AvCallManager()872 AvCallManager::~AvCallManager()
873 {
874 delete d;
875 }
876
createOutgoing()877 AvCall *AvCallManager::createOutgoing()
878 {
879 AvCall *call = new AvCall;
880 call->d->manager = d;
881 call->d->incoming = false;
882 return call;
883 }
884
takeIncoming()885 AvCall *AvCallManager::takeIncoming()
886 {
887 return d->pending.takeFirst();
888 }
889
config()890 void AvCallManager::config()
891 {
892 // TODO: remove this function?
893 }
894
isSupported()895 bool AvCallManager::isSupported()
896 {
897 ensureLoaded();
898 if(!QCA::isSupported("hmac(sha1)"))
899 {
900 printf("hmac support missing for voice calls, install qca-ossl\n");
901 return false;
902 }
903 return PsiMedia::isSupported();
904 }
905
isVideoSupported()906 bool AvCallManager::isVideoSupported()
907 {
908 if(!isSupported())
909 return false;
910
911 if(PsiOptions::instance()->getOption("options.media.video-support").toBool())
912 return true;
913
914 if(!QString::fromLatin1(qgetenv("PSI_ENABLE_VIDEO")).isEmpty())
915 return true;
916 else
917 return false;
918 }
919
setSelfAddress(const QHostAddress & addr)920 void AvCallManager::setSelfAddress(const QHostAddress &addr)
921 {
922 d->rtpManager->setSelfAddress(addr);
923 }
924
setStunBindService(const QString & host,int port)925 void AvCallManager::setStunBindService(const QString &host, int port)
926 {
927 d->rtpManager->setStunBindService(host, port);
928 }
929
setStunRelayUdpService(const QString & host,int port,const QString & user,const QString & pass)930 void AvCallManager::setStunRelayUdpService(const QString &host, int port, const QString &user, const QString &pass)
931 {
932 d->rtpManager->setStunRelayUdpService(host, port, user, pass);
933 }
934
setStunRelayTcpService(const QString & host,int port,const XMPP::AdvancedConnector::Proxy & proxy,const QString & user,const QString & pass)935 void AvCallManager::setStunRelayTcpService(const QString &host, int port, const XMPP::AdvancedConnector::Proxy &proxy, const QString &user, const QString &pass)
936 {
937 d->rtpManager->setStunRelayTcpService(host, port, proxy, user, pass);
938 }
939
setBasePort(int port)940 void AvCallManager::setBasePort(int port)
941 {
942 if(port == 0)
943 port = -1;
944 g_config->basePort = port;
945 }
946
setExternalAddress(const QString & host)947 void AvCallManager::setExternalAddress(const QString &host)
948 {
949 g_config->extHost = host;
950 }
951
setAudioOutDevice(const QString & id)952 void AvCallManager::setAudioOutDevice(const QString &id)
953 {
954 g_config->audioOutDeviceId = id;
955 }
956
setAudioInDevice(const QString & id)957 void AvCallManager::setAudioInDevice(const QString &id)
958 {
959 g_config->audioInDeviceId = id;
960 }
961
setVideoInDevice(const QString & id)962 void AvCallManager::setVideoInDevice(const QString &id)
963 {
964 g_config->videoInDeviceId = id;
965 }
966
967 #include "avcall.moc"
968